From 4517d558ae6a1887ea7cfad6e28e414a43abbebb Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Fri, 25 Apr 2014 11:40:11 +0100 Subject: [PATCH 1/1] Add to git. --- .cvsignore | 1 + COPYING.LIB | 481 +++++++++++++ INSTALL | 32 + Makefile+ | 604 ++++++++++++++++ README | 3 + README.FreeBSD | 5 + README.Solaris | 16 + README.openbsd | 29 + conf/test_setcontext.c | 53 ++ conf/test_setcontext.sh | 24 + configure | 66 ++ doc/.cvsignore | 0 doc/cgi_erase.3.html | 132 ++++ doc/cgi_escape.3.html | 87 +++ doc/cgi_get_post_max.3.html | 88 +++ doc/cgi_param.3.html | 132 ++++ doc/cgi_param_list.3.html | 132 ++++ doc/cgi_params.3.html | 132 ++++ doc/cgi_set_post_max.3.html | 88 +++ doc/cgi_unescape.3.html | 87 +++ doc/copy_cgi.3.html | 132 ++++ doc/eg_echo.c | 60 ++ doc/ftpc_ascii.3.html | 100 +++ doc/ftpc_binary.3.html | 100 +++ doc/ftpc_cdup.3.html | 93 +++ doc/ftpc_cwd.3.html | 93 +++ doc/ftpc_delete.3.html | 84 +++ doc/ftpc_dir.3.html | 103 +++ doc/ftpc_get.3.html | 94 +++ doc/ftpc_login.3.html | 90 +++ doc/ftpc_ls.3.html | 103 +++ doc/ftpc_mkdir.3.html | 86 +++ doc/ftpc_put.3.html | 94 +++ doc/ftpc_pwd.3.html | 84 +++ doc/ftpc_quit.3.html | 86 +++ doc/ftpc_quote.3.html | 85 +++ doc/ftpc_rmdir.3.html | 86 +++ doc/ftpc_set_passive_mode.3.html | 93 +++ doc/ftpc_type.3.html | 100 +++ doc/http_get_log_file.3.html | 137 ++++ doc/http_get_servername.3.html | 92 +++ doc/http_request_get_header.3.html | 165 +++++ doc/http_request_get_headers.3.html | 165 +++++ doc/http_request_is_HEAD.3.html | 165 +++++ doc/http_request_method.3.html | 165 +++++ doc/http_request_method_string.3.html | 165 +++++ doc/http_request_nr_headers.3.html | 165 +++++ doc/http_request_path.3.html | 165 +++++ doc/http_request_query_string.3.html | 165 +++++ doc/http_request_time.3.html | 165 +++++ doc/http_request_url.3.html | 165 +++++ doc/http_request_version.3.html | 165 +++++ doc/http_response_end_headers.3.html | 122 ++++ doc/http_response_send_header.3.html | 122 ++++ doc/http_response_send_headers.3.html | 122 ++++ doc/http_set_log_file.3.html | 137 ++++ doc/http_set_servername.3.html | 92 +++ doc/index.html | 1088 +++++++++++++++++++++++++++++ doc/io_copy.3.html | 172 +++++ doc/io_fclose.3.html | 172 +++++ doc/io_fdopen.3.html | 172 +++++ doc/io_fflush.3.html | 172 +++++ doc/io_fgetc.3.html | 172 +++++ doc/io_fgets.3.html | 172 +++++ doc/io_fileno.3.html | 172 +++++ doc/io_fprintf.3.html | 172 +++++ doc/io_fputc.3.html | 172 +++++ doc/io_fputs.3.html | 172 +++++ doc/io_fread.3.html | 172 +++++ doc/io_fwrite.3.html | 172 +++++ doc/io_get_inbufcount.3.html | 172 +++++ doc/io_get_outbufcount.3.html | 172 +++++ doc/io_pclose.3.html | 172 +++++ doc/io_popen.3.html | 172 +++++ doc/io_setbufmode.3.html | 172 +++++ doc/io_ungetc.3.html | 172 +++++ doc/mutex_enter.3.html | 128 ++++ doc/mutex_leave.3.html | 128 ++++ doc/mutex_try_enter.3.html | 128 ++++ doc/new_cgi.3.html | 132 ++++ doc/new_ftpc.3.html | 92 +++ doc/new_http_request.3.html | 165 +++++ doc/new_http_response.3.html | 122 ++++ doc/new_mutex.3.html | 128 ++++ doc/new_pseudothread.3.html | 110 +++ doc/new_rwlock.3.html | 184 +++++ doc/new_wait_queue.3.html | 217 ++++++ doc/pseudothread_count_threads.3.html | 110 +++ doc/pseudothread_get_stack_size.3.html | 91 +++ doc/pseudothread_get_threads.3.html | 110 +++ doc/pseudothread_set_stack_size.3.html | 91 +++ doc/pth_accept.3.html | 112 +++ doc/pth_catch.3.html | 120 ++++ doc/pth_connect.3.html | 112 +++ doc/pth_die.3.html | 120 ++++ doc/pth_exit.3.html | 120 ++++ doc/pth_get_PC.3.html | 188 +++++ doc/pth_get_SP.3.html | 188 +++++ doc/pth_get_data.3.html | 188 +++++ doc/pth_get_language.3.html | 188 +++++ doc/pth_get_name.3.html | 188 +++++ doc/pth_get_pool.3.html | 188 +++++ doc/pth_get_run.3.html | 188 +++++ doc/pth_get_stack.3.html | 188 +++++ doc/pth_get_stack_size.3.html | 188 +++++ doc/pth_get_thread_num.3.html | 188 +++++ doc/pth_get_tz.3.html | 188 +++++ doc/pth_millisleep.3.html | 112 +++ doc/pth_nanosleep.3.html | 112 +++ doc/pth_poll.3.html | 105 +++ doc/pth_read.3.html | 112 +++ doc/pth_recv.3.html | 91 +++ doc/pth_recvfrom.3.html | 91 +++ doc/pth_recvmsg.3.html | 91 +++ doc/pth_select.3.html | 105 +++ doc/pth_send.3.html | 91 +++ doc/pth_sendmsg.3.html | 91 +++ doc/pth_sendto.3.html | 91 +++ doc/pth_set_language.3.html | 188 +++++ doc/pth_set_name.3.html | 188 +++++ doc/pth_set_tz.3.html | 188 +++++ doc/pth_sleep.3.html | 112 +++ doc/pth_start.3.html | 110 +++ doc/pth_timeout.3.html | 112 +++ doc/pth_write.3.html | 112 +++ doc/pthr_server_chroot.3.html | 171 +++++ doc/pthr_server_default_port.3.html | 171 +++++ doc/pthr_server_disable_chdir.3.html | 171 +++++ doc/pthr_server_disable_close.3.html | 171 +++++ doc/pthr_server_disable_fork.3.html | 171 +++++ doc/pthr_server_disable_syslog.3.html | 171 +++++ doc/pthr_server_main.3.html | 125 ++++ doc/pthr_server_main_loop.3.html | 171 +++++ doc/pthr_server_package_name.3.html | 171 +++++ doc/pthr_server_port_option_name.3.html | 171 +++++ doc/pthr_server_startup_fn.3.html | 171 +++++ doc/pthr_server_stderr_file.3.html | 171 +++++ doc/pthr_server_username.3.html | 171 +++++ doc/rwlock_enter_read.3.html | 184 +++++ doc/rwlock_enter_write.3.html | 184 +++++ doc/rwlock_leave.3.html | 184 +++++ doc/rwlock_readers_have_priority.3.html | 184 +++++ doc/rwlock_try_enter_read.3.html | 184 +++++ doc/rwlock_try_enter_write.3.html | 184 +++++ doc/rwlock_writers_have_priority.3.html | 184 +++++ doc/wq_nr_sleepers.3.html | 217 ++++++ doc/wq_sleep_on.3.html | 217 ++++++ doc/wq_wake_up.3.html | 217 ++++++ doc/wq_wake_up_one.3.html | 217 ++++++ examples/.cvsignore | 0 examples/README | 53 ++ examples/pthr_eg1_echo.c | 144 ++++ examples/pthr_eg1_echo.h | 33 + examples/pthr_eg1_echo_main.c | 44 ++ examples/pthr_eg2_server.c | 350 ++++++++++ examples/pthr_eg2_server.h | 48 ++ examples/pthr_eg2_server_main.c | 82 +++ src/.cvsignore | 0 src/pthr_cgi.c | 350 ++++++++++ src/pthr_cgi.h | 103 +++ src/pthr_context.c | 160 +++++ src/pthr_context.h | 75 ++ src/pthr_dbi.c | 1145 +++++++++++++++++++++++++++++++ src/pthr_dbi.h | 268 ++++++++ src/pthr_ftpc.c | 724 +++++++++++++++++++ src/pthr_ftpc.h | 213 ++++++ src/pthr_http.c | 702 +++++++++++++++++++ src/pthr_http.h | 244 +++++++ src/pthr_iolib.c | 569 +++++++++++++++ src/pthr_iolib.h | 137 ++++ src/pthr_listener.c | 104 +++ src/pthr_listener.h | 31 + src/pthr_mutex.c | 167 +++++ src/pthr_mutex.h | 74 ++ src/pthr_pseudothread.c | 1024 +++++++++++++++++++++++++++ src/pthr_pseudothread.h | 320 +++++++++ src/pthr_reactor.c | 556 +++++++++++++++ src/pthr_reactor.h | 68 ++ src/pthr_rwlock.c | 306 +++++++++ src/pthr_rwlock.h | 103 +++ src/pthr_server.c | 389 +++++++++++ src/pthr_server.h | 123 ++++ src/pthr_stack.c | 119 ++++ src/pthr_stack.h | 26 + src/pthr_version.h.in | 4 + src/pthr_wait_queue.c | 203 ++++++ src/pthr_wait_queue.h | 124 ++++ src/test_bigstack.c | 62 ++ src/test_context.c | 99 +++ src/test_dbi.c | 316 +++++++++ src/test_except1.c | 67 ++ src/test_except2.c | 54 ++ src/test_except3.c | 76 ++ src/test_mutex.c | 122 ++++ src/test_pseudothread.c | 130 ++++ src/test_reactor.c | 97 +++ src/test_rwlock.c | 152 ++++ src/test_select.c | 148 ++++ 198 files changed, 32312 insertions(+) create mode 100644 .cvsignore create mode 100644 COPYING.LIB create mode 100644 INSTALL create mode 100644 Makefile+ create mode 100644 README create mode 100644 README.FreeBSD create mode 100644 README.Solaris create mode 100644 README.openbsd create mode 100644 conf/test_setcontext.c create mode 100755 conf/test_setcontext.sh create mode 100755 configure create mode 100644 doc/.cvsignore create mode 100644 doc/cgi_erase.3.html create mode 100644 doc/cgi_escape.3.html create mode 100644 doc/cgi_get_post_max.3.html create mode 100644 doc/cgi_param.3.html create mode 100644 doc/cgi_param_list.3.html create mode 100644 doc/cgi_params.3.html create mode 100644 doc/cgi_set_post_max.3.html create mode 100644 doc/cgi_unescape.3.html create mode 100644 doc/copy_cgi.3.html create mode 100644 doc/eg_echo.c create mode 100644 doc/ftpc_ascii.3.html create mode 100644 doc/ftpc_binary.3.html create mode 100644 doc/ftpc_cdup.3.html create mode 100644 doc/ftpc_cwd.3.html create mode 100644 doc/ftpc_delete.3.html create mode 100644 doc/ftpc_dir.3.html create mode 100644 doc/ftpc_get.3.html create mode 100644 doc/ftpc_login.3.html create mode 100644 doc/ftpc_ls.3.html create mode 100644 doc/ftpc_mkdir.3.html create mode 100644 doc/ftpc_put.3.html create mode 100644 doc/ftpc_pwd.3.html create mode 100644 doc/ftpc_quit.3.html create mode 100644 doc/ftpc_quote.3.html create mode 100644 doc/ftpc_rmdir.3.html create mode 100644 doc/ftpc_set_passive_mode.3.html create mode 100644 doc/ftpc_type.3.html create mode 100644 doc/http_get_log_file.3.html create mode 100644 doc/http_get_servername.3.html create mode 100644 doc/http_request_get_header.3.html create mode 100644 doc/http_request_get_headers.3.html create mode 100644 doc/http_request_is_HEAD.3.html create mode 100644 doc/http_request_method.3.html create mode 100644 doc/http_request_method_string.3.html create mode 100644 doc/http_request_nr_headers.3.html create mode 100644 doc/http_request_path.3.html create mode 100644 doc/http_request_query_string.3.html create mode 100644 doc/http_request_time.3.html create mode 100644 doc/http_request_url.3.html create mode 100644 doc/http_request_version.3.html create mode 100644 doc/http_response_end_headers.3.html create mode 100644 doc/http_response_send_header.3.html create mode 100644 doc/http_response_send_headers.3.html create mode 100644 doc/http_set_log_file.3.html create mode 100644 doc/http_set_servername.3.html create mode 100644 doc/index.html create mode 100644 doc/io_copy.3.html create mode 100644 doc/io_fclose.3.html create mode 100644 doc/io_fdopen.3.html create mode 100644 doc/io_fflush.3.html create mode 100644 doc/io_fgetc.3.html create mode 100644 doc/io_fgets.3.html create mode 100644 doc/io_fileno.3.html create mode 100644 doc/io_fprintf.3.html create mode 100644 doc/io_fputc.3.html create mode 100644 doc/io_fputs.3.html create mode 100644 doc/io_fread.3.html create mode 100644 doc/io_fwrite.3.html create mode 100644 doc/io_get_inbufcount.3.html create mode 100644 doc/io_get_outbufcount.3.html create mode 100644 doc/io_pclose.3.html create mode 100644 doc/io_popen.3.html create mode 100644 doc/io_setbufmode.3.html create mode 100644 doc/io_ungetc.3.html create mode 100644 doc/mutex_enter.3.html create mode 100644 doc/mutex_leave.3.html create mode 100644 doc/mutex_try_enter.3.html create mode 100644 doc/new_cgi.3.html create mode 100644 doc/new_ftpc.3.html create mode 100644 doc/new_http_request.3.html create mode 100644 doc/new_http_response.3.html create mode 100644 doc/new_mutex.3.html create mode 100644 doc/new_pseudothread.3.html create mode 100644 doc/new_rwlock.3.html create mode 100644 doc/new_wait_queue.3.html create mode 100644 doc/pseudothread_count_threads.3.html create mode 100644 doc/pseudothread_get_stack_size.3.html create mode 100644 doc/pseudothread_get_threads.3.html create mode 100644 doc/pseudothread_set_stack_size.3.html create mode 100644 doc/pth_accept.3.html create mode 100644 doc/pth_catch.3.html create mode 100644 doc/pth_connect.3.html create mode 100644 doc/pth_die.3.html create mode 100644 doc/pth_exit.3.html create mode 100644 doc/pth_get_PC.3.html create mode 100644 doc/pth_get_SP.3.html create mode 100644 doc/pth_get_data.3.html create mode 100644 doc/pth_get_language.3.html create mode 100644 doc/pth_get_name.3.html create mode 100644 doc/pth_get_pool.3.html create mode 100644 doc/pth_get_run.3.html create mode 100644 doc/pth_get_stack.3.html create mode 100644 doc/pth_get_stack_size.3.html create mode 100644 doc/pth_get_thread_num.3.html create mode 100644 doc/pth_get_tz.3.html create mode 100644 doc/pth_millisleep.3.html create mode 100644 doc/pth_nanosleep.3.html create mode 100644 doc/pth_poll.3.html create mode 100644 doc/pth_read.3.html create mode 100644 doc/pth_recv.3.html create mode 100644 doc/pth_recvfrom.3.html create mode 100644 doc/pth_recvmsg.3.html create mode 100644 doc/pth_select.3.html create mode 100644 doc/pth_send.3.html create mode 100644 doc/pth_sendmsg.3.html create mode 100644 doc/pth_sendto.3.html create mode 100644 doc/pth_set_language.3.html create mode 100644 doc/pth_set_name.3.html create mode 100644 doc/pth_set_tz.3.html create mode 100644 doc/pth_sleep.3.html create mode 100644 doc/pth_start.3.html create mode 100644 doc/pth_timeout.3.html create mode 100644 doc/pth_write.3.html create mode 100644 doc/pthr_server_chroot.3.html create mode 100644 doc/pthr_server_default_port.3.html create mode 100644 doc/pthr_server_disable_chdir.3.html create mode 100644 doc/pthr_server_disable_close.3.html create mode 100644 doc/pthr_server_disable_fork.3.html create mode 100644 doc/pthr_server_disable_syslog.3.html create mode 100644 doc/pthr_server_main.3.html create mode 100644 doc/pthr_server_main_loop.3.html create mode 100644 doc/pthr_server_package_name.3.html create mode 100644 doc/pthr_server_port_option_name.3.html create mode 100644 doc/pthr_server_startup_fn.3.html create mode 100644 doc/pthr_server_stderr_file.3.html create mode 100644 doc/pthr_server_username.3.html create mode 100644 doc/rwlock_enter_read.3.html create mode 100644 doc/rwlock_enter_write.3.html create mode 100644 doc/rwlock_leave.3.html create mode 100644 doc/rwlock_readers_have_priority.3.html create mode 100644 doc/rwlock_try_enter_read.3.html create mode 100644 doc/rwlock_try_enter_write.3.html create mode 100644 doc/rwlock_writers_have_priority.3.html create mode 100644 doc/wq_nr_sleepers.3.html create mode 100644 doc/wq_sleep_on.3.html create mode 100644 doc/wq_wake_up.3.html create mode 100644 doc/wq_wake_up_one.3.html create mode 100644 examples/.cvsignore create mode 100644 examples/README create mode 100644 examples/pthr_eg1_echo.c create mode 100644 examples/pthr_eg1_echo.h create mode 100644 examples/pthr_eg1_echo_main.c create mode 100644 examples/pthr_eg2_server.c create mode 100644 examples/pthr_eg2_server.h create mode 100644 examples/pthr_eg2_server_main.c create mode 100644 src/.cvsignore create mode 100644 src/pthr_cgi.c create mode 100644 src/pthr_cgi.h create mode 100644 src/pthr_context.c create mode 100644 src/pthr_context.h create mode 100644 src/pthr_dbi.c create mode 100644 src/pthr_dbi.h create mode 100644 src/pthr_ftpc.c create mode 100644 src/pthr_ftpc.h create mode 100644 src/pthr_http.c create mode 100644 src/pthr_http.h create mode 100644 src/pthr_iolib.c create mode 100644 src/pthr_iolib.h create mode 100644 src/pthr_listener.c create mode 100644 src/pthr_listener.h create mode 100644 src/pthr_mutex.c create mode 100644 src/pthr_mutex.h create mode 100644 src/pthr_pseudothread.c create mode 100644 src/pthr_pseudothread.h create mode 100644 src/pthr_reactor.c create mode 100644 src/pthr_reactor.h create mode 100644 src/pthr_rwlock.c create mode 100644 src/pthr_rwlock.h create mode 100644 src/pthr_server.c create mode 100644 src/pthr_server.h create mode 100644 src/pthr_stack.c create mode 100644 src/pthr_stack.h create mode 100644 src/pthr_version.h.in create mode 100644 src/pthr_wait_queue.c create mode 100644 src/pthr_wait_queue.h create mode 100644 src/test_bigstack.c create mode 100644 src/test_context.c create mode 100644 src/test_dbi.c create mode 100644 src/test_except1.c create mode 100644 src/test_except2.c create mode 100644 src/test_except3.c create mode 100644 src/test_mutex.c create mode 100644 src/test_pseudothread.c create mode 100644 src/test_reactor.c create mode 100644 src/test_rwlock.c create mode 100644 src/test_select.c diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..1964f99 --- /dev/null +++ b/.cvsignore @@ -0,0 +1 @@ +build-* \ No newline at end of file diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 0000000..eb685a5 --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..2f2ecca --- /dev/null +++ b/INSTALL @@ -0,0 +1,32 @@ +Required packages +----------------- + +Before installing pthrlib, you need to make sure you have the following +packages installed: + +postgresql libpq From your distro or from http://www.postgresql.org/ +c2lib http://www.annexia.org/freeware/c2lib/ +cdoc http://www.annexia.org/freeware/cdoc/ +make+ http://www.annexia.org/freeware/makeplus/ +pcre http://www.pcre.org/ + +Building and installing +----------------------- + +./configure [--prefix=/usr --sysconfdir=/etc] +make+ +make+ test +make+ install + +See the make+ documentation for additional targets (eg. building RPMs). + +Problems +-------- + +If the build fails, please examine the following files for clues: + +build-*/config.h +build-*/config.mk +build-*/config.log + +If you discover a bug, please contact the package author. diff --git a/Makefile+ b/Makefile+ new file mode 100644 index 0000000..29b1474 --- /dev/null +++ b/Makefile+ @@ -0,0 +1,604 @@ +# -*- Makefile -*- +# +# This is a make+ file. Make+ is a set of scripts which enhance GNU +# make and let you build RPMs, and other package types with just one +# control file. To build this package you will need to download make+ +# from this site: http://www.annexia.org/freeware/makeplus/ + +PACKAGE := pthrlib +VERSION_MAJOR := 3 +VERSION_MINOR := 3.1 +VERSION := $(VERSION_MAJOR).$(VERSION_MINOR) + +SUMMARY := small, fast, efficient server library for C +COPYRIGHT := GNU LGPL +AUTHOR := Richard W.M. Jones + +define DESCRIPTION +pthrlib is a library for writing small, fast and efficient servers in +C. It offers a list of advanced features. This library has been used +to write a very tiny and fast web server called rws and a closed +source chat server. All functions are documented in manual pages, and +example servers are included. +endef + +RPM_REQUIRES := c2lib >= 1.4.0, postgresql-devel >= 7.1 +RPM_GROUP := Development/Libraries + +CFLAGS += -Wall -Werror -g -O2 -I$(includedir)/c2lib +ifeq (1,$(HAVE_PG_CONFIG)) +CFLAGS += -I$(shell pg_config --includedir) +endif +ifneq ($(shell uname), SunOS) +# Avoid a warning about reordering system include paths. +CFLAGS += $(shell pcre-config --cflags) +endif + +ifeq (1,$(HAVE_PG_CONFIG)) +LIBS += -L$(shell pg_config --libdir) -lpq +endif +LIBS += $(shell pcre-config --libs) -lm + +OBJS := src/pthr_cgi.o src/pthr_context.o src/pthr_dbi.o \ + src/pthr_ftpc.o src/pthr_http.o src/pthr_iolib.o \ + src/pthr_listener.o src/pthr_mutex.o \ + src/pthr_pseudothread.o src/pthr_reactor.o \ + src/pthr_rwlock.o src/pthr_server.o src/pthr_stack.o \ + src/pthr_wait_queue.o +LOBJS := $(OBJS:.o=.lo) + +HEADERS := $(srcdir)/src/pthr_cgi.h $(srcdir)/src/pthr_context.h \ + $(srcdir)/src/pthr_dbi.h $(srcdir)/src/pthr_ftpc.h \ + $(srcdir)/src/pthr_http.h $(srcdir)/src/pthr_iolib.h \ + $(srcdir)/src/pthr_listener.h $(srcdir)/src/pthr_mutex.h \ + $(srcdir)/src/pthr_pseudothread.h \ + $(srcdir)/src/pthr_reactor.h \ + $(srcdir)/src/pthr_rwlock.h $(srcdir)/src/pthr_server.h \ + $(srcdir)/src/pthr_stack.h $(srcdir)/src/pthr_wait_queue.h + +all: build + +configure: + @pg_config --version || \ + ( echo "PostgreSQL must be installed - make sure PATH" \ + "contains pg bin directory"; exit 1 ) + $(MP_CONFIGURE_START) + $(MP_REQUIRE_LIB) pmap c2lib + $(MP_CHECK_PROG) pg_config + $(MP_CHECK_LIB) PQconnectStart pq + $(MP_CHECK_HEADERS) alloca.h arpa/inet.h assert.h \ + ctype.h dirent.h errno.h \ + execinfo.h fcntl.h grp.h libpq-fe.h netdb.h \ + netinet/in.h netinet/ip.h netinet/ip_icmp.h postgresql/libpq-fe.h \ + pwd.h setjmp.h signal.h string.h syslog.h sys/mman.h sys/poll.h \ + sys/socket.h sys/stat.h sys/syslimits.h sys/time.h sys/types.h \ + sys/uio.h sys/wait.h \ + time.h ucontext.h unistd.h + $(MP_CHECK_FUNCS) backtrace getenv gettimeofday gmtime putenv setenv \ + socket strftime syslog time unsetenv PQescapeString + $(srcdir)/conf/test_setcontext.sh + $(MP_CONFIGURE_END) + +build: static dynamic examples/pthr_eg1_echo examples/pthr_eg2_server \ + manpages syms + +# Build the static library. + +static: src/libpthrlib.a + +src/libpthrlib.a: $(OBJS) + $(MP_LINK_STATIC) $@ $^ + +# Build the dynamic library. + +dynamic: src/libpthrlib.so + +src/libpthrlib.so: $(LOBJS) + $(MP_LINK_DYNAMIC) $@ $^ $(LIBS) + +# Build object files. +src/%.o: src/%.c + $(CC) $(CFLAGS) -I../src -c $< -o $@ + +# Build dynamic object files. +src/%.lo: src/%.c + $(CC) $(CFLAGS) -fPIC -I../src -c $< -o $@ + +# Build the example programs. + +examples/pthr_eg1_echo: examples/pthr_eg1_echo.o examples/pthr_eg1_echo_main.o + $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS) + +examples/pthr_eg2_server: examples/pthr_eg2_server.o \ + examples/pthr_eg2_server_main.o + $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS) + +# Build object files. +examples/%.o: examples/%.c + $(CC) $(CFLAGS) -I../src -c $< -o $@ + +# Build the manual pages. + +manpages: $(srcdir)/src/*.h + if cdoc; then \ + rm -f *.3; \ + cdoc \ + --author '$(AUTHOR)' \ + --license '$(COPYRIGHT)' \ + --version '$(PACKAGE)-$(VERSION)' \ + $^; \ + fi + +# Build the symbols table. + +syms: src/libpthrlib.syms + +src/libpthrlib.syms: src/libpthrlib.so + nm $< | sort | grep -i '^[0-9a-f]' | awk '{print $$1 " " $$3}' > $@ + +test: src/test_context src/test_reactor src/test_pseudothread src/test_select \ + src/test_bigstack src/test_except1 src/test_except2 src/test_except3 \ + src/test_mutex src/test_rwlock src/test_dbi + LD_LIBRARY_PATH=src:$(LD_LIBRARY_PATH) $(MP_RUN_TESTS) $^ + +src/test_context: src/test_context.o + $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS) +src/test_reactor: src/test_reactor.o + $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS) +src/test_pseudothread: src/test_pseudothread.o + $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS) +src/test_select: src/test_select.o + $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS) +src/test_bigstack: src/test_bigstack.o + $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS) +src/test_except1: src/test_except1.o + $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS) +src/test_except2: src/test_except2.o + $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS) +src/test_except3: src/test_except3.o + $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS) +src/test_mutex: src/test_mutex.o + $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS) +src/test_rwlock: src/test_rwlock.o + $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS) +src/test_dbi: src/test_dbi.o + $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS) + +install: + install -d $(DESTDIR)$(libdir) + install -d $(DESTDIR)$(includedir) + install -d $(DESTDIR)$(man3dir) + install -d $(DESTDIR)$(datadir)/rws/symtabs/ + install -d $(DESTDIR)$(bindir) + + $(MP_INSTALL_STATIC_LIB) src/libpthrlib.a + $(MP_INSTALL_DYNAMIC_LIB) src/libpthrlib.so + + install -m 0644 $(HEADERS) $(DESTDIR)$(includedir) + install -m 0644 *.3 $(DESTDIR)$(man3dir) + install -m 0644 src/*.syms $(DESTDIR)$(datadir)/rws/symtabs/ + install -m 0755 examples/pthr_eg1_echo examples/pthr_eg2_server \ + $(DESTDIR)$(bindir) + +define WEBSITE +<% include page_header.msp %> + +

$(PACKAGE) - $(SUMMARY)

+ +

+ pthrlib is a library for writing small, fast and efficient + servers in C. It offers a list of advanced features (see below). + This library has been used to write a very tiny and fast + web server called rws and + a closed source chat server. All functions are documented in + manual pages, and example servers are included. +

+ +

+ It contains the following features: +

+ +
    +
  • reactor & pseudothreads: underpinning + the whole library is a lightweight cooperative threading + library written on top of a Reactor pattern. Typically + you can create of the order of thousands of threads + (the number of threads is generally limited by other + things like how many file descriptors your C library + supports -- assuming each thread is handling one client + over one socket). Pseudothreads support thread + listings and throw/catch-style exception handling. +
  • iolib: a buffered I/O library written + on top of the pseudothread "syscalls". +
  • http: a library for writing HTTP/1.1 + RFC-compliant servers. +
  • cgi: a library for writing CGI scripts + in C which run inside the server [new in pthrlib 2.0.1]. +
  • dbi: a PostgreSQL database interface, + modelled on Perl's DBI. Includes connection pooling + [new in pthrlib 3.0.8]. +
  • wait_queue: synchronize between threads + using wait queues (several threads go to sleep on + the wait queue until some point later when another + thread wakes them all up). +
  • mutex, rwlock: simple mutual exclusion + locks and multiple-reader/single-writer locks. +
  • listener: a thread class which listens + for connections on a given port and throws off + threads to process them. +
  • ftpc: an FTP client library. +
+ +

+ There is extensive documentation and + a tutorial here. +

+ +

Download

+ + + + + + + + + + + + + + + + + + + + + + +
File Format Contents
$(PACKAGE)-$(VERSION).tar.gz tar.gz Latest source distribution
$(PACKAGE)-$(VERSION)-1.i686.rpm i686 binary RPM Binary development libraries, header files, man pages + for Red Hat Linux
$(PACKAGE)-$(VERSION)-1.src.rpm source RPM Source files for Red Hat Linux
+ +

+ This library requires c2lib. + To rebuild the manual pages which come with the + package, you will also need to download the + cdoc program. Since + 3.2.0, pthrlib requires the make+ + build system. +

+ +

+ Patches between versions + ... +

+ +

News

+ +

+Sat Feb 1 2003: +Updated README and INSTALL files. Updated +Makefile+ to work with the new +version of make+. Ported to FreeBSD 5.0. +Added connection pooling to pthr_dbi library. +NB: The RPMs were built on Debian and may not work on Red Hat +Linux.

+ +

+Sat Feb 8 17:00:47 GMT 2003: +Ported to Solaris, OpenBSD and FreeBSD (thanks to +Jeremy Sowden +and Richard Baker +for help and equipment). Updated to work with gcc 3.2 (multi-line +strings are now deprecated). Fixed for RH 7.3. +

+ +

+ Sun Dec 8 13:44:32 GMT 2002: + Major API change: current_pth contains + the current thread. A lot of functions which took + pth as a parameter now no longer need + these parameter. Fixed io_popen to + call _exit instead of exit + so that the registered atexit functions + aren't called incorrectly in the child process. + Fixed pth_wait_writable, pth_wait_readable. + Updated to use make+. + Enabled debugging and optimisations. + Fixes to compile on RH 7.3. +

+ +

+ Mon Nov 25 09:31:37 GMT 2002: + Added symbols file for full symbol resolution in monolith. + Catch segfaults and dump a stack trace. + Added mutex_nr_sleepers function. +

+ +

+ Sun Nov 17 23:31:32 GMT 2002: Debian packages. Added MSP + files. Added patches to enabled chunked encoding (thanks to + Steve Atkins). Fixes to compile on RH 7.3. Support for + DBI_TIMESTAMP, DBI_INTERVAL, + DBI_INT_OR_NULL, DBI_CHAR, + DBI_BOOL in the + DBI library. Better handling of NULLs in the DBI library. + Debugging for DBI. +

+ +

+ Thu Nov 14 15:33:29 GMT 2002: Major checkpoint release + for Monolith. +

+ +

+ Sun Oct 20 14:46:46 BST 2002: Support for cookies. + Get last serial number from an INSERT. + DBI now correctly handles nulls in execute statements. + Fixed elusive bugs in pth_wait_readable and + pth_wait_writable which were causing monolith + to crash. Optimisation to wait queues which also removes + a crashing bug in monolith. +

+ +

+ Tue Oct 15 23:40:42 BST 2002: Multiple bug fixes. +

+ +

+ Sun Oct 13 18:47:41 BST 2002: Patch to + disable test_dbi code if no DBI configured. + Added pthr_server_default_address + and pthr_server_address_option which + allows the server to listen on a single interface + instead of on INADDR_ANY. + (Both patches thanks to Steve Atkins - steve at blighty com). +

+ +

+ Sun Oct 13 12:55:08 BST 2002: Added a + complete PostgreSQL database interface library, + similar to the Perl DBI. See src/test_dbi.c + for example usage. This requires + c2lib >= 1.2.21. +

+ +

+ Wed Oct 9 19:10:38 BST 2002: Added + http_request_set_url method which + is used by rws to update + the URL during internal rewrites. +

+ +

+ Sat Sep 7 15:38:33 BST 2002: Packages are now + available as i686 binary RPMs and source RPMs. +

+ +

Old news and old versions

+ +

+ Sat Aug 31 14:53:52 BST 2002 +

+ +

+ pthrlib-3.0.4.tar.gz + released. This contains a complete tutorial and some + documentation fixes. +

+ +

+ Fri Aug 23 15:00:51 BST 2002 +

+ +

+ pthrlib-3.0.3.tar.gz + released. This adds the copy_cgi and + cgi_erase functions required by + monolith. +

+ +

+ Thu Aug 22 13:20:32 BST 2002 +

+ +

+ pthrlib-3.0.2.tar.gz + released. + This includes manual pages which were accidentally omitted + from the previous version. +

+ +

+ Wed Aug 21 14:20:12 BST 2002 +

+ +

+ pthrlib-3.0.1.tar.gz + fixes a few silly bugs which stopped the new version + of rws from working. +

+ +

+ Wed Aug 21 11:59:17 BST 2002 +

+ +

+ pthrlib-3.0.0.tar.gz + released. This version replaces the old + pthr_server_main function with a newer, simpler + pthr_server_main_loop function. The + pthr_listener thread code has been modified + slightly to allow multiple listeners to run at the same time + (necessary for Fleet). This breaks a lot of old code, hence the + major version number increase. All the included examples + have been changed to support the new interface. +

+ +

+ Fri Nov 16 10:43:00 GMT 2001 +

+ +

+ pthrlib-2.2.1.tar.gz released. + A few bug fixes for glibc 2.2.4. You will need to use this + version if you are using Red Hat Linux 7.x. +

+ +

+ Mon Jul 9 07:43:07 BST 2001 +

+ +

+ pthrlib-2.2.0.tar.gz released. + Added an FTP client library. +

+ +

+ Fri Jun 15 15:46:10 BST 2001 +

+ +

+ pthrlib-2.1.6.tar.gz released. + Stack support improved: stack overflows cause core dumps + rather than random memory corruption; stacks are + stored in memory mapped regions so they don't take memory + unless you actually use them; stack size is configurable + at run time. HTTP library uses + exceptions for error messages. + Support for profiling. Added a second example webserver. + Tests for exception handling code. Added API call to + allow you to count the number of threads running. + Documentation updates. +

+ + Wed May 30 13:01:46 BST 2001 +

+ +

+ pthrlib-2.1.0.tar.gz released. + This has experimental support for throw/catch-style exception + handling. Please see the pth_die(3) and + pth_catch(3) manual pages for full details. +

+ +

+ Tue May 22 14:11:21 BST 2001 +

+ +

+ pthrlib-2.0.5.tar.gz released. + Backed out previous URL-escaping patch. The fix goes better + into rws instead. +

+ +

+ Tue May 22 11:37:10 BST 2001 +

+ +

+ pthrlib-2.0.4.tar.gz released. + URLs are now unescaped properly, allowing clients to correctly + request URLs like '/%7Euser/'. +

+ +

+ Tue Apr 10 16:04:41 BST 2001 +

+ +

+ pthrlib-2.0.2.tar.gz released. + Added support for generating logfiles. +

+ +

+ Mon Apr 9 17:23:59 BST 2001 +

+ +

+ pthrlib-2.0.1.tar.gz released. + The http library component in version 1.x has + been split more logically to support only the mechanics + of parsing HTTP connections. The CGI part of it (eg. + query string parsing, etc.) has been moved into a + new library called cgi. This necessarily breaks + older programs, hence the major version number + increase. There are some other minor changes and + bug fixes: the server name string can now be + changed; CGI.pm-like POST_MAX can be set; fixed + a really nasty bug in iolib -- it wasn't + setting the output buffering mode to the correct + default; fixed io_popen(3) so it can + handle the case where fds 0 and/or 1 are already + closed; added io_copy(3) function to + copy bytes between two file descriptors; + pthr_server_main now includes sys/types.h + correctly. +

+ +

+ Mon Mar 26 13:05:42 BST 2001 +

+ +

+ pthrlib-1.6.6.tar.gz released. + Add -lm when linking with c2lib libraries. + Documentation fixes. +

+ +

+ Mon Mar 12 12:12:49 GMT 2001 +

+ +

+ pthrlib-1.6.5.tar.gz released. + This fixes a bug in the way the library was calling + pstrcat (in c2lib). + Upgrading is highly recommended. You will also need + to upgrade to the latest c2lib. +

+ + +

+ Fri Mar 9 17:32:13 GMT 2001 +

+ +

+ pthrlib-1.6.4.tar.gz released. + Unit test suite added which covers much of the functionality. + Fixed a nasty bug in io_popen. Fixed another nasty + bug in the pseudothread code. Fixed a false assertion in + the rwlock code (revealed during unit testing). +

+ +

+ Fri Feb 23 16:37:58 GMT 2001 +

+ +

+ pthrlib-1.6.2.tar.gz released. + pth_timeout function added and a nasty race condition + in the reactor (which affected wait queues) fixed. +

+ +

+ Fri Feb 16 18:02:46 GMT 2001 +

+ +

+ pthrlib-1.6.1.tar.gz released. + All functions are documented in manual pages. +

+ +<% include page_footer.msp %> +endef + +upload_website: + scp $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE)-$(VERSION)-1.*.rpm \ + $(PACKAGE)-$(VERSION).bin.tar.gz \ + 10.0.0.248:annexia.org/freeware/$(PACKAGE)/ + scp index.html \ + 10.0.0.248:annexia.org/freeware/$(PACKAGE)/index.msp + +.PHONY: build configure test upload_website diff --git a/README b/README new file mode 100644 index 0000000..34bacb1 --- /dev/null +++ b/README @@ -0,0 +1,3 @@ +This is pthrlib from http://www.annexia.org/freeware/pthrlib/ + +pthrlib is a library for writing small, fast and efficient servers in C. \ No newline at end of file diff --git a/README.FreeBSD b/README.FreeBSD new file mode 100644 index 0000000..e99f7ae --- /dev/null +++ b/README.FreeBSD @@ -0,0 +1,5 @@ +pthrlib has been ported to FreeBSD 5.0 on 2003/02/25. + +Currently postgresql7 and pcre are required - install them as necessary +from the FreeBSD ports or packages collection. We hope to remove the +requirement to have postgresql libraries at a later date. diff --git a/README.Solaris b/README.Solaris new file mode 100644 index 0000000..c03edbe --- /dev/null +++ b/README.Solaris @@ -0,0 +1,16 @@ +You need to install c2lib, postgresql (and development libraries) and pcre. + +PostgreSQL is normally installed in /usr/local/pgsql, so you need to +add the right directories to your $CFLAGS and $LIBS, eg: + +CFLAGS='-I/usr/local/pgsql/include' export CFLAGS +LIBS='-L/usr/local/pgsql/lib' export LIBS + +If in doubt, check build-*/config.h, and make sure it contains the +following line after the configure stage: + +#define HAVE_LIBPQ_FE_H 1 + +Solaris supports the setcontext/makecontext family of functions, and +pthrlib should be able to detect this. Check that HAVE_WORKING_SETCONTEXT +is defined in build-*/config.h. \ No newline at end of file diff --git a/README.openbsd b/README.openbsd new file mode 100644 index 0000000..58a23a7 --- /dev/null +++ b/README.openbsd @@ -0,0 +1,29 @@ +Notes on the OpenBSD port +------------------------- + +You need the gcc toolchain, GNU make, postgresql, pcre installed. + +OpenBSD has a serious bug in the order constructors in shared libraries +are called. It calls them in, essentially, a random order, instead of +in dependency-order. See the following newsgroup article: + +http://groups.google.com/groups?selm=b049u2%24227u%241%40FreeBSD.csie.NCTU.edu.tw&oe=UTF-8&output=gplain + +I have added some grotesque hacks to work around this problem. + +For pthr_context.c, I found the contents of jmp_buf by examination +in gdb. jmp_buf is an array of 10 longs with the following layout: + + offset contents + 0 PC (return address) + 1 ebx + 2 SP (stack pointer) + 3 ebp + 4 esi + 5 edi + 6 ? + 7 ? + 8 ? + 9 ? + +Rich 2003/01/26 diff --git a/conf/test_setcontext.c b/conf/test_setcontext.c new file mode 100644 index 0000000..b3d0a41 --- /dev/null +++ b/conf/test_setcontext.c @@ -0,0 +1,53 @@ +/* Test for a working setcontext implementation. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: test_setcontext.c,v 1.1 2003/02/02 18:05:30 rich Exp $ + */ + +#include +#include + +#include + +#define MAGIC 0x14151617 + +static void fn (int); + +int +main () +{ + ucontext_t ucp; + + if (getcontext (&ucp) == -1) + exit (1); + + makecontext (&ucp, fn, 1, MAGIC); + + setcontext (&ucp); + exit (1); +} + +static void +fn (int magic) +{ + if (magic != MAGIC) + exit (1); + + /* Working implementation. */ + printf ("ok\n"); + exit (0); +} diff --git a/conf/test_setcontext.sh b/conf/test_setcontext.sh new file mode 100755 index 0000000..e209772 --- /dev/null +++ b/conf/test_setcontext.sh @@ -0,0 +1,24 @@ +# Test for a working setcontext implementation. +# +# Copyright (C) 2003 Richard W.M. Jones +# +# Older versions of Linux had the setcontext/makecontext functions +# in glibc, but the functions were null. Duh! So the only way to +# determine if these functions are implemented and actually work +# is to try them out. + +result=no + +if $CC $CFLAGS \ + $srcdir/conf/test_setcontext.c -o conf/test_setcontext 2>/dev/null +then + if conf/test_setcontext | grep 'ok' >/dev/null 2>&1; then + result=yes + fi +fi + +if [ "x$result" = "xyes" ]; then + echo "#define HAVE_WORKING_SETCONTEXT 1" >> config.h +else + echo "/* #define HAVE_WORKING_SETCONTEXT 1 */" >> config.h +fi diff --git a/configure b/configure new file mode 100755 index 0000000..04d9db0 --- /dev/null +++ b/configure @@ -0,0 +1,66 @@ +#!/bin/sh - +# +# This is make+. Make+ is a set of scripts which enhance GNU make and +# let you build RPMs, and other packages types with just one control +# file. Read more at http://www.annexia.org/freeware/makeplus/ +# +# The original author is Richard W.M. Jones . +# +# This software has been explicitly placed in the PUBLIC DOMAIN. You +# do not need any sort of license or agreement to use or copy this +# software. You may also copyright this software yourself, and/or +# relicense it under any terms you want, at any time and at no cost. +# This allows you (among other things) to include this software with +# other packages so that the user does not need to download and +# install make+ separately. + +mp_options="" + +usage () +{ + cat < + + + +new_cgi + + + +

new_cgi

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_cgi, cgi_params, cgi_param, cgi_param_list, cgi_erase, copy_cgi - Library for parsing CGI query strings.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_cgi.h>
+
+cgi new_cgi (pool, http_request, io_handle);
+vector cgi_params (cgi);
+const char *cgi_param (cgi, const char *name);
+const vector cgi_param_list (cgi, const char *name);
+int cgi_erase (cgi, const char *name);
+cgi copy_cgi (pool, cgi);
+
+ +

DESCRIPTION

+ + + +
+new_cgi creates a new CGI object from an existing +HTTP request. It reads the query string or POSTed parameters +and parses them internally. CGI parameters are case +sensitive, and multiple parameters may be passed with the +same name. Parameter values are automatically unescaped by +the library before you get to see them.
+ + + +
+cgi_params returns a list of all the names of the +parameters passed to the script. The list is returned as a +vector of char *.
+ + + +
+cgi_param returns the value of a single named CGI +parameter, or NULL if there is no such parameter. If +multiple parameters were given with the same name, this +returns one of them, essentially at random.
+ + + +
+cgi_param_list returns the list of values of the +named CGI parameter. The list is returned as a vector +of char *.
+ + + +
+cgi_erase erases the named parameter. If a parameter +was erased, this returns true, else this returns +false.
+ + + +
+copy_cgi copies cgi into pool +pool.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+cgi_get_post_max(3), cgi_escape(3), +new_http_request(3).
+
+ + diff --git a/doc/cgi_escape.3.html b/doc/cgi_escape.3.html new file mode 100644 index 0000000..3449bcc --- /dev/null +++ b/doc/cgi_escape.3.html @@ -0,0 +1,87 @@ + + + + +cgi_escape + + + +

cgi_escape

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+cgi_escape, cgi_unescape - %-escape and unescape CGI arguments.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_cgi.h>
+
+char *cgi_escape (pool, const char *str);
+char *cgi_unescape (pool, const char *str);
+
+ +

DESCRIPTION

+ + + +
+These functions do %-escaping and unescaping on CGI +arguments. When %-escaping a string, " " is +replaced by "+", and non-printable +characters are replaced by "%hh" where +hh is a two-digit hex code. Unescaping a string +reverses this process.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_cgi(3), new_http_request(3).
+
+ + diff --git a/doc/cgi_get_post_max.3.html b/doc/cgi_get_post_max.3.html new file mode 100644 index 0000000..2f2ee9f --- /dev/null +++ b/doc/cgi_get_post_max.3.html @@ -0,0 +1,88 @@ + + + + +cgi_get_post_max + + + +

cgi_get_post_max

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+cgi_get_post_max, cgi_set_post_max - Get and set the internal POST_MAX parameter.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_cgi.h>
+
+int cgi_get_post_max (void);
+int cgi_set_post_max (int new_post_max);
+
+ +

DESCRIPTION

+ + + +
+These functions get and set the internal POST_MAX parameter +which can be used to limit the size of POST method +requests which this library will handle. If set to a +non-negative integer, then POST requests will be limited to +the number of bytes given. The default is -1 (unlimited) +which can leave the server open to denial of service +attacks.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_cgi(3).
+
+ + diff --git a/doc/cgi_param.3.html b/doc/cgi_param.3.html new file mode 100644 index 0000000..c9e2b22 --- /dev/null +++ b/doc/cgi_param.3.html @@ -0,0 +1,132 @@ + + + + +new_cgi + + + +

new_cgi

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_cgi, cgi_params, cgi_param, cgi_param_list, cgi_erase, copy_cgi - Library for parsing CGI query strings.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_cgi.h>
+
+cgi new_cgi (pool, http_request, io_handle);
+vector cgi_params (cgi);
+const char *cgi_param (cgi, const char *name);
+const vector cgi_param_list (cgi, const char *name);
+int cgi_erase (cgi, const char *name);
+cgi copy_cgi (pool, cgi);
+
+ +

DESCRIPTION

+ + + +
+new_cgi creates a new CGI object from an existing +HTTP request. It reads the query string or POSTed parameters +and parses them internally. CGI parameters are case +sensitive, and multiple parameters may be passed with the +same name. Parameter values are automatically unescaped by +the library before you get to see them.
+ + + +
+cgi_params returns a list of all the names of the +parameters passed to the script. The list is returned as a +vector of char *.
+ + + +
+cgi_param returns the value of a single named CGI +parameter, or NULL if there is no such parameter. If +multiple parameters were given with the same name, this +returns one of them, essentially at random.
+ + + +
+cgi_param_list returns the list of values of the +named CGI parameter. The list is returned as a vector +of char *.
+ + + +
+cgi_erase erases the named parameter. If a parameter +was erased, this returns true, else this returns +false.
+ + + +
+copy_cgi copies cgi into pool +pool.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+cgi_get_post_max(3), cgi_escape(3), +new_http_request(3).
+
+ + diff --git a/doc/cgi_param_list.3.html b/doc/cgi_param_list.3.html new file mode 100644 index 0000000..c9e2b22 --- /dev/null +++ b/doc/cgi_param_list.3.html @@ -0,0 +1,132 @@ + + + + +new_cgi + + + +

new_cgi

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_cgi, cgi_params, cgi_param, cgi_param_list, cgi_erase, copy_cgi - Library for parsing CGI query strings.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_cgi.h>
+
+cgi new_cgi (pool, http_request, io_handle);
+vector cgi_params (cgi);
+const char *cgi_param (cgi, const char *name);
+const vector cgi_param_list (cgi, const char *name);
+int cgi_erase (cgi, const char *name);
+cgi copy_cgi (pool, cgi);
+
+ +

DESCRIPTION

+ + + +
+new_cgi creates a new CGI object from an existing +HTTP request. It reads the query string or POSTed parameters +and parses them internally. CGI parameters are case +sensitive, and multiple parameters may be passed with the +same name. Parameter values are automatically unescaped by +the library before you get to see them.
+ + + +
+cgi_params returns a list of all the names of the +parameters passed to the script. The list is returned as a +vector of char *.
+ + + +
+cgi_param returns the value of a single named CGI +parameter, or NULL if there is no such parameter. If +multiple parameters were given with the same name, this +returns one of them, essentially at random.
+ + + +
+cgi_param_list returns the list of values of the +named CGI parameter. The list is returned as a vector +of char *.
+ + + +
+cgi_erase erases the named parameter. If a parameter +was erased, this returns true, else this returns +false.
+ + + +
+copy_cgi copies cgi into pool +pool.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+cgi_get_post_max(3), cgi_escape(3), +new_http_request(3).
+
+ + diff --git a/doc/cgi_params.3.html b/doc/cgi_params.3.html new file mode 100644 index 0000000..c3faebc --- /dev/null +++ b/doc/cgi_params.3.html @@ -0,0 +1,132 @@ + + + + +new_cgi + + + +

new_cgi

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_cgi, cgi_params, cgi_param, cgi_param_list, cgi_erase, copy_cgi - Library for parsing CGI query strings.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_cgi.h>
+
+cgi new_cgi (pool, http_request, io_handle);
+vector cgi_params (cgi);
+const char *cgi_param (cgi, const char *name);
+const vector cgi_param_list (cgi, const char *name);
+int cgi_erase (cgi, const char *name);
+cgi copy_cgi (pool, cgi);
+
+ +

DESCRIPTION

+ + + +
+new_cgi creates a new CGI object from an existing +HTTP request. It reads the query string or POSTed parameters +and parses them internally. CGI parameters are case +sensitive, and multiple parameters may be passed with the +same name. Parameter values are automatically unescaped by +the library before you get to see them.
+ + + +
+cgi_params returns a list of all the names of the +parameters passed to the script. The list is returned as a +vector of char *.
+ + + +
+cgi_param returns the value of a single named CGI +parameter, or NULL if there is no such parameter. If +multiple parameters were given with the same name, this +returns one of them, essentially at random.
+ + + +
+cgi_param_list returns the list of values of the +named CGI parameter. The list is returned as a vector +of char *.
+ + + +
+cgi_erase erases the named parameter. If a parameter +was erased, this returns true, else this returns +false.
+ + + +
+copy_cgi copies cgi into pool +pool.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+cgi_get_post_max(3), cgi_escape(3), +new_http_request(3).
+
+ + diff --git a/doc/cgi_set_post_max.3.html b/doc/cgi_set_post_max.3.html new file mode 100644 index 0000000..db97fc0 --- /dev/null +++ b/doc/cgi_set_post_max.3.html @@ -0,0 +1,88 @@ + + + + +cgi_get_post_max + + + +

cgi_get_post_max

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+cgi_get_post_max, cgi_set_post_max - Get and set the internal POST_MAX parameter.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_cgi.h>
+
+int cgi_get_post_max (void);
+int cgi_set_post_max (int new_post_max);
+
+ +

DESCRIPTION

+ + + +
+These functions get and set the internal POST_MAX parameter +which can be used to limit the size of POST method +requests which this library will handle. If set to a +non-negative integer, then POST requests will be limited to +the number of bytes given. The default is -1 (unlimited) +which can leave the server open to denial of service +attacks.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_cgi(3).
+
+ + diff --git a/doc/cgi_unescape.3.html b/doc/cgi_unescape.3.html new file mode 100644 index 0000000..25bc5e4 --- /dev/null +++ b/doc/cgi_unescape.3.html @@ -0,0 +1,87 @@ + + + + +cgi_escape + + + +

cgi_escape

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+cgi_escape, cgi_unescape - %-escape and unescape CGI arguments.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_cgi.h>
+
+char *cgi_escape (pool, const char *str);
+char *cgi_unescape (pool, const char *str);
+
+ +

DESCRIPTION

+ + + +
+These functions do %-escaping and unescaping on CGI +arguments. When %-escaping a string, " " is +replaced by "+", and non-printable +characters are replaced by "%hh" where +hh is a two-digit hex code. Unescaping a string +reverses this process.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_cgi(3), new_http_request(3).
+
+ + diff --git a/doc/copy_cgi.3.html b/doc/copy_cgi.3.html new file mode 100644 index 0000000..c3faebc --- /dev/null +++ b/doc/copy_cgi.3.html @@ -0,0 +1,132 @@ + + + + +new_cgi + + + +

new_cgi

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_cgi, cgi_params, cgi_param, cgi_param_list, cgi_erase, copy_cgi - Library for parsing CGI query strings.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_cgi.h>
+
+cgi new_cgi (pool, http_request, io_handle);
+vector cgi_params (cgi);
+const char *cgi_param (cgi, const char *name);
+const vector cgi_param_list (cgi, const char *name);
+int cgi_erase (cgi, const char *name);
+cgi copy_cgi (pool, cgi);
+
+ +

DESCRIPTION

+ + + +
+new_cgi creates a new CGI object from an existing +HTTP request. It reads the query string or POSTed parameters +and parses them internally. CGI parameters are case +sensitive, and multiple parameters may be passed with the +same name. Parameter values are automatically unescaped by +the library before you get to see them.
+ + + +
+cgi_params returns a list of all the names of the +parameters passed to the script. The list is returned as a +vector of char *.
+ + + +
+cgi_param returns the value of a single named CGI +parameter, or NULL if there is no such parameter. If +multiple parameters were given with the same name, this +returns one of them, essentially at random.
+ + + +
+cgi_param_list returns the list of values of the +named CGI parameter. The list is returned as a vector +of char *.
+ + + +
+cgi_erase erases the named parameter. If a parameter +was erased, this returns true, else this returns +false.
+ + + +
+copy_cgi copies cgi into pool +pool.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+cgi_get_post_max(3), cgi_escape(3), +new_http_request(3).
+
+ + diff --git a/doc/eg_echo.c b/doc/eg_echo.c new file mode 100644 index 0000000..2df5582 --- /dev/null +++ b/doc/eg_echo.c @@ -0,0 +1,60 @@ +#include +#include +#include + +#include + +#include +#include +#include + +static void start_processor (int sock, void *data); +static void run (void *); + +typedef struct processor_thread +{ + pseudothread pth; /* Pseudothread handle. */ + int sock; /* Socket. */ +} *processor_thread; + +int +main (int argc, char *argv[]) +{ + /* Start up the server. */ + pthr_server_main_loop (argc, argv, start_processor); + + exit (0); +} + +static void +start_processor (int sock, void *data) +{ + pool pool; + processor_thread p; + + pool = new_pool (); + p = pmalloc (pool, sizeof *p); + + p->sock = sock; + p->pth = new_pseudothread (pool, run, p, "processor thread"); + + pth_start (p->pth); +} + +static void +run (void *vp) +{ + processor_thread p = (processor_thread) vp; + io_handle io; + char buffer[256]; + + io = io_fdopen (p->pth, p->sock); + + /* Sit in a loop reading strings and echoing them back. */ + while (io_fgets (buffer, sizeof buffer, io, 1)) + io_fputs (buffer, io); + + io_fclose (io); + + pth_exit (p->pth); +} diff --git a/doc/ftpc_ascii.3.html b/doc/ftpc_ascii.3.html new file mode 100644 index 0000000..6e345c9 --- /dev/null +++ b/doc/ftpc_ascii.3.html @@ -0,0 +1,100 @@ + + + + +ftpc_type + + + +

ftpc_type

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_type, ftpc_ascii, ftpc_binary - Set connection type.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+int ftpc_type (ftpc ftpc, char type);
+int ftpc_ascii (ftpc ftpc);
+int ftpc_binary (ftpc ftpc);
+
+ +

DESCRIPTION

+ + + +
+ftpc_type sets the connection type. Most FTP servers +only support type 'a' (ASCII) or type 'i' (bInary), although +esoteric FTP servers might support 'e' +(EBCDIC).
+ + + +
+ftpc_ascii sets the type to ASCII.
+ + + +
+ftpc_binary sets the type to bInary.
+ +

RETURNS

+ + + +
+0 if successful, -1 if the attempt failed. If a fatal error +occurs with the connection, an exception is +thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/ftpc_binary.3.html b/doc/ftpc_binary.3.html new file mode 100644 index 0000000..7ee3300 --- /dev/null +++ b/doc/ftpc_binary.3.html @@ -0,0 +1,100 @@ + + + + +ftpc_type + + + +

ftpc_type

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_type, ftpc_ascii, ftpc_binary - Set connection type.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+int ftpc_type (ftpc ftpc, char type);
+int ftpc_ascii (ftpc ftpc);
+int ftpc_binary (ftpc ftpc);
+
+ +

DESCRIPTION

+ + + +
+ftpc_type sets the connection type. Most FTP servers +only support type 'a' (ASCII) or type 'i' (bInary), although +esoteric FTP servers might support 'e' +(EBCDIC).
+ + + +
+ftpc_ascii sets the type to ASCII.
+ + + +
+ftpc_binary sets the type to bInary.
+ +

RETURNS

+ + + +
+0 if successful, -1 if the attempt failed. If a fatal error +occurs with the connection, an exception is +thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/ftpc_cdup.3.html b/doc/ftpc_cdup.3.html new file mode 100644 index 0000000..a65ace5 --- /dev/null +++ b/doc/ftpc_cdup.3.html @@ -0,0 +1,93 @@ + + + + +ftpc_cwd + + + +

ftpc_cwd

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_cwd, ftpc_cdup - Change directory on the server.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+int ftpc_cwd (ftpc ftpc, const char *pathname);
+int ftpc_cdup (ftpc ftpc);
+
+ +

DESCRIPTION

+ + + +
+ftpc_cwd changes the directory to +pathname.
+ + + +
+ftpc_cdup moves to the parent directory. On most +Unix-like FTP servers this is equivalent to doing CWD +..
+ +

RETURNS

+ + + +
+0 if successful, -1 if the attempt failed. If a fatal error +occurs with the connection, an exception is +thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/ftpc_cwd.3.html b/doc/ftpc_cwd.3.html new file mode 100644 index 0000000..a65ace5 --- /dev/null +++ b/doc/ftpc_cwd.3.html @@ -0,0 +1,93 @@ + + + + +ftpc_cwd + + + +

ftpc_cwd

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_cwd, ftpc_cdup - Change directory on the server.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+int ftpc_cwd (ftpc ftpc, const char *pathname);
+int ftpc_cdup (ftpc ftpc);
+
+ +

DESCRIPTION

+ + + +
+ftpc_cwd changes the directory to +pathname.
+ + + +
+ftpc_cdup moves to the parent directory. On most +Unix-like FTP servers this is equivalent to doing CWD +..
+ +

RETURNS

+ + + +
+0 if successful, -1 if the attempt failed. If a fatal error +occurs with the connection, an exception is +thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/ftpc_delete.3.html b/doc/ftpc_delete.3.html new file mode 100644 index 0000000..4c98b79 --- /dev/null +++ b/doc/ftpc_delete.3.html @@ -0,0 +1,84 @@ + + + + +ftpc_delete + + + +

ftpc_delete

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_delete - Delete a file on the server.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+int ftpc_delete (ftpc ftpc, const char *pathname);
+
+ +

DESCRIPTION

+ + + +
+ftpc_delete deletes a file called pathname on +the server.
+ +

RETURNS

+ + + +
+0 if successful, -1 if the attempt failed. If a fatal error +occurs with the connection, an exception is +thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/ftpc_dir.3.html b/doc/ftpc_dir.3.html new file mode 100644 index 0000000..fdaff34 --- /dev/null +++ b/doc/ftpc_dir.3.html @@ -0,0 +1,103 @@ + + + + +ftpc_ls + + + +

ftpc_ls

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_ls, ftpc_dir - List the contents of a directory on the server.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+vector ftpc_ls (ftpc ftpc, pool, const char *pathname);
+vector ftpc_dir (ftpc ftpc, pool, const char *pathname);
+
+ +

DESCRIPTION

+ + + +
+ftpc_ls and ftpc_dir list the contents of +either the current directory (if pathname is +NULL) or else another directory +pathname.
+ + + +
+ftpc_ls issues the command NLST -a, returning +a vector of strings giving the name of each +file.
+ + + +
+ftpc_dir issues the command LIST -a, returning +a human-readable list of filenames, similar to issuing the +ls -al command in Unix.
+ +

RETURNS

+ + + +
+0 if successful, -1 if the attempt failed. If a fatal error +occurs with the connection, an exception is +thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/ftpc_get.3.html b/doc/ftpc_get.3.html new file mode 100644 index 0000000..ce2af92 --- /dev/null +++ b/doc/ftpc_get.3.html @@ -0,0 +1,94 @@ + + + + +ftpc_get + + + +

ftpc_get

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_get, ftpc_put - Download or upload a file.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+int ftpc_get (ftpc ftpc, const char *remote_file, const char *local_file);
+int ftpc_put (ftpc ftpc, const char *local_file, const char *remote_file);
+
+ +

DESCRIPTION

+ + + +
+ftpc_get attempts to download remote_file from +the server and store it in a local file called +local_file.
+ + + +
+ftpc_put attempts to upload a file called +local_file to the server and store it in a file on +the server called remote_file.
+ +

RETURNS

+ + + +
+0 if successful, -1 if the attempt failed. If a fatal error +occurs with the connection, an exception is +thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/ftpc_login.3.html b/doc/ftpc_login.3.html new file mode 100644 index 0000000..582a03c --- /dev/null +++ b/doc/ftpc_login.3.html @@ -0,0 +1,90 @@ + + + + +ftpc_login + + + +

ftpc_login

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_login - Log onto the FTP server.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+int ftpc_login (ftpc ftpc, const char *username, const char *password);
+
+ +

DESCRIPTION

+ + + +
+Attempt to log onto the FTP server as user username +with password password. If username is +NULL, "ftp" or +"anonymous", then log in anonymously. For +anonymous logins, the password may be NULL, in +which case the environment variable LOGNAME followed +by a single @ character is used as the +password.
+ +

RETURNS

+ + + +
+0 if successful, -1 if the attempt failed. If a fatal error +occurs with the connection, an exception is +thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/ftpc_ls.3.html b/doc/ftpc_ls.3.html new file mode 100644 index 0000000..fdaff34 --- /dev/null +++ b/doc/ftpc_ls.3.html @@ -0,0 +1,103 @@ + + + + +ftpc_ls + + + +

ftpc_ls

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_ls, ftpc_dir - List the contents of a directory on the server.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+vector ftpc_ls (ftpc ftpc, pool, const char *pathname);
+vector ftpc_dir (ftpc ftpc, pool, const char *pathname);
+
+ +

DESCRIPTION

+ + + +
+ftpc_ls and ftpc_dir list the contents of +either the current directory (if pathname is +NULL) or else another directory +pathname.
+ + + +
+ftpc_ls issues the command NLST -a, returning +a vector of strings giving the name of each +file.
+ + + +
+ftpc_dir issues the command LIST -a, returning +a human-readable list of filenames, similar to issuing the +ls -al command in Unix.
+ +

RETURNS

+ + + +
+0 if successful, -1 if the attempt failed. If a fatal error +occurs with the connection, an exception is +thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/ftpc_mkdir.3.html b/doc/ftpc_mkdir.3.html new file mode 100644 index 0000000..eeb7f5b --- /dev/null +++ b/doc/ftpc_mkdir.3.html @@ -0,0 +1,86 @@ + + + + +ftpc_mkdir + + + +

ftpc_mkdir

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_mkdir, ftpc_rmdir - Create or remove directories on the server.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+int ftpc_mkdir (ftpc ftpc, const char *pathname);
+int ftpc_rmdir (ftpc ftpc, const char *pathname);
+
+ +

DESCRIPTION

+ + + +
+ftpc_mkdir creates a directory called pathname +on the server. ftpc_rmdir removes a directory called +pathname on the server.
+ +

RETURNS

+ + + +
+0 if successful, -1 if the attempt failed. If a fatal error +occurs with the connection, an exception is +thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/ftpc_put.3.html b/doc/ftpc_put.3.html new file mode 100644 index 0000000..aa044ee --- /dev/null +++ b/doc/ftpc_put.3.html @@ -0,0 +1,94 @@ + + + + +ftpc_get + + + +

ftpc_get

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_get, ftpc_put - Download or upload a file.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+int ftpc_get (ftpc ftpc, const char *remote_file, const char *local_file);
+int ftpc_put (ftpc ftpc, const char *local_file, const char *remote_file);
+
+ +

DESCRIPTION

+ + + +
+ftpc_get attempts to download remote_file from +the server and store it in a local file called +local_file.
+ + + +
+ftpc_put attempts to upload a file called +local_file to the server and store it in a file on +the server called remote_file.
+ +

RETURNS

+ + + +
+0 if successful, -1 if the attempt failed. If a fatal error +occurs with the connection, an exception is +thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/ftpc_pwd.3.html b/doc/ftpc_pwd.3.html new file mode 100644 index 0000000..239020b --- /dev/null +++ b/doc/ftpc_pwd.3.html @@ -0,0 +1,84 @@ + + + + +ftpc_pwd + + + +

ftpc_pwd

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_pwd - Return current directory on the server.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+char *ftpc_pwd (ftpc ftpc);
+
+ +

DESCRIPTION

+ + + +
+ftpc_pwd returns the current directory on the +server.
+ +

RETURNS

+ + + +
+The current directory, as a string allocated in the pool, or +NULL if the command fails. If a fatal error occurs with the +connection, an exception is thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/ftpc_quit.3.html b/doc/ftpc_quit.3.html new file mode 100644 index 0000000..128190f --- /dev/null +++ b/doc/ftpc_quit.3.html @@ -0,0 +1,86 @@ + + + + +ftpc_quit + + + +

ftpc_quit

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_quit - Nicely disconnect from the FTP server.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+int ftpc_quit (ftpc ftpc);
+
+ +

DESCRIPTION

+ + + +
+ftpc_quit sends a QUIT command to the FTP +server and then drops the network connection. After using +this function, the ftpc object associated with the +connection is invalid and should not be used.
+ +

RETURNS

+ + + +
+0 if successful, -1 if the attempt failed. If a fatal error +occurs with the connection, an exception is +thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/ftpc_quote.3.html b/doc/ftpc_quote.3.html new file mode 100644 index 0000000..20165b2 --- /dev/null +++ b/doc/ftpc_quote.3.html @@ -0,0 +1,85 @@ + + + + +ftpc_quote + + + +

ftpc_quote

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_quote - Issue a command to the FTP server.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+int ftpc_quote (ftpc ftpc, const char *cmd);
+
+ +

DESCRIPTION

+ + + +
+ftpc_quote issues a command directly to the FTP +server. This function may be used for issuing SITE +commands for example.
+ +

RETURNS

+ + + +
+0 if successful, -1 if the attempt failed. If a fatal error +occurs with the connection, an exception is +thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/ftpc_rmdir.3.html b/doc/ftpc_rmdir.3.html new file mode 100644 index 0000000..8828550 --- /dev/null +++ b/doc/ftpc_rmdir.3.html @@ -0,0 +1,86 @@ + + + + +ftpc_mkdir + + + +

ftpc_mkdir

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_mkdir, ftpc_rmdir - Create or remove directories on the server.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+int ftpc_mkdir (ftpc ftpc, const char *pathname);
+int ftpc_rmdir (ftpc ftpc, const char *pathname);
+
+ +

DESCRIPTION

+ + + +
+ftpc_mkdir creates a directory called pathname +on the server. ftpc_rmdir removes a directory called +pathname on the server.
+ +

RETURNS

+ + + +
+0 if successful, -1 if the attempt failed. If a fatal error +occurs with the connection, an exception is +thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/ftpc_set_passive_mode.3.html b/doc/ftpc_set_passive_mode.3.html new file mode 100644 index 0000000..a74dd07 --- /dev/null +++ b/doc/ftpc_set_passive_mode.3.html @@ -0,0 +1,93 @@ + + + + +ftpc_set_passive_mode + + + +

ftpc_set_passive_mode

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_set_passive_mode - Change to/from active or passive mode.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+int ftpc_set_passive_mode (ftpc ftpc, int flag);
+
+ +

DESCRIPTION

+ + + +
+If flag is true, all future connections on this +ftpc object will be in passive mode. If flag +is false, all future connections will be in active +mode.
+ + + +
+Passive mode is required by a few servers and some +firewalls.
+ +

RETURNS

+ + + +
+0 if successful, -1 if the attempt failed. If a fatal error +occurs with the connection, an exception is +thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/ftpc_type.3.html b/doc/ftpc_type.3.html new file mode 100644 index 0000000..de5404c --- /dev/null +++ b/doc/ftpc_type.3.html @@ -0,0 +1,100 @@ + + + + +ftpc_type + + + +

ftpc_type

+NAME
+SYNOPSIS
+DESCRIPTION
+RETURNS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+ftpc_type, ftpc_ascii, ftpc_binary - Set connection type.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+int ftpc_type (ftpc ftpc, char type);
+int ftpc_ascii (ftpc ftpc);
+int ftpc_binary (ftpc ftpc);
+
+ +

DESCRIPTION

+ + + +
+ftpc_type sets the connection type. Most FTP servers +only support type 'a' (ASCII) or type 'i' (bInary), although +esoteric FTP servers might support 'e' +(EBCDIC).
+ + + +
+ftpc_ascii sets the type to ASCII.
+ + + +
+ftpc_binary sets the type to bInary.
+ +

RETURNS

+ + + +
+0 if successful, -1 if the attempt failed. If a fatal error +occurs with the connection, an exception is +thrown.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/http_get_log_file.3.html b/doc/http_get_log_file.3.html new file mode 100644 index 0000000..b4bd8d7 --- /dev/null +++ b/doc/http_get_log_file.3.html @@ -0,0 +1,137 @@ + + + + +http_set_log_file + + + +

http_set_log_file

+NAME
+SYNOPSIS
+DESCRIPTION
+BUGS
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+http_set_log_file, http_get_log_file - enable HTTP logs on file pointer
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+FILE *http_set_log_file (FILE *fp);
+FILE *http_get_log_file (void);
+
+ +

DESCRIPTION

+ + + +
+The FILE *fp argument to http_set_log_file +sets the file pointer on which HTTP logs are generated. To +disable logging, set fp to NULL. The function +returns fp.
+ + + +
+http_get_log_file returns the current file pointer or +NULL if logging is disabled.
+ + + +
+The default is that logging is disabled.
+ + + +
+Currently log messages are generated at the end of the HTTP +response headers and have the following fixed +format:
+ + + +
+YYYY/MM/DD HH:MM ip:port - "METHOD URL HTTP/x.y" +CODE length "Referer" "User +Agent"
+ + + +
+The first "-" is intended to store the HTTP auth +username, when HTTP authorization is supported by the +library. The "length" field is only known if the +caller sends back a "Content-Length" header. +Otherwise 0 is printed in that position.
+ +

BUGS

+ + + +
+Log format should be customizable. It should be possible +(optionally, of course) to look up the IP address and print +a hostname.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_request(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/http_get_servername.3.html b/doc/http_get_servername.3.html new file mode 100644 index 0000000..dd234e3 --- /dev/null +++ b/doc/http_get_servername.3.html @@ -0,0 +1,92 @@ + + + + +http_get_servername + + + +

http_get_servername

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+http_get_servername, http_set_servername - get and set the server name string
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+const char *http_get_servername (void);
+const char *http_set_servername (const char *new_server_name);
+
+ +

DESCRIPTION

+ + + +
+Get and set the server name (which is sent in the +Server) header by the server when it responds to +requests.
+ + + +
+The default string is +pthrlib-httpd/version.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_request(3), new_http_response(3), +new_cgi(3).
+
+ + diff --git a/doc/http_request_get_header.3.html b/doc/http_request_get_header.3.html new file mode 100644 index 0000000..7e3aa0e --- /dev/null +++ b/doc/http_request_get_header.3.html @@ -0,0 +1,165 @@ + + + + +new_http_request + + + +

new_http_request

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+
+ +

DESCRIPTION

+ + + +
+These functions allow you to efficiently parse incoming HTTP +requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1 +clients. The request parser understands GET, HEAD and POST +requests and conforms as far as possible to RFC +2616.
+ + + +
+new_http_request creates a new request object, +parsing the incoming request on the given io_handle. +If the stream closes at the beginning of the request the +function returns NULL. If the request is faulty, then +the library prints a message to syslog and throws an +exception by calling pth_die(3). Otherwise it +initializes a complete http_request object and +returns it.
+ + + +
+http_request_time returns the timestamp of the +incoming request.
+ + + +
+http_request_url returns the complete URL of the +request. http_request_path returns just the path +component of the URL (ie. without the query string if there +was one). http_request_query_string returns just the +query string (for GET requests only). Do not do your own +parsing of query strings: there is a CGI library built into +pthrlib (see: new_cgi(3)).
+ + + +
+http_request_method returns the method, one of +HTTP_METHOD_GET, HTTP_METHOD_HEAD or +HTTP_METHOD_POST. http_request_is_HEAD is just +a quick way of testing if the method is a HEAD request. +http_request_method_string returns the method as a +string rather than a coded number.
+ + + +
+http_request_version returns the major and minor +numbers of the HTTP request (eg. major = 1, minor = 0 for a +HTTP/1.0 request).
+ + + +
+http_request_nr_headers, +http_request_get_headers and +http_request_get_header return the number of HTTP +headers, the list of HTTP headers and a particular HTTP +header (if it exists). http_request_get_headers +returns a vector or struct http_header. This +structure contains at least two fields called key and +value. HTTP header keys are case insensitive when +searching, and you will find that the list of keys returned +by http_request_get_headers has been converted to +lowercase.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_response(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/http_request_get_headers.3.html b/doc/http_request_get_headers.3.html new file mode 100644 index 0000000..7e3aa0e --- /dev/null +++ b/doc/http_request_get_headers.3.html @@ -0,0 +1,165 @@ + + + + +new_http_request + + + +

new_http_request

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+
+ +

DESCRIPTION

+ + + +
+These functions allow you to efficiently parse incoming HTTP +requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1 +clients. The request parser understands GET, HEAD and POST +requests and conforms as far as possible to RFC +2616.
+ + + +
+new_http_request creates a new request object, +parsing the incoming request on the given io_handle. +If the stream closes at the beginning of the request the +function returns NULL. If the request is faulty, then +the library prints a message to syslog and throws an +exception by calling pth_die(3). Otherwise it +initializes a complete http_request object and +returns it.
+ + + +
+http_request_time returns the timestamp of the +incoming request.
+ + + +
+http_request_url returns the complete URL of the +request. http_request_path returns just the path +component of the URL (ie. without the query string if there +was one). http_request_query_string returns just the +query string (for GET requests only). Do not do your own +parsing of query strings: there is a CGI library built into +pthrlib (see: new_cgi(3)).
+ + + +
+http_request_method returns the method, one of +HTTP_METHOD_GET, HTTP_METHOD_HEAD or +HTTP_METHOD_POST. http_request_is_HEAD is just +a quick way of testing if the method is a HEAD request. +http_request_method_string returns the method as a +string rather than a coded number.
+ + + +
+http_request_version returns the major and minor +numbers of the HTTP request (eg. major = 1, minor = 0 for a +HTTP/1.0 request).
+ + + +
+http_request_nr_headers, +http_request_get_headers and +http_request_get_header return the number of HTTP +headers, the list of HTTP headers and a particular HTTP +header (if it exists). http_request_get_headers +returns a vector or struct http_header. This +structure contains at least two fields called key and +value. HTTP header keys are case insensitive when +searching, and you will find that the list of keys returned +by http_request_get_headers has been converted to +lowercase.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_response(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/http_request_is_HEAD.3.html b/doc/http_request_is_HEAD.3.html new file mode 100644 index 0000000..7e3aa0e --- /dev/null +++ b/doc/http_request_is_HEAD.3.html @@ -0,0 +1,165 @@ + + + + +new_http_request + + + +

new_http_request

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+
+ +

DESCRIPTION

+ + + +
+These functions allow you to efficiently parse incoming HTTP +requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1 +clients. The request parser understands GET, HEAD and POST +requests and conforms as far as possible to RFC +2616.
+ + + +
+new_http_request creates a new request object, +parsing the incoming request on the given io_handle. +If the stream closes at the beginning of the request the +function returns NULL. If the request is faulty, then +the library prints a message to syslog and throws an +exception by calling pth_die(3). Otherwise it +initializes a complete http_request object and +returns it.
+ + + +
+http_request_time returns the timestamp of the +incoming request.
+ + + +
+http_request_url returns the complete URL of the +request. http_request_path returns just the path +component of the URL (ie. without the query string if there +was one). http_request_query_string returns just the +query string (for GET requests only). Do not do your own +parsing of query strings: there is a CGI library built into +pthrlib (see: new_cgi(3)).
+ + + +
+http_request_method returns the method, one of +HTTP_METHOD_GET, HTTP_METHOD_HEAD or +HTTP_METHOD_POST. http_request_is_HEAD is just +a quick way of testing if the method is a HEAD request. +http_request_method_string returns the method as a +string rather than a coded number.
+ + + +
+http_request_version returns the major and minor +numbers of the HTTP request (eg. major = 1, minor = 0 for a +HTTP/1.0 request).
+ + + +
+http_request_nr_headers, +http_request_get_headers and +http_request_get_header return the number of HTTP +headers, the list of HTTP headers and a particular HTTP +header (if it exists). http_request_get_headers +returns a vector or struct http_header. This +structure contains at least two fields called key and +value. HTTP header keys are case insensitive when +searching, and you will find that the list of keys returned +by http_request_get_headers has been converted to +lowercase.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_response(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/http_request_method.3.html b/doc/http_request_method.3.html new file mode 100644 index 0000000..486a8cb --- /dev/null +++ b/doc/http_request_method.3.html @@ -0,0 +1,165 @@ + + + + +new_http_request + + + +

new_http_request

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+
+ +

DESCRIPTION

+ + + +
+These functions allow you to efficiently parse incoming HTTP +requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1 +clients. The request parser understands GET, HEAD and POST +requests and conforms as far as possible to RFC +2616.
+ + + +
+new_http_request creates a new request object, +parsing the incoming request on the given io_handle. +If the stream closes at the beginning of the request the +function returns NULL. If the request is faulty, then +the library prints a message to syslog and throws an +exception by calling pth_die(3). Otherwise it +initializes a complete http_request object and +returns it.
+ + + +
+http_request_time returns the timestamp of the +incoming request.
+ + + +
+http_request_url returns the complete URL of the +request. http_request_path returns just the path +component of the URL (ie. without the query string if there +was one). http_request_query_string returns just the +query string (for GET requests only). Do not do your own +parsing of query strings: there is a CGI library built into +pthrlib (see: new_cgi(3)).
+ + + +
+http_request_method returns the method, one of +HTTP_METHOD_GET, HTTP_METHOD_HEAD or +HTTP_METHOD_POST. http_request_is_HEAD is just +a quick way of testing if the method is a HEAD request. +http_request_method_string returns the method as a +string rather than a coded number.
+ + + +
+http_request_version returns the major and minor +numbers of the HTTP request (eg. major = 1, minor = 0 for a +HTTP/1.0 request).
+ + + +
+http_request_nr_headers, +http_request_get_headers and +http_request_get_header return the number of HTTP +headers, the list of HTTP headers and a particular HTTP +header (if it exists). http_request_get_headers +returns a vector or struct http_header. This +structure contains at least two fields called key and +value. HTTP header keys are case insensitive when +searching, and you will find that the list of keys returned +by http_request_get_headers has been converted to +lowercase.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_response(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/http_request_method_string.3.html b/doc/http_request_method_string.3.html new file mode 100644 index 0000000..486a8cb --- /dev/null +++ b/doc/http_request_method_string.3.html @@ -0,0 +1,165 @@ + + + + +new_http_request + + + +

new_http_request

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+
+ +

DESCRIPTION

+ + + +
+These functions allow you to efficiently parse incoming HTTP +requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1 +clients. The request parser understands GET, HEAD and POST +requests and conforms as far as possible to RFC +2616.
+ + + +
+new_http_request creates a new request object, +parsing the incoming request on the given io_handle. +If the stream closes at the beginning of the request the +function returns NULL. If the request is faulty, then +the library prints a message to syslog and throws an +exception by calling pth_die(3). Otherwise it +initializes a complete http_request object and +returns it.
+ + + +
+http_request_time returns the timestamp of the +incoming request.
+ + + +
+http_request_url returns the complete URL of the +request. http_request_path returns just the path +component of the URL (ie. without the query string if there +was one). http_request_query_string returns just the +query string (for GET requests only). Do not do your own +parsing of query strings: there is a CGI library built into +pthrlib (see: new_cgi(3)).
+ + + +
+http_request_method returns the method, one of +HTTP_METHOD_GET, HTTP_METHOD_HEAD or +HTTP_METHOD_POST. http_request_is_HEAD is just +a quick way of testing if the method is a HEAD request. +http_request_method_string returns the method as a +string rather than a coded number.
+ + + +
+http_request_version returns the major and minor +numbers of the HTTP request (eg. major = 1, minor = 0 for a +HTTP/1.0 request).
+ + + +
+http_request_nr_headers, +http_request_get_headers and +http_request_get_header return the number of HTTP +headers, the list of HTTP headers and a particular HTTP +header (if it exists). http_request_get_headers +returns a vector or struct http_header. This +structure contains at least two fields called key and +value. HTTP header keys are case insensitive when +searching, and you will find that the list of keys returned +by http_request_get_headers has been converted to +lowercase.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_response(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/http_request_nr_headers.3.html b/doc/http_request_nr_headers.3.html new file mode 100644 index 0000000..486a8cb --- /dev/null +++ b/doc/http_request_nr_headers.3.html @@ -0,0 +1,165 @@ + + + + +new_http_request + + + +

new_http_request

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+
+ +

DESCRIPTION

+ + + +
+These functions allow you to efficiently parse incoming HTTP +requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1 +clients. The request parser understands GET, HEAD and POST +requests and conforms as far as possible to RFC +2616.
+ + + +
+new_http_request creates a new request object, +parsing the incoming request on the given io_handle. +If the stream closes at the beginning of the request the +function returns NULL. If the request is faulty, then +the library prints a message to syslog and throws an +exception by calling pth_die(3). Otherwise it +initializes a complete http_request object and +returns it.
+ + + +
+http_request_time returns the timestamp of the +incoming request.
+ + + +
+http_request_url returns the complete URL of the +request. http_request_path returns just the path +component of the URL (ie. without the query string if there +was one). http_request_query_string returns just the +query string (for GET requests only). Do not do your own +parsing of query strings: there is a CGI library built into +pthrlib (see: new_cgi(3)).
+ + + +
+http_request_method returns the method, one of +HTTP_METHOD_GET, HTTP_METHOD_HEAD or +HTTP_METHOD_POST. http_request_is_HEAD is just +a quick way of testing if the method is a HEAD request. +http_request_method_string returns the method as a +string rather than a coded number.
+ + + +
+http_request_version returns the major and minor +numbers of the HTTP request (eg. major = 1, minor = 0 for a +HTTP/1.0 request).
+ + + +
+http_request_nr_headers, +http_request_get_headers and +http_request_get_header return the number of HTTP +headers, the list of HTTP headers and a particular HTTP +header (if it exists). http_request_get_headers +returns a vector or struct http_header. This +structure contains at least two fields called key and +value. HTTP header keys are case insensitive when +searching, and you will find that the list of keys returned +by http_request_get_headers has been converted to +lowercase.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_response(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/http_request_path.3.html b/doc/http_request_path.3.html new file mode 100644 index 0000000..486a8cb --- /dev/null +++ b/doc/http_request_path.3.html @@ -0,0 +1,165 @@ + + + + +new_http_request + + + +

new_http_request

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+
+ +

DESCRIPTION

+ + + +
+These functions allow you to efficiently parse incoming HTTP +requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1 +clients. The request parser understands GET, HEAD and POST +requests and conforms as far as possible to RFC +2616.
+ + + +
+new_http_request creates a new request object, +parsing the incoming request on the given io_handle. +If the stream closes at the beginning of the request the +function returns NULL. If the request is faulty, then +the library prints a message to syslog and throws an +exception by calling pth_die(3). Otherwise it +initializes a complete http_request object and +returns it.
+ + + +
+http_request_time returns the timestamp of the +incoming request.
+ + + +
+http_request_url returns the complete URL of the +request. http_request_path returns just the path +component of the URL (ie. without the query string if there +was one). http_request_query_string returns just the +query string (for GET requests only). Do not do your own +parsing of query strings: there is a CGI library built into +pthrlib (see: new_cgi(3)).
+ + + +
+http_request_method returns the method, one of +HTTP_METHOD_GET, HTTP_METHOD_HEAD or +HTTP_METHOD_POST. http_request_is_HEAD is just +a quick way of testing if the method is a HEAD request. +http_request_method_string returns the method as a +string rather than a coded number.
+ + + +
+http_request_version returns the major and minor +numbers of the HTTP request (eg. major = 1, minor = 0 for a +HTTP/1.0 request).
+ + + +
+http_request_nr_headers, +http_request_get_headers and +http_request_get_header return the number of HTTP +headers, the list of HTTP headers and a particular HTTP +header (if it exists). http_request_get_headers +returns a vector or struct http_header. This +structure contains at least two fields called key and +value. HTTP header keys are case insensitive when +searching, and you will find that the list of keys returned +by http_request_get_headers has been converted to +lowercase.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_response(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/http_request_query_string.3.html b/doc/http_request_query_string.3.html new file mode 100644 index 0000000..d94f954 --- /dev/null +++ b/doc/http_request_query_string.3.html @@ -0,0 +1,165 @@ + + + + +new_http_request + + + +

new_http_request

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+
+ +

DESCRIPTION

+ + + +
+These functions allow you to efficiently parse incoming HTTP +requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1 +clients. The request parser understands GET, HEAD and POST +requests and conforms as far as possible to RFC +2616.
+ + + +
+new_http_request creates a new request object, +parsing the incoming request on the given io_handle. +If the stream closes at the beginning of the request the +function returns NULL. If the request is faulty, then +the library prints a message to syslog and throws an +exception by calling pth_die(3). Otherwise it +initializes a complete http_request object and +returns it.
+ + + +
+http_request_time returns the timestamp of the +incoming request.
+ + + +
+http_request_url returns the complete URL of the +request. http_request_path returns just the path +component of the URL (ie. without the query string if there +was one). http_request_query_string returns just the +query string (for GET requests only). Do not do your own +parsing of query strings: there is a CGI library built into +pthrlib (see: new_cgi(3)).
+ + + +
+http_request_method returns the method, one of +HTTP_METHOD_GET, HTTP_METHOD_HEAD or +HTTP_METHOD_POST. http_request_is_HEAD is just +a quick way of testing if the method is a HEAD request. +http_request_method_string returns the method as a +string rather than a coded number.
+ + + +
+http_request_version returns the major and minor +numbers of the HTTP request (eg. major = 1, minor = 0 for a +HTTP/1.0 request).
+ + + +
+http_request_nr_headers, +http_request_get_headers and +http_request_get_header return the number of HTTP +headers, the list of HTTP headers and a particular HTTP +header (if it exists). http_request_get_headers +returns a vector or struct http_header. This +structure contains at least two fields called key and +value. HTTP header keys are case insensitive when +searching, and you will find that the list of keys returned +by http_request_get_headers has been converted to +lowercase.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_response(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/http_request_time.3.html b/doc/http_request_time.3.html new file mode 100644 index 0000000..d94f954 --- /dev/null +++ b/doc/http_request_time.3.html @@ -0,0 +1,165 @@ + + + + +new_http_request + + + +

new_http_request

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+
+ +

DESCRIPTION

+ + + +
+These functions allow you to efficiently parse incoming HTTP +requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1 +clients. The request parser understands GET, HEAD and POST +requests and conforms as far as possible to RFC +2616.
+ + + +
+new_http_request creates a new request object, +parsing the incoming request on the given io_handle. +If the stream closes at the beginning of the request the +function returns NULL. If the request is faulty, then +the library prints a message to syslog and throws an +exception by calling pth_die(3). Otherwise it +initializes a complete http_request object and +returns it.
+ + + +
+http_request_time returns the timestamp of the +incoming request.
+ + + +
+http_request_url returns the complete URL of the +request. http_request_path returns just the path +component of the URL (ie. without the query string if there +was one). http_request_query_string returns just the +query string (for GET requests only). Do not do your own +parsing of query strings: there is a CGI library built into +pthrlib (see: new_cgi(3)).
+ + + +
+http_request_method returns the method, one of +HTTP_METHOD_GET, HTTP_METHOD_HEAD or +HTTP_METHOD_POST. http_request_is_HEAD is just +a quick way of testing if the method is a HEAD request. +http_request_method_string returns the method as a +string rather than a coded number.
+ + + +
+http_request_version returns the major and minor +numbers of the HTTP request (eg. major = 1, minor = 0 for a +HTTP/1.0 request).
+ + + +
+http_request_nr_headers, +http_request_get_headers and +http_request_get_header return the number of HTTP +headers, the list of HTTP headers and a particular HTTP +header (if it exists). http_request_get_headers +returns a vector or struct http_header. This +structure contains at least two fields called key and +value. HTTP header keys are case insensitive when +searching, and you will find that the list of keys returned +by http_request_get_headers has been converted to +lowercase.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_response(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/http_request_url.3.html b/doc/http_request_url.3.html new file mode 100644 index 0000000..d94f954 --- /dev/null +++ b/doc/http_request_url.3.html @@ -0,0 +1,165 @@ + + + + +new_http_request + + + +

new_http_request

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+
+ +

DESCRIPTION

+ + + +
+These functions allow you to efficiently parse incoming HTTP +requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1 +clients. The request parser understands GET, HEAD and POST +requests and conforms as far as possible to RFC +2616.
+ + + +
+new_http_request creates a new request object, +parsing the incoming request on the given io_handle. +If the stream closes at the beginning of the request the +function returns NULL. If the request is faulty, then +the library prints a message to syslog and throws an +exception by calling pth_die(3). Otherwise it +initializes a complete http_request object and +returns it.
+ + + +
+http_request_time returns the timestamp of the +incoming request.
+ + + +
+http_request_url returns the complete URL of the +request. http_request_path returns just the path +component of the URL (ie. without the query string if there +was one). http_request_query_string returns just the +query string (for GET requests only). Do not do your own +parsing of query strings: there is a CGI library built into +pthrlib (see: new_cgi(3)).
+ + + +
+http_request_method returns the method, one of +HTTP_METHOD_GET, HTTP_METHOD_HEAD or +HTTP_METHOD_POST. http_request_is_HEAD is just +a quick way of testing if the method is a HEAD request. +http_request_method_string returns the method as a +string rather than a coded number.
+ + + +
+http_request_version returns the major and minor +numbers of the HTTP request (eg. major = 1, minor = 0 for a +HTTP/1.0 request).
+ + + +
+http_request_nr_headers, +http_request_get_headers and +http_request_get_header return the number of HTTP +headers, the list of HTTP headers and a particular HTTP +header (if it exists). http_request_get_headers +returns a vector or struct http_header. This +structure contains at least two fields called key and +value. HTTP header keys are case insensitive when +searching, and you will find that the list of keys returned +by http_request_get_headers has been converted to +lowercase.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_response(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/http_request_version.3.html b/doc/http_request_version.3.html new file mode 100644 index 0000000..d94f954 --- /dev/null +++ b/doc/http_request_version.3.html @@ -0,0 +1,165 @@ + + + + +new_http_request + + + +

new_http_request

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+
+ +

DESCRIPTION

+ + + +
+These functions allow you to efficiently parse incoming HTTP +requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1 +clients. The request parser understands GET, HEAD and POST +requests and conforms as far as possible to RFC +2616.
+ + + +
+new_http_request creates a new request object, +parsing the incoming request on the given io_handle. +If the stream closes at the beginning of the request the +function returns NULL. If the request is faulty, then +the library prints a message to syslog and throws an +exception by calling pth_die(3). Otherwise it +initializes a complete http_request object and +returns it.
+ + + +
+http_request_time returns the timestamp of the +incoming request.
+ + + +
+http_request_url returns the complete URL of the +request. http_request_path returns just the path +component of the URL (ie. without the query string if there +was one). http_request_query_string returns just the +query string (for GET requests only). Do not do your own +parsing of query strings: there is a CGI library built into +pthrlib (see: new_cgi(3)).
+ + + +
+http_request_method returns the method, one of +HTTP_METHOD_GET, HTTP_METHOD_HEAD or +HTTP_METHOD_POST. http_request_is_HEAD is just +a quick way of testing if the method is a HEAD request. +http_request_method_string returns the method as a +string rather than a coded number.
+ + + +
+http_request_version returns the major and minor +numbers of the HTTP request (eg. major = 1, minor = 0 for a +HTTP/1.0 request).
+ + + +
+http_request_nr_headers, +http_request_get_headers and +http_request_get_header return the number of HTTP +headers, the list of HTTP headers and a particular HTTP +header (if it exists). http_request_get_headers +returns a vector or struct http_header. This +structure contains at least two fields called key and +value. HTTP header keys are case insensitive when +searching, and you will find that the list of keys returned +by http_request_get_headers has been converted to +lowercase.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_response(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/http_response_end_headers.3.html b/doc/http_response_end_headers.3.html new file mode 100644 index 0000000..d0cbae1 --- /dev/null +++ b/doc/http_response_end_headers.3.html @@ -0,0 +1,122 @@ + + + + +new_http_response + + + +

new_http_response

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_http_response, http_response_send_header, http_response_send_headers, http_response_end_headers - functions for sending HTTP responses
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+http_response new_http_response (pseudothread, http_request, io_handle, int code, const char *msg);
+void http_response_send_header (http_response, const char *key, const char *value);
+void http_response_send_headers (http_response, ...);
+int http_response_end_headers (http_response h);
+
+ +

DESCRIPTION

+ + + +
+These functions allow you to efficiently generate outgoing +HTTP responses.
+ + + +
+new_http_response generates a new HTTP response +object and returns it. code is the HTTP response code +(see RFC 2616 for a list of codes), and msg is the +HTTP response message.
+ + + +
+http_response_send_header sends a single HTTP header +back to the client. The header is constructed by +concatenating key, ": ", +value and CR LF.
+ + + +
+http_response_send_headers sends back several headers +in a single call. The arguments to this function are a list +of key, value pairs followed by a single +NULL argument which terminates the list.
+ + + +
+http_response_end_headers ends the header list. It +causes the code to emit any missing-but-required headers and +then send the final CR LF characters.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_request(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/http_response_send_header.3.html b/doc/http_response_send_header.3.html new file mode 100644 index 0000000..d0cbae1 --- /dev/null +++ b/doc/http_response_send_header.3.html @@ -0,0 +1,122 @@ + + + + +new_http_response + + + +

new_http_response

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_http_response, http_response_send_header, http_response_send_headers, http_response_end_headers - functions for sending HTTP responses
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+http_response new_http_response (pseudothread, http_request, io_handle, int code, const char *msg);
+void http_response_send_header (http_response, const char *key, const char *value);
+void http_response_send_headers (http_response, ...);
+int http_response_end_headers (http_response h);
+
+ +

DESCRIPTION

+ + + +
+These functions allow you to efficiently generate outgoing +HTTP responses.
+ + + +
+new_http_response generates a new HTTP response +object and returns it. code is the HTTP response code +(see RFC 2616 for a list of codes), and msg is the +HTTP response message.
+ + + +
+http_response_send_header sends a single HTTP header +back to the client. The header is constructed by +concatenating key, ": ", +value and CR LF.
+ + + +
+http_response_send_headers sends back several headers +in a single call. The arguments to this function are a list +of key, value pairs followed by a single +NULL argument which terminates the list.
+ + + +
+http_response_end_headers ends the header list. It +causes the code to emit any missing-but-required headers and +then send the final CR LF characters.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_request(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/http_response_send_headers.3.html b/doc/http_response_send_headers.3.html new file mode 100644 index 0000000..d0cbae1 --- /dev/null +++ b/doc/http_response_send_headers.3.html @@ -0,0 +1,122 @@ + + + + +new_http_response + + + +

new_http_response

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_http_response, http_response_send_header, http_response_send_headers, http_response_end_headers - functions for sending HTTP responses
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+http_response new_http_response (pseudothread, http_request, io_handle, int code, const char *msg);
+void http_response_send_header (http_response, const char *key, const char *value);
+void http_response_send_headers (http_response, ...);
+int http_response_end_headers (http_response h);
+
+ +

DESCRIPTION

+ + + +
+These functions allow you to efficiently generate outgoing +HTTP responses.
+ + + +
+new_http_response generates a new HTTP response +object and returns it. code is the HTTP response code +(see RFC 2616 for a list of codes), and msg is the +HTTP response message.
+ + + +
+http_response_send_header sends a single HTTP header +back to the client. The header is constructed by +concatenating key, ": ", +value and CR LF.
+ + + +
+http_response_send_headers sends back several headers +in a single call. The arguments to this function are a list +of key, value pairs followed by a single +NULL argument which terminates the list.
+ + + +
+http_response_end_headers ends the header list. It +causes the code to emit any missing-but-required headers and +then send the final CR LF characters.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_request(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/http_set_log_file.3.html b/doc/http_set_log_file.3.html new file mode 100644 index 0000000..f663d0b --- /dev/null +++ b/doc/http_set_log_file.3.html @@ -0,0 +1,137 @@ + + + + +http_set_log_file + + + +

http_set_log_file

+NAME
+SYNOPSIS
+DESCRIPTION
+BUGS
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+http_set_log_file, http_get_log_file - enable HTTP logs on file pointer
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+FILE *http_set_log_file (FILE *fp);
+FILE *http_get_log_file (void);
+
+ +

DESCRIPTION

+ + + +
+The FILE *fp argument to http_set_log_file +sets the file pointer on which HTTP logs are generated. To +disable logging, set fp to NULL. The function +returns fp.
+ + + +
+http_get_log_file returns the current file pointer or +NULL if logging is disabled.
+ + + +
+The default is that logging is disabled.
+ + + +
+Currently log messages are generated at the end of the HTTP +response headers and have the following fixed +format:
+ + + +
+YYYY/MM/DD HH:MM ip:port - "METHOD URL HTTP/x.y" +CODE length "Referer" "User +Agent"
+ + + +
+The first "-" is intended to store the HTTP auth +username, when HTTP authorization is supported by the +library. The "length" field is only known if the +caller sends back a "Content-Length" header. +Otherwise 0 is printed in that position.
+ +

BUGS

+ + + +
+Log format should be customizable. It should be possible +(optionally, of course) to look up the IP address and print +a hostname.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_request(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/http_set_servername.3.html b/doc/http_set_servername.3.html new file mode 100644 index 0000000..923720f --- /dev/null +++ b/doc/http_set_servername.3.html @@ -0,0 +1,92 @@ + + + + +http_get_servername + + + +

http_get_servername

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+http_get_servername, http_set_servername - get and set the server name string
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+const char *http_get_servername (void);
+const char *http_set_servername (const char *new_server_name);
+
+ +

DESCRIPTION

+ + + +
+Get and set the server name (which is sent in the +Server) header by the server when it responds to +requests.
+ + + +
+The default string is +pthrlib-httpd/version.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_request(3), new_http_response(3), +new_cgi(3).
+
+ + diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..a46d3cf --- /dev/null +++ b/doc/index.html @@ -0,0 +1,1088 @@ + + + + pthrlib documentation index + + + + +

pthrlib documentation index

+ +

+ pthrlib is a library for writing small, fast + and efficient servers in C. It offers a list of advanced + features. This library has been used to write a + very + tiny and fast web server called rws and a closed + source chat server. +

+ +

+ The primary aims of pthrlib are: +

+ +
    +
  • Be very simple to use. +
  • Provide rich functionality for C programmers out of the box. +
  • Meticulous attention paid to the number of syscalls + issued and the efficiency of those syscalls, so typically + a pthrlib server will outperform any other + server architecture or language (on non-SMP). +
  • Tiny memory footprint for running servers. +
  • Cooperative threading reduces the danger and agony + of thread programming. +
  • Use of c2lib removes many risks of + buffer overflows. +
+ +

Tutorial and programming examples

+ +

+ At the heart of pthrlib is a threading + library called pseudothreads. This library + is a typical lightweight threading library, written + from scratch to be as small and fast as possible (it + therefore lacks many of the unnecessary features + which complicate other lightweight threading libraries, + such as the ability to suspend threads). +

+ +

+ A small pthrlib server will start off + with just a single listener thread, listening for + new connections on a socket. When connections come + in, a new thread is spun off to handle it: +

+ + + + + + + +
listener thread
processing thread, connected to client #1
processing thread, connected to client #2
processing thread, connected to client #3
...
+ +

+ More complex pthrlib servers may contain + several core threads: for example our closed-source + chat server has one extra thread called autoannounce + which periodically sends out announcement messages to + all clients. They may also use more than one thread + per client. Since threads are very lightweight, you + should be able to create as many threads as necessary + for your application. +

+ +

Simple echo server

+ +

+ To help you create a server with a listener thread + spinning off threads for each incoming connection, + there is a helper function called pthr_server_main_loop(3). + Almost all programs will want to use it, such as the + following simple echo program (I have split + the program into chunks for readability). +

+ +

+ Standard includes for socket programs, and predeclare + static functions: +

+ +
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+
+#include <pool.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_iolib.h>
+#include <pthr_server.h>
+
+static void start_processor (int sock, void *data);
+static void run (void *);
+
+ +

+ Recall from the diagram above that we will start one + processing thread for each client. The following structure + is used to store the per-thread information about that + processing thread: +

+ +
+typedef struct processor_thread
+{
+  pseudothread pth;		/* Pseudothread handle. */
+  int sock;			/* Socket. */
+} *processor_thread;
+
+ +

+ main is very simple, since + pthr_server_main_loop does all the hard work + of opening up a listening socket, forking into the + background, parsing command line arguments and so on. + Note that we pass a pointer to our + start_processor function. +

+ +
+int
+main (int argc, char *argv[])
+{
+  /* Start up the server. */
+  pthr_server_main_loop (argc, argv, start_processor);
+
+  exit (0);
+}
+
+ +

+ Whenever a client makes a new connection to our server, + the listener thread is going to call start_processor. + This creates allocates the per-thread data structure + and starts the new thread. The run + function is the actual new processing thread running. +

+ +
+static void
+start_processor (int sock, void *data)
+{
+  pool pool;
+  processor_thread p;
+
+  pool = new_pool ();
+  p = pmalloc (pool, sizeof *p);
+
+  p->sock = sock;
+  p->pth = new_pseudothread (pool, run, p, "processor thread");
+
+  pth_start (p->pth);
+}
+
+static void
+run (void *vp)
+{
+  processor_thread p = (processor_thread) vp;
+  io_handle io;
+  char buffer[256];
+
+  io = io_fdopen (p->sock);
+
+  /* Sit in a loop reading strings and echoing them back. */
+  while (io_fgets (buffer, sizeof buffer, io, 1))
+    io_fputs (buffer, io);
+
+  io_fclose (io);
+
+  pth_exit ();
+}
+
+ +

+ Here is a typical run with this program (what I + typed is shown in bold text): +

+ +
+$ ./eg_echo -p 9000
+$ telnet localhost 9000
+Trying 127.0.0.1...
+Connected to localhost.localnet (127.0.0.1).
+Escape character is '^]'.
+hello
+hello
+goodbye
+goodbye
+^]
+
+telnet> quit
+Connection closed.
+
+ +

Simple HTTP server

+ +

+ Note: Although it is possible to write complete + mini webservers using just pthrlib, it is + often more flexible and just as fast to use + rws's + shared object scripts. rws provides you + with the complete web serving framework. If you don't + use rws and you need to, say, serve an + image or a static page at some point in your application, + then you will need to either link to another web server + like Apache, or else write your own static file service + code (it can be done -- we did it for the chat server -- + but it's unnecessary). +

+ +

+ The following code comes from example 1 supplied with + pthrlib. You can find the working code + in the examples/ directory. I have omitted + some parts of the code in order to concentrate on the + interesting and relevant bits. +

+ +

+ First of all, the main function: +

+ +
+static void start_processor (int sock, void *);
+
+int
+main (int argc, char *argv[])
+{
+  /* Start up the server. */
+  pthr_server_main_loop (argc, argv, start_processor);
+
+  exit (0);
+}
+
+static void
+start_processor (int sock, void *data)
+{
+  (void) new_eg1_echo_processor (sock);
+}
+
+ +

+ Again, we are using pthr_server_main_loop to + do the hard work. start_processor starts the + processor thread. The processor thread's run + function has the following outline: +

+ +
+static void
+run (void *vp)
+{
+  eg1_echo_processor p = (eg1_echo_processor) vp;
+  int close = 0;
+  io_handle io;
+
+  io = io_fdopen (p->sock);
+
+  /* Sit in a loop reading HTTP requests. */
+  while (!close)
+    {
+      /* Parse the HTTP request. */
+            :    :    :
+            :    :    :
+
+      /* Form the HTTP response. */
+            :    :    :
+            :    :    :
+    }
+
+  io_fclose (io);
+
+  pth_exit ();
+}
+
+ +

+ The purpose of this loop is to deal with HTTP keepalives, + where a client (or perhaps many different clients through a + proxy) makes a series of requests over the same TCP connection. + For each request, we'll make an iteration of the while + loop. Each request is independent of the previous one. +

+ +

+ At the beginning of the thread, the listening thread hands us + a socket file descriptor in sock. Doing I/O directly + on a file descriptor is inconvenient, and it can't be + wrapped up directly in a stdio FILE * + because these block, hanging the entire process (and all other + threads). iolib is a replacement for stdio + which works with pools and doesn't block. io_fdopen + wraps up a file descriptor in a full buffered io_handle. +

+ +

+ Now lets look at the step which parses the HTTP request: +

+ +
+  http_request http_request;
+  cgi cgi;
+  pool pool = pth_get_pool (p->pth);
+            :    :    :
+            :    :    :
+
+      /* ----- HTTP request ----- */
+      http_request = new_http_request (pool, io);
+      if (http_request == 0)	/* Normal end of file. */
+        break;
+
+      cgi = new_cgi (pool, http_request, io);
+      if (cgi == 0)		/* XXX Should send an error here. */
+	break;
+
+ +

+ The new_http_request function parses the + HTTP headers. It does pretty much the equivalent of what + Apache does just before it hands off to a normal CGI script. + You can think of new_cgi as being somewhat + equivalent to Perl's CGI.pm. +

+ +

+ Here's the code which generates the HTTP response: +

+ +
+  http_response http_response;
+            :    :    :
+            :    :    :
+
+      http_response = new_http_response (pool, http_request,
+					 io,
+					 200, "OK");
+      http_response_send_header (http_response,
+                                 "Content-Type", "text/plain");
+      close = http_response_end_headers (http_response);
+
+      if (!http_request_is_HEAD (http_request))
+	{
+	  io_fprintf (io, "Hello. This is your server.\r\n\r\n");
+	  io_fprintf (io, "Your browser sent the following headers:\r\n");
+
+	  headers = http_request_get_headers (http_request);
+	  for (i = 0; i < vector_size (headers); ++i)
+	    {
+	      vector_get (headers, i, header);
+	      io_fprintf (io, "\t%s: %s\r\n", header.key, header.value);
+	    }
+
+	  io_fprintf (io, "----- end of headers -----\r\n");
+
+	  io_fprintf (io, "The URL was: %s\r\n",
+		      http_request_get_url (http_request));
+	  io_fprintf (io, "The path component was: %s\r\n",
+		      http_request_path (http_request));
+	  io_fprintf (io, "The query string was: %s\r\n",
+		      http_request_query_string (http_request));
+	  io_fprintf (io, "The query arguments were:\r\n");
+
+	  params = cgi_params (cgi);
+	  for (i = 0; i < vector_size (params); ++i)
+	    {
+	      vector_get (params, i, name);
+	      value = cgi_param (cgi, name);
+	      io_fprintf (io, "\t%s=%s\r\n", name, value);
+	    }
+
+	  io_fprintf (io, "----- end of parameters -----\r\n");
+	}
+
+ +

+ new_http_response, + http_response_send_header and + http_response_end_headers generates the + HTTP headers for the response back to the client. We'll + see those headers in a minute. + Notice that we send back an explicit + Content-Type: text/plain + header. +

+ +

+ The rest of the code actually generates the page. The + simplest way to describe it is to show an actual interaction + with the server. What I typed is shown in bold text. +

+ +
+$ ./pthr_eg1_echo -p 9000
+$ telnet localhost 9000
+Trying 127.0.0.1...
+Connected to localhost.
+Escape character is '^]'.
+GET /path/here?abc=123&def=456 HTTP/1.0
+Host: localhost:9000
+
+HTTP/1.1 200 OK
+Content-Type: text/plain
+Server: pthrlib-httpd/3.0.3
+Date: Fri, 30 Aug 2002 17:04:03 GMT
+Connection: close
+
+Hello. This is your server.
+
+Your browser sent the following headers:
+        host: localhost:9000
+----- end of headers -----
+The URL was: /path/here?abc=123&def=456
+The path component was: /path/here
+The query string was: abc=123&def=456
+The query arguments were:
+        abc=123
+        def=456
+----- end of parameters -----
+Connection closed by foreign host.
+
+ +

Static file webserver

+ +

+ This following code is from example 2. You can find + the complete working program in the examples/ + directory. It's a very minimal webserver which can + only serve static files from a single directory. If + you start the server up as root, then + the server will chroot(2) itself into + a configurable directory, and change its user ID to + nobody.nobody +

+ +

+ Again the main function uses + pthr_server_main_loop for simplicity. + However one thing which pthr_server_main_loop + can't do (yet) is set up signal handlers, so we have + to do those by hand first: +

+ +
+int
+main (int argc, char *argv[])
+{
+  struct sigaction sa;
+
+  /* Intercept signals. */
+  memset (&sa, 0, sizeof sa);
+  sa.sa_handler = catch_quit_signal;
+  sa.sa_flags = SA_RESTART;
+  sigaction (SIGINT, &sa, 0);
+  sigaction (SIGQUIT, &sa, 0);
+  sigaction (SIGTERM, &sa, 0);
+
+  /* ... but ignore SIGPIPE errors. */
+  sa.sa_handler = SIG_IGN;
+  sa.sa_flags = SA_RESTART;
+  sigaction (SIGPIPE, &sa, 0);
+
+  /* Start up the server. */
+  pthr_server_chroot (root);
+  pthr_server_username (user);
+  pthr_server_main_loop (argc, argv, start_processor);
+
+  exit (0);
+}
+
+static void
+start_processor (int sock, void *data)
+{
+  (void) new_eg2_server_processor (sock);
+}
+
+static void
+catch_quit_signal (int sig)
+{
+  exit (0);
+}
+
+ +

+ Notice that just before we actually call + pthr_server_main_loop, we configure + the main loop code first by telling it the + root directory (where we want to chroot(2) + to) and the username (nobody). +

+ +

+ The eg2_server_processor thread + structure contains a little more data this time. It + contains most of the information about the current + request: +

+ +
+struct eg2_server_processor
+{
+  /* Pseudothread handle. */
+  pseudothread pth;
+
+  /* Socket. */
+  int sock;
+
+  /* Pool for memory allocations. */
+  struct pool *pool;
+
+  /* HTTP request. */
+  http_request http_request;
+
+  /* IO handle. */
+  io_handle io;
+};
+
+ +

+ The run function has the same basic outline, + ie. a while loop to process each request on + the same keep-alive connection, and a call to + new_http_request to parse the HTTP headers. The + outline code is shown in red text + below. The code to handle the response is shown in + black. +

+ +
+static void
+run (void *vp)
+{
+  eg2_server_processor p = (eg2_server_processor) vp;
+  int close = 0;
+  const char *path;
+  struct stat statbuf;
+
+  p->io = io_fdopen (p->sock);
+
+  /* Sit in a loop reading HTTP requests. */
+  while (!close)
+    {
+      /* ----- HTTP request ----- */
+      p->http_request = new_http_request (pool, p->io);
+      if (p->http_request == 0)	/* Normal end of file. */
+        break;
+
+      /* Get the path and locate the file. */
+      path = http_request_path (p->http_request);
+      if (stat (path, &statbuf) == -1)
+	{
+	  close = file_not_found_error (p);
+	  continue;
+	}
+
+      /* File or directory? */
+      if (S_ISDIR (statbuf.st_mode))
+	{
+	  close = serve_directory (p, path, &statbuf);
+	  continue;
+	}
+      else if (S_ISREG (statbuf.st_mode))
+	{
+	  close = serve_file (p, path, &statbuf);
+	  continue;
+	}
+      else
+	{
+	  close = file_not_found_error (p);
+	  continue;
+	}
+    }
+
+  io_fclose (p->io);
+
+  pth_exit ();
+}
+
+ +

+ This is a very simple webserver, so all it does is take the + path component of the request, and uses it directly + as a filename (note that it relies completely on the + chroot(2) environment for security). +

+ +

+ Firstly it calls stat to find out if the filename + is a directory or a regular file. If it is neither, or if the + file doesn't exist, it calls file_not_found_error + which sends back a 404 FILE NOT FOUND error. +

+ +

+ If the file is a regular file, we call serve_file, + which is a simple piece of code: +

+ +
+static int
+serve_file (eg2_server_processor p, const char *path,
+	    const struct stat *statbuf)
+{
+  http_response http_response;
+  const int n = 4096;
+  char *buffer = alloca (n);
+  int cl, fd, r;
+  char *content_length = pitoa (p->pool, statbuf->st_size);
+
+  fd = open (path, O_RDONLY);
+  if (fd < 0)
+    return file_not_found_error (p);
+
+  http_response = new_http_response (pool, p->http_request, p->io,
+				     200, "OK");
+  http_response_send_headers (http_response,
+			      /* Content type. */
+			      "Content-Type", "text/plain",
+			      "Content-Length", content_length,
+			      /* End of headers. */
+			      NULL);
+  cl = http_response_end_headers (http_response);
+
+  if (http_request_is_HEAD (p->http_request)) return cl;
+
+  while ((r = read (fd, buffer, n)) > 0)
+    {
+      io_fwrite (buffer, r, 1, p->io);
+    }
+
+  if (r < 0)
+    perror ("read");
+
+  close (fd);
+
+  return cl;
+}
+
+ +

+ Firstly we work out the size of the file, using the + statbuf.st_size field. The + c2lib + function pitoa turns this into a string (all + headers must be passed as strings). Next we open the + file. If this fails, then the file is inaccessible or + has just gone, so we return a 404 instead. +

+ +

+ Next we generate our headers: +

+ +
+Content-Type: text/plain
+Content-Length: (size of the file in octets)
+
+ +

+ pthrlib will generate other standard + headers as well. +

+ +

+ If the request was a HEAD request, then + the client only wants to see the headers, so we stop + right there. Otherwise we copy the file back to our + user. +

+ +

+ Party question: Why is it OK to use read(2) + when reading the file, but not OK to use write(2) + when writing to the socket? Why will this not cause + the whole server process to block (on Linux at least)? +

+ +

+ Serving a directory is more complicated, so we'll take it in + steps. Recall that to serve a directory, we actually need + to create an HTML page which lists the files, with information + about those files and links to the files themselves. +

+ +

+ Firstly if the user requested the directory as: +

+ +
+http://your.hostname/path/to/directory
+
+ +

+ then we need to redirect them to: +

+ +
+http://your.hostname/path/to/directory/
+
+ +

+ (note the trailing slash). The reason for this is that + relative links within our page won't work otherwise. The + browser will request /path/to/file instead of + /path/to/directory/file. This is actually a + bit of webserver arcana which is often forgotten. If you + don't believe me, Apache does this too: go look at the source! +

+ +
+static int
+serve_directory (eg2_server_processor p, const char *path,
+		 const struct stat *statbuf)
+{
+  http_response http_response;
+  int close;
+  DIR *dir;
+  struct dirent *d;
+
+  /* If the path doesn't end with a "/", then we need to send
+   * a redirect back to the client so it refetches the page
+   * with "/" appended.
+   */
+  if (path[strlen (path)-1] != '/')
+    {
+      char *location = psprintf (p->pool, "%s/", path);
+      return moved_permanently (p, location);
+    }
+
+ +

+ moved_permanently sends back a 301 MOVED PERMANENTLY + page causing the browser to re-request the new location. +

+ +

+ The next piece of code should be familiar boilerplate. We open + the directory, and send back headers. If the request is + HEAD we then drop out. +

+ +
+  dir = opendir (path);
+  if (dir == 0)
+    return file_not_found_error (p);
+
+  http_response = new_http_response (pool, p->http_request, p->io,
+				     200, "OK");
+  http_response_send_headers (http_response,
+			      /* Content type. */
+			      "Content-Type", "text/html",
+			      NO_CACHE_HEADERS,
+			      /* End of headers. */
+			      NULL);
+  close = http_response_end_headers (http_response);
+
+  if (http_request_is_HEAD (p->http_request)) return close;
+
+ +

+ The next piece of code is the complicated bit which generates + the HTML page listing the files: +

+ +
+  io_fprintf (p->io,
+	      "<html><head><title>Directory: %s</title></head>" CRLF
+	      "<body bgcolor=\"#ffffff\">" CRLF
+	      "<h1>Directory: %s</h1>" CRLF
+	      "<table>" CRLF
+	      "<tr><td></td><td></td>"
+	      "<td><a href=\"..\">Parent directory</a></td></tr>" CRLF,
+	      path, path);
+
+  while ((d = readdir (dir)) != 0)
+    {
+      if (d->d_name[0] != '.')	/* Ignore hidden files. */
+	{
+	  const char *filename;
+	  struct stat fstatbuf;
+
+	  /* Generate the full pathname to this file. */
+	  filename = psprintf (p->pool, "%s/%s", path, d->d_name);
+
+	  /* Stat the file to find out what it is. */
+	  if (lstat (filename, &fstatbuf) == 0)
+	    {
+	      const char *type;
+	      int size;
+
+	      if (S_ISDIR (fstatbuf.st_mode))
+		type = "dir";
+	      else if (S_ISREG (fstatbuf.st_mode))
+		type = "file";
+	      else if (S_ISLNK (fstatbuf.st_mode))
+		type = "link";
+	      else
+		type = "special";
+
+	      size = fstatbuf.st_size;
+
+	      /* Print the details. */
+	      io_fprintf (p->io,
+			  "<tr><td>[ %s ]</td><td align=right>%d</td>"
+			  "<td><a href=\"%s%s\">%s</a>",
+			  type, size,
+			  d->d_name,
+			  S_ISDIR (fstatbuf.st_mode) ? "/" : "",
+			  d->d_name);
+
+	      if (S_ISLNK (fstatbuf.st_mode))
+		{
+		  char link[NAME_MAX+1];
+		  int r;
+
+		  r = readlink (filename, link, NAME_MAX);
+		  if (r >= 0) link[r] = '\0';
+		  else strcpy (link, "unknown");
+
+		  io_fprintf (p->io, " -&gt; %s", link);
+		}
+
+	      io_fputs ("</td></tr>" CRLF, p->io);
+	    }
+	}
+    }
+
+  io_fprintf (p->io,
+	      "</table></body></html>" CRLF);
+
+  return close;
+
+ +

+ We first send the top of the HTML page, and the beginning + of the table (the whole page is one large table, of course). +

+ +

+ Next we loop over the directory entries using readdir(3) + to read each one. Ignoring files which start with a dot (.) we + lstat(2) each file to find out if it's a directory, + file or symbolic link, or some type of special device node. +

+ +

+ Depending on the file type, we generate a different bit + of HTML containing a relative link to the file or + directory (if it's a directory we need to remember to + append a trailing slash to the name to avoid that extra + 301 redirect). +

+ +

+ Finally after we reach the end of the directory we finish + of the table and the page and return. +

+ +

Further examples

+ +

+ That's the end of this pthrlib tutorial, I + hope you enjoyed it. +

+ +

+ pthrlib isn't just about writing web servers. + You can use it to write all sorts of types of servers, + or even clients (it has an FTP client library which I + used to load-test Net::FTPServer). +

+ +

+ If, however, you feel like using pthrlib to + write a web server, I strongly urge you to use + rws + and shared object scripts. These are described in + the rws + documentation. (rws uses pthrlib). +

+ +

Links to manual pages

+ +

+ (These manual pages are not always up to date. For the + latest documentation, always consult the manual pages + supplied with the latest pthrlib package!) +

+ +

Pseudothreads

+ + + +

Server main loop

+ + + +

Buffered I/O library

+ + + +

HTTP server library

+ + + +

CGI library

+ + + +

Thread synchronisation (mutexes, R/W-locks, wait queues)

+ + + +

FTP client library

+ + + +
+
Richard Jones
+ + +Last modified: Sun Dec 1 14:44:00 GMT 2002 + + + diff --git a/doc/io_copy.3.html b/doc/io_copy.3.html new file mode 100644 index 0000000..50fe5ee --- /dev/null +++ b/doc/io_copy.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_fclose.3.html b/doc/io_fclose.3.html new file mode 100644 index 0000000..50fe5ee --- /dev/null +++ b/doc/io_fclose.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_fdopen.3.html b/doc/io_fdopen.3.html new file mode 100644 index 0000000..50fe5ee --- /dev/null +++ b/doc/io_fdopen.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_fflush.3.html b/doc/io_fflush.3.html new file mode 100644 index 0000000..7a0a76c --- /dev/null +++ b/doc/io_fflush.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_fgetc.3.html b/doc/io_fgetc.3.html new file mode 100644 index 0000000..7a0a76c --- /dev/null +++ b/doc/io_fgetc.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_fgets.3.html b/doc/io_fgets.3.html new file mode 100644 index 0000000..7a0a76c --- /dev/null +++ b/doc/io_fgets.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_fileno.3.html b/doc/io_fileno.3.html new file mode 100644 index 0000000..7a0a76c --- /dev/null +++ b/doc/io_fileno.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_fprintf.3.html b/doc/io_fprintf.3.html new file mode 100644 index 0000000..772278f --- /dev/null +++ b/doc/io_fprintf.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_fputc.3.html b/doc/io_fputc.3.html new file mode 100644 index 0000000..772278f --- /dev/null +++ b/doc/io_fputc.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_fputs.3.html b/doc/io_fputs.3.html new file mode 100644 index 0000000..772278f --- /dev/null +++ b/doc/io_fputs.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_fread.3.html b/doc/io_fread.3.html new file mode 100644 index 0000000..772278f --- /dev/null +++ b/doc/io_fread.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_fwrite.3.html b/doc/io_fwrite.3.html new file mode 100644 index 0000000..b81547f --- /dev/null +++ b/doc/io_fwrite.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_get_inbufcount.3.html b/doc/io_get_inbufcount.3.html new file mode 100644 index 0000000..b81547f --- /dev/null +++ b/doc/io_get_inbufcount.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_get_outbufcount.3.html b/doc/io_get_outbufcount.3.html new file mode 100644 index 0000000..b81547f --- /dev/null +++ b/doc/io_get_outbufcount.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_pclose.3.html b/doc/io_pclose.3.html new file mode 100644 index 0000000..c43aab6 --- /dev/null +++ b/doc/io_pclose.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_popen.3.html b/doc/io_popen.3.html new file mode 100644 index 0000000..c43aab6 --- /dev/null +++ b/doc/io_popen.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_setbufmode.3.html b/doc/io_setbufmode.3.html new file mode 100644 index 0000000..c43aab6 --- /dev/null +++ b/doc/io_setbufmode.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/io_ungetc.3.html b/doc/io_ungetc.3.html new file mode 100644 index 0000000..c43aab6 --- /dev/null +++ b/doc/io_ungetc.3.html @@ -0,0 +1,172 @@ + + + + +io_fdopen + + + +

io_fdopen

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+
+ +

DESCRIPTION

+ + + +
+The io_* functions replace the normal blocking C +library f* functions with equivalents which work on +non-blocking pseudothread file descriptors.
+ + + +
+All of the functions in the synopsis above work identically +to the C library equivalents, except where documented +below.
+ + + +
+io_fdopen associates a socket sock with a I/O +handle. The association cannot be broken later (so use +dup(2) if you wish to later take back control of the +underlying socket). If either the current thread exits or +io_fclose is called, the underlying socket is closed +(with close(2)).
+ + + +
+io_fclose flushes all unwritten data out of the +socket and closes it.
+ + + +
+io_fgets operates similarly to the ordinary C library +function fgets(3), except that it contains a useful +fourth argument, store_eol. If this fourth argument +is false, then the end of line characters (CR, CR +LF or LF) are stripped from the string before it +is stored.
+ + + +
+io_copy copies len bytes from from_io +to to_io. If len equals -1 then bytes are +copied from from_io until end of file is reached. If +len equals 0, then no bytes are copied. The number of +bytes actually copied is returned.
+ + + +
+io_setbufmode sets the output buffer mode, and works +completely differently to the ordinary C library function +setbufmode(3). The three mode arguments possible are: +IO_MODE_LINE_BUFFERED, IO_MODE_UNBUFFERED and +IO_MODE_FULLY_BUFFERED, and these correspond to line +buffering, no buffering and full (block) +buffering.
+ + + +
+io_get_inbufcount and io_get_outbufcount +return the number of characters read and written on the +socket since the socket was associated with the I/O +object.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+fgetc(3), fgets(3), ungetc(3), +fread(3), fputc(3), fputs(3), +fprintf(3), fwrite(3), fflush(3), +fileno(3), popen(3), pclose(3), +pth_exit(3).
+
+ + diff --git a/doc/mutex_enter.3.html b/doc/mutex_enter.3.html new file mode 100644 index 0000000..4d3e29d --- /dev/null +++ b/doc/mutex_enter.3.html @@ -0,0 +1,128 @@ + + + + +new_mutex + + + +

new_mutex

+NAME
+SYNOPSIS
+DESCRIPTION
+BUGS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+new_mutex, mutex_enter, mutex_leave, mutex_try_enter - mutual exclusion (mutex) locks
+ +

SYNOPSIS

+ + + +
+
#include <pthr_mutex.h>
+
+mutex new_mutex (pool);
+void mutex_enter (mutex, pseudothread);
+void mutex_leave (mutex, pseudothread);
+int mutex_try_enter (mutex, pseudothread);
+
+ +

DESCRIPTION

+ + + +
+Mutex locks are simple: at most one pseudothread may enter +the critical area protected by the lock at once. If instead +you wish multiple reader / single writer semantics, then +please see new_rwlock(3).
+ + + +
+Mutex locks are automatically released if they are being +held when the thread exits.
+ + + +
+Note that there are possible deadlocks when using locks. To +avoid deadlocks, always ensure every thread acquires locks +in the same order.
+ + + +
+new_mutex creates a new mutex object.
+ + + +
+mutex_enter and mutex_leave enter and leave +the critical section. Only one thread can run at a time +inside the critical section.
+ + + +
+mutex_try_enter is identical to mutex_enter +except that it does not block if the lock is held by another +thread. The function returns true if the lock was +successfully acquired, or false if another thread is +currently holding it.
+ +

BUGS

+ + + +
+A common mistake is to accidentally call mutex_leave +when you are not holding the lock. This generally causes the +library to crash.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/mutex_leave.3.html b/doc/mutex_leave.3.html new file mode 100644 index 0000000..4d3e29d --- /dev/null +++ b/doc/mutex_leave.3.html @@ -0,0 +1,128 @@ + + + + +new_mutex + + + +

new_mutex

+NAME
+SYNOPSIS
+DESCRIPTION
+BUGS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+new_mutex, mutex_enter, mutex_leave, mutex_try_enter - mutual exclusion (mutex) locks
+ +

SYNOPSIS

+ + + +
+
#include <pthr_mutex.h>
+
+mutex new_mutex (pool);
+void mutex_enter (mutex, pseudothread);
+void mutex_leave (mutex, pseudothread);
+int mutex_try_enter (mutex, pseudothread);
+
+ +

DESCRIPTION

+ + + +
+Mutex locks are simple: at most one pseudothread may enter +the critical area protected by the lock at once. If instead +you wish multiple reader / single writer semantics, then +please see new_rwlock(3).
+ + + +
+Mutex locks are automatically released if they are being +held when the thread exits.
+ + + +
+Note that there are possible deadlocks when using locks. To +avoid deadlocks, always ensure every thread acquires locks +in the same order.
+ + + +
+new_mutex creates a new mutex object.
+ + + +
+mutex_enter and mutex_leave enter and leave +the critical section. Only one thread can run at a time +inside the critical section.
+ + + +
+mutex_try_enter is identical to mutex_enter +except that it does not block if the lock is held by another +thread. The function returns true if the lock was +successfully acquired, or false if another thread is +currently holding it.
+ +

BUGS

+ + + +
+A common mistake is to accidentally call mutex_leave +when you are not holding the lock. This generally causes the +library to crash.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/mutex_try_enter.3.html b/doc/mutex_try_enter.3.html new file mode 100644 index 0000000..4d3e29d --- /dev/null +++ b/doc/mutex_try_enter.3.html @@ -0,0 +1,128 @@ + + + + +new_mutex + + + +

new_mutex

+NAME
+SYNOPSIS
+DESCRIPTION
+BUGS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+new_mutex, mutex_enter, mutex_leave, mutex_try_enter - mutual exclusion (mutex) locks
+ +

SYNOPSIS

+ + + +
+
#include <pthr_mutex.h>
+
+mutex new_mutex (pool);
+void mutex_enter (mutex, pseudothread);
+void mutex_leave (mutex, pseudothread);
+int mutex_try_enter (mutex, pseudothread);
+
+ +

DESCRIPTION

+ + + +
+Mutex locks are simple: at most one pseudothread may enter +the critical area protected by the lock at once. If instead +you wish multiple reader / single writer semantics, then +please see new_rwlock(3).
+ + + +
+Mutex locks are automatically released if they are being +held when the thread exits.
+ + + +
+Note that there are possible deadlocks when using locks. To +avoid deadlocks, always ensure every thread acquires locks +in the same order.
+ + + +
+new_mutex creates a new mutex object.
+ + + +
+mutex_enter and mutex_leave enter and leave +the critical section. Only one thread can run at a time +inside the critical section.
+ + + +
+mutex_try_enter is identical to mutex_enter +except that it does not block if the lock is held by another +thread. The function returns true if the lock was +successfully acquired, or false if another thread is +currently holding it.
+ +

BUGS

+ + + +
+A common mistake is to accidentally call mutex_leave +when you are not holding the lock. This generally causes the +library to crash.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/new_cgi.3.html b/doc/new_cgi.3.html new file mode 100644 index 0000000..f83bb91 --- /dev/null +++ b/doc/new_cgi.3.html @@ -0,0 +1,132 @@ + + + + +new_cgi + + + +

new_cgi

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_cgi, cgi_params, cgi_param, cgi_param_list, cgi_erase, copy_cgi - Library for parsing CGI query strings.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_cgi.h>
+
+cgi new_cgi (pool, http_request, io_handle);
+vector cgi_params (cgi);
+const char *cgi_param (cgi, const char *name);
+const vector cgi_param_list (cgi, const char *name);
+int cgi_erase (cgi, const char *name);
+cgi copy_cgi (pool, cgi);
+
+ +

DESCRIPTION

+ + + +
+new_cgi creates a new CGI object from an existing +HTTP request. It reads the query string or POSTed parameters +and parses them internally. CGI parameters are case +sensitive, and multiple parameters may be passed with the +same name. Parameter values are automatically unescaped by +the library before you get to see them.
+ + + +
+cgi_params returns a list of all the names of the +parameters passed to the script. The list is returned as a +vector of char *.
+ + + +
+cgi_param returns the value of a single named CGI +parameter, or NULL if there is no such parameter. If +multiple parameters were given with the same name, this +returns one of them, essentially at random.
+ + + +
+cgi_param_list returns the list of values of the +named CGI parameter. The list is returned as a vector +of char *.
+ + + +
+cgi_erase erases the named parameter. If a parameter +was erased, this returns true, else this returns +false.
+ + + +
+copy_cgi copies cgi into pool +pool.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+cgi_get_post_max(3), cgi_escape(3), +new_http_request(3).
+
+ + diff --git a/doc/new_ftpc.3.html b/doc/new_ftpc.3.html new file mode 100644 index 0000000..c856941 --- /dev/null +++ b/doc/new_ftpc.3.html @@ -0,0 +1,92 @@ + + + + +new_ftpc + + + +

new_ftpc

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_ftpc - Create a new FTP client object.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_ftpc.h>
+
+ftpc new_ftpc (pool, pseudothread pth, const char *server);
+
+ +

DESCRIPTION

+ + + +
+Create a new FTP client object, connected to the FTP server +called server. The server may be an IP address +or a hostname. If the server name ends with +:port then port is the port number to connect +to.
+ + + +
+The default mode for new connections is active mode. Call +ftpc_set_mode(3) to change the mode.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+ftpc_login(3), ftpc_set_mode(3).
+
+ + diff --git a/doc/new_http_request.3.html b/doc/new_http_request.3.html new file mode 100644 index 0000000..d661b90 --- /dev/null +++ b/doc/new_http_request.3.html @@ -0,0 +1,165 @@ + + + + +new_http_request + + + +

new_http_request

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+
+ +

DESCRIPTION

+ + + +
+These functions allow you to efficiently parse incoming HTTP +requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1 +clients. The request parser understands GET, HEAD and POST +requests and conforms as far as possible to RFC +2616.
+ + + +
+new_http_request creates a new request object, +parsing the incoming request on the given io_handle. +If the stream closes at the beginning of the request the +function returns NULL. If the request is faulty, then +the library prints a message to syslog and throws an +exception by calling pth_die(3). Otherwise it +initializes a complete http_request object and +returns it.
+ + + +
+http_request_time returns the timestamp of the +incoming request.
+ + + +
+http_request_url returns the complete URL of the +request. http_request_path returns just the path +component of the URL (ie. without the query string if there +was one). http_request_query_string returns just the +query string (for GET requests only). Do not do your own +parsing of query strings: there is a CGI library built into +pthrlib (see: new_cgi(3)).
+ + + +
+http_request_method returns the method, one of +HTTP_METHOD_GET, HTTP_METHOD_HEAD or +HTTP_METHOD_POST. http_request_is_HEAD is just +a quick way of testing if the method is a HEAD request. +http_request_method_string returns the method as a +string rather than a coded number.
+ + + +
+http_request_version returns the major and minor +numbers of the HTTP request (eg. major = 1, minor = 0 for a +HTTP/1.0 request).
+ + + +
+http_request_nr_headers, +http_request_get_headers and +http_request_get_header return the number of HTTP +headers, the list of HTTP headers and a particular HTTP +header (if it exists). http_request_get_headers +returns a vector or struct http_header. This +structure contains at least two fields called key and +value. HTTP header keys are case insensitive when +searching, and you will find that the list of keys returned +by http_request_get_headers has been converted to +lowercase.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_response(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/new_http_response.3.html b/doc/new_http_response.3.html new file mode 100644 index 0000000..e428de4 --- /dev/null +++ b/doc/new_http_response.3.html @@ -0,0 +1,122 @@ + + + + +new_http_response + + + +

new_http_response

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+new_http_response, http_response_send_header, http_response_send_headers, http_response_end_headers - functions for sending HTTP responses
+ +

SYNOPSIS

+ + + +
+
#include <pthr_http.h>
+
+http_response new_http_response (pseudothread, http_request, io_handle, int code, const char *msg);
+void http_response_send_header (http_response, const char *key, const char *value);
+void http_response_send_headers (http_response, ...);
+int http_response_end_headers (http_response h);
+
+ +

DESCRIPTION

+ + + +
+These functions allow you to efficiently generate outgoing +HTTP responses.
+ + + +
+new_http_response generates a new HTTP response +object and returns it. code is the HTTP response code +(see RFC 2616 for a list of codes), and msg is the +HTTP response message.
+ + + +
+http_response_send_header sends a single HTTP header +back to the client. The header is constructed by +concatenating key, ": ", +value and CR LF.
+ + + +
+http_response_send_headers sends back several headers +in a single call. The arguments to this function are a list +of key, value pairs followed by a single +NULL argument which terminates the list.
+ + + +
+http_response_end_headers ends the header list. It +causes the code to emit any missing-but-required headers and +then send the final CR LF characters.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_http_request(3), new_cgi(3), +new_pseudothread(3), io_fdopen(3), RFC +2616.
+
+ + diff --git a/doc/new_mutex.3.html b/doc/new_mutex.3.html new file mode 100644 index 0000000..9bfae72 --- /dev/null +++ b/doc/new_mutex.3.html @@ -0,0 +1,128 @@ + + + + +new_mutex + + + +

new_mutex

+NAME
+SYNOPSIS
+DESCRIPTION
+BUGS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+new_mutex, mutex_enter, mutex_leave, mutex_try_enter - mutual exclusion (mutex) locks
+ +

SYNOPSIS

+ + + +
+
#include <pthr_mutex.h>
+
+mutex new_mutex (pool);
+void mutex_enter (mutex, pseudothread);
+void mutex_leave (mutex, pseudothread);
+int mutex_try_enter (mutex, pseudothread);
+
+ +

DESCRIPTION

+ + + +
+Mutex locks are simple: at most one pseudothread may enter +the critical area protected by the lock at once. If instead +you wish multiple reader / single writer semantics, then +please see new_rwlock(3).
+ + + +
+Mutex locks are automatically released if they are being +held when the thread exits.
+ + + +
+Note that there are possible deadlocks when using locks. To +avoid deadlocks, always ensure every thread acquires locks +in the same order.
+ + + +
+new_mutex creates a new mutex object.
+ + + +
+mutex_enter and mutex_leave enter and leave +the critical section. Only one thread can run at a time +inside the critical section.
+ + + +
+mutex_try_enter is identical to mutex_enter +except that it does not block if the lock is held by another +thread. The function returns true if the lock was +successfully acquired, or false if another thread is +currently holding it.
+ +

BUGS

+ + + +
+A common mistake is to accidentally call mutex_leave +when you are not holding the lock. This generally causes the +library to crash.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/new_pseudothread.3.html b/doc/new_pseudothread.3.html new file mode 100644 index 0000000..a2355a7 --- /dev/null +++ b/doc/new_pseudothread.3.html @@ -0,0 +1,110 @@ + + + + +new_pseudothread + + + +

new_pseudothread

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+new_pseudothread, pth_start, pseudothread_get_threads, pseudothread_count_threads - lightweight "pseudothreads" library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pseudothread new_pseudothread (pool, void (*run) (void *), void *data, const char *name);
+void pth_start (pseudothread pth);
+vector pseudothread_get_threads (pool);
+int pseudothread_count_threads (void);
+
+ +

DESCRIPTION

+ + + +
+Pseudothreads are lightweight, cooperatively scheduled +threads.
+ + + +
+new_pseudothread creates a new pseudothread. The +thread only starts running when you call pth_start. +The pool argument passed is used for all allocations +within the thread. This pool is automatically deleted when +the thread exits. The name argument is the name of +the thread (used in thread listings -- see +pseudothread_get_threads(3)). You may change the name +later. The run and data arguments are the +entry point into the thread. The entry point is called as +run (data).
+ + + +
+pseudothread_get_threads returns a list of all the +currently running pseudothreads. This allows you to +implement a "process listing" for a program. The +returned value is a vector of pseudothread structures (not +pointers). These structures are opaque to you, but you can +call the pth_get_* functions on the address of each +one.
+ + + +
+pseudothread_count_threads counts the number of +currently running threads.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/new_rwlock.3.html b/doc/new_rwlock.3.html new file mode 100644 index 0000000..d3a82f0 --- /dev/null +++ b/doc/new_rwlock.3.html @@ -0,0 +1,184 @@ + + + + +new_rwlock + + + +

new_rwlock

+NAME
+SYNOPSIS
+DESCRIPTION
+BUGS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+new_rwlock, rwlock_writers_have_priority, rwlock_readers_have_priority, rwlock_enter_read, rwlock_enter_write, rwlock_try_enter_read, rwlock_try_enter_write, rwlock_leave - multiple reader / single writer locks (rwlocks)
+ +

SYNOPSIS

+ + + +
+
#include <pthr_rwlock.h>
+
+rwlock new_rwlock (pool);
+void rwlock_writers_have_priority (rwlock);
+void rwlock_readers_have_priority (rwlock);
+void rwlock_enter_read (rwlock, pseudothread);
+void rwlock_enter_write (rwlock, pseudothread);
+int rwlock_try_enter_read (rwlock, pseudothread);
+int rwlock_try_enter_write (rwlock, pseudothread);
+void rwlock_leave (rwlock, pseudothread);
+
+ +

DESCRIPTION

+ + + +
+Multiple reader / single writer locks (rwlocks) do what they +say. They allow either many readers to access a critical +section or a single writer (but not both). If instead you +require simple mutex semantics, then please see +<pthr_mutex.h>.
+ + + +
+RWlocks are automatically released if they are being held +when the thread exits.
+ + + +
+RWlocks are not ``upgradable''. The implementation is too +complex to justify that.
+ + + +
+Note that there are possible deadlocks when using locks. To +avoid deadlocks, always ensure every thread acquires locks +in the same order.
+ + + +
+new_rwlock creates a new rwlock object.
+ + + +
+rwlock_writers_have_priority changes the nature of +the lock so that writers have priority over readers. If this +is the case then new readers will not be able to enter a +critical section if there are writers waiting to enter. [NB: +This is the default.]
+ + + +
+rwlock_readers_have_priority changes the nature of +the lock so that readers have priority over writers. Note +that if this is the case then writers are likely to be +starved if the lock is frequently read.
+ + + +
+rwlock_enter_read enters the critical section as a +reader. Any number of readers are allowed to enter a +critical section at the same time. This function may +block.
+ + + +
+rwlock_enter_write enters the critical section as a +writer. Only a single writer is allowed to enter a critical +section, and then only if there are no readers. This +function may block.
+ + + +
+rwlock_try_enter_read is identical to +rwlock_enter_read, but it does not block. It returns +true if the lock was successfully acquired, or false if the +operation would block.
+ + + +
+rwlock_try_enter_write is identical to +rwlock_enter_write, but it does not block. It returns +true if the lock was successfully acquired, or false if the +operation would block.
+ + + +
+rwlock_leave leaves the critical +section.
+ +

BUGS

+ + + +
+A common mistake is to accidentally call rwlock_leave +when you are not holding the lock. This generally causes the +library to crash.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/new_wait_queue.3.html b/doc/new_wait_queue.3.html new file mode 100644 index 0000000..df65c8e --- /dev/null +++ b/doc/new_wait_queue.3.html @@ -0,0 +1,217 @@ + + + + +new_wait_queue + + + +

new_wait_queue

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+HISTORY
+ +
+ + + +

NAME

+ + +
+new_wait_queue, wq_wake_up, wq_wake_up_one, wq_sleep_on, wq_nr_sleepers - wait queues
+ +

SYNOPSIS

+ + + +
+
#include <pthr_wait_queue.h>
+
+wait_queue new_wait_queue (pool);
+void wq_wake_up (wait_queue);
+void wq_wake_up_one (wait_queue);
+void wq_sleep_on (wait_queue, pseudothread);
+int wq_nr_sleepers (wait_queue);
+
+ +

DESCRIPTION

+ + + +
+new_wait_queue creates a wait queue +object.
+ + + +
+wq_wake_up wakes up all the threads which are +currently sleeping on the wait queue. Note that this +function does not block, and because pseudothreads are +non-preemptive, none of the sleeping threads will actually +begin running until at least the current thread blocks +somewhere else.
+ + + +
+wq_wake_up_one wakes up just the first thread at the +head of the wait queue (the one which has been waiting the +longest).
+ + + +
+wq_sleep_on sends the current thread to sleep on the +wait queue. This call blocks (obviously).
+ + + +
+wq_nr_sleepers returns the number of threads which +are currently asleep on the wait queue.
+ + + +
+Please read the HISTORY section below for some background +into how wait queues are implemented. This may help if you +find there are tricky race conditions in your +code.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

HISTORY

+ + + +
+Originally, wait queues were implemented using underlying +Unix pipes. This worked (to some extent) but the overhead of +requiring one pipe (ie. one inode, two file descriptors) per +wait queue made this implementation unacceptably +heavyweight.
+ + + +
+Wait queues are now implemented using a simple hack in the +reactor which will be described below.
+ + + +
+Wait queues are subtle. Consider this example: Threads 1, 2 +and 3 are sleeping on a wait queue. Now thread 4 wakes up +the queue. You would expect (probably) threads 1, 2 and 3 to +each be woken up and allowed to start running. However, +since this is a cooperatively multitasking environment, it +may happen that thread 1 wakes up first, does some work and +then goes back to sleep on the wait queue, all before +threads 2 and 3 have woken up. With a naive implementation +of wait queues, thread 4 might end up waking up thread 1 +*again* (and even again after that), never waking up threads +2 and 3 and ultimately starving those threads.
+ + + +
+To avoid this situation, we might consider two possible +alternatives: either when thread 1 goes back to sleep, it +goes to sleep on a 'different' queue, or else thread 4 might +take a copy of the wait queue and delete the queue before it +wakes any of the threads up.
+ + + +
+Another nasty situation which might arise in real life is +this: Again, threads 1, 2 and 3 are sleeping. Thread 4 wakes +them up. Thread 1, while processing its work, happens also +to wake up the same wait queue. What should happen to this +second wake-up event? Should it be ignored? Should it wake +up threads 2 and 3? Should it wake up any other threads +which happen to have gone to sleep on the queue after 1, 2 +and 3? Or perhaps some combination of these?
+ + + +
+The solution that we have come up with is as follows. A wait +queue consists of a simple list of threads which are +sleeping on it. When a thread wishes to sleep on the wait +queue, it is added to this list, and it switches back into +the reactor context. When a thread wishes to wake up all +sleepers, it: (a) copies the list of sleeping pseudothreads +into its own private space (b) clears the list of sleeping +pseudothreads (c) registers a prepoll handler to run which +will wake up (ie. switch into the context of) each of these +threads in turn (d) continues to run to completion. A thread +which wishes to wake just one pseudothread works similarly +except that it only copies (and removes) a single item off +the list.
+ + + +
+Note various invariant conditions: A thread cannot be +entered on the wait queue sleeping list more than once +(because it cannot call sleep_on when it is already +sleeping). For similar reasons, a thread cannot be entered +on any of the multiple lists at the same time. This implies +that a thread cannot be woken up multiple +times.
+ + + +
+The reader should satisfy themselves that this algorithm is +free of races, and solves all the problems outlined above. +In addition, it has the desirable property that wake_up* +never sleeps.
+
+ + diff --git a/doc/pseudothread_count_threads.3.html b/doc/pseudothread_count_threads.3.html new file mode 100644 index 0000000..31b698a --- /dev/null +++ b/doc/pseudothread_count_threads.3.html @@ -0,0 +1,110 @@ + + + + +new_pseudothread + + + +

new_pseudothread

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+new_pseudothread, pth_start, pseudothread_get_threads, pseudothread_count_threads - lightweight "pseudothreads" library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pseudothread new_pseudothread (pool, void (*run) (void *), void *data, const char *name);
+void pth_start (pseudothread pth);
+vector pseudothread_get_threads (pool);
+int pseudothread_count_threads (void);
+
+ +

DESCRIPTION

+ + + +
+Pseudothreads are lightweight, cooperatively scheduled +threads.
+ + + +
+new_pseudothread creates a new pseudothread. The +thread only starts running when you call pth_start. +The pool argument passed is used for all allocations +within the thread. This pool is automatically deleted when +the thread exits. The name argument is the name of +the thread (used in thread listings -- see +pseudothread_get_threads(3)). You may change the name +later. The run and data arguments are the +entry point into the thread. The entry point is called as +run (data).
+ + + +
+pseudothread_get_threads returns a list of all the +currently running pseudothreads. This allows you to +implement a "process listing" for a program. The +returned value is a vector of pseudothread structures (not +pointers). These structures are opaque to you, but you can +call the pth_get_* functions on the address of each +one.
+ + + +
+pseudothread_count_threads counts the number of +currently running threads.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pseudothread_get_stack_size.3.html b/doc/pseudothread_get_stack_size.3.html new file mode 100644 index 0000000..837823b --- /dev/null +++ b/doc/pseudothread_get_stack_size.3.html @@ -0,0 +1,91 @@ + + + + +pseudothread_set_stack_size + + + +

pseudothread_set_stack_size

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pseudothread_set_stack_size, pseudothread_get_stack_size - set and get default stack size
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pseudothread_set_stack_size (int size);
+int pseudothread_get_stack_size (void);
+
+ +

DESCRIPTION

+ + + +
+pseudothread_set_stack_size sets the stack size for +newly created threads. The default stack size is 64 +KBytes.
+ + + +
+pseudothread_get_stack_size returns the current stack +size setting.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_pseudothread(3).
+
+ + diff --git a/doc/pseudothread_get_threads.3.html b/doc/pseudothread_get_threads.3.html new file mode 100644 index 0000000..556d5ae --- /dev/null +++ b/doc/pseudothread_get_threads.3.html @@ -0,0 +1,110 @@ + + + + +new_pseudothread + + + +

new_pseudothread

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+new_pseudothread, pth_start, pseudothread_get_threads, pseudothread_count_threads - lightweight "pseudothreads" library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pseudothread new_pseudothread (pool, void (*run) (void *), void *data, const char *name);
+void pth_start (pseudothread pth);
+vector pseudothread_get_threads (pool);
+int pseudothread_count_threads (void);
+
+ +

DESCRIPTION

+ + + +
+Pseudothreads are lightweight, cooperatively scheduled +threads.
+ + + +
+new_pseudothread creates a new pseudothread. The +thread only starts running when you call pth_start. +The pool argument passed is used for all allocations +within the thread. This pool is automatically deleted when +the thread exits. The name argument is the name of +the thread (used in thread listings -- see +pseudothread_get_threads(3)). You may change the name +later. The run and data arguments are the +entry point into the thread. The entry point is called as +run (data).
+ + + +
+pseudothread_get_threads returns a list of all the +currently running pseudothreads. This allows you to +implement a "process listing" for a program. The +returned value is a vector of pseudothread structures (not +pointers). These structures are opaque to you, but you can +call the pth_get_* functions on the address of each +one.
+ + + +
+pseudothread_count_threads counts the number of +currently running threads.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pseudothread_set_stack_size.3.html b/doc/pseudothread_set_stack_size.3.html new file mode 100644 index 0000000..fd031b9 --- /dev/null +++ b/doc/pseudothread_set_stack_size.3.html @@ -0,0 +1,91 @@ + + + + +pseudothread_set_stack_size + + + +

pseudothread_set_stack_size

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pseudothread_set_stack_size, pseudothread_get_stack_size - set and get default stack size
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pseudothread_set_stack_size (int size);
+int pseudothread_get_stack_size (void);
+
+ +

DESCRIPTION

+ + + +
+pseudothread_set_stack_size sets the stack size for +newly created threads. The default stack size is 64 +KBytes.
+ + + +
+pseudothread_get_stack_size returns the current stack +size setting.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+new_pseudothread(3).
+
+ + diff --git a/doc/pth_accept.3.html b/doc/pth_accept.3.html new file mode 100644 index 0000000..e0c0f08 --- /dev/null +++ b/doc/pth_accept.3.html @@ -0,0 +1,112 @@ + + + + +pth_accept + + + +

pth_accept

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pth_accept, pth_connect, pth_read, pth_write, pth_sleep, pth_millisleep, pth_nanosleep, pth_timeout - pseudothread system calls
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pth_accept (pseudothread, int, struct sockaddr *, int *);
+int pth_connect (pseudothread, int, struct sockaddr *, int);
+ssize_t pth_read (pseudothread, int, void *, size_t);
+ssize_t pth_write (pseudothread, int, const void *, size_t);
+int pth_sleep (pseudothread, int seconds);
+int pth_millisleep (pseudothread, int millis);
+int pth_nanosleep (pseudothread, const struct timespec *req, struct timespec *rem);
+void pth_timeout (pseudothread, int seconds);
+
+ +

DESCRIPTION

+ + + +
+pth_accept, pth_connect, pth_read, +pth_write, pth_sleep and pth_nanosleep +behave just like the corresponding system calls. However +these calls handle non-blocking sockets and cause the thread +to sleep on the reactor if it would block. For general I/O +you will probably wish to wrap up your sockets in I/O handle +objects, which give you a higher-level buffered interface to +sockets (see io_fdopen(3)).
+ + + +
+pth_millisleep sleeps for a given number of +milliseconds.
+ + + +
+pth_timeout is similar to the alarm(2) system +call: it registers a timeout (in seconds). The thread will +exit automatically (even in the middle of a system call) if +the timeout is reached. To reset the timeout, call +pth_timeout with a timeout of 0.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+pth_poll(3), pth_select(3).
+
+ + diff --git a/doc/pth_catch.3.html b/doc/pth_catch.3.html new file mode 100644 index 0000000..30801d6 --- /dev/null +++ b/doc/pth_catch.3.html @@ -0,0 +1,120 @@ + + + + +pth_exit + + + +

pth_exit

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_exit, pth_die, pth_catch - exit a pseudothread and exception handling
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+void pth_exit (pseudothread) __attribute__((noreturn));
+#define pth_die(pth,msg) _pth_die ((pth), (msg), __FILE__, __LINE__)
+const char *pth_catch (pseudothread pth, void (*fn) (void *), void *data);
+
+ +

DESCRIPTION

+ + + +
+pth_exit causes the current thread to exit +immediately. Note: you cannot force another thread to exit +by calling this function. If you try to do that, strange and +undefined things are likely to happen. Only the currently +running thread may call pth_exit on +itself.
+ + + +
+pth_die is similar in concept to pth_exit, +except that it throws an exception which may be caught by +using the pth_catch function. The distinction between +pth_die and pth_exit is the same as the +distinction between the Perl functions die and +exit, in as much as exit in Perl always exits +the process immediately, and die in Perl generally +exits the process immediately unless the programmer catches +the exception with eval and handles it +appropriately.
+ + + +
+pth_catch is used to catch exceptions thrown by +pth_die. You give fn (function) and +data arguments, and the function calls fn +(data). If, during this call, the code calls +pth_die, then the exception message is immediately +returned from pth_catch. If the code runs +successfully to completion, then pth_catch will +return NULL.
+ + + +
+Exceptions may be nested.
+ + + +
+Note that pth_catch will not catch calls to +pth_exit.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_connect.3.html b/doc/pth_connect.3.html new file mode 100644 index 0000000..66e25b7 --- /dev/null +++ b/doc/pth_connect.3.html @@ -0,0 +1,112 @@ + + + + +pth_accept + + + +

pth_accept

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pth_accept, pth_connect, pth_read, pth_write, pth_sleep, pth_millisleep, pth_nanosleep, pth_timeout - pseudothread system calls
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pth_accept (pseudothread, int, struct sockaddr *, int *);
+int pth_connect (pseudothread, int, struct sockaddr *, int);
+ssize_t pth_read (pseudothread, int, void *, size_t);
+ssize_t pth_write (pseudothread, int, const void *, size_t);
+int pth_sleep (pseudothread, int seconds);
+int pth_millisleep (pseudothread, int millis);
+int pth_nanosleep (pseudothread, const struct timespec *req, struct timespec *rem);
+void pth_timeout (pseudothread, int seconds);
+
+ +

DESCRIPTION

+ + + +
+pth_accept, pth_connect, pth_read, +pth_write, pth_sleep and pth_nanosleep +behave just like the corresponding system calls. However +these calls handle non-blocking sockets and cause the thread +to sleep on the reactor if it would block. For general I/O +you will probably wish to wrap up your sockets in I/O handle +objects, which give you a higher-level buffered interface to +sockets (see io_fdopen(3)).
+ + + +
+pth_millisleep sleeps for a given number of +milliseconds.
+ + + +
+pth_timeout is similar to the alarm(2) system +call: it registers a timeout (in seconds). The thread will +exit automatically (even in the middle of a system call) if +the timeout is reached. To reset the timeout, call +pth_timeout with a timeout of 0.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+pth_poll(3), pth_select(3).
+
+ + diff --git a/doc/pth_die.3.html b/doc/pth_die.3.html new file mode 100644 index 0000000..61f1abd --- /dev/null +++ b/doc/pth_die.3.html @@ -0,0 +1,120 @@ + + + + +pth_exit + + + +

pth_exit

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_exit, pth_die, pth_catch - exit a pseudothread and exception handling
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+void pth_exit (pseudothread) __attribute__((noreturn));
+#define pth_die(pth,msg) _pth_die ((pth), (msg), __FILE__, __LINE__)
+const char *pth_catch (pseudothread pth, void (*fn) (void *), void *data);
+
+ +

DESCRIPTION

+ + + +
+pth_exit causes the current thread to exit +immediately. Note: you cannot force another thread to exit +by calling this function. If you try to do that, strange and +undefined things are likely to happen. Only the currently +running thread may call pth_exit on +itself.
+ + + +
+pth_die is similar in concept to pth_exit, +except that it throws an exception which may be caught by +using the pth_catch function. The distinction between +pth_die and pth_exit is the same as the +distinction between the Perl functions die and +exit, in as much as exit in Perl always exits +the process immediately, and die in Perl generally +exits the process immediately unless the programmer catches +the exception with eval and handles it +appropriately.
+ + + +
+pth_catch is used to catch exceptions thrown by +pth_die. You give fn (function) and +data arguments, and the function calls fn +(data). If, during this call, the code calls +pth_die, then the exception message is immediately +returned from pth_catch. If the code runs +successfully to completion, then pth_catch will +return NULL.
+ + + +
+Exceptions may be nested.
+ + + +
+Note that pth_catch will not catch calls to +pth_exit.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_exit.3.html b/doc/pth_exit.3.html new file mode 100644 index 0000000..61f1abd --- /dev/null +++ b/doc/pth_exit.3.html @@ -0,0 +1,120 @@ + + + + +pth_exit + + + +

pth_exit

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_exit, pth_die, pth_catch - exit a pseudothread and exception handling
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+void pth_exit (pseudothread) __attribute__((noreturn));
+#define pth_die(pth,msg) _pth_die ((pth), (msg), __FILE__, __LINE__)
+const char *pth_catch (pseudothread pth, void (*fn) (void *), void *data);
+
+ +

DESCRIPTION

+ + + +
+pth_exit causes the current thread to exit +immediately. Note: you cannot force another thread to exit +by calling this function. If you try to do that, strange and +undefined things are likely to happen. Only the currently +running thread may call pth_exit on +itself.
+ + + +
+pth_die is similar in concept to pth_exit, +except that it throws an exception which may be caught by +using the pth_catch function. The distinction between +pth_die and pth_exit is the same as the +distinction between the Perl functions die and +exit, in as much as exit in Perl always exits +the process immediately, and die in Perl generally +exits the process immediately unless the programmer catches +the exception with eval and handles it +appropriately.
+ + + +
+pth_catch is used to catch exceptions thrown by +pth_die. You give fn (function) and +data arguments, and the function calls fn +(data). If, during this call, the code calls +pth_die, then the exception message is immediately +returned from pth_catch. If the code runs +successfully to completion, then pth_catch will +return NULL.
+ + + +
+Exceptions may be nested.
+ + + +
+Note that pth_catch will not catch calls to +pth_exit.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_get_PC.3.html b/doc/pth_get_PC.3.html new file mode 100644 index 0000000..33e8070 --- /dev/null +++ b/doc/pth_get_PC.3.html @@ -0,0 +1,188 @@ + + + + +pth_get_pool + + + +

pth_get_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+
+ +

DESCRIPTION

+ + + +
+pth_get_pool returns the pool associated with this +thread. The thread should use this pool for all allocations, +ensuring that they are cleaned up correctly when the thread +is deleted. See new_pool(3).
+ + + +
+pth_get_name returns the name of the thread. Note +that this string is normally stored in thread's local pool, +and therefore the string memory may be unexpected +deallocated if the thread exits. Callers should take a copy +of the string in their own pool if they are likely to need +the string for a long period of time.
+ + + +
+pth_get_thread_num returns the thread number (roughly +the equivalent of a process ID).
+ + + +
+pth_get_run and pth_get_data return the +original entry point information of the thread.
+ + + +
+pth_get_language returns the value of the +LANGUAGE environment variable for this thread. See +the discussion of pth_set_language +below.
+ + + +
+pth_get_language returns the value of the TZ +environment variable for this thread. See the discussion of +pth_set_tz below.
+ + + +
+pth_get_stack returns the top of the stack for this +thread.
+ + + +
+pth_get_stack_size returns the maximum size of the +stack for this thread.
+ + + +
+pth_get_PC returns the current program counter (PC) +for this thread. Obviously it only makes sense to call this +from another thread.
+ + + +
+pth_get_SP returns the current stack pointer (SP) for +this thread. Obviously it only makes sense to call this from +another thread.
+ + + +
+pth_set_name changes the name of the thread. The +string name argument passed must be either statically +allocated, or allocated in the thread's own pool (the normal +case), or else allocated in another pool with a longer life +than the current thread.
+ + + +
+pth_set_language changes the per-thread +LANGUAGE environment variable. This is useful when +serving clients from multiple locales, and using GNU gettext +for translation.
+ + + +
+pth_set_tz changes the per-thread TZ +environment variable. This is useful when serving clients +from multiple timezones, and using localtime and +related functions.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_get_SP.3.html b/doc/pth_get_SP.3.html new file mode 100644 index 0000000..c278b68 --- /dev/null +++ b/doc/pth_get_SP.3.html @@ -0,0 +1,188 @@ + + + + +pth_get_pool + + + +

pth_get_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+
+ +

DESCRIPTION

+ + + +
+pth_get_pool returns the pool associated with this +thread. The thread should use this pool for all allocations, +ensuring that they are cleaned up correctly when the thread +is deleted. See new_pool(3).
+ + + +
+pth_get_name returns the name of the thread. Note +that this string is normally stored in thread's local pool, +and therefore the string memory may be unexpected +deallocated if the thread exits. Callers should take a copy +of the string in their own pool if they are likely to need +the string for a long period of time.
+ + + +
+pth_get_thread_num returns the thread number (roughly +the equivalent of a process ID).
+ + + +
+pth_get_run and pth_get_data return the +original entry point information of the thread.
+ + + +
+pth_get_language returns the value of the +LANGUAGE environment variable for this thread. See +the discussion of pth_set_language +below.
+ + + +
+pth_get_language returns the value of the TZ +environment variable for this thread. See the discussion of +pth_set_tz below.
+ + + +
+pth_get_stack returns the top of the stack for this +thread.
+ + + +
+pth_get_stack_size returns the maximum size of the +stack for this thread.
+ + + +
+pth_get_PC returns the current program counter (PC) +for this thread. Obviously it only makes sense to call this +from another thread.
+ + + +
+pth_get_SP returns the current stack pointer (SP) for +this thread. Obviously it only makes sense to call this from +another thread.
+ + + +
+pth_set_name changes the name of the thread. The +string name argument passed must be either statically +allocated, or allocated in the thread's own pool (the normal +case), or else allocated in another pool with a longer life +than the current thread.
+ + + +
+pth_set_language changes the per-thread +LANGUAGE environment variable. This is useful when +serving clients from multiple locales, and using GNU gettext +for translation.
+ + + +
+pth_set_tz changes the per-thread TZ +environment variable. This is useful when serving clients +from multiple timezones, and using localtime and +related functions.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_get_data.3.html b/doc/pth_get_data.3.html new file mode 100644 index 0000000..c278b68 --- /dev/null +++ b/doc/pth_get_data.3.html @@ -0,0 +1,188 @@ + + + + +pth_get_pool + + + +

pth_get_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+
+ +

DESCRIPTION

+ + + +
+pth_get_pool returns the pool associated with this +thread. The thread should use this pool for all allocations, +ensuring that they are cleaned up correctly when the thread +is deleted. See new_pool(3).
+ + + +
+pth_get_name returns the name of the thread. Note +that this string is normally stored in thread's local pool, +and therefore the string memory may be unexpected +deallocated if the thread exits. Callers should take a copy +of the string in their own pool if they are likely to need +the string for a long period of time.
+ + + +
+pth_get_thread_num returns the thread number (roughly +the equivalent of a process ID).
+ + + +
+pth_get_run and pth_get_data return the +original entry point information of the thread.
+ + + +
+pth_get_language returns the value of the +LANGUAGE environment variable for this thread. See +the discussion of pth_set_language +below.
+ + + +
+pth_get_language returns the value of the TZ +environment variable for this thread. See the discussion of +pth_set_tz below.
+ + + +
+pth_get_stack returns the top of the stack for this +thread.
+ + + +
+pth_get_stack_size returns the maximum size of the +stack for this thread.
+ + + +
+pth_get_PC returns the current program counter (PC) +for this thread. Obviously it only makes sense to call this +from another thread.
+ + + +
+pth_get_SP returns the current stack pointer (SP) for +this thread. Obviously it only makes sense to call this from +another thread.
+ + + +
+pth_set_name changes the name of the thread. The +string name argument passed must be either statically +allocated, or allocated in the thread's own pool (the normal +case), or else allocated in another pool with a longer life +than the current thread.
+ + + +
+pth_set_language changes the per-thread +LANGUAGE environment variable. This is useful when +serving clients from multiple locales, and using GNU gettext +for translation.
+ + + +
+pth_set_tz changes the per-thread TZ +environment variable. This is useful when serving clients +from multiple timezones, and using localtime and +related functions.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_get_language.3.html b/doc/pth_get_language.3.html new file mode 100644 index 0000000..c278b68 --- /dev/null +++ b/doc/pth_get_language.3.html @@ -0,0 +1,188 @@ + + + + +pth_get_pool + + + +

pth_get_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+
+ +

DESCRIPTION

+ + + +
+pth_get_pool returns the pool associated with this +thread. The thread should use this pool for all allocations, +ensuring that they are cleaned up correctly when the thread +is deleted. See new_pool(3).
+ + + +
+pth_get_name returns the name of the thread. Note +that this string is normally stored in thread's local pool, +and therefore the string memory may be unexpected +deallocated if the thread exits. Callers should take a copy +of the string in their own pool if they are likely to need +the string for a long period of time.
+ + + +
+pth_get_thread_num returns the thread number (roughly +the equivalent of a process ID).
+ + + +
+pth_get_run and pth_get_data return the +original entry point information of the thread.
+ + + +
+pth_get_language returns the value of the +LANGUAGE environment variable for this thread. See +the discussion of pth_set_language +below.
+ + + +
+pth_get_language returns the value of the TZ +environment variable for this thread. See the discussion of +pth_set_tz below.
+ + + +
+pth_get_stack returns the top of the stack for this +thread.
+ + + +
+pth_get_stack_size returns the maximum size of the +stack for this thread.
+ + + +
+pth_get_PC returns the current program counter (PC) +for this thread. Obviously it only makes sense to call this +from another thread.
+ + + +
+pth_get_SP returns the current stack pointer (SP) for +this thread. Obviously it only makes sense to call this from +another thread.
+ + + +
+pth_set_name changes the name of the thread. The +string name argument passed must be either statically +allocated, or allocated in the thread's own pool (the normal +case), or else allocated in another pool with a longer life +than the current thread.
+ + + +
+pth_set_language changes the per-thread +LANGUAGE environment variable. This is useful when +serving clients from multiple locales, and using GNU gettext +for translation.
+ + + +
+pth_set_tz changes the per-thread TZ +environment variable. This is useful when serving clients +from multiple timezones, and using localtime and +related functions.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_get_name.3.html b/doc/pth_get_name.3.html new file mode 100644 index 0000000..c278b68 --- /dev/null +++ b/doc/pth_get_name.3.html @@ -0,0 +1,188 @@ + + + + +pth_get_pool + + + +

pth_get_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+
+ +

DESCRIPTION

+ + + +
+pth_get_pool returns the pool associated with this +thread. The thread should use this pool for all allocations, +ensuring that they are cleaned up correctly when the thread +is deleted. See new_pool(3).
+ + + +
+pth_get_name returns the name of the thread. Note +that this string is normally stored in thread's local pool, +and therefore the string memory may be unexpected +deallocated if the thread exits. Callers should take a copy +of the string in their own pool if they are likely to need +the string for a long period of time.
+ + + +
+pth_get_thread_num returns the thread number (roughly +the equivalent of a process ID).
+ + + +
+pth_get_run and pth_get_data return the +original entry point information of the thread.
+ + + +
+pth_get_language returns the value of the +LANGUAGE environment variable for this thread. See +the discussion of pth_set_language +below.
+ + + +
+pth_get_language returns the value of the TZ +environment variable for this thread. See the discussion of +pth_set_tz below.
+ + + +
+pth_get_stack returns the top of the stack for this +thread.
+ + + +
+pth_get_stack_size returns the maximum size of the +stack for this thread.
+ + + +
+pth_get_PC returns the current program counter (PC) +for this thread. Obviously it only makes sense to call this +from another thread.
+ + + +
+pth_get_SP returns the current stack pointer (SP) for +this thread. Obviously it only makes sense to call this from +another thread.
+ + + +
+pth_set_name changes the name of the thread. The +string name argument passed must be either statically +allocated, or allocated in the thread's own pool (the normal +case), or else allocated in another pool with a longer life +than the current thread.
+ + + +
+pth_set_language changes the per-thread +LANGUAGE environment variable. This is useful when +serving clients from multiple locales, and using GNU gettext +for translation.
+ + + +
+pth_set_tz changes the per-thread TZ +environment variable. This is useful when serving clients +from multiple timezones, and using localtime and +related functions.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_get_pool.3.html b/doc/pth_get_pool.3.html new file mode 100644 index 0000000..2c5b6be --- /dev/null +++ b/doc/pth_get_pool.3.html @@ -0,0 +1,188 @@ + + + + +pth_get_pool + + + +

pth_get_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+
+ +

DESCRIPTION

+ + + +
+pth_get_pool returns the pool associated with this +thread. The thread should use this pool for all allocations, +ensuring that they are cleaned up correctly when the thread +is deleted. See new_pool(3).
+ + + +
+pth_get_name returns the name of the thread. Note +that this string is normally stored in thread's local pool, +and therefore the string memory may be unexpected +deallocated if the thread exits. Callers should take a copy +of the string in their own pool if they are likely to need +the string for a long period of time.
+ + + +
+pth_get_thread_num returns the thread number (roughly +the equivalent of a process ID).
+ + + +
+pth_get_run and pth_get_data return the +original entry point information of the thread.
+ + + +
+pth_get_language returns the value of the +LANGUAGE environment variable for this thread. See +the discussion of pth_set_language +below.
+ + + +
+pth_get_language returns the value of the TZ +environment variable for this thread. See the discussion of +pth_set_tz below.
+ + + +
+pth_get_stack returns the top of the stack for this +thread.
+ + + +
+pth_get_stack_size returns the maximum size of the +stack for this thread.
+ + + +
+pth_get_PC returns the current program counter (PC) +for this thread. Obviously it only makes sense to call this +from another thread.
+ + + +
+pth_get_SP returns the current stack pointer (SP) for +this thread. Obviously it only makes sense to call this from +another thread.
+ + + +
+pth_set_name changes the name of the thread. The +string name argument passed must be either statically +allocated, or allocated in the thread's own pool (the normal +case), or else allocated in another pool with a longer life +than the current thread.
+ + + +
+pth_set_language changes the per-thread +LANGUAGE environment variable. This is useful when +serving clients from multiple locales, and using GNU gettext +for translation.
+ + + +
+pth_set_tz changes the per-thread TZ +environment variable. This is useful when serving clients +from multiple timezones, and using localtime and +related functions.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_get_run.3.html b/doc/pth_get_run.3.html new file mode 100644 index 0000000..2c5b6be --- /dev/null +++ b/doc/pth_get_run.3.html @@ -0,0 +1,188 @@ + + + + +pth_get_pool + + + +

pth_get_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+
+ +

DESCRIPTION

+ + + +
+pth_get_pool returns the pool associated with this +thread. The thread should use this pool for all allocations, +ensuring that they are cleaned up correctly when the thread +is deleted. See new_pool(3).
+ + + +
+pth_get_name returns the name of the thread. Note +that this string is normally stored in thread's local pool, +and therefore the string memory may be unexpected +deallocated if the thread exits. Callers should take a copy +of the string in their own pool if they are likely to need +the string for a long period of time.
+ + + +
+pth_get_thread_num returns the thread number (roughly +the equivalent of a process ID).
+ + + +
+pth_get_run and pth_get_data return the +original entry point information of the thread.
+ + + +
+pth_get_language returns the value of the +LANGUAGE environment variable for this thread. See +the discussion of pth_set_language +below.
+ + + +
+pth_get_language returns the value of the TZ +environment variable for this thread. See the discussion of +pth_set_tz below.
+ + + +
+pth_get_stack returns the top of the stack for this +thread.
+ + + +
+pth_get_stack_size returns the maximum size of the +stack for this thread.
+ + + +
+pth_get_PC returns the current program counter (PC) +for this thread. Obviously it only makes sense to call this +from another thread.
+ + + +
+pth_get_SP returns the current stack pointer (SP) for +this thread. Obviously it only makes sense to call this from +another thread.
+ + + +
+pth_set_name changes the name of the thread. The +string name argument passed must be either statically +allocated, or allocated in the thread's own pool (the normal +case), or else allocated in another pool with a longer life +than the current thread.
+ + + +
+pth_set_language changes the per-thread +LANGUAGE environment variable. This is useful when +serving clients from multiple locales, and using GNU gettext +for translation.
+ + + +
+pth_set_tz changes the per-thread TZ +environment variable. This is useful when serving clients +from multiple timezones, and using localtime and +related functions.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_get_stack.3.html b/doc/pth_get_stack.3.html new file mode 100644 index 0000000..2c5b6be --- /dev/null +++ b/doc/pth_get_stack.3.html @@ -0,0 +1,188 @@ + + + + +pth_get_pool + + + +

pth_get_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+
+ +

DESCRIPTION

+ + + +
+pth_get_pool returns the pool associated with this +thread. The thread should use this pool for all allocations, +ensuring that they are cleaned up correctly when the thread +is deleted. See new_pool(3).
+ + + +
+pth_get_name returns the name of the thread. Note +that this string is normally stored in thread's local pool, +and therefore the string memory may be unexpected +deallocated if the thread exits. Callers should take a copy +of the string in their own pool if they are likely to need +the string for a long period of time.
+ + + +
+pth_get_thread_num returns the thread number (roughly +the equivalent of a process ID).
+ + + +
+pth_get_run and pth_get_data return the +original entry point information of the thread.
+ + + +
+pth_get_language returns the value of the +LANGUAGE environment variable for this thread. See +the discussion of pth_set_language +below.
+ + + +
+pth_get_language returns the value of the TZ +environment variable for this thread. See the discussion of +pth_set_tz below.
+ + + +
+pth_get_stack returns the top of the stack for this +thread.
+ + + +
+pth_get_stack_size returns the maximum size of the +stack for this thread.
+ + + +
+pth_get_PC returns the current program counter (PC) +for this thread. Obviously it only makes sense to call this +from another thread.
+ + + +
+pth_get_SP returns the current stack pointer (SP) for +this thread. Obviously it only makes sense to call this from +another thread.
+ + + +
+pth_set_name changes the name of the thread. The +string name argument passed must be either statically +allocated, or allocated in the thread's own pool (the normal +case), or else allocated in another pool with a longer life +than the current thread.
+ + + +
+pth_set_language changes the per-thread +LANGUAGE environment variable. This is useful when +serving clients from multiple locales, and using GNU gettext +for translation.
+ + + +
+pth_set_tz changes the per-thread TZ +environment variable. This is useful when serving clients +from multiple timezones, and using localtime and +related functions.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_get_stack_size.3.html b/doc/pth_get_stack_size.3.html new file mode 100644 index 0000000..2c5b6be --- /dev/null +++ b/doc/pth_get_stack_size.3.html @@ -0,0 +1,188 @@ + + + + +pth_get_pool + + + +

pth_get_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+
+ +

DESCRIPTION

+ + + +
+pth_get_pool returns the pool associated with this +thread. The thread should use this pool for all allocations, +ensuring that they are cleaned up correctly when the thread +is deleted. See new_pool(3).
+ + + +
+pth_get_name returns the name of the thread. Note +that this string is normally stored in thread's local pool, +and therefore the string memory may be unexpected +deallocated if the thread exits. Callers should take a copy +of the string in their own pool if they are likely to need +the string for a long period of time.
+ + + +
+pth_get_thread_num returns the thread number (roughly +the equivalent of a process ID).
+ + + +
+pth_get_run and pth_get_data return the +original entry point information of the thread.
+ + + +
+pth_get_language returns the value of the +LANGUAGE environment variable for this thread. See +the discussion of pth_set_language +below.
+ + + +
+pth_get_language returns the value of the TZ +environment variable for this thread. See the discussion of +pth_set_tz below.
+ + + +
+pth_get_stack returns the top of the stack for this +thread.
+ + + +
+pth_get_stack_size returns the maximum size of the +stack for this thread.
+ + + +
+pth_get_PC returns the current program counter (PC) +for this thread. Obviously it only makes sense to call this +from another thread.
+ + + +
+pth_get_SP returns the current stack pointer (SP) for +this thread. Obviously it only makes sense to call this from +another thread.
+ + + +
+pth_set_name changes the name of the thread. The +string name argument passed must be either statically +allocated, or allocated in the thread's own pool (the normal +case), or else allocated in another pool with a longer life +than the current thread.
+ + + +
+pth_set_language changes the per-thread +LANGUAGE environment variable. This is useful when +serving clients from multiple locales, and using GNU gettext +for translation.
+ + + +
+pth_set_tz changes the per-thread TZ +environment variable. This is useful when serving clients +from multiple timezones, and using localtime and +related functions.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_get_thread_num.3.html b/doc/pth_get_thread_num.3.html new file mode 100644 index 0000000..2e86836 --- /dev/null +++ b/doc/pth_get_thread_num.3.html @@ -0,0 +1,188 @@ + + + + +pth_get_pool + + + +

pth_get_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+
+ +

DESCRIPTION

+ + + +
+pth_get_pool returns the pool associated with this +thread. The thread should use this pool for all allocations, +ensuring that they are cleaned up correctly when the thread +is deleted. See new_pool(3).
+ + + +
+pth_get_name returns the name of the thread. Note +that this string is normally stored in thread's local pool, +and therefore the string memory may be unexpected +deallocated if the thread exits. Callers should take a copy +of the string in their own pool if they are likely to need +the string for a long period of time.
+ + + +
+pth_get_thread_num returns the thread number (roughly +the equivalent of a process ID).
+ + + +
+pth_get_run and pth_get_data return the +original entry point information of the thread.
+ + + +
+pth_get_language returns the value of the +LANGUAGE environment variable for this thread. See +the discussion of pth_set_language +below.
+ + + +
+pth_get_language returns the value of the TZ +environment variable for this thread. See the discussion of +pth_set_tz below.
+ + + +
+pth_get_stack returns the top of the stack for this +thread.
+ + + +
+pth_get_stack_size returns the maximum size of the +stack for this thread.
+ + + +
+pth_get_PC returns the current program counter (PC) +for this thread. Obviously it only makes sense to call this +from another thread.
+ + + +
+pth_get_SP returns the current stack pointer (SP) for +this thread. Obviously it only makes sense to call this from +another thread.
+ + + +
+pth_set_name changes the name of the thread. The +string name argument passed must be either statically +allocated, or allocated in the thread's own pool (the normal +case), or else allocated in another pool with a longer life +than the current thread.
+ + + +
+pth_set_language changes the per-thread +LANGUAGE environment variable. This is useful when +serving clients from multiple locales, and using GNU gettext +for translation.
+ + + +
+pth_set_tz changes the per-thread TZ +environment variable. This is useful when serving clients +from multiple timezones, and using localtime and +related functions.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_get_tz.3.html b/doc/pth_get_tz.3.html new file mode 100644 index 0000000..2e86836 --- /dev/null +++ b/doc/pth_get_tz.3.html @@ -0,0 +1,188 @@ + + + + +pth_get_pool + + + +

pth_get_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+
+ +

DESCRIPTION

+ + + +
+pth_get_pool returns the pool associated with this +thread. The thread should use this pool for all allocations, +ensuring that they are cleaned up correctly when the thread +is deleted. See new_pool(3).
+ + + +
+pth_get_name returns the name of the thread. Note +that this string is normally stored in thread's local pool, +and therefore the string memory may be unexpected +deallocated if the thread exits. Callers should take a copy +of the string in their own pool if they are likely to need +the string for a long period of time.
+ + + +
+pth_get_thread_num returns the thread number (roughly +the equivalent of a process ID).
+ + + +
+pth_get_run and pth_get_data return the +original entry point information of the thread.
+ + + +
+pth_get_language returns the value of the +LANGUAGE environment variable for this thread. See +the discussion of pth_set_language +below.
+ + + +
+pth_get_language returns the value of the TZ +environment variable for this thread. See the discussion of +pth_set_tz below.
+ + + +
+pth_get_stack returns the top of the stack for this +thread.
+ + + +
+pth_get_stack_size returns the maximum size of the +stack for this thread.
+ + + +
+pth_get_PC returns the current program counter (PC) +for this thread. Obviously it only makes sense to call this +from another thread.
+ + + +
+pth_get_SP returns the current stack pointer (SP) for +this thread. Obviously it only makes sense to call this from +another thread.
+ + + +
+pth_set_name changes the name of the thread. The +string name argument passed must be either statically +allocated, or allocated in the thread's own pool (the normal +case), or else allocated in another pool with a longer life +than the current thread.
+ + + +
+pth_set_language changes the per-thread +LANGUAGE environment variable. This is useful when +serving clients from multiple locales, and using GNU gettext +for translation.
+ + + +
+pth_set_tz changes the per-thread TZ +environment variable. This is useful when serving clients +from multiple timezones, and using localtime and +related functions.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_millisleep.3.html b/doc/pth_millisleep.3.html new file mode 100644 index 0000000..e96429b --- /dev/null +++ b/doc/pth_millisleep.3.html @@ -0,0 +1,112 @@ + + + + +pth_accept + + + +

pth_accept

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pth_accept, pth_connect, pth_read, pth_write, pth_sleep, pth_millisleep, pth_nanosleep, pth_timeout - pseudothread system calls
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pth_accept (pseudothread, int, struct sockaddr *, int *);
+int pth_connect (pseudothread, int, struct sockaddr *, int);
+ssize_t pth_read (pseudothread, int, void *, size_t);
+ssize_t pth_write (pseudothread, int, const void *, size_t);
+int pth_sleep (pseudothread, int seconds);
+int pth_millisleep (pseudothread, int millis);
+int pth_nanosleep (pseudothread, const struct timespec *req, struct timespec *rem);
+void pth_timeout (pseudothread, int seconds);
+
+ +

DESCRIPTION

+ + + +
+pth_accept, pth_connect, pth_read, +pth_write, pth_sleep and pth_nanosleep +behave just like the corresponding system calls. However +these calls handle non-blocking sockets and cause the thread +to sleep on the reactor if it would block. For general I/O +you will probably wish to wrap up your sockets in I/O handle +objects, which give you a higher-level buffered interface to +sockets (see io_fdopen(3)).
+ + + +
+pth_millisleep sleeps for a given number of +milliseconds.
+ + + +
+pth_timeout is similar to the alarm(2) system +call: it registers a timeout (in seconds). The thread will +exit automatically (even in the middle of a system call) if +the timeout is reached. To reset the timeout, call +pth_timeout with a timeout of 0.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+pth_poll(3), pth_select(3).
+
+ + diff --git a/doc/pth_nanosleep.3.html b/doc/pth_nanosleep.3.html new file mode 100644 index 0000000..e96429b --- /dev/null +++ b/doc/pth_nanosleep.3.html @@ -0,0 +1,112 @@ + + + + +pth_accept + + + +

pth_accept

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pth_accept, pth_connect, pth_read, pth_write, pth_sleep, pth_millisleep, pth_nanosleep, pth_timeout - pseudothread system calls
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pth_accept (pseudothread, int, struct sockaddr *, int *);
+int pth_connect (pseudothread, int, struct sockaddr *, int);
+ssize_t pth_read (pseudothread, int, void *, size_t);
+ssize_t pth_write (pseudothread, int, const void *, size_t);
+int pth_sleep (pseudothread, int seconds);
+int pth_millisleep (pseudothread, int millis);
+int pth_nanosleep (pseudothread, const struct timespec *req, struct timespec *rem);
+void pth_timeout (pseudothread, int seconds);
+
+ +

DESCRIPTION

+ + + +
+pth_accept, pth_connect, pth_read, +pth_write, pth_sleep and pth_nanosleep +behave just like the corresponding system calls. However +these calls handle non-blocking sockets and cause the thread +to sleep on the reactor if it would block. For general I/O +you will probably wish to wrap up your sockets in I/O handle +objects, which give you a higher-level buffered interface to +sockets (see io_fdopen(3)).
+ + + +
+pth_millisleep sleeps for a given number of +milliseconds.
+ + + +
+pth_timeout is similar to the alarm(2) system +call: it registers a timeout (in seconds). The thread will +exit automatically (even in the middle of a system call) if +the timeout is reached. To reset the timeout, call +pth_timeout with a timeout of 0.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+pth_poll(3), pth_select(3).
+
+ + diff --git a/doc/pth_poll.3.html b/doc/pth_poll.3.html new file mode 100644 index 0000000..d8ee8b4 --- /dev/null +++ b/doc/pth_poll.3.html @@ -0,0 +1,105 @@ + + + + +pth_poll + + + +

pth_poll

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pth_poll, pth_select - pseudothread poll and select system calls
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pth_poll (pseudothread, struct pollfd *fds, unsigned int n, int timeout);
+int pth_select (pseudothread, int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
+
+ +

DESCRIPTION

+ + + +
+pth_poll behaves like the poll(2) system call. +It specifies an array n of fds file descriptor +structures each of which is checked for an I/O event. If +timeout is greater than or equal to zero, then the +call will return after this many milliseconds if no I/O +events are detected. If timeout is negative, then the +timeout is infinite.
+ + + +
+pth_select behaves like the select(2) system +call.
+ + + +
+Note that pth_select is implemented as a library on +top of pth_poll, and is considerably less efficient +than pth_poll. It is recommended that you rewrite any +code which uses pth_select/select to use +pth_poll/poll instead.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+pth_timeout(3).
+
+ + diff --git a/doc/pth_read.3.html b/doc/pth_read.3.html new file mode 100644 index 0000000..b3d0b98 --- /dev/null +++ b/doc/pth_read.3.html @@ -0,0 +1,112 @@ + + + + +pth_accept + + + +

pth_accept

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pth_accept, pth_connect, pth_read, pth_write, pth_sleep, pth_millisleep, pth_nanosleep, pth_timeout - pseudothread system calls
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pth_accept (pseudothread, int, struct sockaddr *, int *);
+int pth_connect (pseudothread, int, struct sockaddr *, int);
+ssize_t pth_read (pseudothread, int, void *, size_t);
+ssize_t pth_write (pseudothread, int, const void *, size_t);
+int pth_sleep (pseudothread, int seconds);
+int pth_millisleep (pseudothread, int millis);
+int pth_nanosleep (pseudothread, const struct timespec *req, struct timespec *rem);
+void pth_timeout (pseudothread, int seconds);
+
+ +

DESCRIPTION

+ + + +
+pth_accept, pth_connect, pth_read, +pth_write, pth_sleep and pth_nanosleep +behave just like the corresponding system calls. However +these calls handle non-blocking sockets and cause the thread +to sleep on the reactor if it would block. For general I/O +you will probably wish to wrap up your sockets in I/O handle +objects, which give you a higher-level buffered interface to +sockets (see io_fdopen(3)).
+ + + +
+pth_millisleep sleeps for a given number of +milliseconds.
+ + + +
+pth_timeout is similar to the alarm(2) system +call: it registers a timeout (in seconds). The thread will +exit automatically (even in the middle of a system call) if +the timeout is reached. To reset the timeout, call +pth_timeout with a timeout of 0.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+pth_poll(3), pth_select(3).
+
+ + diff --git a/doc/pth_recv.3.html b/doc/pth_recv.3.html new file mode 100644 index 0000000..944ddfb --- /dev/null +++ b/doc/pth_recv.3.html @@ -0,0 +1,91 @@ + + + + +pth_send + + + +

pth_send

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pth_send, pth_sendto, pth_sendmsg, pth_recv, pth_recvfrom, pth_recvmsg - pseudothread network system calls
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pth_send (pseudothread pth, int s, const void *msg, int len, unsigned int flags);
+int pth_sendto (pseudothread pth, int s, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
+int pth_sendmsg (pseudothread pth, int s, const struct msghdr *msg, unsigned int flags);
+int pth_recv (pseudothread pth, int s, void *buf, int len, unsigned int flags);
+int pth_recvfrom (pseudothread pth, int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
+int pth_recvmsg (pseudothread pth, int s, struct msghdr *msg, unsigned int flags);
+
+ +

DESCRIPTION

+ + + +
+pth_send, pth_sendto, pth_sendmsg, +pth_recv, pth_recvfrom and pth_recvmsg +behave just like the corresponding system calls. However +these calls handle non-blocking sockets and cause the thread +to sleep on the reactor if it would block.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+pth_poll(3), pth_read(3), +pth_write(3).
+
+ + diff --git a/doc/pth_recvfrom.3.html b/doc/pth_recvfrom.3.html new file mode 100644 index 0000000..944ddfb --- /dev/null +++ b/doc/pth_recvfrom.3.html @@ -0,0 +1,91 @@ + + + + +pth_send + + + +

pth_send

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pth_send, pth_sendto, pth_sendmsg, pth_recv, pth_recvfrom, pth_recvmsg - pseudothread network system calls
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pth_send (pseudothread pth, int s, const void *msg, int len, unsigned int flags);
+int pth_sendto (pseudothread pth, int s, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
+int pth_sendmsg (pseudothread pth, int s, const struct msghdr *msg, unsigned int flags);
+int pth_recv (pseudothread pth, int s, void *buf, int len, unsigned int flags);
+int pth_recvfrom (pseudothread pth, int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
+int pth_recvmsg (pseudothread pth, int s, struct msghdr *msg, unsigned int flags);
+
+ +

DESCRIPTION

+ + + +
+pth_send, pth_sendto, pth_sendmsg, +pth_recv, pth_recvfrom and pth_recvmsg +behave just like the corresponding system calls. However +these calls handle non-blocking sockets and cause the thread +to sleep on the reactor if it would block.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+pth_poll(3), pth_read(3), +pth_write(3).
+
+ + diff --git a/doc/pth_recvmsg.3.html b/doc/pth_recvmsg.3.html new file mode 100644 index 0000000..944ddfb --- /dev/null +++ b/doc/pth_recvmsg.3.html @@ -0,0 +1,91 @@ + + + + +pth_send + + + +

pth_send

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pth_send, pth_sendto, pth_sendmsg, pth_recv, pth_recvfrom, pth_recvmsg - pseudothread network system calls
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pth_send (pseudothread pth, int s, const void *msg, int len, unsigned int flags);
+int pth_sendto (pseudothread pth, int s, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
+int pth_sendmsg (pseudothread pth, int s, const struct msghdr *msg, unsigned int flags);
+int pth_recv (pseudothread pth, int s, void *buf, int len, unsigned int flags);
+int pth_recvfrom (pseudothread pth, int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
+int pth_recvmsg (pseudothread pth, int s, struct msghdr *msg, unsigned int flags);
+
+ +

DESCRIPTION

+ + + +
+pth_send, pth_sendto, pth_sendmsg, +pth_recv, pth_recvfrom and pth_recvmsg +behave just like the corresponding system calls. However +these calls handle non-blocking sockets and cause the thread +to sleep on the reactor if it would block.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+pth_poll(3), pth_read(3), +pth_write(3).
+
+ + diff --git a/doc/pth_select.3.html b/doc/pth_select.3.html new file mode 100644 index 0000000..6721ae4 --- /dev/null +++ b/doc/pth_select.3.html @@ -0,0 +1,105 @@ + + + + +pth_poll + + + +

pth_poll

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pth_poll, pth_select - pseudothread poll and select system calls
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pth_poll (pseudothread, struct pollfd *fds, unsigned int n, int timeout);
+int pth_select (pseudothread, int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
+
+ +

DESCRIPTION

+ + + +
+pth_poll behaves like the poll(2) system call. +It specifies an array n of fds file descriptor +structures each of which is checked for an I/O event. If +timeout is greater than or equal to zero, then the +call will return after this many milliseconds if no I/O +events are detected. If timeout is negative, then the +timeout is infinite.
+ + + +
+pth_select behaves like the select(2) system +call.
+ + + +
+Note that pth_select is implemented as a library on +top of pth_poll, and is considerably less efficient +than pth_poll. It is recommended that you rewrite any +code which uses pth_select/select to use +pth_poll/poll instead.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+pth_timeout(3).
+
+ + diff --git a/doc/pth_send.3.html b/doc/pth_send.3.html new file mode 100644 index 0000000..2163446 --- /dev/null +++ b/doc/pth_send.3.html @@ -0,0 +1,91 @@ + + + + +pth_send + + + +

pth_send

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pth_send, pth_sendto, pth_sendmsg, pth_recv, pth_recvfrom, pth_recvmsg - pseudothread network system calls
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pth_send (pseudothread pth, int s, const void *msg, int len, unsigned int flags);
+int pth_sendto (pseudothread pth, int s, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
+int pth_sendmsg (pseudothread pth, int s, const struct msghdr *msg, unsigned int flags);
+int pth_recv (pseudothread pth, int s, void *buf, int len, unsigned int flags);
+int pth_recvfrom (pseudothread pth, int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
+int pth_recvmsg (pseudothread pth, int s, struct msghdr *msg, unsigned int flags);
+
+ +

DESCRIPTION

+ + + +
+pth_send, pth_sendto, pth_sendmsg, +pth_recv, pth_recvfrom and pth_recvmsg +behave just like the corresponding system calls. However +these calls handle non-blocking sockets and cause the thread +to sleep on the reactor if it would block.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+pth_poll(3), pth_read(3), +pth_write(3).
+
+ + diff --git a/doc/pth_sendmsg.3.html b/doc/pth_sendmsg.3.html new file mode 100644 index 0000000..2163446 --- /dev/null +++ b/doc/pth_sendmsg.3.html @@ -0,0 +1,91 @@ + + + + +pth_send + + + +

pth_send

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pth_send, pth_sendto, pth_sendmsg, pth_recv, pth_recvfrom, pth_recvmsg - pseudothread network system calls
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pth_send (pseudothread pth, int s, const void *msg, int len, unsigned int flags);
+int pth_sendto (pseudothread pth, int s, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
+int pth_sendmsg (pseudothread pth, int s, const struct msghdr *msg, unsigned int flags);
+int pth_recv (pseudothread pth, int s, void *buf, int len, unsigned int flags);
+int pth_recvfrom (pseudothread pth, int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
+int pth_recvmsg (pseudothread pth, int s, struct msghdr *msg, unsigned int flags);
+
+ +

DESCRIPTION

+ + + +
+pth_send, pth_sendto, pth_sendmsg, +pth_recv, pth_recvfrom and pth_recvmsg +behave just like the corresponding system calls. However +these calls handle non-blocking sockets and cause the thread +to sleep on the reactor if it would block.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+pth_poll(3), pth_read(3), +pth_write(3).
+
+ + diff --git a/doc/pth_sendto.3.html b/doc/pth_sendto.3.html new file mode 100644 index 0000000..2163446 --- /dev/null +++ b/doc/pth_sendto.3.html @@ -0,0 +1,91 @@ + + + + +pth_send + + + +

pth_send

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pth_send, pth_sendto, pth_sendmsg, pth_recv, pth_recvfrom, pth_recvmsg - pseudothread network system calls
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pth_send (pseudothread pth, int s, const void *msg, int len, unsigned int flags);
+int pth_sendto (pseudothread pth, int s, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
+int pth_sendmsg (pseudothread pth, int s, const struct msghdr *msg, unsigned int flags);
+int pth_recv (pseudothread pth, int s, void *buf, int len, unsigned int flags);
+int pth_recvfrom (pseudothread pth, int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
+int pth_recvmsg (pseudothread pth, int s, struct msghdr *msg, unsigned int flags);
+
+ +

DESCRIPTION

+ + + +
+pth_send, pth_sendto, pth_sendmsg, +pth_recv, pth_recvfrom and pth_recvmsg +behave just like the corresponding system calls. However +these calls handle non-blocking sockets and cause the thread +to sleep on the reactor if it would block.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+pth_poll(3), pth_read(3), +pth_write(3).
+
+ + diff --git a/doc/pth_set_language.3.html b/doc/pth_set_language.3.html new file mode 100644 index 0000000..a1ed004 --- /dev/null +++ b/doc/pth_set_language.3.html @@ -0,0 +1,188 @@ + + + + +pth_get_pool + + + +

pth_get_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+
+ +

DESCRIPTION

+ + + +
+pth_get_pool returns the pool associated with this +thread. The thread should use this pool for all allocations, +ensuring that they are cleaned up correctly when the thread +is deleted. See new_pool(3).
+ + + +
+pth_get_name returns the name of the thread. Note +that this string is normally stored in thread's local pool, +and therefore the string memory may be unexpected +deallocated if the thread exits. Callers should take a copy +of the string in their own pool if they are likely to need +the string for a long period of time.
+ + + +
+pth_get_thread_num returns the thread number (roughly +the equivalent of a process ID).
+ + + +
+pth_get_run and pth_get_data return the +original entry point information of the thread.
+ + + +
+pth_get_language returns the value of the +LANGUAGE environment variable for this thread. See +the discussion of pth_set_language +below.
+ + + +
+pth_get_language returns the value of the TZ +environment variable for this thread. See the discussion of +pth_set_tz below.
+ + + +
+pth_get_stack returns the top of the stack for this +thread.
+ + + +
+pth_get_stack_size returns the maximum size of the +stack for this thread.
+ + + +
+pth_get_PC returns the current program counter (PC) +for this thread. Obviously it only makes sense to call this +from another thread.
+ + + +
+pth_get_SP returns the current stack pointer (SP) for +this thread. Obviously it only makes sense to call this from +another thread.
+ + + +
+pth_set_name changes the name of the thread. The +string name argument passed must be either statically +allocated, or allocated in the thread's own pool (the normal +case), or else allocated in another pool with a longer life +than the current thread.
+ + + +
+pth_set_language changes the per-thread +LANGUAGE environment variable. This is useful when +serving clients from multiple locales, and using GNU gettext +for translation.
+ + + +
+pth_set_tz changes the per-thread TZ +environment variable. This is useful when serving clients +from multiple timezones, and using localtime and +related functions.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_set_name.3.html b/doc/pth_set_name.3.html new file mode 100644 index 0000000..a1ed004 --- /dev/null +++ b/doc/pth_set_name.3.html @@ -0,0 +1,188 @@ + + + + +pth_get_pool + + + +

pth_get_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+
+ +

DESCRIPTION

+ + + +
+pth_get_pool returns the pool associated with this +thread. The thread should use this pool for all allocations, +ensuring that they are cleaned up correctly when the thread +is deleted. See new_pool(3).
+ + + +
+pth_get_name returns the name of the thread. Note +that this string is normally stored in thread's local pool, +and therefore the string memory may be unexpected +deallocated if the thread exits. Callers should take a copy +of the string in their own pool if they are likely to need +the string for a long period of time.
+ + + +
+pth_get_thread_num returns the thread number (roughly +the equivalent of a process ID).
+ + + +
+pth_get_run and pth_get_data return the +original entry point information of the thread.
+ + + +
+pth_get_language returns the value of the +LANGUAGE environment variable for this thread. See +the discussion of pth_set_language +below.
+ + + +
+pth_get_language returns the value of the TZ +environment variable for this thread. See the discussion of +pth_set_tz below.
+ + + +
+pth_get_stack returns the top of the stack for this +thread.
+ + + +
+pth_get_stack_size returns the maximum size of the +stack for this thread.
+ + + +
+pth_get_PC returns the current program counter (PC) +for this thread. Obviously it only makes sense to call this +from another thread.
+ + + +
+pth_get_SP returns the current stack pointer (SP) for +this thread. Obviously it only makes sense to call this from +another thread.
+ + + +
+pth_set_name changes the name of the thread. The +string name argument passed must be either statically +allocated, or allocated in the thread's own pool (the normal +case), or else allocated in another pool with a longer life +than the current thread.
+ + + +
+pth_set_language changes the per-thread +LANGUAGE environment variable. This is useful when +serving clients from multiple locales, and using GNU gettext +for translation.
+ + + +
+pth_set_tz changes the per-thread TZ +environment variable. This is useful when serving clients +from multiple timezones, and using localtime and +related functions.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_set_tz.3.html b/doc/pth_set_tz.3.html new file mode 100644 index 0000000..a1ed004 --- /dev/null +++ b/doc/pth_set_tz.3.html @@ -0,0 +1,188 @@ + + + + +pth_get_pool + + + +

pth_get_pool

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+
+ +

DESCRIPTION

+ + + +
+pth_get_pool returns the pool associated with this +thread. The thread should use this pool for all allocations, +ensuring that they are cleaned up correctly when the thread +is deleted. See new_pool(3).
+ + + +
+pth_get_name returns the name of the thread. Note +that this string is normally stored in thread's local pool, +and therefore the string memory may be unexpected +deallocated if the thread exits. Callers should take a copy +of the string in their own pool if they are likely to need +the string for a long period of time.
+ + + +
+pth_get_thread_num returns the thread number (roughly +the equivalent of a process ID).
+ + + +
+pth_get_run and pth_get_data return the +original entry point information of the thread.
+ + + +
+pth_get_language returns the value of the +LANGUAGE environment variable for this thread. See +the discussion of pth_set_language +below.
+ + + +
+pth_get_language returns the value of the TZ +environment variable for this thread. See the discussion of +pth_set_tz below.
+ + + +
+pth_get_stack returns the top of the stack for this +thread.
+ + + +
+pth_get_stack_size returns the maximum size of the +stack for this thread.
+ + + +
+pth_get_PC returns the current program counter (PC) +for this thread. Obviously it only makes sense to call this +from another thread.
+ + + +
+pth_get_SP returns the current stack pointer (SP) for +this thread. Obviously it only makes sense to call this from +another thread.
+ + + +
+pth_set_name changes the name of the thread. The +string name argument passed must be either statically +allocated, or allocated in the thread's own pool (the normal +case), or else allocated in another pool with a longer life +than the current thread.
+ + + +
+pth_set_language changes the per-thread +LANGUAGE environment variable. This is useful when +serving clients from multiple locales, and using GNU gettext +for translation.
+ + + +
+pth_set_tz changes the per-thread TZ +environment variable. This is useful when serving clients +from multiple timezones, and using localtime and +related functions.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_sleep.3.html b/doc/pth_sleep.3.html new file mode 100644 index 0000000..0adb59e --- /dev/null +++ b/doc/pth_sleep.3.html @@ -0,0 +1,112 @@ + + + + +pth_accept + + + +

pth_accept

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pth_accept, pth_connect, pth_read, pth_write, pth_sleep, pth_millisleep, pth_nanosleep, pth_timeout - pseudothread system calls
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pth_accept (pseudothread, int, struct sockaddr *, int *);
+int pth_connect (pseudothread, int, struct sockaddr *, int);
+ssize_t pth_read (pseudothread, int, void *, size_t);
+ssize_t pth_write (pseudothread, int, const void *, size_t);
+int pth_sleep (pseudothread, int seconds);
+int pth_millisleep (pseudothread, int millis);
+int pth_nanosleep (pseudothread, const struct timespec *req, struct timespec *rem);
+void pth_timeout (pseudothread, int seconds);
+
+ +

DESCRIPTION

+ + + +
+pth_accept, pth_connect, pth_read, +pth_write, pth_sleep and pth_nanosleep +behave just like the corresponding system calls. However +these calls handle non-blocking sockets and cause the thread +to sleep on the reactor if it would block. For general I/O +you will probably wish to wrap up your sockets in I/O handle +objects, which give you a higher-level buffered interface to +sockets (see io_fdopen(3)).
+ + + +
+pth_millisleep sleeps for a given number of +milliseconds.
+ + + +
+pth_timeout is similar to the alarm(2) system +call: it registers a timeout (in seconds). The thread will +exit automatically (even in the middle of a system call) if +the timeout is reached. To reset the timeout, call +pth_timeout with a timeout of 0.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+pth_poll(3), pth_select(3).
+
+ + diff --git a/doc/pth_start.3.html b/doc/pth_start.3.html new file mode 100644 index 0000000..6ca16c4 --- /dev/null +++ b/doc/pth_start.3.html @@ -0,0 +1,110 @@ + + + + +new_pseudothread + + + +

new_pseudothread

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+new_pseudothread, pth_start, pseudothread_get_threads, pseudothread_count_threads - lightweight "pseudothreads" library
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+pseudothread new_pseudothread (pool, void (*run) (void *), void *data, const char *name);
+void pth_start (pseudothread pth);
+vector pseudothread_get_threads (pool);
+int pseudothread_count_threads (void);
+
+ +

DESCRIPTION

+ + + +
+Pseudothreads are lightweight, cooperatively scheduled +threads.
+ + + +
+new_pseudothread creates a new pseudothread. The +thread only starts running when you call pth_start. +The pool argument passed is used for all allocations +within the thread. This pool is automatically deleted when +the thread exits. The name argument is the name of +the thread (used in thread listings -- see +pseudothread_get_threads(3)). You may change the name +later. The run and data arguments are the +entry point into the thread. The entry point is called as +run (data).
+ + + +
+pseudothread_get_threads returns a list of all the +currently running pseudothreads. This allows you to +implement a "process listing" for a program. The +returned value is a vector of pseudothread structures (not +pointers). These structures are opaque to you, but you can +call the pth_get_* functions on the address of each +one.
+ + + +
+pseudothread_count_threads counts the number of +currently running threads.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pth_timeout.3.html b/doc/pth_timeout.3.html new file mode 100644 index 0000000..4042870 --- /dev/null +++ b/doc/pth_timeout.3.html @@ -0,0 +1,112 @@ + + + + +pth_accept + + + +

pth_accept

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pth_accept, pth_connect, pth_read, pth_write, pth_sleep, pth_millisleep, pth_nanosleep, pth_timeout - pseudothread system calls
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pth_accept (pseudothread, int, struct sockaddr *, int *);
+int pth_connect (pseudothread, int, struct sockaddr *, int);
+ssize_t pth_read (pseudothread, int, void *, size_t);
+ssize_t pth_write (pseudothread, int, const void *, size_t);
+int pth_sleep (pseudothread, int seconds);
+int pth_millisleep (pseudothread, int millis);
+int pth_nanosleep (pseudothread, const struct timespec *req, struct timespec *rem);
+void pth_timeout (pseudothread, int seconds);
+
+ +

DESCRIPTION

+ + + +
+pth_accept, pth_connect, pth_read, +pth_write, pth_sleep and pth_nanosleep +behave just like the corresponding system calls. However +these calls handle non-blocking sockets and cause the thread +to sleep on the reactor if it would block. For general I/O +you will probably wish to wrap up your sockets in I/O handle +objects, which give you a higher-level buffered interface to +sockets (see io_fdopen(3)).
+ + + +
+pth_millisleep sleeps for a given number of +milliseconds.
+ + + +
+pth_timeout is similar to the alarm(2) system +call: it registers a timeout (in seconds). The thread will +exit automatically (even in the middle of a system call) if +the timeout is reached. To reset the timeout, call +pth_timeout with a timeout of 0.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+pth_poll(3), pth_select(3).
+
+ + diff --git a/doc/pth_write.3.html b/doc/pth_write.3.html new file mode 100644 index 0000000..4042870 --- /dev/null +++ b/doc/pth_write.3.html @@ -0,0 +1,112 @@ + + + + +pth_accept + + + +

pth_accept

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+SEE ALSO
+ +
+ + + +

NAME

+ + +
+pth_accept, pth_connect, pth_read, pth_write, pth_sleep, pth_millisleep, pth_nanosleep, pth_timeout - pseudothread system calls
+ +

SYNOPSIS

+ + + +
+
#include <pthr_pseudothread.h>
+
+int pth_accept (pseudothread, int, struct sockaddr *, int *);
+int pth_connect (pseudothread, int, struct sockaddr *, int);
+ssize_t pth_read (pseudothread, int, void *, size_t);
+ssize_t pth_write (pseudothread, int, const void *, size_t);
+int pth_sleep (pseudothread, int seconds);
+int pth_millisleep (pseudothread, int millis);
+int pth_nanosleep (pseudothread, const struct timespec *req, struct timespec *rem);
+void pth_timeout (pseudothread, int seconds);
+
+ +

DESCRIPTION

+ + + +
+pth_accept, pth_connect, pth_read, +pth_write, pth_sleep and pth_nanosleep +behave just like the corresponding system calls. However +these calls handle non-blocking sockets and cause the thread +to sleep on the reactor if it would block. For general I/O +you will probably wish to wrap up your sockets in I/O handle +objects, which give you a higher-level buffered interface to +sockets (see io_fdopen(3)).
+ + + +
+pth_millisleep sleeps for a given number of +milliseconds.
+ + + +
+pth_timeout is similar to the alarm(2) system +call: it registers a timeout (in seconds). The thread will +exit automatically (even in the middle of a system call) if +the timeout is reached. To reset the timeout, call +pth_timeout with a timeout of 0.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

SEE ALSO

+ + + +
+pth_poll(3), pth_select(3).
+
+ + diff --git a/doc/pthr_server_chroot.3.html b/doc/pthr_server_chroot.3.html new file mode 100644 index 0000000..aa44d83 --- /dev/null +++ b/doc/pthr_server_chroot.3.html @@ -0,0 +1,171 @@ + + + + +pthr_server_main_loop + + + +

pthr_server_main_loop

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+
+ +

DESCRIPTION

+ + + +
+The function pthr_server_main_loop is a helper +function which allows you to write very simple servers +quickly using pthrlib. Normally your main +should just contain a call to pthr_server_main_loop +(argc, argv, processor) and you would include a function +called processor which is called on every +connection.
+ + + +
+The other functions allow you to customise the behaviour of +pthr_server_main_loop. You should call any of these +once in your main before calling +pthr_server_main_loop.
+ + + +
+By default the server listens on port 80 and allows you to +override this on the command line using the -p +option. To change this, call pthr_server_default_port +and/or pthr_server_port_option_name.
+ + + +
+By default the server writes package and version information +to syslog (although it will not be able to correctly +determine the package). Use +pthr_server_disable_syslog to disable syslogging +entirely, or pthr_server_package_name to change the +name displayed in the logs.
+ + + +
+By default the server forks into the background, changes to +the root directory and disconnects entirely from the +terminal. Use pthr_server_disable_fork, +pthr_server_disable_chdir and +pthr_server_disable_close to change these +respectively.
+ + + +
+If you wish to have the server chroot after start up, then +call pthr_server_chroot. This is only possible if the +server is run as root, otherwise the chroot is silently +ignored.
+ + + +
+If you wish to have the server change user and group after +start up, then call pthr_server_username (the group +is taken from the password file and initgroups(3) is +also called). This is only possible if the server is run as +root, otherwise the change user is silently +ignored.
+ + + +
+If you wish to have stderr connected to a file (this +is done after changing user, so make sure the file is +accessible or owned by the user, not by root), then call +pthr_server_stderr_file. Note that unless you call +this function or pthr_server_disable_close then +stderr is sent to /dev/null!
+ + + +
+The startup_fn is called after all of the above +operations have completed, but before the listener thread is +created. You do miscellaneous start-of-day functions from +here, in particular starting up other global threads. The +command line arguments are also passed to this +function.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pthr_server_default_port.3.html b/doc/pthr_server_default_port.3.html new file mode 100644 index 0000000..6d9c716 --- /dev/null +++ b/doc/pthr_server_default_port.3.html @@ -0,0 +1,171 @@ + + + + +pthr_server_main_loop + + + +

pthr_server_main_loop

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+
+ +

DESCRIPTION

+ + + +
+The function pthr_server_main_loop is a helper +function which allows you to write very simple servers +quickly using pthrlib. Normally your main +should just contain a call to pthr_server_main_loop +(argc, argv, processor) and you would include a function +called processor which is called on every +connection.
+ + + +
+The other functions allow you to customise the behaviour of +pthr_server_main_loop. You should call any of these +once in your main before calling +pthr_server_main_loop.
+ + + +
+By default the server listens on port 80 and allows you to +override this on the command line using the -p +option. To change this, call pthr_server_default_port +and/or pthr_server_port_option_name.
+ + + +
+By default the server writes package and version information +to syslog (although it will not be able to correctly +determine the package). Use +pthr_server_disable_syslog to disable syslogging +entirely, or pthr_server_package_name to change the +name displayed in the logs.
+ + + +
+By default the server forks into the background, changes to +the root directory and disconnects entirely from the +terminal. Use pthr_server_disable_fork, +pthr_server_disable_chdir and +pthr_server_disable_close to change these +respectively.
+ + + +
+If you wish to have the server chroot after start up, then +call pthr_server_chroot. This is only possible if the +server is run as root, otherwise the chroot is silently +ignored.
+ + + +
+If you wish to have the server change user and group after +start up, then call pthr_server_username (the group +is taken from the password file and initgroups(3) is +also called). This is only possible if the server is run as +root, otherwise the change user is silently +ignored.
+ + + +
+If you wish to have stderr connected to a file (this +is done after changing user, so make sure the file is +accessible or owned by the user, not by root), then call +pthr_server_stderr_file. Note that unless you call +this function or pthr_server_disable_close then +stderr is sent to /dev/null!
+ + + +
+The startup_fn is called after all of the above +operations have completed, but before the listener thread is +created. You do miscellaneous start-of-day functions from +here, in particular starting up other global threads. The +command line arguments are also passed to this +function.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pthr_server_disable_chdir.3.html b/doc/pthr_server_disable_chdir.3.html new file mode 100644 index 0000000..6d9c716 --- /dev/null +++ b/doc/pthr_server_disable_chdir.3.html @@ -0,0 +1,171 @@ + + + + +pthr_server_main_loop + + + +

pthr_server_main_loop

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+
+ +

DESCRIPTION

+ + + +
+The function pthr_server_main_loop is a helper +function which allows you to write very simple servers +quickly using pthrlib. Normally your main +should just contain a call to pthr_server_main_loop +(argc, argv, processor) and you would include a function +called processor which is called on every +connection.
+ + + +
+The other functions allow you to customise the behaviour of +pthr_server_main_loop. You should call any of these +once in your main before calling +pthr_server_main_loop.
+ + + +
+By default the server listens on port 80 and allows you to +override this on the command line using the -p +option. To change this, call pthr_server_default_port +and/or pthr_server_port_option_name.
+ + + +
+By default the server writes package and version information +to syslog (although it will not be able to correctly +determine the package). Use +pthr_server_disable_syslog to disable syslogging +entirely, or pthr_server_package_name to change the +name displayed in the logs.
+ + + +
+By default the server forks into the background, changes to +the root directory and disconnects entirely from the +terminal. Use pthr_server_disable_fork, +pthr_server_disable_chdir and +pthr_server_disable_close to change these +respectively.
+ + + +
+If you wish to have the server chroot after start up, then +call pthr_server_chroot. This is only possible if the +server is run as root, otherwise the chroot is silently +ignored.
+ + + +
+If you wish to have the server change user and group after +start up, then call pthr_server_username (the group +is taken from the password file and initgroups(3) is +also called). This is only possible if the server is run as +root, otherwise the change user is silently +ignored.
+ + + +
+If you wish to have stderr connected to a file (this +is done after changing user, so make sure the file is +accessible or owned by the user, not by root), then call +pthr_server_stderr_file. Note that unless you call +this function or pthr_server_disable_close then +stderr is sent to /dev/null!
+ + + +
+The startup_fn is called after all of the above +operations have completed, but before the listener thread is +created. You do miscellaneous start-of-day functions from +here, in particular starting up other global threads. The +command line arguments are also passed to this +function.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pthr_server_disable_close.3.html b/doc/pthr_server_disable_close.3.html new file mode 100644 index 0000000..6d9c716 --- /dev/null +++ b/doc/pthr_server_disable_close.3.html @@ -0,0 +1,171 @@ + + + + +pthr_server_main_loop + + + +

pthr_server_main_loop

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+
+ +

DESCRIPTION

+ + + +
+The function pthr_server_main_loop is a helper +function which allows you to write very simple servers +quickly using pthrlib. Normally your main +should just contain a call to pthr_server_main_loop +(argc, argv, processor) and you would include a function +called processor which is called on every +connection.
+ + + +
+The other functions allow you to customise the behaviour of +pthr_server_main_loop. You should call any of these +once in your main before calling +pthr_server_main_loop.
+ + + +
+By default the server listens on port 80 and allows you to +override this on the command line using the -p +option. To change this, call pthr_server_default_port +and/or pthr_server_port_option_name.
+ + + +
+By default the server writes package and version information +to syslog (although it will not be able to correctly +determine the package). Use +pthr_server_disable_syslog to disable syslogging +entirely, or pthr_server_package_name to change the +name displayed in the logs.
+ + + +
+By default the server forks into the background, changes to +the root directory and disconnects entirely from the +terminal. Use pthr_server_disable_fork, +pthr_server_disable_chdir and +pthr_server_disable_close to change these +respectively.
+ + + +
+If you wish to have the server chroot after start up, then +call pthr_server_chroot. This is only possible if the +server is run as root, otherwise the chroot is silently +ignored.
+ + + +
+If you wish to have the server change user and group after +start up, then call pthr_server_username (the group +is taken from the password file and initgroups(3) is +also called). This is only possible if the server is run as +root, otherwise the change user is silently +ignored.
+ + + +
+If you wish to have stderr connected to a file (this +is done after changing user, so make sure the file is +accessible or owned by the user, not by root), then call +pthr_server_stderr_file. Note that unless you call +this function or pthr_server_disable_close then +stderr is sent to /dev/null!
+ + + +
+The startup_fn is called after all of the above +operations have completed, but before the listener thread is +created. You do miscellaneous start-of-day functions from +here, in particular starting up other global threads. The +command line arguments are also passed to this +function.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pthr_server_disable_fork.3.html b/doc/pthr_server_disable_fork.3.html new file mode 100644 index 0000000..6d9c716 --- /dev/null +++ b/doc/pthr_server_disable_fork.3.html @@ -0,0 +1,171 @@ + + + + +pthr_server_main_loop + + + +

pthr_server_main_loop

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+
+ +

DESCRIPTION

+ + + +
+The function pthr_server_main_loop is a helper +function which allows you to write very simple servers +quickly using pthrlib. Normally your main +should just contain a call to pthr_server_main_loop +(argc, argv, processor) and you would include a function +called processor which is called on every +connection.
+ + + +
+The other functions allow you to customise the behaviour of +pthr_server_main_loop. You should call any of these +once in your main before calling +pthr_server_main_loop.
+ + + +
+By default the server listens on port 80 and allows you to +override this on the command line using the -p +option. To change this, call pthr_server_default_port +and/or pthr_server_port_option_name.
+ + + +
+By default the server writes package and version information +to syslog (although it will not be able to correctly +determine the package). Use +pthr_server_disable_syslog to disable syslogging +entirely, or pthr_server_package_name to change the +name displayed in the logs.
+ + + +
+By default the server forks into the background, changes to +the root directory and disconnects entirely from the +terminal. Use pthr_server_disable_fork, +pthr_server_disable_chdir and +pthr_server_disable_close to change these +respectively.
+ + + +
+If you wish to have the server chroot after start up, then +call pthr_server_chroot. This is only possible if the +server is run as root, otherwise the chroot is silently +ignored.
+ + + +
+If you wish to have the server change user and group after +start up, then call pthr_server_username (the group +is taken from the password file and initgroups(3) is +also called). This is only possible if the server is run as +root, otherwise the change user is silently +ignored.
+ + + +
+If you wish to have stderr connected to a file (this +is done after changing user, so make sure the file is +accessible or owned by the user, not by root), then call +pthr_server_stderr_file. Note that unless you call +this function or pthr_server_disable_close then +stderr is sent to /dev/null!
+ + + +
+The startup_fn is called after all of the above +operations have completed, but before the listener thread is +created. You do miscellaneous start-of-day functions from +here, in particular starting up other global threads. The +command line arguments are also passed to this +function.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pthr_server_disable_syslog.3.html b/doc/pthr_server_disable_syslog.3.html new file mode 100644 index 0000000..e5b8d8e --- /dev/null +++ b/doc/pthr_server_disable_syslog.3.html @@ -0,0 +1,171 @@ + + + + +pthr_server_main_loop + + + +

pthr_server_main_loop

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+
+ +

DESCRIPTION

+ + + +
+The function pthr_server_main_loop is a helper +function which allows you to write very simple servers +quickly using pthrlib. Normally your main +should just contain a call to pthr_server_main_loop +(argc, argv, processor) and you would include a function +called processor which is called on every +connection.
+ + + +
+The other functions allow you to customise the behaviour of +pthr_server_main_loop. You should call any of these +once in your main before calling +pthr_server_main_loop.
+ + + +
+By default the server listens on port 80 and allows you to +override this on the command line using the -p +option. To change this, call pthr_server_default_port +and/or pthr_server_port_option_name.
+ + + +
+By default the server writes package and version information +to syslog (although it will not be able to correctly +determine the package). Use +pthr_server_disable_syslog to disable syslogging +entirely, or pthr_server_package_name to change the +name displayed in the logs.
+ + + +
+By default the server forks into the background, changes to +the root directory and disconnects entirely from the +terminal. Use pthr_server_disable_fork, +pthr_server_disable_chdir and +pthr_server_disable_close to change these +respectively.
+ + + +
+If you wish to have the server chroot after start up, then +call pthr_server_chroot. This is only possible if the +server is run as root, otherwise the chroot is silently +ignored.
+ + + +
+If you wish to have the server change user and group after +start up, then call pthr_server_username (the group +is taken from the password file and initgroups(3) is +also called). This is only possible if the server is run as +root, otherwise the change user is silently +ignored.
+ + + +
+If you wish to have stderr connected to a file (this +is done after changing user, so make sure the file is +accessible or owned by the user, not by root), then call +pthr_server_stderr_file. Note that unless you call +this function or pthr_server_disable_close then +stderr is sent to /dev/null!
+ + + +
+The startup_fn is called after all of the above +operations have completed, but before the listener thread is +created. You do miscellaneous start-of-day functions from +here, in particular starting up other global threads. The +command line arguments are also passed to this +function.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pthr_server_main.3.html b/doc/pthr_server_main.3.html new file mode 100644 index 0000000..d57116c --- /dev/null +++ b/doc/pthr_server_main.3.html @@ -0,0 +1,125 @@ + + + + +pthr_server_main + + + +

pthr_server_main

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pthr_server_main - server framework
+ +

SYNOPSIS

+ + + +
+
#include <pthr_server_main.h>
+
+void pthr_server_main (const char *name, void (*processor_fn) (int sock, struct sockaddr_in addr), void (*global_fn) (void), int port, int fork_into_background, int close_io, int chdir_root) __attribute__((noreturn));
+
+ +

DESCRIPTION

+ + + +
+pthr_server_main is a convenient framework function +for writing servers quickly using pthrlib. It creates a +listener thread which listens on the given port number and +throws off new processor threads to handle each incoming +connection.
+ + + +
+name is the name of the server.
+ + + +
+processor_fn is a function which should create a new +processor thread for each incoming connection.
+ + + +
+global_fn is called after forking, but before +starting to accept new connections. It should perform any +necessary initialization.
+ + + +
+port is the port number to listen on.
+ + + +
+fork_into_background should be set if the process +should fork into the background (this is normally desirable +for server processes).
+ + + +
+close_io should be set if the process should close +file descriptors 0, 1 and 2 before starting up.
+ + + +
+chdir_root should be set if the process should change +to the root directory before starting up.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-2.2.2
+
+ + diff --git a/doc/pthr_server_main_loop.3.html b/doc/pthr_server_main_loop.3.html new file mode 100644 index 0000000..e5b8d8e --- /dev/null +++ b/doc/pthr_server_main_loop.3.html @@ -0,0 +1,171 @@ + + + + +pthr_server_main_loop + + + +

pthr_server_main_loop

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+
+ +

DESCRIPTION

+ + + +
+The function pthr_server_main_loop is a helper +function which allows you to write very simple servers +quickly using pthrlib. Normally your main +should just contain a call to pthr_server_main_loop +(argc, argv, processor) and you would include a function +called processor which is called on every +connection.
+ + + +
+The other functions allow you to customise the behaviour of +pthr_server_main_loop. You should call any of these +once in your main before calling +pthr_server_main_loop.
+ + + +
+By default the server listens on port 80 and allows you to +override this on the command line using the -p +option. To change this, call pthr_server_default_port +and/or pthr_server_port_option_name.
+ + + +
+By default the server writes package and version information +to syslog (although it will not be able to correctly +determine the package). Use +pthr_server_disable_syslog to disable syslogging +entirely, or pthr_server_package_name to change the +name displayed in the logs.
+ + + +
+By default the server forks into the background, changes to +the root directory and disconnects entirely from the +terminal. Use pthr_server_disable_fork, +pthr_server_disable_chdir and +pthr_server_disable_close to change these +respectively.
+ + + +
+If you wish to have the server chroot after start up, then +call pthr_server_chroot. This is only possible if the +server is run as root, otherwise the chroot is silently +ignored.
+ + + +
+If you wish to have the server change user and group after +start up, then call pthr_server_username (the group +is taken from the password file and initgroups(3) is +also called). This is only possible if the server is run as +root, otherwise the change user is silently +ignored.
+ + + +
+If you wish to have stderr connected to a file (this +is done after changing user, so make sure the file is +accessible or owned by the user, not by root), then call +pthr_server_stderr_file. Note that unless you call +this function or pthr_server_disable_close then +stderr is sent to /dev/null!
+ + + +
+The startup_fn is called after all of the above +operations have completed, but before the listener thread is +created. You do miscellaneous start-of-day functions from +here, in particular starting up other global threads. The +command line arguments are also passed to this +function.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pthr_server_package_name.3.html b/doc/pthr_server_package_name.3.html new file mode 100644 index 0000000..e5b8d8e --- /dev/null +++ b/doc/pthr_server_package_name.3.html @@ -0,0 +1,171 @@ + + + + +pthr_server_main_loop + + + +

pthr_server_main_loop

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+
+ +

DESCRIPTION

+ + + +
+The function pthr_server_main_loop is a helper +function which allows you to write very simple servers +quickly using pthrlib. Normally your main +should just contain a call to pthr_server_main_loop +(argc, argv, processor) and you would include a function +called processor which is called on every +connection.
+ + + +
+The other functions allow you to customise the behaviour of +pthr_server_main_loop. You should call any of these +once in your main before calling +pthr_server_main_loop.
+ + + +
+By default the server listens on port 80 and allows you to +override this on the command line using the -p +option. To change this, call pthr_server_default_port +and/or pthr_server_port_option_name.
+ + + +
+By default the server writes package and version information +to syslog (although it will not be able to correctly +determine the package). Use +pthr_server_disable_syslog to disable syslogging +entirely, or pthr_server_package_name to change the +name displayed in the logs.
+ + + +
+By default the server forks into the background, changes to +the root directory and disconnects entirely from the +terminal. Use pthr_server_disable_fork, +pthr_server_disable_chdir and +pthr_server_disable_close to change these +respectively.
+ + + +
+If you wish to have the server chroot after start up, then +call pthr_server_chroot. This is only possible if the +server is run as root, otherwise the chroot is silently +ignored.
+ + + +
+If you wish to have the server change user and group after +start up, then call pthr_server_username (the group +is taken from the password file and initgroups(3) is +also called). This is only possible if the server is run as +root, otherwise the change user is silently +ignored.
+ + + +
+If you wish to have stderr connected to a file (this +is done after changing user, so make sure the file is +accessible or owned by the user, not by root), then call +pthr_server_stderr_file. Note that unless you call +this function or pthr_server_disable_close then +stderr is sent to /dev/null!
+ + + +
+The startup_fn is called after all of the above +operations have completed, but before the listener thread is +created. You do miscellaneous start-of-day functions from +here, in particular starting up other global threads. The +command line arguments are also passed to this +function.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pthr_server_port_option_name.3.html b/doc/pthr_server_port_option_name.3.html new file mode 100644 index 0000000..e5b8d8e --- /dev/null +++ b/doc/pthr_server_port_option_name.3.html @@ -0,0 +1,171 @@ + + + + +pthr_server_main_loop + + + +

pthr_server_main_loop

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+
+ +

DESCRIPTION

+ + + +
+The function pthr_server_main_loop is a helper +function which allows you to write very simple servers +quickly using pthrlib. Normally your main +should just contain a call to pthr_server_main_loop +(argc, argv, processor) and you would include a function +called processor which is called on every +connection.
+ + + +
+The other functions allow you to customise the behaviour of +pthr_server_main_loop. You should call any of these +once in your main before calling +pthr_server_main_loop.
+ + + +
+By default the server listens on port 80 and allows you to +override this on the command line using the -p +option. To change this, call pthr_server_default_port +and/or pthr_server_port_option_name.
+ + + +
+By default the server writes package and version information +to syslog (although it will not be able to correctly +determine the package). Use +pthr_server_disable_syslog to disable syslogging +entirely, or pthr_server_package_name to change the +name displayed in the logs.
+ + + +
+By default the server forks into the background, changes to +the root directory and disconnects entirely from the +terminal. Use pthr_server_disable_fork, +pthr_server_disable_chdir and +pthr_server_disable_close to change these +respectively.
+ + + +
+If you wish to have the server chroot after start up, then +call pthr_server_chroot. This is only possible if the +server is run as root, otherwise the chroot is silently +ignored.
+ + + +
+If you wish to have the server change user and group after +start up, then call pthr_server_username (the group +is taken from the password file and initgroups(3) is +also called). This is only possible if the server is run as +root, otherwise the change user is silently +ignored.
+ + + +
+If you wish to have stderr connected to a file (this +is done after changing user, so make sure the file is +accessible or owned by the user, not by root), then call +pthr_server_stderr_file. Note that unless you call +this function or pthr_server_disable_close then +stderr is sent to /dev/null!
+ + + +
+The startup_fn is called after all of the above +operations have completed, but before the listener thread is +created. You do miscellaneous start-of-day functions from +here, in particular starting up other global threads. The +command line arguments are also passed to this +function.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pthr_server_startup_fn.3.html b/doc/pthr_server_startup_fn.3.html new file mode 100644 index 0000000..86b91ca --- /dev/null +++ b/doc/pthr_server_startup_fn.3.html @@ -0,0 +1,171 @@ + + + + +pthr_server_main_loop + + + +

pthr_server_main_loop

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+
+ +

DESCRIPTION

+ + + +
+The function pthr_server_main_loop is a helper +function which allows you to write very simple servers +quickly using pthrlib. Normally your main +should just contain a call to pthr_server_main_loop +(argc, argv, processor) and you would include a function +called processor which is called on every +connection.
+ + + +
+The other functions allow you to customise the behaviour of +pthr_server_main_loop. You should call any of these +once in your main before calling +pthr_server_main_loop.
+ + + +
+By default the server listens on port 80 and allows you to +override this on the command line using the -p +option. To change this, call pthr_server_default_port +and/or pthr_server_port_option_name.
+ + + +
+By default the server writes package and version information +to syslog (although it will not be able to correctly +determine the package). Use +pthr_server_disable_syslog to disable syslogging +entirely, or pthr_server_package_name to change the +name displayed in the logs.
+ + + +
+By default the server forks into the background, changes to +the root directory and disconnects entirely from the +terminal. Use pthr_server_disable_fork, +pthr_server_disable_chdir and +pthr_server_disable_close to change these +respectively.
+ + + +
+If you wish to have the server chroot after start up, then +call pthr_server_chroot. This is only possible if the +server is run as root, otherwise the chroot is silently +ignored.
+ + + +
+If you wish to have the server change user and group after +start up, then call pthr_server_username (the group +is taken from the password file and initgroups(3) is +also called). This is only possible if the server is run as +root, otherwise the change user is silently +ignored.
+ + + +
+If you wish to have stderr connected to a file (this +is done after changing user, so make sure the file is +accessible or owned by the user, not by root), then call +pthr_server_stderr_file. Note that unless you call +this function or pthr_server_disable_close then +stderr is sent to /dev/null!
+ + + +
+The startup_fn is called after all of the above +operations have completed, but before the listener thread is +created. You do miscellaneous start-of-day functions from +here, in particular starting up other global threads. The +command line arguments are also passed to this +function.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pthr_server_stderr_file.3.html b/doc/pthr_server_stderr_file.3.html new file mode 100644 index 0000000..86b91ca --- /dev/null +++ b/doc/pthr_server_stderr_file.3.html @@ -0,0 +1,171 @@ + + + + +pthr_server_main_loop + + + +

pthr_server_main_loop

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+
+ +

DESCRIPTION

+ + + +
+The function pthr_server_main_loop is a helper +function which allows you to write very simple servers +quickly using pthrlib. Normally your main +should just contain a call to pthr_server_main_loop +(argc, argv, processor) and you would include a function +called processor which is called on every +connection.
+ + + +
+The other functions allow you to customise the behaviour of +pthr_server_main_loop. You should call any of these +once in your main before calling +pthr_server_main_loop.
+ + + +
+By default the server listens on port 80 and allows you to +override this on the command line using the -p +option. To change this, call pthr_server_default_port +and/or pthr_server_port_option_name.
+ + + +
+By default the server writes package and version information +to syslog (although it will not be able to correctly +determine the package). Use +pthr_server_disable_syslog to disable syslogging +entirely, or pthr_server_package_name to change the +name displayed in the logs.
+ + + +
+By default the server forks into the background, changes to +the root directory and disconnects entirely from the +terminal. Use pthr_server_disable_fork, +pthr_server_disable_chdir and +pthr_server_disable_close to change these +respectively.
+ + + +
+If you wish to have the server chroot after start up, then +call pthr_server_chroot. This is only possible if the +server is run as root, otherwise the chroot is silently +ignored.
+ + + +
+If you wish to have the server change user and group after +start up, then call pthr_server_username (the group +is taken from the password file and initgroups(3) is +also called). This is only possible if the server is run as +root, otherwise the change user is silently +ignored.
+ + + +
+If you wish to have stderr connected to a file (this +is done after changing user, so make sure the file is +accessible or owned by the user, not by root), then call +pthr_server_stderr_file. Note that unless you call +this function or pthr_server_disable_close then +stderr is sent to /dev/null!
+ + + +
+The startup_fn is called after all of the above +operations have completed, but before the listener thread is +created. You do miscellaneous start-of-day functions from +here, in particular starting up other global threads. The +command line arguments are also passed to this +function.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/pthr_server_username.3.html b/doc/pthr_server_username.3.html new file mode 100644 index 0000000..86b91ca --- /dev/null +++ b/doc/pthr_server_username.3.html @@ -0,0 +1,171 @@ + + + + +pthr_server_main_loop + + + +

pthr_server_main_loop

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.
+ +

SYNOPSIS

+ + + +
+
#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+
+ +

DESCRIPTION

+ + + +
+The function pthr_server_main_loop is a helper +function which allows you to write very simple servers +quickly using pthrlib. Normally your main +should just contain a call to pthr_server_main_loop +(argc, argv, processor) and you would include a function +called processor which is called on every +connection.
+ + + +
+The other functions allow you to customise the behaviour of +pthr_server_main_loop. You should call any of these +once in your main before calling +pthr_server_main_loop.
+ + + +
+By default the server listens on port 80 and allows you to +override this on the command line using the -p +option. To change this, call pthr_server_default_port +and/or pthr_server_port_option_name.
+ + + +
+By default the server writes package and version information +to syslog (although it will not be able to correctly +determine the package). Use +pthr_server_disable_syslog to disable syslogging +entirely, or pthr_server_package_name to change the +name displayed in the logs.
+ + + +
+By default the server forks into the background, changes to +the root directory and disconnects entirely from the +terminal. Use pthr_server_disable_fork, +pthr_server_disable_chdir and +pthr_server_disable_close to change these +respectively.
+ + + +
+If you wish to have the server chroot after start up, then +call pthr_server_chroot. This is only possible if the +server is run as root, otherwise the chroot is silently +ignored.
+ + + +
+If you wish to have the server change user and group after +start up, then call pthr_server_username (the group +is taken from the password file and initgroups(3) is +also called). This is only possible if the server is run as +root, otherwise the change user is silently +ignored.
+ + + +
+If you wish to have stderr connected to a file (this +is done after changing user, so make sure the file is +accessible or owned by the user, not by root), then call +pthr_server_stderr_file. Note that unless you call +this function or pthr_server_disable_close then +stderr is sent to /dev/null!
+ + + +
+The startup_fn is called after all of the above +operations have completed, but before the listener thread is +created. You do miscellaneous start-of-day functions from +here, in particular starting up other global threads. The +command line arguments are also passed to this +function.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/rwlock_enter_read.3.html b/doc/rwlock_enter_read.3.html new file mode 100644 index 0000000..d7bfb59 --- /dev/null +++ b/doc/rwlock_enter_read.3.html @@ -0,0 +1,184 @@ + + + + +new_rwlock + + + +

new_rwlock

+NAME
+SYNOPSIS
+DESCRIPTION
+BUGS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+new_rwlock, rwlock_writers_have_priority, rwlock_readers_have_priority, rwlock_enter_read, rwlock_enter_write, rwlock_try_enter_read, rwlock_try_enter_write, rwlock_leave - multiple reader / single writer locks (rwlocks)
+ +

SYNOPSIS

+ + + +
+
#include <pthr_rwlock.h>
+
+rwlock new_rwlock (pool);
+void rwlock_writers_have_priority (rwlock);
+void rwlock_readers_have_priority (rwlock);
+void rwlock_enter_read (rwlock, pseudothread);
+void rwlock_enter_write (rwlock, pseudothread);
+int rwlock_try_enter_read (rwlock, pseudothread);
+int rwlock_try_enter_write (rwlock, pseudothread);
+void rwlock_leave (rwlock, pseudothread);
+
+ +

DESCRIPTION

+ + + +
+Multiple reader / single writer locks (rwlocks) do what they +say. They allow either many readers to access a critical +section or a single writer (but not both). If instead you +require simple mutex semantics, then please see +<pthr_mutex.h>.
+ + + +
+RWlocks are automatically released if they are being held +when the thread exits.
+ + + +
+RWlocks are not ``upgradable''. The implementation is too +complex to justify that.
+ + + +
+Note that there are possible deadlocks when using locks. To +avoid deadlocks, always ensure every thread acquires locks +in the same order.
+ + + +
+new_rwlock creates a new rwlock object.
+ + + +
+rwlock_writers_have_priority changes the nature of +the lock so that writers have priority over readers. If this +is the case then new readers will not be able to enter a +critical section if there are writers waiting to enter. [NB: +This is the default.]
+ + + +
+rwlock_readers_have_priority changes the nature of +the lock so that readers have priority over writers. Note +that if this is the case then writers are likely to be +starved if the lock is frequently read.
+ + + +
+rwlock_enter_read enters the critical section as a +reader. Any number of readers are allowed to enter a +critical section at the same time. This function may +block.
+ + + +
+rwlock_enter_write enters the critical section as a +writer. Only a single writer is allowed to enter a critical +section, and then only if there are no readers. This +function may block.
+ + + +
+rwlock_try_enter_read is identical to +rwlock_enter_read, but it does not block. It returns +true if the lock was successfully acquired, or false if the +operation would block.
+ + + +
+rwlock_try_enter_write is identical to +rwlock_enter_write, but it does not block. It returns +true if the lock was successfully acquired, or false if the +operation would block.
+ + + +
+rwlock_leave leaves the critical +section.
+ +

BUGS

+ + + +
+A common mistake is to accidentally call rwlock_leave +when you are not holding the lock. This generally causes the +library to crash.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/rwlock_enter_write.3.html b/doc/rwlock_enter_write.3.html new file mode 100644 index 0000000..b4fe6d8 --- /dev/null +++ b/doc/rwlock_enter_write.3.html @@ -0,0 +1,184 @@ + + + + +new_rwlock + + + +

new_rwlock

+NAME
+SYNOPSIS
+DESCRIPTION
+BUGS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+new_rwlock, rwlock_writers_have_priority, rwlock_readers_have_priority, rwlock_enter_read, rwlock_enter_write, rwlock_try_enter_read, rwlock_try_enter_write, rwlock_leave - multiple reader / single writer locks (rwlocks)
+ +

SYNOPSIS

+ + + +
+
#include <pthr_rwlock.h>
+
+rwlock new_rwlock (pool);
+void rwlock_writers_have_priority (rwlock);
+void rwlock_readers_have_priority (rwlock);
+void rwlock_enter_read (rwlock, pseudothread);
+void rwlock_enter_write (rwlock, pseudothread);
+int rwlock_try_enter_read (rwlock, pseudothread);
+int rwlock_try_enter_write (rwlock, pseudothread);
+void rwlock_leave (rwlock, pseudothread);
+
+ +

DESCRIPTION

+ + + +
+Multiple reader / single writer locks (rwlocks) do what they +say. They allow either many readers to access a critical +section or a single writer (but not both). If instead you +require simple mutex semantics, then please see +<pthr_mutex.h>.
+ + + +
+RWlocks are automatically released if they are being held +when the thread exits.
+ + + +
+RWlocks are not ``upgradable''. The implementation is too +complex to justify that.
+ + + +
+Note that there are possible deadlocks when using locks. To +avoid deadlocks, always ensure every thread acquires locks +in the same order.
+ + + +
+new_rwlock creates a new rwlock object.
+ + + +
+rwlock_writers_have_priority changes the nature of +the lock so that writers have priority over readers. If this +is the case then new readers will not be able to enter a +critical section if there are writers waiting to enter. [NB: +This is the default.]
+ + + +
+rwlock_readers_have_priority changes the nature of +the lock so that readers have priority over writers. Note +that if this is the case then writers are likely to be +starved if the lock is frequently read.
+ + + +
+rwlock_enter_read enters the critical section as a +reader. Any number of readers are allowed to enter a +critical section at the same time. This function may +block.
+ + + +
+rwlock_enter_write enters the critical section as a +writer. Only a single writer is allowed to enter a critical +section, and then only if there are no readers. This +function may block.
+ + + +
+rwlock_try_enter_read is identical to +rwlock_enter_read, but it does not block. It returns +true if the lock was successfully acquired, or false if the +operation would block.
+ + + +
+rwlock_try_enter_write is identical to +rwlock_enter_write, but it does not block. It returns +true if the lock was successfully acquired, or false if the +operation would block.
+ + + +
+rwlock_leave leaves the critical +section.
+ +

BUGS

+ + + +
+A common mistake is to accidentally call rwlock_leave +when you are not holding the lock. This generally causes the +library to crash.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/rwlock_leave.3.html b/doc/rwlock_leave.3.html new file mode 100644 index 0000000..b4fe6d8 --- /dev/null +++ b/doc/rwlock_leave.3.html @@ -0,0 +1,184 @@ + + + + +new_rwlock + + + +

new_rwlock

+NAME
+SYNOPSIS
+DESCRIPTION
+BUGS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+new_rwlock, rwlock_writers_have_priority, rwlock_readers_have_priority, rwlock_enter_read, rwlock_enter_write, rwlock_try_enter_read, rwlock_try_enter_write, rwlock_leave - multiple reader / single writer locks (rwlocks)
+ +

SYNOPSIS

+ + + +
+
#include <pthr_rwlock.h>
+
+rwlock new_rwlock (pool);
+void rwlock_writers_have_priority (rwlock);
+void rwlock_readers_have_priority (rwlock);
+void rwlock_enter_read (rwlock, pseudothread);
+void rwlock_enter_write (rwlock, pseudothread);
+int rwlock_try_enter_read (rwlock, pseudothread);
+int rwlock_try_enter_write (rwlock, pseudothread);
+void rwlock_leave (rwlock, pseudothread);
+
+ +

DESCRIPTION

+ + + +
+Multiple reader / single writer locks (rwlocks) do what they +say. They allow either many readers to access a critical +section or a single writer (but not both). If instead you +require simple mutex semantics, then please see +<pthr_mutex.h>.
+ + + +
+RWlocks are automatically released if they are being held +when the thread exits.
+ + + +
+RWlocks are not ``upgradable''. The implementation is too +complex to justify that.
+ + + +
+Note that there are possible deadlocks when using locks. To +avoid deadlocks, always ensure every thread acquires locks +in the same order.
+ + + +
+new_rwlock creates a new rwlock object.
+ + + +
+rwlock_writers_have_priority changes the nature of +the lock so that writers have priority over readers. If this +is the case then new readers will not be able to enter a +critical section if there are writers waiting to enter. [NB: +This is the default.]
+ + + +
+rwlock_readers_have_priority changes the nature of +the lock so that readers have priority over writers. Note +that if this is the case then writers are likely to be +starved if the lock is frequently read.
+ + + +
+rwlock_enter_read enters the critical section as a +reader. Any number of readers are allowed to enter a +critical section at the same time. This function may +block.
+ + + +
+rwlock_enter_write enters the critical section as a +writer. Only a single writer is allowed to enter a critical +section, and then only if there are no readers. This +function may block.
+ + + +
+rwlock_try_enter_read is identical to +rwlock_enter_read, but it does not block. It returns +true if the lock was successfully acquired, or false if the +operation would block.
+ + + +
+rwlock_try_enter_write is identical to +rwlock_enter_write, but it does not block. It returns +true if the lock was successfully acquired, or false if the +operation would block.
+ + + +
+rwlock_leave leaves the critical +section.
+ +

BUGS

+ + + +
+A common mistake is to accidentally call rwlock_leave +when you are not holding the lock. This generally causes the +library to crash.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/rwlock_readers_have_priority.3.html b/doc/rwlock_readers_have_priority.3.html new file mode 100644 index 0000000..b4fe6d8 --- /dev/null +++ b/doc/rwlock_readers_have_priority.3.html @@ -0,0 +1,184 @@ + + + + +new_rwlock + + + +

new_rwlock

+NAME
+SYNOPSIS
+DESCRIPTION
+BUGS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+new_rwlock, rwlock_writers_have_priority, rwlock_readers_have_priority, rwlock_enter_read, rwlock_enter_write, rwlock_try_enter_read, rwlock_try_enter_write, rwlock_leave - multiple reader / single writer locks (rwlocks)
+ +

SYNOPSIS

+ + + +
+
#include <pthr_rwlock.h>
+
+rwlock new_rwlock (pool);
+void rwlock_writers_have_priority (rwlock);
+void rwlock_readers_have_priority (rwlock);
+void rwlock_enter_read (rwlock, pseudothread);
+void rwlock_enter_write (rwlock, pseudothread);
+int rwlock_try_enter_read (rwlock, pseudothread);
+int rwlock_try_enter_write (rwlock, pseudothread);
+void rwlock_leave (rwlock, pseudothread);
+
+ +

DESCRIPTION

+ + + +
+Multiple reader / single writer locks (rwlocks) do what they +say. They allow either many readers to access a critical +section or a single writer (but not both). If instead you +require simple mutex semantics, then please see +<pthr_mutex.h>.
+ + + +
+RWlocks are automatically released if they are being held +when the thread exits.
+ + + +
+RWlocks are not ``upgradable''. The implementation is too +complex to justify that.
+ + + +
+Note that there are possible deadlocks when using locks. To +avoid deadlocks, always ensure every thread acquires locks +in the same order.
+ + + +
+new_rwlock creates a new rwlock object.
+ + + +
+rwlock_writers_have_priority changes the nature of +the lock so that writers have priority over readers. If this +is the case then new readers will not be able to enter a +critical section if there are writers waiting to enter. [NB: +This is the default.]
+ + + +
+rwlock_readers_have_priority changes the nature of +the lock so that readers have priority over writers. Note +that if this is the case then writers are likely to be +starved if the lock is frequently read.
+ + + +
+rwlock_enter_read enters the critical section as a +reader. Any number of readers are allowed to enter a +critical section at the same time. This function may +block.
+ + + +
+rwlock_enter_write enters the critical section as a +writer. Only a single writer is allowed to enter a critical +section, and then only if there are no readers. This +function may block.
+ + + +
+rwlock_try_enter_read is identical to +rwlock_enter_read, but it does not block. It returns +true if the lock was successfully acquired, or false if the +operation would block.
+ + + +
+rwlock_try_enter_write is identical to +rwlock_enter_write, but it does not block. It returns +true if the lock was successfully acquired, or false if the +operation would block.
+ + + +
+rwlock_leave leaves the critical +section.
+ +

BUGS

+ + + +
+A common mistake is to accidentally call rwlock_leave +when you are not holding the lock. This generally causes the +library to crash.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/rwlock_try_enter_read.3.html b/doc/rwlock_try_enter_read.3.html new file mode 100644 index 0000000..b4fe6d8 --- /dev/null +++ b/doc/rwlock_try_enter_read.3.html @@ -0,0 +1,184 @@ + + + + +new_rwlock + + + +

new_rwlock

+NAME
+SYNOPSIS
+DESCRIPTION
+BUGS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+new_rwlock, rwlock_writers_have_priority, rwlock_readers_have_priority, rwlock_enter_read, rwlock_enter_write, rwlock_try_enter_read, rwlock_try_enter_write, rwlock_leave - multiple reader / single writer locks (rwlocks)
+ +

SYNOPSIS

+ + + +
+
#include <pthr_rwlock.h>
+
+rwlock new_rwlock (pool);
+void rwlock_writers_have_priority (rwlock);
+void rwlock_readers_have_priority (rwlock);
+void rwlock_enter_read (rwlock, pseudothread);
+void rwlock_enter_write (rwlock, pseudothread);
+int rwlock_try_enter_read (rwlock, pseudothread);
+int rwlock_try_enter_write (rwlock, pseudothread);
+void rwlock_leave (rwlock, pseudothread);
+
+ +

DESCRIPTION

+ + + +
+Multiple reader / single writer locks (rwlocks) do what they +say. They allow either many readers to access a critical +section or a single writer (but not both). If instead you +require simple mutex semantics, then please see +<pthr_mutex.h>.
+ + + +
+RWlocks are automatically released if they are being held +when the thread exits.
+ + + +
+RWlocks are not ``upgradable''. The implementation is too +complex to justify that.
+ + + +
+Note that there are possible deadlocks when using locks. To +avoid deadlocks, always ensure every thread acquires locks +in the same order.
+ + + +
+new_rwlock creates a new rwlock object.
+ + + +
+rwlock_writers_have_priority changes the nature of +the lock so that writers have priority over readers. If this +is the case then new readers will not be able to enter a +critical section if there are writers waiting to enter. [NB: +This is the default.]
+ + + +
+rwlock_readers_have_priority changes the nature of +the lock so that readers have priority over writers. Note +that if this is the case then writers are likely to be +starved if the lock is frequently read.
+ + + +
+rwlock_enter_read enters the critical section as a +reader. Any number of readers are allowed to enter a +critical section at the same time. This function may +block.
+ + + +
+rwlock_enter_write enters the critical section as a +writer. Only a single writer is allowed to enter a critical +section, and then only if there are no readers. This +function may block.
+ + + +
+rwlock_try_enter_read is identical to +rwlock_enter_read, but it does not block. It returns +true if the lock was successfully acquired, or false if the +operation would block.
+ + + +
+rwlock_try_enter_write is identical to +rwlock_enter_write, but it does not block. It returns +true if the lock was successfully acquired, or false if the +operation would block.
+ + + +
+rwlock_leave leaves the critical +section.
+ +

BUGS

+ + + +
+A common mistake is to accidentally call rwlock_leave +when you are not holding the lock. This generally causes the +library to crash.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/rwlock_try_enter_write.3.html b/doc/rwlock_try_enter_write.3.html new file mode 100644 index 0000000..46b92df --- /dev/null +++ b/doc/rwlock_try_enter_write.3.html @@ -0,0 +1,184 @@ + + + + +new_rwlock + + + +

new_rwlock

+NAME
+SYNOPSIS
+DESCRIPTION
+BUGS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+new_rwlock, rwlock_writers_have_priority, rwlock_readers_have_priority, rwlock_enter_read, rwlock_enter_write, rwlock_try_enter_read, rwlock_try_enter_write, rwlock_leave - multiple reader / single writer locks (rwlocks)
+ +

SYNOPSIS

+ + + +
+
#include <pthr_rwlock.h>
+
+rwlock new_rwlock (pool);
+void rwlock_writers_have_priority (rwlock);
+void rwlock_readers_have_priority (rwlock);
+void rwlock_enter_read (rwlock, pseudothread);
+void rwlock_enter_write (rwlock, pseudothread);
+int rwlock_try_enter_read (rwlock, pseudothread);
+int rwlock_try_enter_write (rwlock, pseudothread);
+void rwlock_leave (rwlock, pseudothread);
+
+ +

DESCRIPTION

+ + + +
+Multiple reader / single writer locks (rwlocks) do what they +say. They allow either many readers to access a critical +section or a single writer (but not both). If instead you +require simple mutex semantics, then please see +<pthr_mutex.h>.
+ + + +
+RWlocks are automatically released if they are being held +when the thread exits.
+ + + +
+RWlocks are not ``upgradable''. The implementation is too +complex to justify that.
+ + + +
+Note that there are possible deadlocks when using locks. To +avoid deadlocks, always ensure every thread acquires locks +in the same order.
+ + + +
+new_rwlock creates a new rwlock object.
+ + + +
+rwlock_writers_have_priority changes the nature of +the lock so that writers have priority over readers. If this +is the case then new readers will not be able to enter a +critical section if there are writers waiting to enter. [NB: +This is the default.]
+ + + +
+rwlock_readers_have_priority changes the nature of +the lock so that readers have priority over writers. Note +that if this is the case then writers are likely to be +starved if the lock is frequently read.
+ + + +
+rwlock_enter_read enters the critical section as a +reader. Any number of readers are allowed to enter a +critical section at the same time. This function may +block.
+ + + +
+rwlock_enter_write enters the critical section as a +writer. Only a single writer is allowed to enter a critical +section, and then only if there are no readers. This +function may block.
+ + + +
+rwlock_try_enter_read is identical to +rwlock_enter_read, but it does not block. It returns +true if the lock was successfully acquired, or false if the +operation would block.
+ + + +
+rwlock_try_enter_write is identical to +rwlock_enter_write, but it does not block. It returns +true if the lock was successfully acquired, or false if the +operation would block.
+ + + +
+rwlock_leave leaves the critical +section.
+ +

BUGS

+ + + +
+A common mistake is to accidentally call rwlock_leave +when you are not holding the lock. This generally causes the +library to crash.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/rwlock_writers_have_priority.3.html b/doc/rwlock_writers_have_priority.3.html new file mode 100644 index 0000000..46b92df --- /dev/null +++ b/doc/rwlock_writers_have_priority.3.html @@ -0,0 +1,184 @@ + + + + +new_rwlock + + + +

new_rwlock

+NAME
+SYNOPSIS
+DESCRIPTION
+BUGS
+AUTHOR
+LICENSE
+VERSION
+ +
+ + + +

NAME

+ + +
+new_rwlock, rwlock_writers_have_priority, rwlock_readers_have_priority, rwlock_enter_read, rwlock_enter_write, rwlock_try_enter_read, rwlock_try_enter_write, rwlock_leave - multiple reader / single writer locks (rwlocks)
+ +

SYNOPSIS

+ + + +
+
#include <pthr_rwlock.h>
+
+rwlock new_rwlock (pool);
+void rwlock_writers_have_priority (rwlock);
+void rwlock_readers_have_priority (rwlock);
+void rwlock_enter_read (rwlock, pseudothread);
+void rwlock_enter_write (rwlock, pseudothread);
+int rwlock_try_enter_read (rwlock, pseudothread);
+int rwlock_try_enter_write (rwlock, pseudothread);
+void rwlock_leave (rwlock, pseudothread);
+
+ +

DESCRIPTION

+ + + +
+Multiple reader / single writer locks (rwlocks) do what they +say. They allow either many readers to access a critical +section or a single writer (but not both). If instead you +require simple mutex semantics, then please see +<pthr_mutex.h>.
+ + + +
+RWlocks are automatically released if they are being held +when the thread exits.
+ + + +
+RWlocks are not ``upgradable''. The implementation is too +complex to justify that.
+ + + +
+Note that there are possible deadlocks when using locks. To +avoid deadlocks, always ensure every thread acquires locks +in the same order.
+ + + +
+new_rwlock creates a new rwlock object.
+ + + +
+rwlock_writers_have_priority changes the nature of +the lock so that writers have priority over readers. If this +is the case then new readers will not be able to enter a +critical section if there are writers waiting to enter. [NB: +This is the default.]
+ + + +
+rwlock_readers_have_priority changes the nature of +the lock so that readers have priority over writers. Note +that if this is the case then writers are likely to be +starved if the lock is frequently read.
+ + + +
+rwlock_enter_read enters the critical section as a +reader. Any number of readers are allowed to enter a +critical section at the same time. This function may +block.
+ + + +
+rwlock_enter_write enters the critical section as a +writer. Only a single writer is allowed to enter a critical +section, and then only if there are no readers. This +function may block.
+ + + +
+rwlock_try_enter_read is identical to +rwlock_enter_read, but it does not block. It returns +true if the lock was successfully acquired, or false if the +operation would block.
+ + + +
+rwlock_try_enter_write is identical to +rwlock_enter_write, but it does not block. It returns +true if the lock was successfully acquired, or false if the +operation would block.
+ + + +
+rwlock_leave leaves the critical +section.
+ +

BUGS

+ + + +
+A common mistake is to accidentally call rwlock_leave +when you are not holding the lock. This generally causes the +library to crash.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+
+ + diff --git a/doc/wq_nr_sleepers.3.html b/doc/wq_nr_sleepers.3.html new file mode 100644 index 0000000..8e1e107 --- /dev/null +++ b/doc/wq_nr_sleepers.3.html @@ -0,0 +1,217 @@ + + + + +new_wait_queue + + + +

new_wait_queue

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+HISTORY
+ +
+ + + +

NAME

+ + +
+new_wait_queue, wq_wake_up, wq_wake_up_one, wq_sleep_on, wq_nr_sleepers - wait queues
+ +

SYNOPSIS

+ + + +
+
#include <pthr_wait_queue.h>
+
+wait_queue new_wait_queue (pool);
+void wq_wake_up (wait_queue);
+void wq_wake_up_one (wait_queue);
+void wq_sleep_on (wait_queue, pseudothread);
+int wq_nr_sleepers (wait_queue);
+
+ +

DESCRIPTION

+ + + +
+new_wait_queue creates a wait queue +object.
+ + + +
+wq_wake_up wakes up all the threads which are +currently sleeping on the wait queue. Note that this +function does not block, and because pseudothreads are +non-preemptive, none of the sleeping threads will actually +begin running until at least the current thread blocks +somewhere else.
+ + + +
+wq_wake_up_one wakes up just the first thread at the +head of the wait queue (the one which has been waiting the +longest).
+ + + +
+wq_sleep_on sends the current thread to sleep on the +wait queue. This call blocks (obviously).
+ + + +
+wq_nr_sleepers returns the number of threads which +are currently asleep on the wait queue.
+ + + +
+Please read the HISTORY section below for some background +into how wait queues are implemented. This may help if you +find there are tricky race conditions in your +code.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

HISTORY

+ + + +
+Originally, wait queues were implemented using underlying +Unix pipes. This worked (to some extent) but the overhead of +requiring one pipe (ie. one inode, two file descriptors) per +wait queue made this implementation unacceptably +heavyweight.
+ + + +
+Wait queues are now implemented using a simple hack in the +reactor which will be described below.
+ + + +
+Wait queues are subtle. Consider this example: Threads 1, 2 +and 3 are sleeping on a wait queue. Now thread 4 wakes up +the queue. You would expect (probably) threads 1, 2 and 3 to +each be woken up and allowed to start running. However, +since this is a cooperatively multitasking environment, it +may happen that thread 1 wakes up first, does some work and +then goes back to sleep on the wait queue, all before +threads 2 and 3 have woken up. With a naive implementation +of wait queues, thread 4 might end up waking up thread 1 +*again* (and even again after that), never waking up threads +2 and 3 and ultimately starving those threads.
+ + + +
+To avoid this situation, we might consider two possible +alternatives: either when thread 1 goes back to sleep, it +goes to sleep on a 'different' queue, or else thread 4 might +take a copy of the wait queue and delete the queue before it +wakes any of the threads up.
+ + + +
+Another nasty situation which might arise in real life is +this: Again, threads 1, 2 and 3 are sleeping. Thread 4 wakes +them up. Thread 1, while processing its work, happens also +to wake up the same wait queue. What should happen to this +second wake-up event? Should it be ignored? Should it wake +up threads 2 and 3? Should it wake up any other threads +which happen to have gone to sleep on the queue after 1, 2 +and 3? Or perhaps some combination of these?
+ + + +
+The solution that we have come up with is as follows. A wait +queue consists of a simple list of threads which are +sleeping on it. When a thread wishes to sleep on the wait +queue, it is added to this list, and it switches back into +the reactor context. When a thread wishes to wake up all +sleepers, it: (a) copies the list of sleeping pseudothreads +into its own private space (b) clears the list of sleeping +pseudothreads (c) registers a prepoll handler to run which +will wake up (ie. switch into the context of) each of these +threads in turn (d) continues to run to completion. A thread +which wishes to wake just one pseudothread works similarly +except that it only copies (and removes) a single item off +the list.
+ + + +
+Note various invariant conditions: A thread cannot be +entered on the wait queue sleeping list more than once +(because it cannot call sleep_on when it is already +sleeping). For similar reasons, a thread cannot be entered +on any of the multiple lists at the same time. This implies +that a thread cannot be woken up multiple +times.
+ + + +
+The reader should satisfy themselves that this algorithm is +free of races, and solves all the problems outlined above. +In addition, it has the desirable property that wake_up* +never sleeps.
+
+ + diff --git a/doc/wq_sleep_on.3.html b/doc/wq_sleep_on.3.html new file mode 100644 index 0000000..8e1e107 --- /dev/null +++ b/doc/wq_sleep_on.3.html @@ -0,0 +1,217 @@ + + + + +new_wait_queue + + + +

new_wait_queue

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+HISTORY
+ +
+ + + +

NAME

+ + +
+new_wait_queue, wq_wake_up, wq_wake_up_one, wq_sleep_on, wq_nr_sleepers - wait queues
+ +

SYNOPSIS

+ + + +
+
#include <pthr_wait_queue.h>
+
+wait_queue new_wait_queue (pool);
+void wq_wake_up (wait_queue);
+void wq_wake_up_one (wait_queue);
+void wq_sleep_on (wait_queue, pseudothread);
+int wq_nr_sleepers (wait_queue);
+
+ +

DESCRIPTION

+ + + +
+new_wait_queue creates a wait queue +object.
+ + + +
+wq_wake_up wakes up all the threads which are +currently sleeping on the wait queue. Note that this +function does not block, and because pseudothreads are +non-preemptive, none of the sleeping threads will actually +begin running until at least the current thread blocks +somewhere else.
+ + + +
+wq_wake_up_one wakes up just the first thread at the +head of the wait queue (the one which has been waiting the +longest).
+ + + +
+wq_sleep_on sends the current thread to sleep on the +wait queue. This call blocks (obviously).
+ + + +
+wq_nr_sleepers returns the number of threads which +are currently asleep on the wait queue.
+ + + +
+Please read the HISTORY section below for some background +into how wait queues are implemented. This may help if you +find there are tricky race conditions in your +code.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

HISTORY

+ + + +
+Originally, wait queues were implemented using underlying +Unix pipes. This worked (to some extent) but the overhead of +requiring one pipe (ie. one inode, two file descriptors) per +wait queue made this implementation unacceptably +heavyweight.
+ + + +
+Wait queues are now implemented using a simple hack in the +reactor which will be described below.
+ + + +
+Wait queues are subtle. Consider this example: Threads 1, 2 +and 3 are sleeping on a wait queue. Now thread 4 wakes up +the queue. You would expect (probably) threads 1, 2 and 3 to +each be woken up and allowed to start running. However, +since this is a cooperatively multitasking environment, it +may happen that thread 1 wakes up first, does some work and +then goes back to sleep on the wait queue, all before +threads 2 and 3 have woken up. With a naive implementation +of wait queues, thread 4 might end up waking up thread 1 +*again* (and even again after that), never waking up threads +2 and 3 and ultimately starving those threads.
+ + + +
+To avoid this situation, we might consider two possible +alternatives: either when thread 1 goes back to sleep, it +goes to sleep on a 'different' queue, or else thread 4 might +take a copy of the wait queue and delete the queue before it +wakes any of the threads up.
+ + + +
+Another nasty situation which might arise in real life is +this: Again, threads 1, 2 and 3 are sleeping. Thread 4 wakes +them up. Thread 1, while processing its work, happens also +to wake up the same wait queue. What should happen to this +second wake-up event? Should it be ignored? Should it wake +up threads 2 and 3? Should it wake up any other threads +which happen to have gone to sleep on the queue after 1, 2 +and 3? Or perhaps some combination of these?
+ + + +
+The solution that we have come up with is as follows. A wait +queue consists of a simple list of threads which are +sleeping on it. When a thread wishes to sleep on the wait +queue, it is added to this list, and it switches back into +the reactor context. When a thread wishes to wake up all +sleepers, it: (a) copies the list of sleeping pseudothreads +into its own private space (b) clears the list of sleeping +pseudothreads (c) registers a prepoll handler to run which +will wake up (ie. switch into the context of) each of these +threads in turn (d) continues to run to completion. A thread +which wishes to wake just one pseudothread works similarly +except that it only copies (and removes) a single item off +the list.
+ + + +
+Note various invariant conditions: A thread cannot be +entered on the wait queue sleeping list more than once +(because it cannot call sleep_on when it is already +sleeping). For similar reasons, a thread cannot be entered +on any of the multiple lists at the same time. This implies +that a thread cannot be woken up multiple +times.
+ + + +
+The reader should satisfy themselves that this algorithm is +free of races, and solves all the problems outlined above. +In addition, it has the desirable property that wake_up* +never sleeps.
+
+ + diff --git a/doc/wq_wake_up.3.html b/doc/wq_wake_up.3.html new file mode 100644 index 0000000..e445119 --- /dev/null +++ b/doc/wq_wake_up.3.html @@ -0,0 +1,217 @@ + + + + +new_wait_queue + + + +

new_wait_queue

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+HISTORY
+ +
+ + + +

NAME

+ + +
+new_wait_queue, wq_wake_up, wq_wake_up_one, wq_sleep_on, wq_nr_sleepers - wait queues
+ +

SYNOPSIS

+ + + +
+
#include <pthr_wait_queue.h>
+
+wait_queue new_wait_queue (pool);
+void wq_wake_up (wait_queue);
+void wq_wake_up_one (wait_queue);
+void wq_sleep_on (wait_queue, pseudothread);
+int wq_nr_sleepers (wait_queue);
+
+ +

DESCRIPTION

+ + + +
+new_wait_queue creates a wait queue +object.
+ + + +
+wq_wake_up wakes up all the threads which are +currently sleeping on the wait queue. Note that this +function does not block, and because pseudothreads are +non-preemptive, none of the sleeping threads will actually +begin running until at least the current thread blocks +somewhere else.
+ + + +
+wq_wake_up_one wakes up just the first thread at the +head of the wait queue (the one which has been waiting the +longest).
+ + + +
+wq_sleep_on sends the current thread to sleep on the +wait queue. This call blocks (obviously).
+ + + +
+wq_nr_sleepers returns the number of threads which +are currently asleep on the wait queue.
+ + + +
+Please read the HISTORY section below for some background +into how wait queues are implemented. This may help if you +find there are tricky race conditions in your +code.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

HISTORY

+ + + +
+Originally, wait queues were implemented using underlying +Unix pipes. This worked (to some extent) but the overhead of +requiring one pipe (ie. one inode, two file descriptors) per +wait queue made this implementation unacceptably +heavyweight.
+ + + +
+Wait queues are now implemented using a simple hack in the +reactor which will be described below.
+ + + +
+Wait queues are subtle. Consider this example: Threads 1, 2 +and 3 are sleeping on a wait queue. Now thread 4 wakes up +the queue. You would expect (probably) threads 1, 2 and 3 to +each be woken up and allowed to start running. However, +since this is a cooperatively multitasking environment, it +may happen that thread 1 wakes up first, does some work and +then goes back to sleep on the wait queue, all before +threads 2 and 3 have woken up. With a naive implementation +of wait queues, thread 4 might end up waking up thread 1 +*again* (and even again after that), never waking up threads +2 and 3 and ultimately starving those threads.
+ + + +
+To avoid this situation, we might consider two possible +alternatives: either when thread 1 goes back to sleep, it +goes to sleep on a 'different' queue, or else thread 4 might +take a copy of the wait queue and delete the queue before it +wakes any of the threads up.
+ + + +
+Another nasty situation which might arise in real life is +this: Again, threads 1, 2 and 3 are sleeping. Thread 4 wakes +them up. Thread 1, while processing its work, happens also +to wake up the same wait queue. What should happen to this +second wake-up event? Should it be ignored? Should it wake +up threads 2 and 3? Should it wake up any other threads +which happen to have gone to sleep on the queue after 1, 2 +and 3? Or perhaps some combination of these?
+ + + +
+The solution that we have come up with is as follows. A wait +queue consists of a simple list of threads which are +sleeping on it. When a thread wishes to sleep on the wait +queue, it is added to this list, and it switches back into +the reactor context. When a thread wishes to wake up all +sleepers, it: (a) copies the list of sleeping pseudothreads +into its own private space (b) clears the list of sleeping +pseudothreads (c) registers a prepoll handler to run which +will wake up (ie. switch into the context of) each of these +threads in turn (d) continues to run to completion. A thread +which wishes to wake just one pseudothread works similarly +except that it only copies (and removes) a single item off +the list.
+ + + +
+Note various invariant conditions: A thread cannot be +entered on the wait queue sleeping list more than once +(because it cannot call sleep_on when it is already +sleeping). For similar reasons, a thread cannot be entered +on any of the multiple lists at the same time. This implies +that a thread cannot be woken up multiple +times.
+ + + +
+The reader should satisfy themselves that this algorithm is +free of races, and solves all the problems outlined above. +In addition, it has the desirable property that wake_up* +never sleeps.
+
+ + diff --git a/doc/wq_wake_up_one.3.html b/doc/wq_wake_up_one.3.html new file mode 100644 index 0000000..e445119 --- /dev/null +++ b/doc/wq_wake_up_one.3.html @@ -0,0 +1,217 @@ + + + + +new_wait_queue + + + +

new_wait_queue

+NAME
+SYNOPSIS
+DESCRIPTION
+AUTHOR
+LICENSE
+VERSION
+HISTORY
+ +
+ + + +

NAME

+ + +
+new_wait_queue, wq_wake_up, wq_wake_up_one, wq_sleep_on, wq_nr_sleepers - wait queues
+ +

SYNOPSIS

+ + + +
+
#include <pthr_wait_queue.h>
+
+wait_queue new_wait_queue (pool);
+void wq_wake_up (wait_queue);
+void wq_wake_up_one (wait_queue);
+void wq_sleep_on (wait_queue, pseudothread);
+int wq_nr_sleepers (wait_queue);
+
+ +

DESCRIPTION

+ + + +
+new_wait_queue creates a wait queue +object.
+ + + +
+wq_wake_up wakes up all the threads which are +currently sleeping on the wait queue. Note that this +function does not block, and because pseudothreads are +non-preemptive, none of the sleeping threads will actually +begin running until at least the current thread blocks +somewhere else.
+ + + +
+wq_wake_up_one wakes up just the first thread at the +head of the wait queue (the one which has been waiting the +longest).
+ + + +
+wq_sleep_on sends the current thread to sleep on the +wait queue. This call blocks (obviously).
+ + + +
+wq_nr_sleepers returns the number of threads which +are currently asleep on the wait queue.
+ + + +
+Please read the HISTORY section below for some background +into how wait queues are implemented. This may help if you +find there are tricky race conditions in your +code.
+ +

AUTHOR

+ + + +
+Richard Jones <rich@annexia.org>
+ +

LICENSE

+ + + +
+GNU LGPL (see http://www.gnu.org/)
+ +

VERSION

+ + + +
+pthrlib-3.0.3
+ +

HISTORY

+ + + +
+Originally, wait queues were implemented using underlying +Unix pipes. This worked (to some extent) but the overhead of +requiring one pipe (ie. one inode, two file descriptors) per +wait queue made this implementation unacceptably +heavyweight.
+ + + +
+Wait queues are now implemented using a simple hack in the +reactor which will be described below.
+ + + +
+Wait queues are subtle. Consider this example: Threads 1, 2 +and 3 are sleeping on a wait queue. Now thread 4 wakes up +the queue. You would expect (probably) threads 1, 2 and 3 to +each be woken up and allowed to start running. However, +since this is a cooperatively multitasking environment, it +may happen that thread 1 wakes up first, does some work and +then goes back to sleep on the wait queue, all before +threads 2 and 3 have woken up. With a naive implementation +of wait queues, thread 4 might end up waking up thread 1 +*again* (and even again after that), never waking up threads +2 and 3 and ultimately starving those threads.
+ + + +
+To avoid this situation, we might consider two possible +alternatives: either when thread 1 goes back to sleep, it +goes to sleep on a 'different' queue, or else thread 4 might +take a copy of the wait queue and delete the queue before it +wakes any of the threads up.
+ + + +
+Another nasty situation which might arise in real life is +this: Again, threads 1, 2 and 3 are sleeping. Thread 4 wakes +them up. Thread 1, while processing its work, happens also +to wake up the same wait queue. What should happen to this +second wake-up event? Should it be ignored? Should it wake +up threads 2 and 3? Should it wake up any other threads +which happen to have gone to sleep on the queue after 1, 2 +and 3? Or perhaps some combination of these?
+ + + +
+The solution that we have come up with is as follows. A wait +queue consists of a simple list of threads which are +sleeping on it. When a thread wishes to sleep on the wait +queue, it is added to this list, and it switches back into +the reactor context. When a thread wishes to wake up all +sleepers, it: (a) copies the list of sleeping pseudothreads +into its own private space (b) clears the list of sleeping +pseudothreads (c) registers a prepoll handler to run which +will wake up (ie. switch into the context of) each of these +threads in turn (d) continues to run to completion. A thread +which wishes to wake just one pseudothread works similarly +except that it only copies (and removes) a single item off +the list.
+ + + +
+Note various invariant conditions: A thread cannot be +entered on the wait queue sleeping list more than once +(because it cannot call sleep_on when it is already +sleeping). For similar reasons, a thread cannot be entered +on any of the multiple lists at the same time. This implies +that a thread cannot be woken up multiple +times.
+ + + +
+The reader should satisfy themselves that this algorithm is +free of races, and solves all the problems outlined above. +In addition, it has the desirable property that wake_up* +never sleeps.
+
+ + diff --git a/examples/.cvsignore b/examples/.cvsignore new file mode 100644 index 0000000..e69de29 diff --git a/examples/README b/examples/README new file mode 100644 index 0000000..bfabc92 --- /dev/null +++ b/examples/README @@ -0,0 +1,53 @@ +Examples +-------- + +pthr_eg1_* + + This is a very basic example webserver which simply + prints out the query string and a few other details + like request headers when you connect to it. + + Run it as: + + ./pthr_eg1_echo -p 8002 + + and then try connecting to it (eg. with lynx, netscape, etc.): + + http://localhost:8002/something + +pthr_eg2_* + + This is a slightly more advanced example. This webserver + will serve flat files (only). It can be used for benchmarking, + profiling/optimizing the library, or even as a very simple + production webserver. + + It needs to be started as ``root'' because it chroots and + changes its user ID for security reasons. + + All configuration is by way of command line arguments. Try + typing ``./pthr_eg2_server --help'' for a complete list. + + This webserver is capable of exploiting the full power + of SMP machines. + + To start it (as root): + + ./pthr_eg2_server -p 8003 -r /home/httpd/html -u nobody + + If you have more than one processor, then you need to + use the ``-n'' argument to tell it how many processors + you have. + + Tip for benchmarking: use a lot of client machines + connected through a full-duplex ethernet switch to + the server. On each client machine, run a recursive + ``wget'' to download a large number of files, eg: + + wget -r http://server:8003/ + +rws + + RWS is a full-featured webserver built on top of pthrlib. + It is distributed separately. You can get it from + http://www.annexia.org/freeware/rws/ diff --git a/examples/pthr_eg1_echo.c b/examples/pthr_eg1_echo.c new file mode 100644 index 0000000..c42528c --- /dev/null +++ b/examples/pthr_eg1_echo.c @@ -0,0 +1,144 @@ +/* Pseudothread echo example. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_eg1_echo.c,v 1.6 2002/12/01 14:57:36 rich Exp $ + */ + +#include "config.h" + +#include + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_SYSLOG_H +#include +#endif + +#include + +#include "src/pthr_pseudothread.h" +#include "src/pthr_iolib.h" +#include "src/pthr_http.h" +#include "src/pthr_cgi.h" +#include "pthr_eg1_echo.h" + +struct eg1_echo_processor +{ + /* Pseudothread handle. */ + pseudothread pth; + + /* Socket. */ + int sock; +}; + +static void run (void *vp); + +eg1_echo_processor +new_eg1_echo_processor (int sock) +{ + pool pool; + eg1_echo_processor p; + + pool = new_pool (); + p = pmalloc (pool, sizeof *p); + + p->sock = sock; + p->pth = new_pseudothread (pool, run, p, "eg1_echo_processor"); + + pth_start (p->pth); + + return p; +} + +static void +run (void *vp) +{ + eg1_echo_processor p = (eg1_echo_processor) vp; + int close = 0; + http_request http_request; + cgi cgi; + http_response http_response; + pool pool = pth_get_pool (p->pth); + io_handle io; + int i; + + io = io_fdopen (p->sock); + + /* Sit in a loop reading HTTP requests. */ + while (!close) + { + vector headers, params; + struct http_header header; + const char *name, *value; + + /* ----- HTTP request ----- */ + http_request = new_http_request (pool, io); + if (http_request == 0) /* Normal end of file. */ + break; + + cgi = new_cgi (pool, http_request, io); + if (cgi == 0) /* XXX Should send an error here. */ + break; + + /* ----- HTTP response ----- */ + http_response = new_http_response (pool, http_request, + io, + 200, "OK"); + http_response_send_header (http_response, + "Content-Type", "text/plain"); + close = http_response_end_headers (http_response); + + if (!http_request_is_HEAD (http_request)) + { + io_fprintf (io, "Hello. This is your server.\r\n\r\n"); + io_fprintf (io, "Your browser sent the following headers:\r\n"); + + headers = http_request_get_headers (http_request); + for (i = 0; i < vector_size (headers); ++i) + { + vector_get (headers, i, header); + io_fprintf (io, "\t%s: %s\r\n", header.key, header.value); + } + + io_fprintf (io, "----- end of headers -----\r\n"); + + io_fprintf (io, "The URL was: %s\r\n", + http_request_get_url (http_request)); + io_fprintf (io, "The path component was: %s\r\n", + http_request_path (http_request)); + io_fprintf (io, "The query string was: %s\r\n", + http_request_query_string (http_request)); + io_fprintf (io, "The query arguments were:\r\n"); + + params = cgi_params (cgi); + for (i = 0; i < vector_size (params); ++i) + { + vector_get (params, i, name); + value = cgi_param (cgi, name); + io_fprintf (io, "\t%s=%s\r\n", name, value); + } + + io_fprintf (io, "----- end of parameters -----\r\n"); + } + } + + io_fclose (io); + + pth_exit (); +} diff --git a/examples/pthr_eg1_echo.h b/examples/pthr_eg1_echo.h new file mode 100644 index 0000000..778e29b --- /dev/null +++ b/examples/pthr_eg1_echo.h @@ -0,0 +1,33 @@ +/* Pseudothread echo example. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_eg1_echo.h,v 1.2 2002/08/21 10:42:15 rich Exp $ + */ + +#ifndef PTHR_EG1_ECHO_H +#define PTHR_EG1_ECHO_H + +#include + +#include "src/pthr_pseudothread.h" + +struct eg1_echo_processor; +typedef struct eg1_echo_processor *eg1_echo_processor; + +extern eg1_echo_processor new_eg1_echo_processor (int sock); + +#endif /* PTHR_EG1_ECHO_H */ diff --git a/examples/pthr_eg1_echo_main.c b/examples/pthr_eg1_echo_main.c new file mode 100644 index 0000000..03af897 --- /dev/null +++ b/examples/pthr_eg1_echo_main.c @@ -0,0 +1,44 @@ +/* Pseudothread echo example. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_eg1_echo_main.c,v 1.2 2002/08/21 10:42:15 rich Exp $ + */ + +#include "config.h" + +#include +#include + +#include "src/pthr_server.h" +#include "pthr_eg1_echo.h" + +static void start_processor (int sock, void *); + +int +main (int argc, char *argv[]) +{ + /* Start up the server. */ + pthr_server_main_loop (argc, argv, start_processor); + + exit (0); +} + +static void +start_processor (int sock, void *data) +{ + (void) new_eg1_echo_processor (sock); +} diff --git a/examples/pthr_eg2_server.c b/examples/pthr_eg2_server.c new file mode 100644 index 0000000..71a2c6d --- /dev/null +++ b/examples/pthr_eg2_server.c @@ -0,0 +1,350 @@ +/* Pseudothread server example. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_eg2_server.c,v 1.7 2003/02/05 22:13:32 rich Exp $ + */ + +#include "config.h" + +#include + +#ifdef HAVE_ALLOCA_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_DIRENT_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_SYS_SYSLIMITS_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include + +#include "src/pthr_pseudothread.h" +#include "src/pthr_iolib.h" +#include "src/pthr_http.h" +#include "src/pthr_cgi.h" +#include "pthr_eg2_server.h" + +struct eg2_server_processor +{ + /* Pseudothread handle. */ + pseudothread pth; + + /* Socket. */ + int sock; + + /* Pool for memory allocations. */ + struct pool *pool; + + /* HTTP request. */ + http_request http_request; + + /* IO handle. */ + io_handle io; +}; + +static int file_not_found_error (eg2_server_processor p); +static int moved_permanently (eg2_server_processor p, const char *); +static int serve_directory (eg2_server_processor p, const char *, const struct stat *); +static int serve_file (eg2_server_processor p, const char *, const struct stat *); +static void run (void *vp); + +eg2_server_processor +new_eg2_server_processor (int sock) +{ + pool pool; + eg2_server_processor p; + + pool = new_pool (); + p = pmalloc (pool, sizeof *p); + + p->pool = pool; + p->sock = sock; + p->pth = new_pseudothread (pool, run, p, "eg2_server_processor"); + + pth_start (p->pth); + + return p; +} + +static void +run (void *vp) +{ + eg2_server_processor p = (eg2_server_processor) vp; + int close = 0; + const char *path; + struct stat statbuf; + + p->io = io_fdopen (p->sock); + + /* Sit in a loop reading HTTP requests. */ + while (!close) + { + /* ----- HTTP request ----- */ + p->http_request = new_http_request (p->pool, p->io); + if (p->http_request == 0) /* Normal end of file. */ + break; + + /* Get the path and locate the file. */ + path = http_request_path (p->http_request); + if (stat (path, &statbuf) == -1) + { + close = file_not_found_error (p); + continue; + } + + /* File or directory? */ + if (S_ISDIR (statbuf.st_mode)) + { + close = serve_directory (p, path, &statbuf); + continue; + } + else if (S_ISREG (statbuf.st_mode)) + { + close = serve_file (p, path, &statbuf); + continue; + } + else + { + close = file_not_found_error (p); + continue; + } + } + + io_fclose (p->io); + + pth_exit (); +} + +static int +file_not_found_error (eg2_server_processor p) +{ + http_response http_response; + int close; + + http_response = new_http_response (p->pool, p->http_request, p->io, + 404, "File or directory not found"); + http_response_send_headers (http_response, + /* Content type. */ + "Content-Type", "text/html", + NO_CACHE_HEADERS, + /* End of headers. */ + NULL); + close = http_response_end_headers (http_response); + + if (http_request_is_HEAD (p->http_request)) return close; + + io_fprintf (p->io, + "File or directory not found" CRLF + "" CRLF + "

404 File or directory not found

" CRLF + "The file you requested was not found on this server." CRLF + "" CRLF); + + return close; +} + +int +moved_permanently (eg2_server_processor p, const char *location) +{ + http_response http_response; + int close; + + http_response = new_http_response (p->pool, p->http_request, p->io, + 301, "Moved permanently"); + http_response_send_headers (http_response, + /* Content length. */ + "Content-Length", "0", + /* Location. */ + "Location", location, + /* End of headers. */ + NULL); + close = http_response_end_headers (http_response); + + if (http_request_is_HEAD (p->http_request)) return close; + + return close; +} + +static int +serve_directory (eg2_server_processor p, const char *path, + const struct stat *statbuf) +{ + http_response http_response; + int close; + DIR *dir; + struct dirent *d; + +#ifndef NAME_MAX + /* Solaris defines NAME_MAX on a per-filesystem basis. + * See: http://lists.spine.cx/archives/everybuddy/2002-May/001419.html + */ + long NAME_MAX = pathconf (path, _PC_NAME_MAX); +#endif + + /* If the path doesn't end with a "/", then we need to send + * a redirect back to the client so it refetches the page + * with "/" appended. + */ + if (path[strlen (path)-1] != '/') + { + char *location = psprintf (p->pool, "%s/", path); + return moved_permanently (p, location); + } + + dir = opendir (path); + if (dir == 0) + return file_not_found_error (p); + + http_response = new_http_response (p->pool, p->http_request, p->io, + 200, "OK"); + http_response_send_headers (http_response, + /* Content type. */ + "Content-Type", "text/html", + NO_CACHE_HEADERS, + /* End of headers. */ + NULL); + close = http_response_end_headers (http_response); + + if (http_request_is_HEAD (p->http_request)) return close; + + io_fprintf (p->io, + "Directory: %s" CRLF + "" CRLF + "

Directory: %s

" CRLF + "" CRLF + "" + "" CRLF, + path, path); + + while ((d = readdir (dir)) != 0) + { + if (d->d_name[0] != '.') /* Ignore hidden files. */ + { + const char *filename; + struct stat fstatbuf; + + /* Generate the full pathname to this file. */ + filename = psprintf (p->pool, "%s/%s", path, d->d_name); + + /* Stat the file to find out what it is. */ + if (lstat (filename, &fstatbuf) == 0) + { + const char *type; + int size; + + if (S_ISDIR (fstatbuf.st_mode)) + type = "dir"; + else if (S_ISREG (fstatbuf.st_mode)) + type = "file"; + else if (S_ISLNK (fstatbuf.st_mode)) + type = "link"; + else + type = "special"; + + size = fstatbuf.st_size; + + /* Print the details. */ + io_fprintf (p->io, + "" + "" CRLF, p->io); + } + } + } + + io_fprintf (p->io, + "
Parent directory
[ %s ]%d%s", + type, size, + d->d_name, + S_ISDIR (fstatbuf.st_mode) ? "/" : "", + d->d_name); + + if (S_ISLNK (fstatbuf.st_mode)) + { + char link[NAME_MAX+1]; + int r; + + r = readlink (filename, link, NAME_MAX); + if (r >= 0) link[r] = '\0'; + else strcpy (link, "unknown"); + + io_fprintf (p->io, " -> %s", link); + } + + io_fputs ("
" CRLF); + + return close; +} + +static int +serve_file (eg2_server_processor p, const char *path, + const struct stat *statbuf) +{ + http_response http_response; + const int n = 4096; + char *buffer = alloca (n); + int cl, fd, r; + char *content_length = pitoa (p->pool, statbuf->st_size); + + fd = open (path, O_RDONLY); + if (fd < 0) + return file_not_found_error (p); + + http_response = new_http_response (p->pool, p->http_request, p->io, + 200, "OK"); + http_response_send_headers (http_response, + /* Content type. */ + "Content-Type", "text/plain", + "Content-Length", content_length, + /* End of headers. */ + NULL); + cl = http_response_end_headers (http_response); + + if (http_request_is_HEAD (p->http_request)) return cl; + + while ((r = read (fd, buffer, n)) > 0) + { + io_fwrite (buffer, r, 1, p->io); + } + + if (r < 0) + perror ("read"); + + close (fd); + + return cl; +} diff --git a/examples/pthr_eg2_server.h b/examples/pthr_eg2_server.h new file mode 100644 index 0000000..fd5a464 --- /dev/null +++ b/examples/pthr_eg2_server.h @@ -0,0 +1,48 @@ +/* Pseudothread server example. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_eg2_server.h,v 1.2 2002/08/21 10:42:16 rich Exp $ + */ + +#ifndef PTHR_EG2_SERVER_H +#define PTHR_EG2_SERVER_H + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#include + +#include "src/pthr_pseudothread.h" +#include "src/pthr_http.h" +#include "src/pthr_iolib.h" + +/* Define some RFC-compliant dates to represent past and future. */ +#define DISTANT_PAST "Thu, 01 Dec 1994 16:00:00 GMT" +#define DISTANT_FUTURE "Sun, 01 Dec 2030 16:00:00 GMT" + +/* Headers which are sent to defeat caches. */ +#define NO_CACHE_HEADERS "Cache-Control", "must-revalidate", "Expires", DISTANT_PAST, "Pragma", "no-cache" + +#define CRLF "\r\n" + +struct eg2_server_processor; +typedef struct eg2_server_processor *eg2_server_processor; + +extern eg2_server_processor new_eg2_server_processor (int sock); + +#endif /* PTHR_EG2_SERVER_H */ diff --git a/examples/pthr_eg2_server_main.c b/examples/pthr_eg2_server_main.c new file mode 100644 index 0000000..d2808c8 --- /dev/null +++ b/examples/pthr_eg2_server_main.c @@ -0,0 +1,82 @@ +/* Pseudothread server example. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_eg2_server_main.c,v 1.4 2002/08/21 10:42:16 rich Exp $ + */ + +#include "config.h" + +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SIGNAL_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#include "src/pthr_server.h" +#include "pthr_eg2_server.h" + +static void start_processor (int sock, void *data); +static void catch_quit_signal (int sig); + +const char *root = "/tmp", *user = "nobody"; + +int +main (int argc, char *argv[]) +{ + struct sigaction sa; + + /* Intercept signals. */ + memset (&sa, 0, sizeof sa); + sa.sa_handler = catch_quit_signal; + sa.sa_flags = SA_RESTART; + sigaction (SIGINT, &sa, 0); + sigaction (SIGQUIT, &sa, 0); + sigaction (SIGTERM, &sa, 0); + + /* ... but ignore SIGPIPE errors. */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_RESTART; + sigaction (SIGPIPE, &sa, 0); + + /* Start up the server. */ + pthr_server_chroot (root); + pthr_server_username (user); + pthr_server_main_loop (argc, argv, start_processor); + + exit (0); +} + +static void +start_processor (int sock, void *data) +{ + (void) new_eg2_server_processor (sock); +} + +static void +catch_quit_signal (int sig) +{ + exit (0); +} diff --git a/src/.cvsignore b/src/.cvsignore new file mode 100644 index 0000000..e69de29 diff --git a/src/pthr_cgi.c b/src/pthr_cgi.c new file mode 100644 index 0000000..9d3c66f --- /dev/null +++ b/src/pthr_cgi.c @@ -0,0 +1,350 @@ +/* CGI library. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_cgi.c,v 1.6 2003/02/02 18:05:30 rich Exp $ + */ + +#include "config.h" + +#include +#include + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_CTYPE_H +#include +#endif + +#include +#include +#include +#include + +#include "pthr_http.h" +#include "pthr_cgi.h" + +static int post_max = -1; + +static void parse_qs (cgi, const char *qs); +static void insert_param (cgi, char *name, char *value); + +struct cgi +{ + pool pool; /* Pool for allocations. */ + shash params; /* Parameters (hash of string -> vector). */ +}; + +int +cgi_get_post_max (void) +{ + return post_max; +} + +int +cgi_set_post_max (int new_post_max) +{ + return post_max = new_post_max; +} + +cgi +new_cgi (struct pool *pool, http_request h, io_handle io) +{ + cgi c = pmalloc (pool, sizeof *c); + + c->pool = pool; + c->params = new_shash (pool, vector); + + if (http_request_method (h) != HTTP_METHOD_POST) + { + const char *qs = http_request_query_string (h); + + parse_qs (c, qs); + } + else /* Method POST. */ + { + const char *content_length_s = + http_request_get_header (h, "Content-Length"); + const char *content_type = http_request_get_header (h, "Content-Type"); + int content_length = -1; + const char std_type[] = "application/x-www-form-urlencoded"; + struct pool *tmp = new_subpool (pool); + char *content; + + if (content_length_s && + sscanf (content_length_s, "%d", &content_length) != 1) + return 0; /* Error in field format. */ + + /* Content length too long? */ + if (post_max >= 0 && content_length >= 0 && content_length > post_max) + return 0; /* Content too long. */ + + /* Check content type. If missing, assume it defaults to the + * standard application/x-www-form-urlencoded. + */ + if (content_type && + strncasecmp (content_type, std_type, strlen (std_type)) != 0) + return 0; /* Unexpected/unknown content type. */ + + /* Read the content, either to the end of input of else to the + * expected length. Note that Netscape 4 sends an extra CRLF + * after the POST data with is explicitly forbidden (see: + * RFC 2616, section 4.1). We ignore these next time around when + * we are reading the next request (see code in new_http_request). + */ + if (content_length >= 0) + { + content = pmalloc (tmp, content_length + 1); + if (io_fread (content, 1, content_length, io) < content_length) + return 0; + + content[content_length] = '\0'; + } + else + { + char t[1024]; + int r, n = 0; + + content = pstrdup (tmp, ""); + + while ((r = io_fread (t, 1, 1024, io)) > 0) + { + n += r; + if (post_max >= 0 && n > post_max) + return 0; /* Content too long. */ + + t[r] = '\0'; + content = pstrcat (tmp, content, t); + } + } + + parse_qs (c, content); + + delete_pool (tmp); + } + + return c; +} + +static void +parse_qs (cgi c, const char *qs) +{ + vector v; + int i; + static char *one = "1"; + + if (qs && qs[0] != '\0') + { + /* Parse the query string. */ + v = pstrcsplit (c->pool, qs, '&'); + + for (i = 0; i < vector_size (v); ++i) + { + char *param, *t; + + vector_get (v, i, param); + t = strchr (param, '='); + + /* No '=' char found? Assume it's a parameter of the form name=1. */ + if (!t) { insert_param (c, param, one); continue; } + + /* Split the string on the '=' character and set name and value. */ + *t = '\0'; + insert_param (c, param, t+1); + } + } +} + +static void +insert_param (cgi c, char *name, char *value) +{ + vector v; + char *s; + + if (!shash_get (c->params, name, v)) + v = new_vector (c->pool, char *); + + s = cgi_unescape (c->pool, value); + vector_push_back (v, s); + shash_insert (c->params, name, v); +} + +vector +cgi_params (cgi c) +{ + return shash_keys (c->params); +} + +const char * +cgi_param (cgi c, const char *name) +{ + vector v; + char *s; + + if (!shash_get (c->params, name, v)) + return 0; + + vector_get (v, 0, s); + return s; +} + +const vector +cgi_param_list (cgi c, const char *name) +{ + vector v; + + if (!shash_get (c->params, name, v)) + return 0; + + return v; +} + +int +cgi_erase (cgi c, const char *name) +{ + return shash_erase (c->params, name); +} + +cgi +copy_cgi (pool npool, cgi c) +{ + const char *key, *value; + int i, j; + vector keys, values, v; + + cgi nc = pmalloc (npool, sizeof *nc); + + nc->pool = npool; + nc->params = new_shash (npool, vector); + + keys = shash_keys_in_pool (c->params, npool); + + for (i = 0; i < vector_size (keys); ++i) + { + vector_get (keys, i, key); + values = cgi_param_list (c, key); + + for (j = 0; j < vector_size (values); ++j) + { + vector_get (values, j, value); + value = pstrdup (npool, value); + + if (!shash_get (nc->params, key, v)) + v = new_vector (npool, char *); + + vector_push_back (v, value); + shash_insert (nc->params, key, v); + } + } + + return nc; +} + +char * +cgi_escape (pool pool, const char *str) +{ + int i, j; + int len = strlen (str); + int new_len = 0; + char *new_str; + static const char hexdigits[] = "0123456789abcdef"; + + /* Work out how long the escaped string will be. Escaped strings + * get bigger. + */ + for (i = 0; i < len; ++i) + { + if (isalnum ((int) str[i]) || + str[i] == ',' || str[i] == '-' || str[i] == ' ') + new_len++; + else + new_len += 3; + } + + new_str = pmalloc (pool, new_len + 1); + + /* Escape the string. */ + for (i = 0, j = 0; i < len; ++i) + { + if (isalnum ((int) str[i]) || + str[i] == ',' || str[i] == '-') + new_str[j++] = str[i]; + else if (str[i] == ' ') + new_str[j++] = '+'; + else + { + new_str[j++] = '%'; + new_str[j++] = hexdigits [(str[i] >> 4) & 0xf]; + new_str[j++] = hexdigits [str[i] & 0xf]; + } + } + + new_str[j++] = '\0'; + + return new_str; +} + +static inline int +hex_to_int (char c) +{ + if (c >= '0' && c <= '9') return c - '0'; + else if (c >= 'a' && c <= 'f') return c - 'a' + 10; + else if (c >= 'A' && c <= 'F') return c - 'A' + 10; + else return -1; +} + +char * +cgi_unescape (pool pool, const char *str) +{ + int i, j; + int len = strlen (str); + char *new_str; + + /* Unescaped strings always get smaller. */ + new_str = pmalloc (pool, len + 1); + + for (i = 0, j = 0; i < len; ++i) + { + if (str[i] == '%') + { + if (i+2 < len) + { + int a = hex_to_int (str[i+1]); + int b = hex_to_int (str[i+2]); + + if (a >= 0 && b >= 0) + { + new_str[j++] = a << 4 | b; + i += 2; + } + else + new_str[j++] = str[i]; + } + else + new_str[j++] = str[i]; + } + else if (str[i] == '+') + new_str[j++] = ' '; + else + new_str[j++] = str[i]; + } + + new_str[j++] = '\0'; + + return new_str; +} diff --git a/src/pthr_cgi.h b/src/pthr_cgi.h new file mode 100644 index 0000000..162ebde --- /dev/null +++ b/src/pthr_cgi.h @@ -0,0 +1,103 @@ +/* CGI library. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_cgi.h,v 1.3 2002/08/23 13:53:02 rich Exp $ + */ + +#ifndef PTHR_CGI_H +#define PTHR_CGI_H + +#include + +#include "pthr_http.h" +#include "pthr_iolib.h" + +struct cgi; +typedef struct cgi *cgi; + +/* Function: cgi_get_post_max - Get and set the internal POST_MAX parameter. + * Function: cgi_set_post_max + * + * These functions get and set the internal POST_MAX parameter which + * can be used to limit the size of @code{POST} method requests which + * this library will handle. If set to a non-negative integer, then + * POST requests will be limited to the number of bytes given. The + * default is -1 (unlimited) which can leave the server open to denial + * of service attacks. + * + * See also: @ref{new_cgi(3)}. + */ +extern int cgi_get_post_max (void); +extern int cgi_set_post_max (int new_post_max); + +/* Function: new_cgi - Library for parsing CGI query strings. + * Function: cgi_params + * Function: cgi_param + * Function: cgi_param_list + * Function: cgi_erase + * Function: copy_cgi + * + * @code{new_cgi} creates a new CGI object from an existing HTTP + * request. It reads the query string or POSTed parameters and + * parses them internally. CGI parameters are case sensitive, and + * multiple parameters may be passed with the same name. Parameter + * values are automatically unescaped by the library before you + * get to see them. + * + * @code{cgi_params} returns a list of all the names of the parameters passed + * to the script. The list is returned as a @code{vector} of + * @code{char *}. + * + * @code{cgi_param} returns the value of a single named CGI parameter, + * or @code{NULL} if there is no such parameter. If multiple parameters + * were given with the same name, this returns one of them, essentially + * at random. + * + * @code{cgi_param_list} returns the list of values of the named + * CGI parameter. The list is returned as a @code{vector} of + * @code{char *}. + * + * @code{cgi_erase} erases the named parameter. If a parameter + * was erased, this returns true, else this returns false. + * + * @code{copy_cgi} copies @code{cgi} into pool @code{pool}. + * + * See also: @ref{cgi_get_post_max(3)}, @ref{cgi_escape(3)}, + * @ref{new_http_request(3)}. + */ +extern cgi new_cgi (pool, http_request, io_handle); +extern vector cgi_params (cgi); +extern const char *cgi_param (cgi, const char *name); +extern const vector cgi_param_list (cgi, const char *name); +extern int cgi_erase (cgi, const char *name); +extern cgi copy_cgi (pool, cgi); + +/* Function: cgi_escape - %-escape and unescape CGI arguments. + * Function: cgi_unescape + * + * These functions do %-escaping and unescaping on CGI arguments. + * When %-escaping a string, @code{" "} is replaced by @code{"+"}, + * and non-printable + * characters are replaced by @code{"%hh"} where @code{hh} is a + * two-digit hex code. Unescaping a string reverses this process. + * + * See also: @ref{new_cgi(3)}, @ref{new_http_request(3)}. + */ +extern char *cgi_escape (pool, const char *str); +extern char *cgi_unescape (pool, const char *str); + +#endif /* PTHR_CGI_H */ diff --git a/src/pthr_context.c b/src/pthr_context.c new file mode 100644 index 0000000..396bef3 --- /dev/null +++ b/src/pthr_context.c @@ -0,0 +1,160 @@ +/* Very Linux-specific context switching. + * Written by RWMJ with lots of help from GNU pth library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_context.c,v 1.7 2003/02/25 17:07:23 rich Exp $ + */ + +#include "config.h" + +#include +#include + +#include "pthr_context.h" + +#ifdef HAVE_WORKING_SETCONTEXT + +void +mctx_set (mctx_t *mctx, + void (*sf_addr) (void *), void *sf_arg, + void *sk_addr, int sk_size) +{ + /* Fetch current context. */ + getcontext (&mctx->uc); + + /* Update to point to new stack. */ + mctx->uc.uc_link = 0; + /* XXX I cheated and used code from pth. We should do proper + * automatic detection here instead. + */ +#if defined(__i386__) + mctx->uc.uc_stack.ss_sp = sk_addr + sk_size - 4; + /* mctx->uc.uc_stack.ss_size = sk_size; */ +#elif defined(__sparc__) + mctx->uc.uc_stack.ss_sp = sk_addr + sk_size - 8; + mctx->uc.uc_stack.ss_size = sk_size - 8; +#else +#error "Unsupported architecture" +#endif + mctx->uc.uc_stack.ss_flags = 0; + + /* Make new context. */ + makecontext (&mctx->uc, sf_addr, 1, sf_arg); +} + +unsigned long +mctx_get_PC (mctx_t *mctx) +{ +#if defined(__i386__) || defined(__i386) + return (unsigned long) mctx->uc.uc_mcontext.eip; +#elif defined(__sparc) + return (unsigned long) mctx->uc.uc_mcontext.gregs[REG_PC]; +#else + return 0; /* This is a non-essential function. */ +#endif +} + +unsigned long +mctx_get_SP (mctx_t *mctx) +{ + return (unsigned long) mctx->uc.uc_stack.ss_sp; +} + +#else /* setjmp implementation */ + +#ifdef linux + +# if defined(__GLIBC__) && defined(__GLIBC_MINOR__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && defined(JB_PC) && defined(JB_SP) +# define SET_PC(mctx, v) mctx->jb[0].__jmpbuf[JB_PC] = (int) v +# define SET_SP(mctx, v) mctx->jb[0].__jmpbuf[JB_SP] = (int) v +# define GET_PC(mctx) mctx->jb[0].__jmpbuf[JB_PC] +# define GET_SP(mctx) mctx->jb[0].__jmpbuf[JB_SP] +# elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && defined(__mc68000__) +# define SET_PC(mctx, v) mctx->jb[0].__jmpbuf[0].__aregs[0] = (long int) v +# define SET_SP(mctx, v) mctx->jb[0].__jmpbuf[0].__sp = (int *) v +# define GET_PC(mctx) mctx->jb[0].__jmpbuf[0].__aregs[0] +# define GET_SP(mctx) mctx->jb[0].__jmpbuf[0].__sp +# elif defined(__GNU_LIBRARY__) && defined(__i386__) +# define SET_PC(mctx, v) mctx->jb[0].__jmpbuf[0].__pc = (char *) v +# define SET_SP(mctx, v) mctx->jb[0].__jmpbuf[0].__sp = (void *) v +# define GET_PC(mctx) mctx->jb[0].__jmpbuf[0].__pc +# define GET_SP(mctx) mctx->jb[0].__jmpbuf[0].__sp +# else +# error "Unsupported Linux (g)libc version and/or platform" +# endif + +#elif defined(__OpenBSD__) && defined(__i386__) + +# define GET_PC(mctx) mctx->jb[0] +# define GET_SP(mctx) mctx->jb[2] +# define SET_PC(mctx,v) GET_PC(mctx) = (long) v +# define SET_SP(mctx,v) GET_SP(mctx) = (long) v + +#elif defined(__FreeBSD__) && defined(__i386__) + +# define GET_PC(mctx) mctx->jb[0]._jb[0] +# define GET_SP(mctx) mctx->jb[0]._jb[2] +# define SET_PC(mctx,v) GET_PC(mctx) = (int) v +# define SET_SP(mctx,v) GET_SP(mctx) = (int) v + +#else +#error "setjmp implementation has not been ported to your OS or architecture - please contact the maintainer" +#endif + +void +mctx_set (mctx_t *mctx, + void (*sf_addr) (void *), void *sf_arg, + void *sk_addr, int sk_size) +{ + int *stack_ptr = (int *) (sk_addr + sk_size); + + /* Prevent gcc from allocating these variables in registers, to avoid + * a warning about variables being clobbered by setjmp/longjmp. + */ + (void) &sk_addr; + (void) &stack_ptr; + +#ifdef __i386__ + /* Push function argument onto the stack. */ + *--stack_ptr = (int) sf_arg; + /* Push return address onto the stack. */ + *--stack_ptr = 0xfafafafa; +#if defined(__OpenBSD__) || defined(__FreeBSD__) + /* OpenBSD needs an extra word here for what? Frame pointer? */ + *--stack_ptr = 0; +#endif +#else +#error "unsupported machine architecture" +#endif + + mctx_save (mctx); + SET_PC (mctx, sf_addr); + SET_SP (mctx, stack_ptr); +} + +unsigned long +mctx_get_PC (mctx_t *mctx) +{ + return (unsigned) GET_PC (mctx); +} + +unsigned long +mctx_get_SP (mctx_t *mctx) +{ + return (unsigned long) GET_SP (mctx); +} + +#endif /* ! USE_UCONTEXT */ diff --git a/src/pthr_context.h b/src/pthr_context.h new file mode 100644 index 0000000..28d5b42 --- /dev/null +++ b/src/pthr_context.h @@ -0,0 +1,75 @@ +/* Very Linux-specific context switching. + * Written by RWMJ with lots of help from GNU pth library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_context.h,v 1.7 2003/02/05 22:13:32 rich Exp $ + */ + +#ifndef PTHR_CONTEXT_H +#define PTHR_CONTEXT_H + +#include + +#ifdef HAVE_WORKING_SETCONTEXT + +#include + +typedef struct mctx_st { + ucontext_t uc; +} mctx_t; + +/* Save machine context. */ +#define mctx_save(mctx) (void) getcontext (&(mctx)->uc) + +/* Restore machine context. */ +#define mctx_restore(mctx) (void) setcontext (&(mctx)->uc) + +/* Switch machine context. */ +#define mctx_switch(mctx_old, mctx_new) \ +(void) swapcontext (&((mctx_old)->uc), &((mctx_new)->uc)) + +#else /* setjmp implementation */ + +#include + +typedef struct mctx_st { + jmp_buf jb; +} mctx_t; + +/* Save machine context. */ +#define mctx_save(mctx) (void) setjmp ((mctx)->jb) + +/* Restore machine context. */ +#define mctx_restore(mctx) longjmp ((mctx)->jb, 1) + +/* Switch machine context. */ +#define mctx_switch(mctx_old, mctx_new) \ +do { if (setjmp ((mctx_old)->jb) == 0) longjmp ((mctx_new)->jb, 1); } while(0) + +#endif /* ! USE_UCONTEXT */ + +/* Create machine context. */ +extern void mctx_set (mctx_t *mctx, + void (*sf_addr) (void *), void *sf_arg, + void *sk_addr, int sk_size); + +/* Return PC. */ +extern unsigned long mctx_get_PC (mctx_t *mctx); + +/* Return SP. */ +extern unsigned long mctx_get_SP (mctx_t *mctx); + +#endif /* PTHR_CONTEXT_H */ diff --git a/src/pthr_dbi.c b/src/pthr_dbi.c new file mode 100644 index 0000000..294ca8f --- /dev/null +++ b/src/pthr_dbi.c @@ -0,0 +1,1145 @@ +/* Database interface library. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_dbi.c,v 1.18 2003/06/17 18:10:30 rich Exp $ + */ + +#include "config.h" + +#include +#include +#include +#include + +#ifdef HAVE_ASSERT_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_POSTGRESQL_LIBPQ_FE_H +#include +#endif + +#ifdef HAVE_LIBPQ_FE_H +#include +#endif + +#ifndef HAVE_PQESCAPESTRING +size_t PQescapeString(char *to, const char *from, size_t length); +#endif + +#include +#include +#include +#include +#include + +#include "pthr_pseudothread.h" +#include "pthr_dbi.h" + +#define DBI_FORCE_DEBUG 0 +#define DBI_POOLS_DEBUG 0 + +#define DEBUG(dbh,sth,fs,args...) do { if (DBI_FORCE_DEBUG || ((dbh)->flags & DBI_DEBUG)) { if ((sth) == 0) fprintf (stderr, "dbi: dbh %p: " fs, (dbh) , ## args); else fprintf (stderr, "dbi: dbh %p sth %p: " fs, (dbh), (void *) (sth) , ## args); fputc ('\n', stderr); } } while (0) + +/* Database handle. */ +struct db_handle +{ + pool pool; /* Pool for allocations. */ + const char *conninfo; /* Connection string. */ + int flags; /* Flags. */ + int in_transaction; /* Are we in a transaction yet? */ + PGconn *conn; /* The database connection object. */ +}; + +/* Statement handle. */ +struct st_handle +{ + pool pool; /* Subpool used for allocations. */ + db_handle dbh; /* Parent database connection. */ + const char *orig_query; /* Original query string. */ + vector query; /* Query, split into string, '?' and '@'. */ + vector intypes; /* Placeholder types (vector of int). */ + PGresult *result; /* Last result. */ + int fetch_allowed; /* True if there are tuples to fetch. */ + int next_tuple; /* Next row to fetch. */ + vector outtypes; /* Output types (vector of struct otype). */ +}; + +struct otype +{ + int type; /* Type of this element. */ + void *varptr; /* Pointer to variable for result. */ +}; + +static void init_dbi (void) __attribute__((constructor)); +static void free_dbi (void) __attribute__((destructor)); +static void disconnect (void *vdbh); +static void parse_timestamp (st_handle sth, const char *str, struct dbi_timestamp *ts); +static void parse_interval (st_handle sth, const char *str, struct dbi_interval *inv); + +/* Global variables. */ +static pool dbi_pool; +static const pcre *re_qs, *re_timestamp, *re_interval; + +/* For connection pools. */ +static shash connpools; /* Hash conninfo string -> vector of dbh. */ +static hash inuse; /* Hash dbh -> subpool, of handles in use. */ + +/* Initialise the library. */ +static void +init_dbi () +{ +#ifndef __OpenBSD__ + dbi_pool = new_subpool (global_pool); +#else + dbi_pool = new_pool (); +#endif + re_qs = precomp (dbi_pool, "\\?|@", 0); + re_timestamp = precomp (dbi_pool, +" (?:(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)) # date (YYYY-MM-DD)\n" +" \\s* # space between date and time\n" +" (?:(\\d\\d):(\\d\\d) # HH:MM\n" +" (?::(\\d\\d))? # optional :SS\n" +" (?:\\.(\\d+))? # optional .microseconds\n" +" (?:([+-])(\\d\\d))? # optional +/-OO offset from UTC\n" +" )?", PCRE_EXTENDED); + re_interval = precomp (dbi_pool, +" (?:(\\d+)\\syears?)? # years\n" +" \\s* # \n" +" (?:(\\d+)\\smons?)? # months\n" +" \\s* # \n" +" (?:(\\d+)\\sdays?)? # days\n" +" \\s* # \n" +" (?:(\\d\\d):(\\d\\d) # HH:MM\n" +" (?::(\\d\\d))? # optional :SS\n" +" )?", PCRE_EXTENDED); + + /* Set up connection pools. */ + connpools = new_shash (dbi_pool, vector); + inuse = new_hash (dbi_pool, db_handle, pool); +} + +/* Free up global memory used by the library. */ +static void +free_dbi () +{ + delete_pool (dbi_pool); +} + +db_handle +new_db_handle (pool pool, const char *conninfo, int flags) +{ + db_handle dbh = pmalloc (pool, sizeof *dbh); + int status, fd; + + dbh->pool = pool; + dbh->conninfo = pstrdup (pool, conninfo); + dbh->flags = flags; + dbh->in_transaction = 0; + + /* Begin the database connection. */ + dbh->conn = PQconnectStart (conninfo); + if (dbh->conn == 0) /* Failed. */ + return 0; + + /* See the PostgreSQL documentation for the libpq connect functions + * for details about how the following loop works. + */ + status = PQstatus (dbh->conn); + if (status == CONNECTION_BAD) + { + PQfinish (dbh->conn); + return 0; /* Connection failed immediately. */ + } + + if (PQsetnonblocking (dbh->conn, 1) == -1) abort (); + fd = PQsocket (dbh->conn); + + status = PGRES_POLLING_WRITING; + + while (status != PGRES_POLLING_OK && + status != PGRES_POLLING_FAILED) + { + switch (status) + { + case PGRES_POLLING_WRITING: + pth_wait_writable (fd); + break; + case PGRES_POLLING_READING: + pth_wait_readable (fd); + break; + } + status = PQconnectPoll (dbh->conn); + } + + if (status == PGRES_POLLING_FAILED) + { + PQfinish (dbh->conn); + return 0; /* Connection failed. */ + } + + /*- Connected! -*/ + + DEBUG (dbh, 0, "connected"); + + /* Remember to clean up this connection when the pool gets deleted. */ + pool_register_cleanup_fn (dbh->pool, disconnect, dbh); + + return dbh; +} + +static void +disconnect (void *vdbh) +{ + db_handle dbh = (db_handle) vdbh; + + PQfinish (dbh->conn); + + DEBUG (dbh, 0, "disconnected"); +} + +void +db_set_debug (db_handle dbh, int d) +{ + if (d) + { + dbh->flags |= DBI_DEBUG; + DEBUG (dbh, 0, "debugging enabled"); + } + else + { + DEBUG (dbh, 0, "debugging disabled"); + dbh->flags &= ~DBI_DEBUG; + } +} + +int +db_get_debug (db_handle dbh) +{ + return dbh->flags & DBI_DEBUG; +} + +void +db_commit (db_handle dbh) +{ + st_handle sth; + + sth = st_prepare_cached (dbh, "commit work"); + st_execute (sth); + + dbh->in_transaction = 0; +} + +void +db_rollback (db_handle dbh) +{ + st_handle sth; + + sth = st_prepare_cached (dbh, "rollback work"); + st_execute (sth); + + dbh->in_transaction = 0; +} + +int +db_in_transaction (db_handle dbh) +{ + return dbh->in_transaction; +} + +static void return_dbh (void *vdbh); + +db_handle +get_db_handle (const char *conninfo, int flags) +{ + pool pool; + vector free_dbhs; + db_handle dbh = 0; + + /* Allocate a subpool of the current thread's pool. If the thread dies + * or if the thread calls put_db_handle, this subpool will be deleted, + * resulting in the return_dbh function being called. + */ + pool = new_subpool (pth_get_pool (current_pth)); + + /* Get a free handle. */ + if (shash_get (connpools, conninfo, free_dbhs) && + vector_size (free_dbhs) > 0) + { + /* Push back, pop front to ensure handles are circulated and used + * equally. + */ + vector_pop_front (free_dbhs, dbh); + dbh->flags = flags; + + assert (strcmp (dbh->conninfo, conninfo) == 0); + } + + if (!dbh) /* Need to create a new handle. */ + dbh = new_db_handle (dbi_pool, conninfo, flags); + + /* Remember to return this handle when the subpool is deleted. */ + pool_register_cleanup_fn (pool, return_dbh, dbh); + + /* Remember this handle is in use. */ + hash_insert (inuse, dbh, pool); + +#if DBI_POOLS_DEBUG + fprintf (stderr, "get_db_handle: conninfo \"%s\" -> dbh %p\n", + conninfo, dbh); +#endif + + return dbh; +} + +void +put_db_handle (db_handle dbh) +{ + pool pool; + +#if DBI_POOLS_DEBUG + fprintf (stderr, "put_db_handle: dbh %p (conninfo \"%s\")\n", + dbh, dbh->conninfo); +#endif + + /* Find the corresponding subpool and delete it. */ + if (!hash_get (inuse, dbh, pool)) abort (); + delete_pool (pool); +} + +/* This function is called when the thread exits or calls put_db_handle. We + * need to return this handle to the correct connection pool. + */ +static void +return_dbh (void *vdbh) +{ + db_handle dbh = (db_handle) vdbh; + vector free_dbhs; + +#if DBI_POOLS_DEBUG + fprintf (stderr, "return_dbh: dbh %p (conninfo \"%s\")\n", + dbh, dbh->conninfo); +#endif + + if (dbh->in_transaction) db_rollback (dbh); + + /* Remove from the in-use list. */ + if (!hash_erase (inuse, dbh)) abort (); + + if (shash_get (connpools, dbh->conninfo, free_dbhs)) + vector_push_back (free_dbhs, dbh); + else + { + /* Need to create the free dbhs vector. */ + free_dbhs = new_vector (dbi_pool, db_handle); + vector_push_back (free_dbhs, dbh); + shash_insert (connpools, dbh->conninfo, free_dbhs); + } +} + +int +st_serial (st_handle sth, const char *seq_name) +{ + db_handle dbh = sth->dbh; + st_handle sth2; + int serial; + + /* In PostgreSQL, to fetch the serial number we need to issue another + * command to the database. + */ + sth2 = st_prepare_cached (dbh, "select currval (?)", DBI_STRING); + st_execute (sth2, seq_name); + + st_bind (sth2, 0, serial, DBI_INT); + + if (!st_fetch (sth2)) + { + if ((dbh->flags & DBI_THROW_ERRORS)) + pth_die ("dbi: st_serial: failed to fetch sequence value"); + else + return -1; + } + + return serial; +} + +static void finish_handle (void *vsth); + +st_handle +new_st_handle (db_handle dbh, const char *query, int flags, ...) +{ + st_handle sth; + pool pool; + int i; + va_list args; + + /* XXX Ignore the caching flag at the moment. Statement handles cannot + * be trivially cached, because the same handle might then be used + * concurrently in two different threads, which would cause serious + * problems. Also since PostgreSQL doesn't support PREPARE yet, we + * don't get the particular performance boost by caching query plans + * on the server side anyway. + * + * Actually the first statement isn't strictly true. We should never + * be sharing database handles across threads, so as long as we only + * cache statements in the database handle, we ought to be OK. + * + * (And the second statement isn't true anymore either - since 7.3, + * PostgreSQL supports PREPAREd statements). + */ + + /* Allocating in a subpool isn't strictly necessary at the moment. However + * in the future it will allow us to safely free up the memory associated + * with the statement handle when the handle is 'finished'. + */ + pool = new_subpool (dbh->pool); + sth = pmalloc (pool, sizeof *sth); + + sth->pool = pool; + sth->dbh = dbh; + sth->orig_query = query; + sth->result = 0; + sth->fetch_allowed = 0; + sth->outtypes = 0; + + /* Examine the query string looking for ? and @ placeholders which + * don't occur inside strings. + * XXX Haven't implemented the test for placeholders inside strings + * yet, so avoid using ? and @ as anything but placeholders for the + * moment XXX + */ + sth->query = pstrresplit2 (pool, query, re_qs); + + /* Set up the array of input types. */ + sth->intypes = new_vector (pool, int); + va_start (args, flags); + + for (i = 0; i < vector_size (sth->query); ++i) + { + char *q; + + vector_get (sth->query, i, q); + + if (strcmp (q, "?") == 0 || strcmp (q, "@") == 0) + { + int type = va_arg (args, int); + + assert (DBI_MIN_TYPE <= type && type <= DBI_MAX_TYPE); + vector_push_back (sth->intypes, type); + } + } + + va_end (args); + + /* Remember to clean up this handle when the pool gets deleted. */ + pool_register_cleanup_fn (pool, finish_handle, sth); + + DEBUG (dbh, sth, "handle created for query: %s", sth->orig_query); + + return sth; +} + +static void +finish_handle (void *vsth) +{ + st_handle sth = (st_handle) vsth; + + if (sth->result) + PQclear (sth->result); + sth->result = 0; + + DEBUG (sth->dbh, sth, "finished (implicit)"); +} + +static int exec_error (st_handle sth, PGresult *result); +static char *escape_string (pool, const char *); + +int +st_execute (st_handle sth, ...) +{ + pool pool = sth->pool; + int i, typeidx; + va_list args; + char *query; + PGconn *conn; + PGresult *result; + int fd; + ExecStatusType status; + + /* Formulate a query with the types substituted as appropriate. */ + query = pstrdup (pool, ""); + va_start (args, sth); + + for (i = 0, typeidx = 0; i < vector_size (sth->query); ++i) + { + char *q; + int type; + + vector_get (sth->query, i, q); + + if (strcmp (q, "?") == 0) /* Simple placeholder. */ + { + vector_get (sth->intypes, typeidx, type); typeidx++; + + switch (type) + { + case DBI_INT: + query = pstrcat (pool, query, pitoa (pool, va_arg (args, int))); + break; + + case DBI_INT_OR_NULL: + { + int r = va_arg (args, int); + + if (r != 0) + query = pstrcat (pool, query, pitoa (pool, r)); + else + query = pstrcat (pool, query, "null"); + } + break; + + case DBI_STRING: + { + const char *str = va_arg (args, const char *); + + if (str) + { + query = pstrcat (pool, query, "'"); + query = pstrcat (pool, query, escape_string (pool, str)); + query = pstrcat (pool, query, "'"); + } + else + query = pstrcat (pool, query, "null"); + } + break; + + case DBI_BOOL: + query = + pstrcat (pool, query, va_arg (args, int) ? "'t'" : "'f'"); + break; + + case DBI_CHAR: + { + char str[2] = { va_arg (args, int), '\0' }; /* sic */ + + query = pstrcat (pool, query, "'"); + query = pstrcat (pool, query, escape_string (pool, str)); + query = pstrcat (pool, query, "'"); + } + break; + + case DBI_TIMESTAMP: + case DBI_INTERVAL: + abort (); /* Not implemented yet! */ + + default: + abort (); + } + } + else if (strcmp (q, "@") == 0) /* List placeholder. */ + { + vector v; + + vector_get (sth->intypes, typeidx, type); typeidx++; + + /* We don't know yet if v is a vector of int or char *. */ + v = va_arg (args, vector); + + /* But we _do_ know that if the vector is empty, PG will fail. + * Stupid bug in PostgreSQL. + */ + assert (vector_size (v) > 0); + + switch (type) + { + case DBI_INT: + v = pvitostr (pool, v); + query = pstrcat (pool, query, pjoin (pool, v, ",")); + break; + + case DBI_STRING: + /* XXX Does not handle nulls correctly. */ + query = pstrcat (pool, query, "'"); + query = pstrcat (pool, query, + pjoin (pool, + pmap (pool, v, escape_string), "','")); + query = pstrcat (pool, query, "'"); + break; + + case DBI_BOOL: + case DBI_CHAR: + case DBI_INT_OR_NULL: + case DBI_TIMESTAMP: + case DBI_INTERVAL: + abort (); /* Not implemented yet! */ + + default: + abort (); + } + } + else /* String. */ + query = pstrcat (pool, query, q); + } + + va_end (args); + + /* In transaction? If not, we need to issue a BEGIN WORK command. */ + if (!sth->dbh->in_transaction) + { + st_handle sth_bw; + + /* So we don't go into infinite recursion here ... */ + sth->dbh->in_transaction = 1; + + sth_bw = st_prepare_cached (sth->dbh, "begin work"); + if (st_execute (sth_bw) == -1) + { + sth->dbh->in_transaction = 0; + return exec_error (sth, 0); + } + } + + DEBUG (sth->dbh, sth, "execute: %s", query); + + /* Get the connection. */ + conn = sth->dbh->conn; + assert (PQisnonblocking (conn)); + fd = PQsocket (conn); + + /* Run it. */ + if (PQsendQuery (conn, query) != 1) + return exec_error (sth, 0); + + /* Get all the command results. Ignore all but the last one. */ + do + { + /* Wait for the result. */ + while (PQisBusy (conn)) + { + /* Blocks ..? */ + if (PQflush (conn) == EOF) + return exec_error (sth, 0); + + pth_wait_readable (fd); + + if (PQconsumeInput (conn) != 1) + return exec_error (sth, 0); + } + + result = PQgetResult (conn); + if (result) + { + if (sth->result) PQclear (sth->result); + sth->result = result; + } + } + while (result); + + /* Get the result status. */ + status = PQresultStatus (sth->result); + + if (status == PGRES_COMMAND_OK) /* INSERT, UPDATE, DELETE, etc. */ + { + char *s = PQcmdTuples (sth->result); + int rv; + + sth->fetch_allowed = 0; + + if (s && strlen (s) > 0 && sscanf (s, "%d", &rv) == 1) + return rv; /* Return rows affected. */ + else + return 0; /* Command OK, unknown # rows affected. */ + } + else if (status == PGRES_TUPLES_OK) + { + sth->fetch_allowed = 1; + sth->next_tuple = 0; + + /* SELECT OK, return number of rows in the result. */ + return PQntuples (sth->result); + } + else + /* Some other error. */ + return exec_error (sth, sth->result); +} + +static char * +escape_string (pool pool, const char *s) +{ + int len = strlen (s); + char *r = pmalloc (pool, len * 2 + 1); + + PQescapeString (r, s, len); + return r; +} + +static int +exec_error (st_handle sth, PGresult *result) +{ + if (!result) /* Some sort of connection-related error. */ + { + perror ("dbi: st_execute: database connection error"); + if ((sth->dbh->flags & DBI_THROW_ERRORS)) + pth_die ("dbi: st_execute: database execution error"); + else + return -1; + } + else /* Execution error. */ + { + char *error = psprintf (sth->pool, + "dbi: st_execute: %s", + PQresultErrorMessage (result)); + + fprintf (stderr, "%s\n", error); + if ((sth->dbh->flags & DBI_THROW_ERRORS)) + pth_die (error); + else + return -1; + } +} + +void +_st_bind (st_handle sth, int colidx, void *varptr, int type) +{ + struct otype zero = { 0, 0 }; + struct otype ot; + int extend_by; + + if (sth->outtypes == 0) + sth->outtypes = new_vector (sth->pool, struct otype); + + /* Is the vector large enough? If not, extend it. */ + extend_by = colidx - vector_size (sth->outtypes) + 1; + if (extend_by > 0) + vector_fill (sth->outtypes, zero, extend_by); + + ot.type = type; + ot.varptr = varptr; + vector_replace (sth->outtypes, colidx, ot); +} + +int +st_fetch (st_handle sth) +{ + int nr_rows, i; + struct otype ot; + + if (!sth->result || !sth->fetch_allowed) + { + const char *error = + "dbi: st_fetch: fetch without execute, or on a non-SELECT statement"; + + fprintf (stderr, "%s\n", error); + if ((sth->dbh->flags & DBI_THROW_ERRORS)) + pth_die (error); + else + return -1; + } + + /* Get number of rows in the result. */ + nr_rows = PQntuples (sth->result); + + if (sth->next_tuple >= nr_rows) + { + DEBUG (sth->dbh, sth, "fetch: no more rows in query"); + return 0; /* Finished. */ + } + + DEBUG (sth->dbh, sth, "fetch: starting row fetch"); + + /* Fetch it. */ + if (sth->outtypes) + for (i = 0; i < vector_size (sth->outtypes); ++i) + { + vector_get (sth->outtypes, i, ot); + + if (ot.type != 0 && ot.varptr != 0) + { + int is_null = PQgetisnull (sth->result, sth->next_tuple, i); + char *r = !is_null ? PQgetvalue (sth->result, sth->next_tuple, i) + : 0; + + DEBUG (sth->dbh, sth, "fetch: col %d: %s", i, r); + + switch (ot.type) + { + case DBI_STRING: + * (char **) ot.varptr = r; + break; + + case DBI_INT: + case DBI_INT_OR_NULL: + if (is_null) + * (int *) ot.varptr = 0; /* Best we can do in C ! */ + else + sscanf (r, "%d", (int *) ot.varptr); + break; + + case DBI_BOOL: + if (is_null) + * (int *) ot.varptr = 0; /* Best we can do! */ + else + * (int *) ot.varptr = strcmp (r, "t") == 0; + break; + + case DBI_CHAR: + if (is_null) + * (char *) ot.varptr = 0; /* Best we can do! */ + else + * (char *) ot.varptr = r[0]; + break; + + case DBI_TIMESTAMP: + { + struct dbi_timestamp *ts = + (struct dbi_timestamp *) ot.varptr; + + memset (ts, 0, sizeof *ts); + if (is_null) + ts->is_null = 1; + else + parse_timestamp (sth, r, ts); + } + break; + + case DBI_INTERVAL: + { + struct dbi_interval *inv = + (struct dbi_interval *) ot.varptr; + + memset (inv, 0, sizeof *inv); + if (is_null) + inv->is_null = 1; + else + parse_interval (sth, r, inv); + } + break; + + default: + abort (); + } + } + } + + DEBUG (sth->dbh, sth, "fetch: ended row fetch"); + + sth->next_tuple++; + + return 1; /* Row returned. */ +} + +static inline int +parse_fixed_width_int (const char *str, int width) +{ + int r = 0, i; + + for (i = 0; i < width; ++i) + { + r *= 10; + r += str[i] - '0'; + } + + return r; +} + +/* Parse a timestamp field from a PostgreSQL database, and return it + * broken out into the dbi_timestamp structure. This can also parse + * dates and times. + */ +static void +parse_timestamp (st_handle sth, const char *str, struct dbi_timestamp *ts) +{ + vector v; + const char *s, *sign; + + /* Parse the timestamp. */ + v = prematch (sth->pool, str, re_timestamp, 0); + + if (!v) + pth_die (psprintf (sth->pool, + "dbi: parse_timestamp: invalid timestamp: %s", + str)); + + if (vector_size (v) <= 1) return; + vector_get (v, 1, s); + if (s) ts->year = parse_fixed_width_int (s, 4); + + if (vector_size (v) <= 2) return; + vector_get (v, 2, s); + if (s) ts->month = parse_fixed_width_int (s, 2); + + if (vector_size (v) <= 3) return; + vector_get (v, 3, s); + if (s) ts->day = parse_fixed_width_int (s, 2); + + if (vector_size (v) <= 4) return; + vector_get (v, 4, s); + if (s) ts->hour = parse_fixed_width_int (s, 2); + + if (vector_size (v) <= 5) return; + vector_get (v, 5, s); + if (s) ts->min = parse_fixed_width_int (s, 2); + + if (vector_size (v) <= 6) return; + vector_get (v, 6, s); + if (s) ts->sec = parse_fixed_width_int (s, 2); + + if (vector_size (v) <= 7) return; + vector_get (v, 7, s); + if (s) sscanf (s, "%d", &ts->microsecs); + + if (vector_size (v) <= 9) return; + vector_get (v, 8, sign); + vector_get (v, 9, s); + if (sign && s) + { + ts->utc_offset = parse_fixed_width_int (s, 2); + if (sign[0] == '-') ts->utc_offset = -ts->utc_offset; + } +} + +/* Parse an interval field from a PostgreSQL database, and return it + * broken out into the dbi_interval structure. + */ +static void +parse_interval (st_handle sth, const char *str, struct dbi_interval *inv) +{ + vector v; + const char *s; + + /* Parse the interval. */ + v = prematch (sth->pool, str, re_interval, 0); + + if (!v) + pth_die (psprintf (sth->pool, + "dbi: parse_interval: invalid interval: %s", + str)); + + if (vector_size (v) <= 1) return; + vector_get (v, 1, s); + if (s) sscanf (s, "%d", &inv->years); + + if (vector_size (v) <= 2) return; + vector_get (v, 2, s); + if (s) sscanf (s, "%d", &inv->months); + + if (vector_size (v) <= 3) return; + vector_get (v, 3, s); + if (s) sscanf (s, "%d", &inv->days); + + if (vector_size (v) <= 4) return; + vector_get (v, 4, s); + if (s) inv->hours = parse_fixed_width_int (s, 2); + + if (vector_size (v) <= 5) return; + vector_get (v, 5, s); + if (s) inv->mins = parse_fixed_width_int (s, 2); + + if (vector_size (v) <= 6) return; + vector_get (v, 6, s); + if (s) inv->secs = parse_fixed_width_int (s, 2); +} + +vector +st_fetch_all_rows (st_handle sth) +{ + vector result; + int row, nr_rows; + int col, nr_cols; + + if (!sth->result || !sth->fetch_allowed) + { + const char *error = + "dbi: st_fetch_all_rows: fetch without execute, or on a non-SELECT statement"; + + fprintf (stderr, "%s\n", error); + if ((sth->dbh->flags & DBI_THROW_ERRORS)) + pth_die (error); + else + return 0; + } + + DEBUG (sth->dbh, sth, "fetch_all_rows"); + + result = new_vector (sth->pool, vector); + + /* Get number of rows, columns in the result. */ + nr_rows = PQntuples (sth->result); + nr_cols = PQnfields (sth->result); + + /* Fetch it. */ + for (row = 0; row < nr_rows; ++row) + { + vector v = new_vector (sth->pool, char *); + + for (col = 0; col < nr_cols; ++col) + { + char *s = PQgetisnull (sth->result, row, col) + ? 0 : PQgetvalue (sth->result, row, col); + + vector_push_back (v, s); + } + + vector_push_back (result, v); + } + + return result; +} + +void +st_finish (st_handle sth) +{ + if (sth->result) + PQclear (sth->result); + sth->result = 0; + + DEBUG (sth->dbh, sth, "finished (explicit)"); +} + +#ifndef HAVE_PQESCAPESTRING +/* This is taken from the PostgreSQL source code. */ + +/* --------------- + * Escaping arbitrary strings to get valid SQL strings/identifiers. + * + * Replaces "\\" with "\\\\" and "'" with "''". + * length is the length of the buffer pointed to by + * from. The buffer at to must be at least 2*length + 1 characters + * long. A terminating NUL character is written. + * --------------- + */ + +size_t +PQescapeString(char *to, const char *from, size_t length) +{ + const char *source = from; + char *target = to; + unsigned int remaining = length; + + while (remaining > 0) + { + switch (*source) + { + case '\\': + *target = '\\'; + target++; + *target = '\\'; + /* target and remaining are updated below. */ + break; + + case '\'': + *target = '\''; + target++; + *target = '\''; + /* target and remaining are updated below. */ + break; + + default: + *target = *source; + /* target and remaining are updated below. */ + } + source++; + target++; + remaining--; + } + + /* Write the terminating NUL character. */ + *target = '\0'; + + return target - to; +} +#endif + +#if 0 +/* + * PQescapeBytea - converts from binary string to the + * minimal encoding necessary to include the string in an SQL + * INSERT statement with a bytea type column as the target. + * + * The following transformations are applied + * '\0' == ASCII 0 == \\000 + * '\'' == ASCII 39 == \' + * '\\' == ASCII 92 == \\\\ + * anything >= 0x80 ---> \\ooo (where ooo is an octal expression) + */ +unsigned char * +PQescapeBytea(unsigned char *bintext, size_t binlen, size_t *bytealen) +{ + unsigned char *vp; + unsigned char *rp; + unsigned char *result; + size_t i; + size_t len; + + /* + * empty string has 1 char ('\0') + */ + len = 1; + + vp = bintext; + for (i = binlen; i > 0; i--, vp++) + { + if (*vp == 0 || *vp >= 0x80) + len += 5; /* '5' is for '\\ooo' */ + else if (*vp == '\'') + len += 2; + else if (*vp == '\\') + len += 4; + else + len++; + } + + rp = result = (unsigned char *) malloc(len); + if (rp == NULL) + return NULL; + + vp = bintext; + *bytealen = len; + + for (i = binlen; i > 0; i--, vp++) + { + if (*vp == 0 || *vp >= 0x80) + { + (void) sprintf(rp, "\\\\%03o", *vp); + rp += 5; + } + else if (*vp == '\'') + { + rp[0] = '\\'; + rp[1] = '\''; + rp += 2; + } + else if (*vp == '\\') + { + rp[0] = '\\'; + rp[1] = '\\'; + rp[2] = '\\'; + rp[3] = '\\'; + rp += 4; + } + else + *rp++ = *vp; + } + *rp = '\0'; + + return result; +} +#endif diff --git a/src/pthr_dbi.h b/src/pthr_dbi.h new file mode 100644 index 0000000..43812d7 --- /dev/null +++ b/src/pthr_dbi.h @@ -0,0 +1,268 @@ +/* Database interface library. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_dbi.h,v 1.9 2003/02/22 14:23:06 rich Exp $ + */ + +#ifndef PTHR_DBI_H +#define PTHR_DBI_H + +struct db_handle; +typedef struct db_handle *db_handle; + +struct st_handle; +typedef struct st_handle *st_handle; + +#include +#include + +/* Function: new_db_handle - database interface library + * Function: db_commit + * Function: db_rollback + * Function: db_in_transaction + * Function: get_db_handle + * Function: put_db_handle + * Function: new_st_handle + * Function: st_prepare + * Function: st_prepare_cached + * Function: st_execute + * Function: st_serial + * Function: _st_bind + * Function: st_bind + * Function: st_fetch + * Function: st_fetch_all_rows + * Function: st_finish + * Function: db_set_debug + * Function: db_get_debug + * + * @code{pthr_dbi} is a library for interfacing pthrlib programs + * with the PostgreSQL database (see @code{http://www.postgresql.org/}). + * + * @code{new_db_handle} creates a new database handle, and connects + * to the database. The connection structure is allocated in + * @code{pool}. The connection is automatically closed and the memory + * freed when the pool is deleted. The @code{conninfo} string is + * the connection string passed to @code{libpq}. This string is + * fully documented in the PostgreSQL Programmer's Guide, Section I + * (Client Interfaces), libpq, 1.2 Database Connection Functions. + * Commonly the string will contain: + * + * @code{"host=HOSTNAME dbname=DBNAME"} + * + * or: + * + * @code{"host=HOSTNAME dbname=DBNAME user=USER password=PASSWORD"} + * + * The @code{flags} parameter contains zero or more of the following flags: + * + * @code{DBI_THROW_ERRORS}: If set causes database errors to + * call @code{pth_die} (this is the recommended behaviour). + * + * Normally this function returns a database handle. If the database + * connection fails, this function returns @code{NULL}. + * + * @code{db_commit} commits the current database transaction and + * begins a new one. + * + * @code{db_rollback} rolls back the current database transaction and + * begins a new one. + * + * If a database connection is closed without issuing either a commit + * or rollback (eg. the pool is deleted or the program exits), then + * the database will rollback the transaction. Some of this functionality + * relies on the database to do the right thing. + * + * @code{db_in_transaction} returns a flag indicating whether the + * handle is in a transaction. By this we mean that some commands have + * been executed on the handle, but neither @code{db_commit} nor + * @code{db_rollback} have been called. + * + * @code{get_db_handle} and @code{put_db_handle} are used to implement + * connection pooling. + * + * The @code{get_db_handle} function "borrows" a database handle from + * a process-wide pool of compatible handles. The thread has exclusive + * use of this handle until it either calls @code{put_db_handle} to + * "give it back" to the pool, or until the thread exits, at which + * point the handle is automatically returned to the pool. In either + * case, if the thread wishes to commit changes it has made, it must + * call @code{db_commit} on the handle before it is returned to the + * pool. If a handle is returned to the pool in an uncommitted state, + * then the connection is rolled back. + * + * Calling @code{get_db_handle} multiple times returns multiple + * different handles. + * + * Connection pooling is far more efficient than opening and closing + * connections using @code{new_db_handle}. However certain things are + * not possible with connection pooling: eg. creating a temporary + * table which persists across several threads, or executing a single + * transaction across multiple HTTP requests. In these (rare) cases, + * the program should do its own connection management. + * + * Note that there are separate pools for each @code{conninfo} string. + * + * @code{new_st_handle}, and the synonyms @code{st_prepare} and + * @code{st_prepare_cached} create a new statement and return the + * statement handle. The @code{query} parameter is the SQL query. + * '?' and '@' characters in the query may be used as placeholders + * (when they appear outside strings) for scalar and vector values + * respectively. The final parameter(s) are a list of the types + * of these placeholders, and must correspond exactly to the types + * passed in the @code{st_execute} call. + * + * If the @code{st_prepare_cached} form of statement creation is + * used, then the statement is cached in the database handle. At + * the moment, this doesn't make a lot of difference to performance, + * but when a future version of PostgreSQL properly supports prepared + * statements, this will make a big difference in performance by + * allowing query plans to be cached on the server. In practice it + * is almost always best to use @code{st_prepare_cached}. The only + * possible exception is when using statements which refer to + * temporary tables. + * + * @code{st_execute} executes the query with the given parameter + * list. The parameters are substituted for the '?' and '@' placeholders + * in the query, in order. The tyes of the parameters must correspond + * exactly to the types passed in the prepare call. + * + * @code{st_execute} may be called multiple times on the same + * statement handle. You do not need to (and should not, if possible) + * prepare the statement each time. + * + * @code{st_execute} returns the number of rows affected, for + * @code{INSERT} and @code{UPDATE} statements. + * + * If the command was an @CODE{INSERT} statement, then you can use + * @code{st_serial} as a convenience function to return the serial + * number assigned to the new row. The argument passed is the + * sequence name (usually @code{tablename_columnname_seq}). + * + * @code{st_bind} binds a local variable to a column in the + * result. The arguments are the column number (starting at 0), + * the local variable name, and the type of the variable. + * + * Unlike in Perl DBI, you may call @code{st_bind} at any point + * after preparing the statement, and bindings are persistent + * across executes. + * + * Possible types for the prepare, @code{st_execute} and + * @code{st_bind} calls: @code{DBI_INT}, @code{DBI_INT_OR_NULL}, + * @code{DBI_STRING}, @code{DBI_BOOL}, @code{DBI_CHAR}, + * @code{DBI_TIMESTAMP}, @code{DBI_INTERVAL}, @code{DBI_VECTOR_INT}, + * @code{DBI_VECTOR_INT_OR_NULL}, @code{DBI_VECTOR_STRING}, + * @code{DBI_VECTOR_BOOL}, @code{DBI_VECTOR_CHAR}, + * @code{DBI_VECTOR_TIMESTAMP}, @code{DBI_VECTOR_INTERVAL}. + * + * @code{DBI_INT_OR_NULL} differs from an ordinary @code{DBI_INT} + * in that the integer value of @code{0} is treated as a @code{null} + * (useful when passed as a parameter to @code{st_execute}, not very + * useful otherwise). + * + * The @code{DBI_TIMESTAMP}, @code{DBI_INTERVAL}, @code{DBI_VECTOR_TIMESTAMP} + * and @code{DBI_VECTOR_INTERVAL} types refer respectively to the + * PostgreSQL database types @code{timestamp} and @code{interval} + * and the relevant structures @code{struct dbi_timestamp} and + * @code{struct dbi_interval} defined in @code{}. + * + * @code{st_fetch} fetches the next result row from the query. It + * returns true if the result row was fetched, or false if there + * are no more rows. @code{st_fetch} returns the actual results + * in the variables bound to each column by @code{st_bind}. Any + * unbound columns are ignored. + * + * @code{st_fetch_all_rows} fetches all of the result rows + * in one go, returning a @code{vector} of @code{vector} of @code{char *}. + * + * @code{st_finish} is an optional step which you may use once you + * have finished with a statement handle. It frees up the memory + * used by the results held in the statement handle. (This memory + * would otherwise not be freed up until another @code{st_execute} + * or the pool containing the statement handle is deleted). + * + * The @code{db_(set|get)_debug} functions are used to update the + * state of the debug flag on a database handle. When this handle + * is set to true, then database statements which are executed are + * also printed out to @code{stderr}. The default is no debugging. + * + * It is not likely that we will support other databases in future + * unless something dramatic happens to PostgreSQL. Install and learn + * PostgreSQL and I promise that your life will be happier. + */ +extern db_handle new_db_handle (pool, const char *conninfo, int flags); +extern void db_commit (db_handle); +extern void db_rollback (db_handle); +extern int db_in_transaction (db_handle); +extern db_handle get_db_handle (const char *conninfo, int flags); +extern void put_db_handle (db_handle dbh); +extern st_handle new_st_handle (db_handle, const char *query, int flags, ...); +#define st_prepare(db,query,types...) new_st_handle ((db), (query), 0 , ## types) +#define st_prepare_cached(db,query,types...) new_st_handle ((db), (query), DBI_ST_CACHE , ## types) +extern int st_execute (st_handle, ...); +extern int st_serial (st_handle, const char *seq_name); +extern void _st_bind (st_handle, int colidx, void *varptr, int type); +#define st_bind(sth,colidx,var,type) _st_bind ((sth), (colidx), &(var), (type)) +extern int st_fetch (st_handle); +extern vector st_fetch_all_rows (st_handle); +extern void st_finish (st_handle); +extern void db_set_debug (db_handle, int); +extern int db_get_debug (db_handle); + +/* Flags for new_db_handle. */ +#define DBI_THROW_ERRORS 0x0001 +#define DBI_DEBUG 0x0002 + +/* Flags for new_st_handle. */ +#define DBI_ST_CACHE 0x0001 + +/* Database types. */ +/* NB. 0 must not be a valid type! */ +#define DBI_MIN_TYPE 1001 +#define DBI_INT 1001 +#define DBI_STRING 1002 +#define DBI_BOOL 1003 +#define DBI_CHAR 1004 +#define DBI_TIMESTAMP 1005 +#define DBI_INTERVAL 1006 +#define DBI_INT_OR_NULL 1007 +#define DBI_MAX_TYPE 1007 +#define DBI_VECTOR_INT DBI_INT +#define DBI_VECTOR_STRING DBI_STRING +#define DBI_VECTOR_BOOL DBI_BOOL +#define DBI_VECTOR_CHAR DBI_CHAR +#define DBI_VECTOR_TIMESTAMP DBI_TIMESTAMP +#define DBI_VECTOR_INTERVAL DBI_INTERVAL +#define DBI_VECTOR_INT_OR_NULL DBI_INT_OR_NULL + +/* For the timestamp and interval types, these structures are used. */ +struct dbi_timestamp +{ + int is_null; /* NULL if true (other fields will be zero). */ + int year, month, day; + int hour, min, sec; + int microsecs; + int utc_offset; +}; + +struct dbi_interval +{ + int is_null; /* NULL if true (other fields will be zero). */ + int secs, mins, hours; + int days, months, years; +}; + +#endif /* PTHR_DBI_H */ diff --git a/src/pthr_ftpc.c b/src/pthr_ftpc.c new file mode 100644 index 0000000..6d60bf1 --- /dev/null +++ b/src/pthr_ftpc.c @@ -0,0 +1,724 @@ +/* FTP client library. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_ftpc.c,v 1.7 2002/12/01 14:29:27 rich Exp $ + */ + +#include "config.h" + +#include +#include + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_NETDB_H +#include +#endif + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#include +#include +#include + +#include "pthr_pseudothread.h" +#include "pthr_iolib.h" +#include "pthr_ftpc.h" + +#define IS_1xx(c) ((c) >= 100 && (c) <= 199) +#define IS_2xx(c) ((c) >= 200 && (c) <= 299) +#define IS_3xx(c) ((c) >= 300 && (c) <= 399) +#define IS_4xx(c) ((c) >= 400 && (c) <= 499) +#define IS_5xx(c) ((c) >= 500 && (c) <= 599) +#define IS_NOT_1xx(c) (!(IS_1xx(c))) +#define IS_NOT_2xx(c) (!(IS_2xx(c))) +#define IS_NOT_3xx(c) (!(IS_3xx(c))) +#define IS_NOT_4xx(c) (!(IS_4xx(c))) +#define IS_NOT_5xx(c) (!(IS_5xx(c))) + +#define REPLY_BUFFER_SIZE 2048 + +struct ftpc +{ + /* General information about the connection. */ + pool pool; + io_handle io; + + int port; + int passive_mode; + int verbose; + + struct sockaddr_in addr; + int pasv_data_port; + + /* Reply buffer - stores the last line read from the server. */ + char *reply; + + /* These are kept for information only. */ + char *server; + const char *username; + char *server_greeting; +}; + +static int eat_reply (ftpc); +static int do_command (ftpc, const char *cmd, const char *arg); +static io_handle open_data (ftpc, int data_sock); +static int issue_port_or_pasv (ftpc f); + +ftpc +new_ftpc (pool pool, const char *server) +{ + ftpc f; + char *t; + struct hostent *h; + int sock, code; + + f = pcalloc (pool, 1, sizeof *f); + f->pool = pool; + f->server = pstrdup (pool, server); + f->reply = pmalloc (pool, REPLY_BUFFER_SIZE); + +#if 0 + /* Just during testing, enable verbose mode always. */ + f->verbose = 1; +#endif + + /* Name contains a port number? If so, extract it out first. */ + t = strrchr (f->server, ':'); + if (t) + { + *t = 0; + if (sscanf (t+1, "%d", &f->port) != 1) + { + fprintf (stderr, "bad port number: %s\n", t+1); + return 0; + } + } + else + { + struct servent *se; + + /* Try to determine the default port for FTP. */ + se = getservbyname ("ftp", "tcp"); + if (se) + f->port = ntohs (se->s_port); + else + f->port = 21; /* Default FTP control port. */ + } + + /* Resolve the name of the server, if necessary. */ + h = gethostbyname (f->server); + if (!h) + { + herror (f->server); + return 0; + } + + f->addr.sin_family = AF_INET; + memcpy (&f->addr.sin_addr, h->h_addr, sizeof f->addr.sin_addr); + f->addr.sin_port = htons (f->port); + + /* Create a socket. */ + sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == -1) + { + perror ("socket"); + return 0; + } + + /* Set non-blocking. */ + if (fcntl (sock, F_SETFL, O_NONBLOCK) == -1) + { + perror ("fcntl: O_NONBLOCK"); + return 0; + } + + /* Attempt to connect to the server. */ + if (pth_connect (sock, (struct sockaddr *) &f->addr, sizeof f->addr) + == -1) + { + perror (f->server); + return 0; + } + + /* Wrap up the socket in an IO handle. */ + f->io = io_fdopen (sock); + if (!f->io) return 0; + + /* Expect a response string back immediately from the server. */ + code = eat_reply (f); + + /* Save the server greeting. */ + f->server_greeting = pstrdup (f->pool, f->reply+4); + + if (IS_NOT_2xx (code)) + { + fprintf (stderr, "bad response from server %d\n", code); + return 0; + } + + return f; +} + +int +ftpc_set_passive_mode (ftpc f, int flag) +{ + return f->passive_mode = flag; +} + +int +ftpc_set_verbose (ftpc f, int flag) +{ + return f->verbose = flag; +} + +void +ftpc_perror (ftpc f, const char *msg) +{ + fprintf (stderr, "%s: %s\n", msg, f->reply); +} + +int +ftpc_login (ftpc f, const char *username, const char *password) +{ + int is_anonymous, code; + + is_anonymous = !username || strcmp (username, "ftp") == 0 || + strcmp (username, "anonymous") == 0; + + if (is_anonymous) + { + if (!username) + username = "ftp"; + if (!password) + { + char *logname = getenv ("LOGNAME"); + + if (!logname) logname = "nobody"; + password = psprintf (f->pool, "%s@", logname); + } + } + else + { + if (!password) password = ""; + } + + f->username = username; + + /* Send the USER command. */ + code = do_command (f, "USER", username); + if (IS_NOT_3xx (code)) + return -1; + + /* Send the PASS command. */ + code = do_command (f, "PASS", password); + if (IS_NOT_2xx (code)) + return -1; + + return 0; +} + +int +ftpc_type (ftpc f, char type) +{ + int code; + char t[2] = { type, 0 }; + + code = do_command (f, "TYPE", t); + return IS_2xx (code) ? 0 : -1; +} + +int +ftpc_ascii (ftpc f) +{ + return ftpc_type (f, 'a'); +} + +int +ftpc_binary (ftpc f) +{ + return ftpc_type (f, 'i'); +} + +int +ftpc_cwd (ftpc f, const char *pathname) +{ + int code = do_command (f, "CWD", pathname); + return IS_2xx (code) ? 0 : -1; +} + +int +ftpc_cdup (ftpc f) +{ + int code = do_command (f, "CDUP", 0); + return IS_2xx (code) ? 0 : -1; +} + +char * +ftpc_pwd (ftpc f) +{ + int code, len; + char *path; + + code = do_command (f, "PWD", 0); + if (IS_NOT_2xx (code)) return 0; + + path = pstrdup (f->pool, &f->reply[4]); + + /* Strip quotes around the pathname, if there are any. */ + len = strlen (path); + if (len >= 2 && path[0] == '"' && path[len-1] == '"') + { + path[len-1] = '\0'; + path++; + } + + return path; +} + +int +ftpc_mkdir (ftpc f, const char *pathname) +{ + int code = do_command (f, "MKD", pathname); + return IS_2xx (code) ? 0 : -1; +} + +int +ftpc_rmdir (ftpc f, const char *pathname) +{ + int code = do_command (f, "RMD", pathname); + return IS_2xx (code) ? 0 : -1; +} + +int +ftpc_delete (ftpc f, const char *pathname) +{ + int code = do_command (f, "DELE", pathname); + return IS_2xx (code) ? 0 : -1; +} + +vector +ftpc_ls (ftpc f, pool pool, const char *pathname) +{ + int code, data_sock; + io_handle io; + vector v; + + /* Issue PORT or PASV command and get a socket. */ + if ((data_sock = issue_port_or_pasv (f)) == -1) + return 0; + + if (!pool) pool = f->pool; + + v = new_vector (pool, const char *); + + /* Issue the NLST command. */ + code = do_command (f, "NLST -a", pathname); + if (IS_NOT_1xx (code)) { close (data_sock); return 0; } + + /* Open data connection to server. */ + io = open_data (f, data_sock); + if (!io) return 0; + + /* Read the data, line at a time. */ + while (io_fgets (f->reply, REPLY_BUFFER_SIZE, io, 0)) + { + char *s = pstrdup (pool, f->reply); + vector_push_back (v, s); + } + + /* Close the data connection. */ + io_fclose (io); + + /* Check return code. */ + code = eat_reply (f); + return IS_2xx (code) ? v : 0; +} + +vector +ftpc_dir (ftpc f, pool pool, const char *pathname) +{ + int code, data_sock; + io_handle io; + vector v; + + /* Issue PORT or PASV command and get a socket. */ + if ((data_sock = issue_port_or_pasv (f)) == -1) + return 0; + + if (!pool) pool = f->pool; + + v = new_vector (pool, const char *); + + /* Issue the LIST command. */ + code = do_command (f, "LIST -a", pathname); + if (IS_NOT_1xx (code)) { close (data_sock); return 0; } + + /* Open data connection to server. */ + io = open_data (f, data_sock); + if (!io) return 0; + + /* Read the data, line at a time. */ + while (io_fgets (f->reply, REPLY_BUFFER_SIZE, io, 0)) + { + char *s = pstrdup (pool, f->reply); + vector_push_back (v, s); + } + + /* Close the data connection. */ + io_fclose (io); + + /* Check return code. */ + code = eat_reply (f); + return IS_2xx (code) ? v : 0; +} + +/* XXX Only works for binary transfers. */ +int +ftpc_get (ftpc f, const char *remote_file, const char *local_file) +{ + int code, data_sock, n; + FILE *fp; + io_handle io; + char buf[1024]; + + /* Issue PORT or PASV command and get a socket. */ + if ((data_sock = issue_port_or_pasv (f)) == -1) + return -1; + + /* Issue the RETR command. */ + code = do_command (f, "RETR", remote_file); + if (IS_NOT_1xx (code)) { close (data_sock); return -1; } + + /* Open the local file. */ + fp = fopen (local_file, "w"); + if (fp == 0) { perror (local_file); return -1; } + + /* Open data connection to server. */ + io = open_data (f, data_sock); + if (!io) return -1; + + /* Read the data, block at a time. */ + while ((n = io_fread (buf, 1, sizeof buf, io)) > 0) + { + if (fwrite (buf, 1, n, fp) != n) + { + perror (local_file); + fclose (fp); + io_fclose (io); + return -1; + } + } + + /* Close everything. */ + fclose (fp); + io_fclose (io); + + /* Check return code. */ + code = eat_reply (f); + return IS_2xx (code) ? 0 : -1; +} + +/* XXX Only works for binary transfers. */ +int +ftpc_put (ftpc f, const char *local_file, const char *remote_file) +{ + int code, data_sock, n; + FILE *fp; + io_handle io; + char buf[1024]; + + /* Issue PORT or PASV command and get a socket. */ + if ((data_sock = issue_port_or_pasv (f)) == -1) + return -1; + + /* Issue the STOR command. */ + code = do_command (f, "STOR", remote_file); + if (IS_NOT_1xx (code)) { close (data_sock); return -1; } + + /* Open the local file. */ + fp = fopen (local_file, "r"); + if (fp == 0) { perror (local_file); return -1; } + + /* Open data connection to server. */ + io = open_data (f, data_sock); + if (!io) return -1; + + /* Read the data, block at a time. */ + while ((n = fread (buf, 1, sizeof buf, fp)) > 0) + { + if (io_fwrite (buf, 1, n, io) != n) + { + perror (remote_file); + fclose (fp); + io_fclose (io); + return -1; + } + } + + /* Close everything. */ + fclose (fp); + io_fclose (io); + + /* Check return code. */ + code = eat_reply (f); + return IS_2xx (code) ? 0 : -1; +} + +int +ftpc_quote (ftpc f, const char *cmd) +{ + int code; + + code = do_command (f, cmd, 0); + return IS_2xx (code) ? 0 : -1; +} + +int +ftpc_quit (ftpc f) +{ + int code; + + code = do_command (f, "QUIT", 0); + io_fclose (f->io); + return IS_2xx (code) ? 0 : -1; +} + +/* Execute a command, get the reply and return the code. */ +static int +do_command (ftpc f, const char *cmd, const char *arg) +{ + if (f->verbose) + { + if (f->username) + fprintf (stderr, "%s@%s: ", f->username, f->server); + else + fprintf (stderr, "%s: ", f->server); + + if (arg == 0) + fprintf (stderr, "%s\r\n", cmd); + else + fprintf (stderr, "%s %s\r\n", cmd, arg); + } + + if (arg == 0) + io_fprintf (f->io, "%s\r\n", cmd); + else + io_fprintf (f->io, "%s %s\r\n", cmd, arg); + + return eat_reply (f); +} + +/* Eat the reply from the server, and throw it all away, except for + * the code. + */ +static int +eat_reply (ftpc f) +{ + int code; + + while (io_fgets (f->reply, REPLY_BUFFER_SIZE, f->io, 0)) + { + if (f->verbose) + { + if (f->username) + fprintf (stderr, "%s@%s: %s\n", f->username, f->server, f->reply); + else + fprintf (stderr, "%s: %s\n", f->server, f->reply); + } + + if (strlen (f->reply) < 4 || + f->reply[0] < '1' || f->reply[0] > '5' || + f->reply[1] < '0' || f->reply[1] > '9' || + f->reply[2] < '0' || f->reply[2] > '9' || + (f->reply[3] != ' ' && f->reply[3] != '-')) + pth_die ("badly formatted reply from server"); + + /* Is this the last line? */ + if (f->reply[3] == ' ') + { + /* Yes: extract the code. */ + code = 100 * (f->reply[0] - '0') + + 10 * (f->reply[1] - '0') + (f->reply[2] - '0'); + + return code; + } + } + + pth_die ("server closed the connection unexpectedly"); +} + +/* Issue either PORT or PASV command to the server and get a socket. */ +static int +issue_port_or_pasv (ftpc f) +{ + int code, a1, a2, a3, a4, p1, p2; + char *t; + struct sockaddr_in addr; + int addr_len, data_sock; + + /* Create data socket. */ + data_sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (data_sock == -1) + { + perror ("socket"); + return -1; + } + + /* Set non-blocking. */ + if (fcntl (data_sock, F_SETFL, O_NONBLOCK) == -1) abort (); + + if (f->passive_mode) + { + /* Issue PASV command to get a port number. */ + code = do_command (f, "PASV", 0); + if (IS_NOT_2xx (code)) return -1; + + /* The reply should say something like: + * 227 Entering Passive Mode (A,A,A,A,P,P) + * We ignore the address fields and concentrate on just + * the port number. + */ + t = strchr (&f->reply[4], '('); + if (!t || + sscanf (t, "( %d , %d , %d , %d , %d , %d )", + &a1, &a2, &a3, &a4, &p1, &p2) != 6) + { + close (data_sock); + fprintf (stderr, "cannot parse reply from PASV command\n"); + return -1; + } + + f->pasv_data_port = p1 * 256 + p2; + } + else + { + /* Bind the socket. */ + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = INADDR_ANY; + if (bind (data_sock, (struct sockaddr *) &addr, sizeof addr) == -1) + { + close (data_sock); + perror ("bind"); + return -1; + } + + /* Listen on socket. */ + if (listen (data_sock, 1) == -1) + { + close (data_sock); + perror ("listen"); + return -1; + } + + addr_len = sizeof addr; + if (getsockname (data_sock, (struct sockaddr *) &addr, &addr_len) + == -1) + { + close (data_sock); + perror ("getsockname"); + return -1; + } + + /* Issue a PORT command to tell the server our port number. */ + a1 = (ntohl (f->addr.sin_addr.s_addr) >> 24) & 0xff; + a2 = (ntohl (f->addr.sin_addr.s_addr) >> 16) & 0xff; + a3 = (ntohl (f->addr.sin_addr.s_addr) >> 8) & 0xff; + a4 = ntohl (f->addr.sin_addr.s_addr) & 0xff; + p1 = ntohs (addr.sin_port) / 256; + p2 = ntohs (addr.sin_port) % 256; + t = psprintf (f->pool, "%d,%d,%d,%d,%d,%d", a1, a2, a3, a4, p1, p2); + code = do_command (f, "PORT", t); + if (IS_NOT_2xx (code)) return -1; + } + + return data_sock; +} + +/* Open a data connection to the server (which is expecting it). */ +static io_handle +open_data (ftpc f, int data_sock) +{ + io_handle io; + struct sockaddr_in addr; + int addr_len, sock; + + if (f->passive_mode) + { + /* Connect to server. */ + f->addr.sin_port = htons (f->pasv_data_port); + + if (pth_connect (data_sock, (struct sockaddr *) &f->addr, + sizeof f->addr) == -1) + { + close (data_sock); + perror (f->server); + return 0; + } + + io = io_fdopen (data_sock); + if (!io) return 0; + } + else + { + addr_len = sizeof addr; + + /* Accept connection from server. */ + if ((sock = pth_accept (data_sock, + (struct sockaddr *) &addr, &addr_len)) == -1) + { + close (data_sock); + perror ("accept"); + return 0; + } + + close (data_sock); + + /* Verify this connection comes from the server. */ + if (addr.sin_addr.s_addr != f->addr.sin_addr.s_addr) + { + fprintf (stderr, "connection accepted, but not from FTP server"); + return 0; + } + + io = io_fdopen (sock); + if (!io) return 0; + } + + return io; +} diff --git a/src/pthr_ftpc.h b/src/pthr_ftpc.h new file mode 100644 index 0000000..9a34c39 --- /dev/null +++ b/src/pthr_ftpc.h @@ -0,0 +1,213 @@ +/* FTP client library. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_ftpc.h,v 1.5 2002/12/01 14:29:27 rich Exp $ + */ + +#ifndef PTHR_FTPC_H +#define PTHR_FTPC_H + +#include +#include + +#include +#include + +struct ftpc; +typedef struct ftpc *ftpc; + +/* Function: new_ftpc - Create a new FTP client object. + * + * Create a new FTP client object, connected to the FTP server + * called @code{server}. The @code{server} may be an IP address or a hostname. + * If the @code{server} name ends with @code{:port} then @code{port} + * is the port number to connect to. + * + * The default mode for new connections is active mode. Call + * @ref{ftpc_set_mode(3)} to change the mode. + * + * See also: @ref{ftpc_login(3)}, @ref{ftpc_set_mode(3)}. + */ +extern ftpc new_ftpc (pool, const char *server); + +/* Function: ftpc_set_passive_mode - Change to/from active or passive mode. + * + * If @code{flag} is true, all future connections on this @code{ftpc} + * object will be in passive mode. If @code{flag} is false, all + * future connections will be in active mode. + * + * Passive mode is required by a few servers and some firewalls. + * + * Returns: 0 if successful, -1 if the attempt failed. + * If a fatal error occurs with the connection, an exception + * is thrown. + */ +extern int ftpc_set_passive_mode (ftpc ftpc, int flag); + +extern int ftpc_set_verbose (ftpc ftpc, int flag); + +extern void ftpc_perror (ftpc f, const char *msg); + +/* Function: ftpc_login - Log onto the FTP server. + * + * Attempt to log onto the FTP server as user @code{username} with + * password @code{password}. If @code{username} is @code{NULL}, + * @code{"ftp"} or @code{"anonymous"}, then log in anonymously. + * For anonymous logins, the @code{password} may be @code{NULL}, + * in which case the environment variable @code{LOGNAME} followed + * by a single @code{@} character is used as the password. + * + * Returns: 0 if successful, -1 if the attempt failed. + * If a fatal error occurs with the connection, an exception + * is thrown. + */ +extern int ftpc_login (ftpc ftpc, const char *username, const char *password); + +/* Function: ftpc_type - Set connection type. + * Function: ftpc_ascii + * Function: ftpc_binary + * + * @code{ftpc_type} sets the connection type. Most FTP servers only + * support type 'a' (ASCII) or type 'i' (bInary), although esoteric + * FTP servers might support 'e' (EBCDIC). + * + * @code{ftpc_ascii} sets the type to ASCII. + * + * @code{ftpc_binary} sets the type to bInary. + * + * Returns: 0 if successful, -1 if the attempt failed. + * If a fatal error occurs with the connection, an exception + * is thrown. + */ +extern int ftpc_type (ftpc ftpc, char type); +extern int ftpc_ascii (ftpc ftpc); +extern int ftpc_binary (ftpc ftpc); + +/* Function: ftpc_cwd - Change directory on the server. + * Function: ftpc_cdup + * + * @code{ftpc_cwd} changes the directory to @code{pathname}. + * + * @code{ftpc_cdup} moves to the parent directory. On most Unix-like + * FTP servers this is equivalent to doing @code{CWD ..} + * + * Returns: 0 if successful, -1 if the attempt failed. + * If a fatal error occurs with the connection, an exception + * is thrown. + */ +extern int ftpc_cwd (ftpc ftpc, const char *pathname); +extern int ftpc_cdup (ftpc ftpc); + +/* Function: ftpc_pwd - Return current directory on the server. + * + * @code{ftpc_pwd} returns the current directory on the server. + * + * Returns: The current directory, as a string allocated in the + * pool, or NULL if the command fails. If a fatal error occurs + * with the connection, an exception is thrown. + */ +extern char *ftpc_pwd (ftpc ftpc); + +/* Function: ftpc_mkdir - Create or remove directories on the server. + * Function: ftpc_rmdir + * + * @code{ftpc_mkdir} creates a directory called @code{pathname} + * on the server. @code{ftpc_rmdir} removes a directory called + * @code{pathname} on the server. + * + * Returns: 0 if successful, -1 if the attempt failed. + * If a fatal error occurs with the connection, an exception + * is thrown. + */ +extern int ftpc_mkdir (ftpc ftpc, const char *pathname); +extern int ftpc_rmdir (ftpc ftpc, const char *pathname); + +/* Function: ftpc_delete - Delete a file on the server. + * + * @code{ftpc_delete} deletes a file called @code{pathname} + * on the server. + * + * Returns: 0 if successful, -1 if the attempt failed. + * If a fatal error occurs with the connection, an exception + * is thrown. + */ +extern int ftpc_delete (ftpc ftpc, const char *pathname); + +/* Function: ftpc_ls - List the contents of a directory on the server. + * Function: ftpc_dir + * + * @code{ftpc_ls} and @code{ftpc_dir} list the contents of either + * the current directory (if @code{pathname} is @code{NULL}) or + * else another directory @code{pathname}. + * + * @code{ftpc_ls} issues the command @code{NLST -a}, returning a + * vector of strings giving the name of each file. + * + * @code{ftpc_dir} issues the command @code{LIST -a}, returning + * a human-readable list of filenames, similar to issuing the + * @code{ls -al} command in Unix. + * + * Returns: 0 if successful, -1 if the attempt failed. + * If a fatal error occurs with the connection, an exception + * is thrown. + */ +extern vector ftpc_ls (ftpc ftpc, pool, const char *pathname); +extern vector ftpc_dir (ftpc ftpc, pool, const char *pathname); + +/* Function: ftpc_get - Download or upload a file. + * Function: ftpc_put + * + * @code{ftpc_get} attempts to download @code{remote_file} from the + * server and store it in a local file called @code{local_file}. + * + * @code{ftpc_put} attempts to upload a file called @code{local_file} + * to the server and store it in a file on the server called + * @code{remote_file}. + * + * Returns: 0 if successful, -1 if the attempt failed. + * If a fatal error occurs with the connection, an exception + * is thrown. + */ +extern int ftpc_get (ftpc ftpc, const char *remote_file, const char *local_file); +extern int ftpc_put (ftpc ftpc, const char *local_file, const char *remote_file); + +/* Function: ftpc_quote - Issue a command to the FTP server. + * + * @code{ftpc_quote} issues a command directly to the FTP server. + * This function may be used for issuing @code{SITE} commands for + * example. + * + * Returns: 0 if successful, -1 if the attempt failed. + * If a fatal error occurs with the connection, an exception + * is thrown. + */ +extern int ftpc_quote (ftpc ftpc, const char *cmd); + +/* Function: ftpc_quit - Nicely disconnect from the FTP server. + * + * @code{ftpc_quit} sends a @code{QUIT} command to the FTP server + * and then drops the network connection. After using this function, + * the @code{ftpc} object associated with the connection is + * invalid and should not be used. + * + * Returns: 0 if successful, -1 if the attempt failed. + * If a fatal error occurs with the connection, an exception + * is thrown. + */ +extern int ftpc_quit (ftpc ftpc); + +#endif /* PTHR_FTPC_H */ diff --git a/src/pthr_http.c b/src/pthr_http.c new file mode 100644 index 0000000..37bd52b --- /dev/null +++ b/src/pthr_http.c @@ -0,0 +1,702 @@ +/* HTTP library. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_http.c,v 1.16 2003/02/02 18:05:31 rich Exp $ + */ + +#include "config.h" + +#include +#include +#include +#include + +#ifdef HAVE_SETJMP_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_SYSLOG_H +#include +#endif + +#ifdef HAVE_TIME_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#ifdef HAVE_ALLOCA_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "pthr_reactor.h" +#include "pthr_iolib.h" +#include "pthr_cgi.h" +#include "pthr_http.h" + +#define MAX_LINE_LENGTH 4096 +#define CRLF "\r\n" + +static const char *servername = PACKAGE "-httpd/" VERSION; +static FILE *log_fp = 0; + +struct http_request +{ + pool pool; /* Pool for memory allocations. */ + time_t t; /* Request time. */ + int method; /* Method. */ + const char *original_url; /* Original request URL (used for logging). */ + const char *url; /* The URL. */ + const char *path; /* Path only. */ + const char *query_string; /* Query string only. */ + int is_http09; /* Is it an HTTP/0.9 request? */ + int major, minor; /* Major/minor version numbers. */ + sash headers; /* Headers. */ +}; + +struct http_response +{ + pool pool; /* Pool. */ + http_request request; /* The original request. */ + int code; /* Response code. */ + io_handle io; /* IO handle. */ + unsigned extra_headers; /* Bitmap of extra headers to send. */ +#define _HTTP_XH_SERVER 1 +#define _HTTP_XH_DATE 2 +#define _HTTP_XH_CONTENT_TYPE 4 +#define _HTTP_XH_CONNECTION 8 +#define _HTTP_XH_CONTENT_LENGTH 16 +#define _HTTP_XH_TRANSFER_ENCODING_CHUNKED 32 + int content_length; /* Content length (if seen). */ +}; + +#define _HTTP_XH_LENGTH_DEFINED (_HTTP_XH_CONTENT_LENGTH|_HTTP_XH_TRANSFER_ENCODING_CHUNKED) + +static void parse_url (http_request h); +static void do_logging (http_response h); + +const char * +http_get_servername (void) +{ + return servername; +} + +const char * +http_set_servername (const char *new_server_name) +{ + return servername = new_server_name; +} + +http_request +new_http_request (pool pool, io_handle io) +{ + char line[MAX_LINE_LENGTH]; + char *start_url, *end_url, *end_key; + char *key, *value; + + http_request h = pmalloc (pool, sizeof *h); + + memset (h, 0, sizeof *h); + h->pool = pool; + h->headers = new_sash (h->pool); + h->t = reactor_time / 1000; + + /* Read the first line of the request. As a sop to Netscape 4, ignore + * blank lines (see note below about Netscape generating extra CRLFs + * after POST requests). + */ + again: + if (!io_fgets (line, sizeof line, io, 0)) + return 0; + if (line[0] == '\0') goto again; + + /* Previous versions of the server supported only GET requests. We + * now support GET and HEAD (as required by RFC 2616 section 5.1.1) + * and POST (see: http://www.w3.org/MarkUp/html-spec/html-spec_8.html). + * See RFC 2616 section 14.7 for a description of the Allow header. + */ + if (strncmp (line, "GET ", 4) == 0) + { + h->method = HTTP_METHOD_GET; + start_url = line + 4; + } + else if (strncmp (line, "HEAD ", 5) == 0) + { + h->method = HTTP_METHOD_HEAD; + start_url = line + 5; + } + else if (strncmp (line, "POST ", 5) == 0) + { + h->method = HTTP_METHOD_POST; + start_url = line + 5; + } + else + { + const char *msg = "bad method (not a GET, HEAD or POST request)"; + + syslog (LOG_INFO, msg); + io_fputs ("HTTP/1.1 405 Method not allowed" CRLF + "Allow: GET, HEAD, POST" CRLF + CRLF, io); + pth_die (msg); + } + + /* Parse out the URL. According to RFC 2616 section 5.1.2 we ought + * to be able to support absolute URIs in requests. At the moment + * we can't. Luckily no HTTP/1.1 clients should generate them. + */ + end_url = strchr (start_url, ' '); + if (end_url == 0) + { + /* It's an HTTP/0.9 request! */ + h->original_url = h->url = pstrdup (h->pool, start_url); + parse_url (h); + h->is_http09 = 1; + h->major = 0; + h->minor = 9; + + return h; + } + + /* It's an HTTP > 0.9 request, so there must be headers following. */ + *end_url = '\0'; + h->original_url = h->url = pstrdup (h->pool, start_url); + parse_url (h); + + /* Check HTTP version number. */ + if (strncmp (end_url+1, "HTTP/", 5) != 0 || + !isdigit ((int) *(end_url+6)) || + *(end_url+7) != '.' || + !isdigit ((int) *(end_url+8))) + { + const char *msg = "badly formed request -- no HTTP/x.y"; + + syslog (LOG_INFO, msg); + io_fputs ("HTTP/1.1 400 Badly formed request" CRLF, io); + pth_die (msg); + } + + h->is_http09 = 0; + h->major = *(end_url+6) - '0'; + h->minor = *(end_url+8) - '0'; + + /* Read the headers. */ + for (;;) + { + if (!io_fgets (line, sizeof line, io, 0)) + { + const char *msg = "unexpected EOF reading headers"; + + syslog (LOG_INFO, msg); + io_fputs ("HTTP/1.1 400 Unexpected EOF in request" CRLF, io); + pth_die (msg); + } + + /* End of headers? */ + if (line[0] == '\0') + break; + + /* Check that the header has the form Key: Value. */ + end_key = strchr (line, ':'); + if (end_key == 0) + { + const char *msg = "badly formed header in request"; + + syslog (LOG_INFO, msg); + io_fputs ("HTTP/1.1 400 Badly formed header" CRLF, io); + pth_die (msg); + } + + /* Split up the key and value and store them. */ + *end_key = '\0'; + + /* Find the beginning of the value field. + * RFC822 / RFC2616 does not require a space after the colon + * and effectively says to ignore linear white space after the + * colon. So remove that here. + */ + end_key++; + ptrim (end_key); + + key = pstrdup (h->pool, line); + value = pstrdup (h->pool, end_key); + + /* Canonicalize the key (HTTP header keys are case insensitive). */ + pstrlwr (key); + + sash_insert (h->headers, key, value); + } + + return h; +} + +/* This function is called just after h->url has been set. It + * parses out the path and query string parameters from the URL + * and stores them separately. + */ +static void +parse_url (http_request h) +{ + if (h->method == HTTP_METHOD_POST) + { + h->path = h->url; + h->query_string = 0; + } + else /* GET or HEAD requests. */ + { + char *p, *t; + + p = pstrdup (h->pool, h->url); + t = strchr (p, '?'); + + if (t == 0) /* No query string. */ + { + h->path = p; + h->query_string = 0; + return; + } + + *t = '\0'; + h->path = p; /* Path is everything before '?' char. */ + h->query_string = t+1; /* Query string is everything after it. */ + } +} + +time_t +http_request_time (http_request h) +{ + return h->t; +} + +const char * +http_request_get_url (http_request h) +{ + return h->url; +} + +void +http_request_set_url (http_request h, const char *url) +{ + h->url = url; + parse_url (h); +} + +const char * +http_request_path (http_request h) +{ + return h->path; +} + +const char * +http_request_query_string (http_request h) +{ + return h->query_string; +} + +int +http_request_method (http_request h) +{ + return h->method; +} + +const char * +http_request_method_string (http_request h) +{ + switch (h->method) + { + case HTTP_METHOD_GET: return "GET"; + case HTTP_METHOD_HEAD: return "HEAD"; + case HTTP_METHOD_POST: return "POST"; + } + abort (); +} + +int +http_request_is_HEAD (http_request h) +{ + return h->method == HTTP_METHOD_HEAD; +} + +void +http_request_version (http_request h, int *major, int *minor) +{ + *major = h->major; + *minor = h->minor; +} + +int +http_request_nr_headers (http_request h) +{ + return sash_size (h->headers); +} + +vector +http_request_get_headers (http_request h) +{ + vector keys = sash_keys (h->headers); + vector r = new_vector (h->pool, struct http_header); + int i; + + for (i = 0; i < vector_size (keys); ++i) + { + struct http_header header; + char *key; + + vector_get (keys, i, key); + header.key = key; + sash_get (h->headers, key, header.value); + + vector_push_back (r, header); + } + + return r; +} + +const char * +http_request_get_header (http_request h, const char *key) +{ + char *k = alloca (strlen (key) + 1); + const char *v; + + /* Canonicalize the key. */ + strcpy (k, key); + pstrlwr (k); + + sash_get (h->headers, k, v); + + return v; +} + +const char * +http_request_get_cookie (http_request h, const char *key) +{ + const char *cookie_hdr; + static pcre *re = 0; + vector v; + int keylen = strlen (key); + int i; + + cookie_hdr = http_request_get_header (h, "Cookie"); + if (!cookie_hdr) return 0; + + /* Split it into pieces at whitespace, commas or semi-colons. */ + if (!re) re = precomp (global_pool, "[ \t\n,;]+", 0); + v = pstrresplit (h->pool, cookie_hdr, re); + + for (i = 0; i < vector_size (v); ++i) + { + char *str; + + vector_get (v, i, str); + + /* It'll either be NAME=VALUE or $Path="?VALUE"?. We actually + * don't care too much about the Path or Domain, so ignore them. + */ + if (str[0] != '$') + { + /* Matching name? */ + if (strncasecmp (str, key, keylen) == 0 + && str[keylen] == '=') + { + return cgi_unescape (h->pool, &str[keylen+1]); + } + } + } + + return 0; +} + +http_response +new_http_response (pool pool, http_request request, + io_handle io, + int code, const char *msg) +{ + http_response h = pmalloc (pool, sizeof *h); + + memset (h, 0, sizeof *h); + + h->pool = pool; + h->request = request; + h->code = code; + h->io = io; + h->extra_headers = ~0; + h->content_length = 0; + + /* Set the output mode to fully buffered for efficiency. */ + io_setbufmode (h->io, IO_MODE_FULLY_BUFFERED); + + /* HTTP/0.9? No response line or headers. */ + if (request->is_http09) return h; + + /* Write the response line. */ + io_fprintf (io, "HTTP/1.1 %d %s" CRLF, code, msg); + + return h; +} + +void +http_response_send_header (http_response h, + const char *key, const char *value) +{ + /* HTTP/0.9? No response line or headers. */ + if (h->request->is_http09) return; + + io_fputs (key, h->io); + io_fputs (": ", h->io); + io_fputs (value, h->io); + io_fputs (CRLF, h->io); + + /* Check for caller sending known header key and remove that + * from the bitmap so we don't overwrite caller's header + * in http_response_end_headers function. + */ + if (strcasecmp (key, "Server") == 0) + h->extra_headers &= ~_HTTP_XH_SERVER; + if (strcasecmp (key, "Date") == 0) + h->extra_headers &= ~_HTTP_XH_DATE; + if (strcasecmp (key, "Content-Type") == 0) + h->extra_headers &= ~_HTTP_XH_CONTENT_TYPE; + if (strcasecmp (key, "Connection") == 0) + h->extra_headers &= ~_HTTP_XH_CONNECTION; + if (strcasecmp (key, "Content-Length") == 0) + { + h->extra_headers &= ~_HTTP_XH_CONTENT_LENGTH; + sscanf (value, "%d", &h->content_length); + } + if (strcasecmp (key, "Transfer-Encoding") == 0 && + strcasecmp (value, "chunked") == 0) + h->extra_headers &= ~_HTTP_XH_TRANSFER_ENCODING_CHUNKED; +} + +void +http_response_send_headers (http_response h, ...) +{ + va_list args; + const char *k, *v; + + /* HTTP/0.9? No response line or headers. */ + if (h->request->is_http09) return; + + va_start (args, h); + while ((k = va_arg (args, const char *)) != 0) + { + v = va_arg (args, const char *); + http_response_send_header (h, k, v); + } + va_end (args); +} + +int +http_response_end_headers (http_response h) +{ + int close = 1; + + /* HTTP/0.9? No response line or headers. */ + if (h->request->is_http09) + goto out; + + /* Send any remaining headers. */ + if (h->extra_headers & _HTTP_XH_SERVER) + http_response_send_header (h, "Server", servername); + +#if HAVE_STRFTIME && HAVE_GMTIME + /* See RFC 2616 section 3.3.1. */ + if (h->extra_headers & _HTTP_XH_DATE) + { + time_t t; + char s[64]; + + t = http_request_time (h->request); + strftime (s, sizeof s, "%a, %d %b %Y %H:%M:%S GMT", gmtime (&t)); + http_response_send_header (h, "Date", s); + } +#endif + + /* This is not correct: see RFC 2616 section 3.4.1 for more details. */ + if (h->extra_headers & _HTTP_XH_CONTENT_TYPE) + http_response_send_header (h, "Content-Type", "text/plain"); + + if (h->extra_headers & _HTTP_XH_CONNECTION) + { + /* Deal with persistent connections (see RFC 2616 section 8.1). */ + + /* Server app must have sent a Content-Length header, otherwise + * the connection must close anyway. (RFC 2616 sections 4.4, 8.1.2.1) + */ + if ((h->extra_headers & _HTTP_XH_LENGTH_DEFINED) == + _HTTP_XH_LENGTH_DEFINED) + { + close = 1; + http_response_send_header (h, "Connection", "close"); + } + else + { + /* Otherwise look for the Connection: header in the request. */ + const char *conn_hdr + = http_request_get_header (h->request, "Connection"); + + if (conn_hdr) + { + if (strcasecmp (conn_hdr, "close") == 0) + { + close = 1; + http_response_send_header (h, "Connection", "close"); + } + else if (strcasecmp (conn_hdr, "keep-alive") == 0) + { + close = 0; + http_response_send_header (h, "Connection", "keep-alive"); + } + else + { + close = 1; + http_response_send_header (h, "Connection", "close"); + } + } + else + { + /* Assume close for HTTP/1.0 clients, keep-alive for HTTP/1.1 + * clients. + */ + if (h->request->major == 1 && h->request->minor >= 1) + { + close = 0; + http_response_send_header (h, "Connection", "keep-alive"); + } + else + { + close = 1; + http_response_send_header (h, "Connection", "close"); + } + } + } + } + + io_fputs (CRLF, h->io); + + out: + if (log_fp) do_logging (h); + + return close; +} + +void +http_response_write_chunk (http_response h, const char *data, int length) +{ + io_fprintf (h->io, "%X" CRLF, length); + io_fwrite (data, 1, length, h->io); + io_fprintf (h->io, CRLF); +} + +void +http_response_write_chunk_string (http_response h, const char *string) +{ + io_fprintf (h->io, "%X" CRLF "%s" CRLF, strlen (string), string); +} + +void +http_response_write_chunk_end (http_response h) +{ + io_fputs ("0" CRLF, h->io); +} + +FILE * +http_set_log_file (FILE *fp) +{ + return log_fp = fp; +} + +FILE * +http_get_log_file (void) +{ + return log_fp; +} + +static void +do_logging (http_response h) +{ +#if HAVE_STRFTIME && HAVE_GMTIME + char time_str[64]; + struct tm *tm; + time_t t; +#else + const char *time_str; +#endif + const char *referer; + const char *method; + const char *url; + const char *user_agent; + int major, minor; + struct sockaddr_in addr; + socklen_t addrlen; + const char *addr_str; + int port; + + /* Get the request time. */ +#if HAVE_STRFTIME && HAVE_GMTIME + t = http_request_time (h->request); + tm = gmtime (&t); + strftime (time_str, sizeof time_str, "%Y/%m/%d %H:%M:%S", tm); +#else + time_str = "- -"; +#endif + + /* Get the referer (sic) header. */ + if ((referer = http_request_get_header (h->request, "Referer")) == 0) + referer = "-"; + + /* Get the user agent header. */ + if ((user_agent = http_request_get_header (h->request, "User-Agent")) == 0) + user_agent = "-"; + + /* Get the URL. */ + method = http_request_method_string (h->request); + url = h->request->original_url; + http_request_version (h->request, &major, &minor); + + /* Get the address and port number of the peer (client). */ + addrlen = sizeof addr; + getpeername (io_fileno (h->io), (struct sockaddr *) &addr, &addrlen); + addr_str = inet_ntoa (addr.sin_addr); + port = ntohs (addr.sin_port); + + fprintf (log_fp, + "%s %s:%d \"%s %s HTTP/%d.%d\" %d %d \"%s\" \"%s\"\n", + time_str, addr_str, port, method, url, major, minor, + h->code, h->content_length, referer, user_agent); + fflush (log_fp); +} diff --git a/src/pthr_http.h b/src/pthr_http.h new file mode 100644 index 0000000..7be139e --- /dev/null +++ b/src/pthr_http.h @@ -0,0 +1,244 @@ +/* HTTP library. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_http.h,v 1.15 2002/12/08 13:41:07 rich Exp $ + */ + +#ifndef PTHR_HTTP_H +#define PTHR_HTTP_H + +#include +#include + +#include +#include + +#include +#include +#include + +#include "pthr_pseudothread.h" +#include "pthr_iolib.h" + +struct http_request; +typedef struct http_request *http_request; + +struct http_response; +typedef struct http_response *http_response; + +/* A vector of struct http_header is returned from the + * HTTP_REQUEST_GET_HEADERS call. + */ +struct http_header { const char *key, *value; }; + +/* Supported methods. */ +#define HTTP_METHOD_GET 1 +#define HTTP_METHOD_HEAD 2 +#define HTTP_METHOD_POST 3 + +/* Function: http_get_servername - get and set the server name string + * Function: http_set_servername + * + * Get and set the server name (which is sent in the @code{Server}) + * header by the server when it responds to requests. + * + * The default string is @code{pthrlib-httpd/version}. + * + * See also: @ref{new_http_request(3)}, @ref{new_http_response(3)}, + * @ref{new_cgi(3)}. + */ +const char *http_get_servername (void); +const char *http_set_servername (const char *new_server_name); + +/* Function: new_http_request - functions for parsing HTTP requests + * Function: http_request_time + * Function: http_request_get_url + * Function: http_request_set_url + * Function: http_request_path + * Function: http_request_query_string + * Function: http_request_method + * Function: http_request_method_string + * Function: http_request_is_HEAD + * Function: http_request_version + * Function: http_request_nr_headers + * Function: http_request_get_headers + * Function: http_request_get_header + * Function: http_request_get_cookie + * + * These functions allow you to efficiently parse incoming + * HTTP requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1 + * clients. The request parser understands GET, HEAD and POST + * requests and conforms as far as possible to RFC 2616. + * + * @code{new_http_request} creates a new request object, parsing + * the incoming request on the given @code{io_handle}. If the + * stream closes at the beginning of the request the function + * returns @code{NULL}. If the request is faulty, then the + * library prints a message to syslog and throws an exception + * by calling @ref{pth_die(3)}. Otherwise it initializes a complete + * @code{http_request} object and returns it. + * + * @code{http_request_time} returns the timestamp of the incoming + * request. + * + * @code{http_request_get_url} returns the complete URL of the request. + * + * @code{http_request_path} returns just the path component of + * the URL (ie. without the query string if there was one). + * @code{http_request_query_string} returns just the query string + * (for GET requests only). Do not do your own parsing of query + * strings: there is a CGI library built into pthrlib (see: + * @ref{new_cgi(3)}). + * + * @code{http_request_set_url} updates the URL (and hence path + * and query string). It is used by servers which support internal + * redirects, such as @code{rws}. + * + * @code{http_request_method} returns the method, one of + * @code{HTTP_METHOD_GET}, @code{HTTP_METHOD_HEAD} or + * @code{HTTP_METHOD_POST}. @code{http_request_is_HEAD} is + * just a quick way of testing if the method is a HEAD + * request. @code{http_request_method_string} returns the + * method as a string rather than a coded number. + * + * @code{http_request_version} returns the major and minor + * numbers of the HTTP request (eg. major = 1, minor = 0 for + * a HTTP/1.0 request). + * + * @code{http_request_nr_headers}, @code{http_request_get_headers} + * and @code{http_request_get_header} return the number of + * HTTP headers, the list of HTTP headers and a particular + * HTTP header (if it exists). @code{http_request_get_headers} + * returns a @code{vector} or @code{struct http_header}. This + * structure contains at least two fields called @code{key} and + * @code{value}. HTTP header keys are case insensitive when + * searching, and you will find that the list of keys returned + * by @code{http_request_get_headers} has been converted to + * lowercase. + * + * @code{http_request_get_cookie} gets a named browser cookie + * sent in the request. It returns the value of the cookie + * or @code{NULL} if no such named cookie was sent by the browser. + * + * To send a cookie to the browser, you should generate and send + * a @code{Set-Cookie} header with the appropriate content. Note + * that not all browsers support cookies. + * + * See also: @ref{new_http_response(3)}, @ref{new_cgi(3)}, + * @ref{new_pseudothread(3)}, @ref{io_fdopen(3)}, RFC 2616. + */ +extern http_request new_http_request (pool, io_handle); +extern time_t http_request_time (http_request); +extern const char *http_request_get_url (http_request); +extern void http_request_set_url (http_request, const char *new_url); +extern const char *http_request_path (http_request); +extern const char *http_request_query_string (http_request); +extern int http_request_method (http_request); +extern const char *http_request_method_string (http_request); +extern int http_request_is_HEAD (http_request); +extern void http_request_version (http_request, int *major, int *minor); +extern int http_request_nr_headers (http_request); +extern vector http_request_get_headers (http_request); +extern const char *http_request_get_header (http_request h, const char *key); +extern const char *http_request_get_cookie (http_request h, const char *key); + +/* Function: new_http_response - functions for sending HTTP responses + * Function: http_response_send_header + * Function: http_response_send_headers + * Function: http_response_end_headers + * Function: http_response_write_chunk + * Function: http_response_write_chunk_string + * Function: http_response_write_chunk_end + * + * These functions allow you to efficiently generate outgoing HTTP + * responses. + * + * @code{new_http_response} generates a new HTTP response object and + * returns it. @code{code} is the HTTP response code (see RFC 2616 + * for a list of codes), and @code{msg} is the HTTP response message. + * + * @code{http_response_send_header} sends a single HTTP header back + * to the client. The header is constructed by concatenating + * @code{key}, @code{": "}, @code{value} and @code{CR LF}. + * + * @code{http_response_send_headers} sends back several headers in + * a single call. The arguments to this function are a list of + * @code{key}, @code{value} pairs followed by a single @code{NULL} + * argument which terminates the list. + * + * @code{http_response_end_headers} ends the header list. It causes + * the code to emit any missing-but-required headers and then send + * the final @code{CR LF} characters. + * + * @code{http_response_write_chunk}, @code{http_response_write_chunk_string} + * and @code{http_response_write_chunk_end} + * allow the caller to use chunked encoding, which is an + * alternative to sending the @code{Content-Length} header. To enable + * chunked encoding, the application should first call + * @code{http_response_send_header (h, "Transfer-Encoding", "chunked");}. + * Then, instead of writing directly to @code{io} to send data, + * the application should call @code{http_response_write_chunk} + * or @code{http_response_write_chunk_string} to write each block + * (or string) of data. At the end of the data, the application + * should call @code{http_response_write_chunk_end}. + * (Steve Atkins implemented chunked encoding). + * + * See also: @ref{new_http_request(3)}, @ref{new_cgi(3)}, + * @ref{new_pseudothread(3)}, @ref{io_fdopen(3)}, RFC 2616. + */ +extern http_response new_http_response (pool, http_request, io_handle, int code, const char *msg); +extern void http_response_send_header (http_response, const char *key, const char *value); +extern void http_response_send_headers (http_response, ...); +extern int http_response_end_headers (http_response h); +extern void http_response_write_chunk (http_response, const char *data, int length); +extern void http_response_write_chunk_string (http_response, const char *string); +extern void http_response_write_chunk_end (http_response); + +/* Function: http_set_log_file - enable HTTP logs on file pointer + * Function: http_get_log_file + * + * The @code{FILE *fp} argument to @code{http_set_log_file} sets + * the file pointer on which HTTP logs are generated. To disable + * logging, set @code{fp} to @code{NULL}. The function returns + * @code{fp}. + * + * @code{http_get_log_file} returns the current file pointer + * or @code{NULL} if logging is disabled. + * + * The default is that logging is disabled. + * + * Currently log messages are generated at the end of the + * HTTP response headers and have the following fixed format: + * + * YYYY/MM/DD HH:MM ip:port - "METHOD URL HTTP/x.y" CODE length "Referer" "User Agent" + * + * The first "-" is intended to store the HTTP auth username, when + * HTTP authorization is supported by the library. The "length" + * field is only known if the caller sends back a "Content-Length" + * header. Otherwise 0 is printed in that position. + * + * Bugs: Log format should be customizable. It should be possible + * (optionally, of course) to look up the IP address and print + * a hostname. + * + * See also: @ref{new_http_request(3)}, @ref{new_cgi(3)}, + * @ref{new_pseudothread(3)}, @ref{io_fdopen(3)}, RFC 2616. + */ +extern FILE *http_set_log_file (FILE *fp); +extern FILE *http_get_log_file (void); + +#endif /* PTHR_HTTP_H */ diff --git a/src/pthr_iolib.c b/src/pthr_iolib.c new file mode 100644 index 0000000..37fc368 --- /dev/null +++ b/src/pthr_iolib.c @@ -0,0 +1,569 @@ +/* A small buffered I/O library. + * - by Richard W.M. Jones . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_iolib.c,v 1.9 2003/02/02 18:05:31 rich Exp $ + */ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_SYS_TYPE_H +#include +#endif + +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_ALLOCA_H +#include +#endif + +#include + +#include "pthr_iolib.h" +#include "pthr_pseudothread.h" + +struct io_handle +{ + /* Pool for memory allocation, etc. */ + pool pool; + + /* The underlying socket. */ + int sock; + + /* The incoming buffer. */ + char *inbuf; /* The actual buffer. */ + char *inbufpos; /* Current position within buffer. */ + int inbuflen; /* Number of bytes left to read. */ + + int inbufcount; /* Total number of bytes read. */ + + /* The outgoing buffer. */ + char *outbuf; /* The actual buffer. */ + char *outbufpos; /* Current position within buffer. */ + int outbuffree; /* Number of bytes free left in buffer. */ + + int outbufcount; /* Total number of bytes written. */ + + int outbufmode; /* Output buffer mode. */ +}; + +static void _flush (io_handle io, int ignore_errors); +static void _err (io_handle io, const char *msg) __attribute__((noreturn)); +static void _do_close (io_handle io); + +io_handle +io_fdopen (int sock) +{ + io_handle io; + pool pool = new_subpool (pth_get_pool (current_pth)); + + io = pmalloc (pool, sizeof *io); + + io->inbuf = pmalloc (pool, IOLIB_INPUT_BUFFER_SIZE * sizeof (char)); + + io->outbuf = pmalloc (pool, IOLIB_OUTPUT_BUFFER_SIZE * sizeof (char)); + + io->pool = pool; + io->sock = sock; + + io->inbufpos = &io->inbuf[IOLIB_INPUT_BUFFER_SIZE]; /* Let ungetc work. */ + io->inbuflen = 0; + io->inbufcount = 0; + io->outbufpos = io->outbuf; + io->outbuffree = IOLIB_OUTPUT_BUFFER_SIZE; + io->outbufcount = 0; + io->outbufmode = IO_MODE_LINE_BUFFERED; + + /* Register to automagically close this I/O handle when we + * exit this thread. + */ + pool_register_cleanup_fn (pool, (void (*)(void *)) _do_close, io); + + return io; +} + +inline int +io_fflush (io_handle io) +{ + if (io->outbufpos > io->outbuf) + _flush (io, 0); + + return 0; +} + +void +_do_close (io_handle io) +{ + /* Flush the buffer, but don't worry too much about errors. */ + if (io->outbufpos > io->outbuf) + _flush (io, 1); + + /* Close underlying socket. */ + if (close (io->sock) < 0) _err (io, "close"); +} + +void +io_fclose (io_handle io) +{ + /* Deleting the subpool containing io causes the _do_close callback + * handler to run which actually closes the socket. The memory + * containing io is also freed. + */ + delete_pool (io->pool); +} + +int +io_fgetc (io_handle io) +{ + int r; + + io_fflush (io); + + next: + /* Satify this from the input buffer? */ + if (io->inbuflen > 0) + { + int c = *io->inbufpos; + io->inbufpos++; + io->inbuflen--; + return c; + } + + /* Refill the input buffer from the socket. */ + r = pth_read (io->sock, io->inbuf, IOLIB_INPUT_BUFFER_SIZE * sizeof (char)); + if (r < 0) _err (io, "read"); + + io->inbufpos = io->inbuf; + io->inbuflen = r; + io->inbufcount += r; + + /* End of buffer? */ + if (r == 0) return -1; + + /* Return the next character. */ + goto next; +} + +char * +io_fgets (char *s, int max_size, io_handle io, int store_eol) +{ + int n = 0; + + io_fflush (io); + + while (n < max_size - 1) + { + int c = io_fgetc (io); + + /* End of file? */ + if (c == -1) + { + s[n] = '\0'; + return n > 0 ? s : 0; + } + + s[n++] = c; + + /* End of line? */ + if (c == '\n') + break; + } + + /* Remove the trailing CR LF? */ + if (!store_eol) + { + while (n > 0 && (s[n-1] == '\n' || s[n-1] == '\r')) + n--; + } + + /* Terminate the line. */ + s[n] = '\0'; + return s; +} + +int +io_ungetc (int c, io_handle io) +{ + if (io->inbufpos > io->inbuf) + { + io->inbufpos--; + *io->inbufpos = c; + io->inbuflen++; + return c; + } + + return -1; /* No room left in input buffer. */ +} + +size_t +io_fread (void *ptr, size_t size, size_t nmemb, io_handle io) +{ + size_t n = size * nmemb, c = 0; + int i; + char *cptr = (char *) ptr; + + io_fflush (io); + + /* Satisfy as much as possible from the input buffer. */ + i = n > io->inbuflen ? io->inbuflen : n; + memcpy (cptr, io->inbufpos, i * sizeof (char)); + n -= i; + c += i; + cptr += i; + io->inbuflen -= i; + io->inbufpos += i; + + /* Read the rest directly from the socket until we have either + * satisfied the request or there is an EOF. + */ + while (n > 0) + { + int r = pth_read (io->sock, cptr, n * sizeof (char)); + if (r < 0) _err (io, "read"); + + if (r == 0) /* End of file. */ + return c; + + n -= r; + c += r; + cptr += r; + io->inbufcount += r; + } + + return c; +} + +inline int +io_fputc (int c, io_handle io) +{ + again: + if (io->outbuffree > 0) + { + *io->outbufpos = c; + io->outbufpos++; + io->outbuffree--; + + /* Flush the buffer after each character or line. */ + if (io->outbufmode == IO_MODE_UNBUFFERED || + (io->outbufmode == IO_MODE_LINE_BUFFERED && c == '\n')) + _flush (io, 0); + + return c; + } + + /* We need to flush the output buffer and try again. */ + _flush (io, 0); + goto again; +} + +int +io_fputs (const char *s, io_handle io) +{ + while (*s) + { + io_fputc (*s, io); + s++; + } + + /* According to the manual page, fputs returns a non-negative number + * on success or EOF on error. + */ + return 1; +} + +int +io_fprintf (io_handle io, const char *fs, ...) +{ + va_list args; + int r, n = 4096; + char *s = alloca (n); + + if (s == 0) abort (); + + va_start (args, fs); + r = vsnprintf (s, n, fs, args); + va_end (args); + + /* Did the string get truncated? If so, we can allocate the + * correct sized buffer directly from the pool. + * + * Note: according to the manual page, a return value of -1 indicates + * that the string was truncated. We have found that this is not + * actually true however. In fact, the library seems to return the + * number of characters which would have been written into the string + * (ie. r > n). + */ + if (r > n) + { + n = r + 1; + s = pmalloc (io->pool, n); + + va_start (args, fs); + r = vsnprintf (s, n, fs, args); + va_end (args); + } + + io_fputs (s, io); + + return r; +} + +size_t +io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle io) +{ + size_t n = size * nmemb, c = 0; + char *cptr = (char *) ptr; + + /* Flush out any existing data. */ + io_fflush (io); + + /* Write the data directly to the socket. */ + while (n > 0) + { + int r = pth_write (io->sock, cptr, n * sizeof (char)); + if (r < 0) _err (io, "write"); + + n -= r; + cptr += r; + c += r; + io->outbufcount += r; + } + + return c; +} + +io_handle +io_popen (const char *command, const char *type) +{ + int fd[2], s; + int pid; + int mode = 0; /* 0 for read, 1 for write. */ + io_handle io; + + if (strcmp (type, "r") == 0) + mode = 0; + else if (strcmp (type, "w") == 0) + mode = 1; + else + abort (); + + /* Create a pipe between parent and child. */ + if (pipe (fd) < 0) { perror ("pipe"); return 0; } + + /* Fork and connect up the pipe appropriately. */ + pid = fork (); + if (pid < 0) { close (fd[0]); close (fd[1]); perror ("fork"); return 0; } + else if (pid > 0) /* Parent process. */ + { + if (mode == 0) + { + close (fd[1]); + s = fd[0]; + } + else + { + close (fd[0]); + s = fd[1]; + } + + /* Set the file descriptor to non-blocking. */ + if (fcntl (s, F_SETFL, O_NONBLOCK) < 0) abort (); + + io = io_fdopen (s); + if (io == 0) + { + close (s); + return 0; + } + + return io; + } + else /* Child process. */ + { + if (mode == 0) + { + close (fd[0]); + if (fd[1] != 1) + { + dup2 (fd[1], 1); + close (fd[1]); + } + } + else + { + close (fd[1]); + if (fd[0] != 0) + { + dup2 (fd[0], 0); + close (fd[0]); + } + } + + /* Run the child command. */ + _exit (system (command)); + + /*NOTREACHED*/ + return 0; + } +} + +void +io_pclose (io_handle io) +{ + /* Close connection. */ + io_fclose (io); + + /* Wait for child to exit. */ + wait (NULL); +} + +int +io_copy (io_handle from_io, io_handle to_io, int len) +{ + int written_len = 0; + + /* Synchronize the input handle. */ + io_fflush (from_io); + + while (len != 0) + { + int n; + + /* Read bytes directly from the input buffer. */ + if (from_io->inbuflen == 0) + { + int c; + + /* Need to refill the input buffer. We do this by reading a single + * character and the "ungetting" it back into the buffer. The + * io_ungetc call is guaranteed to work in this case. + */ + c = io_fgetc (from_io); + if (c == -1) return written_len; + io_ungetc (c, from_io); + } + + /* Decide how many bytes to read. */ + if (len == -1) + n = from_io->inbuflen; + else + { + if (len >= from_io->inbuflen) + n = from_io->inbuflen; + else + n = len; + } + + /* Write it. */ + io_fwrite (from_io->inbufpos, 1, n, to_io); + written_len += n; + if (len > 0) len -= n; + from_io->inbufpos += n; + from_io->inbuflen -= n; + } + + return written_len; +} + +void +io_setbufmode (io_handle io, int mode) +{ + io->outbufmode = mode; +} + +static void +_flush (io_handle io, int ignore_errors) +{ + size_t n = io->outbufpos - io->outbuf; + char *cptr = io->outbuf; + + /* Write the data to the socket. */ + while (n > 0) + { + int r = pth_write (io->sock, cptr, n * sizeof (char)); + if (r < 0) + { + if (!ignore_errors) + _err (io, "write"); + else + break; + } + + n -= r; + cptr += r; + io->outbufcount += r; + } + + /* Reset the output buffer. */ + io->outbufpos = io->outbuf; + io->outbuffree = IOLIB_OUTPUT_BUFFER_SIZE; +} + +int +io_fileno (io_handle io) +{ + return io->sock; +} + +int +io_get_inbufcount (io_handle io) +{ + return io->inbufcount; +} + +int +io_get_outbufcount (io_handle io) +{ + return io->outbufcount; +} + +static void +_err (io_handle io, const char *msg) +{ + /* I commented out the following line because, strangely, it seems to + * cause some sort of memory corruption bug. If there is a bug, it's + * probably related to either stack overflow or the stack swapping / + * context switching stuff. Whatever. Removed it for safety. + * -- RWMJ 2000/12/05. + */ +#if 0 + perror (msg); +#endif + + /* Should errors be catchable? ie. Should we call pth_die here? And + * if so, what are the consequences of doing it? + * -- RWMJ 2001/05/30. + */ + pth_exit (); +} diff --git a/src/pthr_iolib.h b/src/pthr_iolib.h new file mode 100644 index 0000000..010356c --- /dev/null +++ b/src/pthr_iolib.h @@ -0,0 +1,137 @@ +/* A small buffered I/O library. + * - by Richard W.M. Jones . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_iolib.h,v 1.5 2002/12/01 14:29:28 rich Exp $ + */ + +#ifndef PTHR_IOLIB_H +#define PTHR_IOLIB_H + +#include +#include + +#include + +#include + +#include "pthr_pseudothread.h" + +#define IOLIB_INPUT_BUFFER_SIZE 1024 +#define IOLIB_OUTPUT_BUFFER_SIZE 1024 + +struct io_handle; +typedef struct io_handle *io_handle; + +/* Buffer modes. */ +#define IO_MODE_LINE_BUFFERED 0 /* Default. */ +#define IO_MODE_UNBUFFERED 1 +#define IO_MODE_FULLY_BUFFERED 2 + +/* Function: io_fdopen - A buffered I/O library + * Function: io_fclose + * Function: io_fgetc + * Function: io_fgets + * Function: io_ungetc + * Function: io_fread + * Function: io_fputc + * Function: io_fputs + * Function: io_fprintf + * Function: io_fwrite + * Function: io_fflush + * Function: io_fileno + * Function: io_popen + * Function: io_pclose + * Function: io_copy + * Function: io_setbufmode + * Function: io_get_inbufcount + * Function: io_get_outbufcount + * + * The @code{io_*} functions replace the normal blocking C library + * @code{f*} functions with equivalents which work on non-blocking + * pseudothread file descriptors. + * + * All of the functions in the synopsis above work identically + * to the C library equivalents, except where documented below. + * + * @code{io_fdopen} associates a socket @code{sock} with a + * I/O handle. The association cannot be broken later (so use + * @ref{dup(2)} if you wish to later take back control of the + * underlying socket). If either the current thread exits + * or @code{io_fclose} is called, the underlying socket is + * closed (with @ref{close(2)}). + * + * @code{io_fclose} flushes all unwritten data out of the socket + * and closes it. + * + * @code{io_fgets} operates similarly to the ordinary C library + * function @ref{fgets(3)}, except that it contains a useful + * fourth argument, @code{store_eol}. If this fourth argument is + * false, then the end of line characters (@code{CR}, @code{CR LF} + * or @code{LF}) are stripped from the string before it is stored. + * + * @code{io_copy} copies @code{len} bytes from @code{from_io} + * to @code{to_io}. If @code{len} equals -1 then bytes are + * copied from @code{from_io} until end of file is reached. + * If @code{len} equals 0, then no bytes are copied. The + * number of bytes actually copied is returned. + * + * @code{io_setbufmode} sets the output buffer mode, and works + * completely differently to the ordinary C library function + * @ref{setbufmode(3)}. The three mode arguments possible are: + * @code{IO_MODE_LINE_BUFFERED}, @code{IO_MODE_UNBUFFERED} and + * @code{IO_MODE_FULLY_BUFFERED}, and these correspond to line + * buffering, no buffering and full (block) buffering. + * + * @code{io_get_inbufcount} and @code{io_get_outbufcount} return + * the number of characters read and written on the socket since + * the socket was associated with the I/O object. + * + * See also: + * @ref{fgetc(3)}, + * @ref{fgets(3)}, + * @ref{ungetc(3)}, + * @ref{fread(3)}, + * @ref{fputc(3)}, + * @ref{fputs(3)}, + * @ref{fprintf(3)}, + * @ref{fwrite(3)}, + * @ref{fflush(3)}, + * @ref{fileno(3)}, + * @ref{popen(3)}, + * @ref{pclose(3)}, + * @ref{pth_exit(3)}. + */ +extern io_handle io_fdopen (int sock); +extern void io_fclose (io_handle); +extern int io_fgetc (io_handle); +extern char *io_fgets (char *s, int max_size, io_handle, int store_eol); +extern int io_ungetc (int c, io_handle); +extern size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle); +extern int io_fputc (int c, io_handle); +extern int io_fputs (const char *s, io_handle); +extern int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3))); +extern size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle); +extern int io_fflush (io_handle); +extern int io_fileno (io_handle); +extern io_handle io_popen (const char *command, const char *mode); +extern void io_pclose (io_handle); +extern int io_copy (io_handle from_io, io_handle to_io, int len); +extern void io_setbufmode (io_handle, int mode); +extern int io_get_inbufcount (io_handle); +extern int io_get_outbufcount (io_handle); + +#endif /* PTHR_IOLIB_H */ diff --git a/src/pthr_listener.c b/src/pthr_listener.c new file mode 100644 index 0000000..cd76379 --- /dev/null +++ b/src/pthr_listener.c @@ -0,0 +1,104 @@ +/* Listener thread. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_listener.c,v 1.5 2002/12/08 13:41:07 rich Exp $ + */ + +#include "config.h" + +#include +#include + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#ifdef HAVE_SYSLOG_H +#include +#endif + +#include "pthr_pseudothread.h" +#include "pthr_listener.h" + +struct listener +{ + pseudothread pth; + int sock; + void (*processor_fn) (int sock, void *data); + void *data; +}; + +static void run (void *vp); + +listener +new_listener (int sock, + void (*processor_fn) (int sock, void *data), void *data) +{ + pool pool; + listener p; + + pool = new_pool (); + p = pmalloc (pool, sizeof *p); + + p->sock = sock; + p->processor_fn = processor_fn; + p->data = data; + p->pth = new_pseudothread (pool, run, p, "listener"); + + pth_start (p->pth); + + return p; +} + +static void +run (void *vp) +{ + listener p = (listener) vp; + + for (;;) + { + struct sockaddr_in addr; + int sz, ns; + + /* Wait for the new connection. */ + sz = sizeof addr; + ns = pth_accept (p->sock, (struct sockaddr *) &addr, &sz); + + if (ns < 0) { perror ("accept"); continue; } + + /* Set the new socket to non-blocking. */ + if (fcntl (ns, F_SETFL, O_NONBLOCK) < 0) abort (); + + /* Create a new processor thread to handle this connection. */ + p->processor_fn (ns, p->data); + } +} diff --git a/src/pthr_listener.h b/src/pthr_listener.h new file mode 100644 index 0000000..beeeab6 --- /dev/null +++ b/src/pthr_listener.h @@ -0,0 +1,31 @@ +/* Listener thread. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_listener.h,v 1.3 2002/08/21 10:42:19 rich Exp $ + */ + +#ifndef PTHR_LISTENER_H +#define PTHR_LISTENER_H + +#include "pthr_reactor.h" +#include "pthr_pseudothread.h" + +struct listener; +typedef struct listener *listener; + +extern listener new_listener (int sock, void (*processor_fn) (int sock, void *data), void *data); + +#endif /* PTHR_LISTENER_H */ diff --git a/src/pthr_mutex.c b/src/pthr_mutex.c new file mode 100644 index 0000000..fd7ceb8 --- /dev/null +++ b/src/pthr_mutex.c @@ -0,0 +1,167 @@ +/* Mutex locks. + * by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_mutex.c,v 1.4 2003/02/02 18:05:31 rich Exp $ + */ + +#include "config.h" + +#ifdef HAVE_ASSERT_H +#include +#endif + +#ifndef __sun__ + +#include + +#include "pthr_pseudothread.h" +#include "pthr_wait_queue.h" + +#else +/* SunOS annoyingly defines a 'struct mutex' type, and even worse defines + * it as soon as you include . + */ +#define POOL_H +struct pool; +typedef struct pool *pool; +extern pool new_subpool (pool); +extern void delete_pool (pool); +extern void *pmalloc (pool, unsigned n); +extern void pool_register_cleanup_fn (pool, void (*fn) (void *), void *); + +#define PTHR_PSEUDOTHREAD_H +struct pseudothread; +typedef struct pseudothread *pseudothread; +extern pseudothread current_pth; +extern pool pth_get_pool (pseudothread pth); + +#define PTHR_WAIT_QUEUE_H +struct wait_queue; +typedef struct wait_queue *wait_queue; +extern wait_queue new_wait_queue (pool); +extern void wq_wake_up (wait_queue); +extern void wq_wake_up_one (wait_queue); +extern void wq_sleep_on (wait_queue); +extern int wq_nr_sleepers (wait_queue); +#endif + +#include "pthr_mutex.h" + +static void _do_release (void *); +static void _delete_mutex (void *); + +struct mutex +{ + pseudothread pth; /* Pseudothread which is holding the lock, or null. */ + wait_queue wq; /* Queue of threads waiting to enter. */ + pool pool; /* Subpool of pth pool which holds the lock. If the + * thread exits without releasing the lock, then + * the subpool is deleted, which causes our callback + * to run, releasing the lock. + */ +}; + +mutex +new_mutex (pool p) +{ + mutex m = pmalloc (p, sizeof *m); + + m->pth = 0; + m->pool = 0; + m->wq = new_wait_queue (p); + + /* The purpose of this cleanup is just to check that the mutex + * isn't released with threads in the critical section. + */ + pool_register_cleanup_fn (p, _delete_mutex, m); + + return m; +} + +static void +_delete_mutex (void *vm) +{ + mutex m = (mutex) vm; + assert (m->pth == 0); +} + +/* This function is identical to MUTEX_ENTER, except that it does not + * block if the lock is held by another thread. The function + * returns TRUE if the lock was successfully acquired, or FALSE + * if another thread is holding it. + */ +inline int +mutex_try_enter (mutex m) +{ + if (m->pth == 0) + { + /* Create a subpool. If the thread exits early, then this subpool + * with be deleted implicitly. If, on the other hand, we release + * the lock in RWLOCK_LEAVE, then we will delete this pool + * explicitly. Either way, _DO_RELEASE will be called. + */ + pool pool = new_subpool (pth_get_pool (current_pth)); + + /* Register _DO_RELEASE to run when the subpool is deleted. */ + pool_register_cleanup_fn (pool, _do_release, m); + + m->pth = current_pth; + m->pool = pool; + return 1; + } + else + return 0; +} + +/* Enter a critical section. This function blocks until the lock is + * acquired. + */ +void +mutex_enter (mutex m) +{ + while (mutex_try_enter (m) == 0) + wq_sleep_on (m->wq); +} + +/* Leave a critical section. + */ +void +mutex_leave (mutex m) +{ + assert (m->pth == current_pth); + + /* Force _DO_RELEASE to run. */ + delete_pool (m->pool); +} + +static void +_do_release (void *vm) +{ + mutex m = (mutex) vm; + + m->pth = 0; + m->pool = 0; + + /* Anyone waiting to enter? */ + if (wq_nr_sleepers (m->wq) > 0) wq_wake_up_one (m->wq); +} + +int +mutex_nr_sleepers (mutex m) +{ + return wq_nr_sleepers (m->wq); +} diff --git a/src/pthr_mutex.h b/src/pthr_mutex.h new file mode 100644 index 0000000..8b78202 --- /dev/null +++ b/src/pthr_mutex.h @@ -0,0 +1,74 @@ +/* Simple mutex locks for pthrlib. + * by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_mutex.h,v 1.4 2002/12/01 14:29:28 rich Exp $ + */ + +#ifndef PTHR_MUTEX_H +#define PTHR_MUTEX_H + +#include + +#include "pthr_pseudothread.h" +#include "pthr_wait_queue.h" + +struct mutex; +typedef struct mutex *mutex; + +/* Function: new_mutex - mutual exclusion (mutex) locks + * Function: mutex_enter + * Function: mutex_leave + * Function: mutex_try_enter + * Function: mutex_nr_sleepers + * + * Mutex locks are simple: at most one pseudothread may enter the + * critical area protected by the lock at once. If instead you + * wish multiple reader / single writer semantics, then please + * see @ref{new_rwlock(3)}. + * + * Mutex locks are automatically released if they are being held when + * the thread exits. + * + * Note that there are possible deadlocks when using locks. To + * avoid deadlocks, always ensure every thread acquires locks + * in the same order. + * + * @code{new_mutex} creates a new mutex object. + * + * @code{mutex_enter} and @code{mutex_leave} enter and leave + * the critical section. Only one thread can run at a time + * inside the critical section. + * + * @code{mutex_try_enter} is identical to @code{mutex_enter} + * except that it does not block if the lock is held by another + * thread. The function returns true if the lock was successfully + * acquired, or false if another thread is currently holding it. + * + * @code{mutex_nr_sleepers} returns the number of threads which + * are queued up waiting to enter the critical section. + * + * Bugs: A common mistake is to accidentally call @code{mutex_leave} + * when you are not holding the lock. This generally causes the + * library to crash. + */ +extern mutex new_mutex (pool); +extern void mutex_enter (mutex); +extern void mutex_leave (mutex); +extern int mutex_try_enter (mutex); +extern int mutex_nr_sleepers (mutex); + +#endif /* PTHR_MUTEX_H */ diff --git a/src/pthr_pseudothread.c b/src/pthr_pseudothread.c new file mode 100644 index 0000000..b641c8c --- /dev/null +++ b/src/pthr_pseudothread.c @@ -0,0 +1,1024 @@ +/* Pseudothread handler. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_pseudothread.c,v 1.22 2003/02/05 22:13:32 rich Exp $ + */ + +#include "config.h" + +#include +#include + +#ifdef HAVE_ASSERT_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SETJMP_H +#include +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#include +#include +#include + +#include "pthr_reactor.h" +#include "pthr_context.h" +#include "pthr_stack.h" +#include "pthr_pseudothread.h" + +struct pseudothread +{ + /* Thread context and calling (reactor) context. */ + mctx_t thread_ctx, calling_ctx; + + /* Thread number. */ + int n; + + /* Pointer to thread stack and size. */ + void *stack; + int stack_size; + + /* Alarm handling. */ + int alarm_received; + reactor_timer alarm_timer; + + /* Pool for memory allocations in this thread. */ + pool pool; + + /* Name of the thread. */ + const char *name; + + /* Used to implement pth_exit. */ + jmp_buf exit_jmp; + + /* Used to implement pth_die. */ + vector exception_jmp_vec; + const char *exception_msg; + + /* Used to implement pth_poll, pth_select. */ + int poll_timeout; + + /* Start point and data for thread. */ + void (*run) (void *); + void *data; + + /* LANGUAGE environment variable for this thread. If null, then + * the variable is unset in the thread. + */ + const char *lang; + + /* TZ environment variable for this thread. If null, then the + * variable is unset in the thread. + */ + const char *tz; +}; + +/* Currently running pseudothread. */ +pseudothread current_pth = 0; + +/* Global list of threads. */ +static vector threads = 0; + +/* Default stack size, in bytes. */ +static int default_stack_size = 65536; + +static void block (int sock, int ops); +static void return_from_block (int sock, int events, void *); +static void _sleep (int timeout); +static void return_from_sleep (void *); +static void _poll (struct pollfd *fds, unsigned int n, int timeout); +static void return_from_poll (int sock, int events, void *); +static void return_from_poll_timeout (void *); +static void return_from_alarm (void *); + +static void thread_trampoline (void *vpth); + +static void pseudothread_init (void) __attribute__ ((constructor)); + +static void +pseudothread_init () +{ +#ifdef __OpenBSD__ + /* OpenBSD doesn't call constructors in the correct order. */ + pool global_pool = new_pool (); +#endif + threads = new_vector (global_pool, struct pseudothread *); +} + +int +pseudothread_set_stack_size (int size) +{ + return default_stack_size = size; +} + +int +pseudothread_get_stack_size (void) +{ + return default_stack_size; +} + +pseudothread +new_pseudothread (pool pool, + void (*run) (void *), void *data, + const char *name) +{ + pseudothread pth; + void *stack_addr; + int i; + + /* Allocate space for the pseudothread. */ + pth = pcalloc (pool, 1, sizeof *pth); + + pth->run = run; + pth->data = data; + pth->pool = pool; + pth->name = name; + + /* Create a stack for this thread. */ + stack_addr = _pth_get_stack (default_stack_size); + if (stack_addr == 0) abort (); + pth->stack = stack_addr; + pth->stack_size = default_stack_size; + + /* Create a new thread context. */ + mctx_set (&pth->thread_ctx, + thread_trampoline, pth, + stack_addr, default_stack_size); + + /* Allocate space in the global threads list for this thread. */ + for (i = 0; i < vector_size (threads); ++i) + { + pseudothread p; + + vector_get (threads, i, p); + if (p == 0) + { + pth->n = i; + vector_replace (threads, i, pth); + goto done; + } + } + + pth->n = i; + vector_push_back (threads, pth); + + done: + return pth; +} + +static void +thread_trampoline (void *vpth) +{ + pseudothread pth = (pseudothread) vpth; + mctx_t calling_ctx; + void *stack; + int stack_size; + const struct pseudothread *null_thread = 0; + + /* Set up the current_pth before running user code. */ + current_pth = pth; + + if (setjmp (pth->exit_jmp) == 0) + pth->run (pth->data); + + /* We return here either when "run" finishes normally or after + * a longjmp caused by the pseudothread calling pth_exit. + */ + + calling_ctx = pth->calling_ctx; + + /* Remove the thread from the list of threads. */ + vector_replace (threads, pth->n, null_thread); + + /* Delete the pool and the stack. */ + stack = pth->stack; + stack_size = pth->stack_size; + delete_pool (pth->pool); + _pth_return_stack (stack, stack_size); + + /* Restore calling context (this never returns ...). */ + mctx_restore (&calling_ctx); +} + +void +pth_start (pseudothread pth) +{ + pseudothread old_pth = current_pth; + + /* Swap into the new context -- this actually calls thread_trampoline. */ + mctx_switch (&pth->calling_ctx, &pth->thread_ctx); + + /* Restore current_pth before returning into user code. */ + current_pth = old_pth; +} + +void +pth_set_name (const char *name) +{ + current_pth->name = name; +} + +const char * +pth_get_name (pseudothread pth) +{ + return pth->name; +} + +int +pth_get_thread_num (pseudothread pth) +{ + return pth->n; +} + +void +(*pth_get_run (pseudothread pth)) (void *) +{ + return pth->run; +} + +void * +pth_get_data (pseudothread pth) +{ + return pth->data; +} + +const char * +pth_get_language (pseudothread pth) +{ + return pth->lang; +} + +const char * +pth_get_tz (pseudothread pth) +{ + return pth->tz; +} + +void * +pth_get_stack (pseudothread pth) +{ + return pth->stack; +} + +int +pth_get_stack_size (pseudothread pth) +{ + return pth->stack_size; +} + +unsigned long +pth_get_PC (pseudothread pth) +{ + return mctx_get_PC (&pth->thread_ctx); +} + +unsigned long +pth_get_SP (pseudothread pth) +{ + return mctx_get_SP (&pth->thread_ctx); +} + +void +pth_exit () +{ + longjmp (current_pth->exit_jmp, 1); +} + +const char * +pth_catch (void (*fn) (void *), void *data) +{ + jmp_buf jb; + + if (setjmp (jb) == 0) + { + /* Register the exception handler. */ + if (current_pth->exception_jmp_vec == 0) + current_pth->exception_jmp_vec + = new_vector (current_pth->pool, jmp_buf); + vector_push_back (current_pth->exception_jmp_vec, jb); + + /* Run the function. */ + fn (data); + + /* No errors: pop the exception handler. */ + vector_pop_back (current_pth->exception_jmp_vec, jb); + + return 0; + } + + /* Exception was fired off. Return the message. */ + return current_pth->exception_msg; +} + +void +_pth_die (const char *msg, const char *filename, int lineno) +{ + /* If there is a surrounding exception handler registered, then + * jump directly to it. + */ + if (current_pth->exception_jmp_vec && + vector_size (current_pth->exception_jmp_vec) > 0) + { + jmp_buf jb; + + current_pth->exception_msg = msg; + vector_pop_back (current_pth->exception_jmp_vec, jb); + longjmp (jb, 1); + /*NOTREACHED*/ + } + + /* Otherwise: print the message and exit the pseudothread immediately. */ + fprintf (stderr, "%s:%d: %s\n", filename, lineno, msg); + pth_exit (); +} + +pool +pth_get_pool (pseudothread pth) +{ + return pth->pool; +} + +int +pth_accept (int s, struct sockaddr *addr, int *size) +{ + int r; + + do + { + block (s, REACTOR_READ); + + /* Accept the connection. */ + r = accept (s, addr, size); + } + while (r == -1 && errno == EWOULDBLOCK); + + return r; +} + +int +pth_connect (int s, struct sockaddr *addr, int size) +{ + int r, sz; + + /* Connect (NB. The socket had better have been set to non-blocking) */ + r = connect (s, addr, size); + + if (r == 0) return 0; /* Connected immediately. */ + else + { + if (errno == EINPROGRESS) + { + /* Wait for the socket to connect. */ + block (s, REACTOR_WRITE); + + /* Read the error code (see connect(2) man page for details). */ + sz = sizeof r; + if (getsockopt (s, SOL_SOCKET, SO_ERROR, &r, &sz) < 0) + return -1; + + /* What is the error code? */ + errno = r; + return r == 0 ? 0 : -1; + } + else + { + /* Some other type of error before blocking. */ + return -1; + } + } +} + +ssize_t +pth_read (int s, void *buf, size_t count) +{ + int r; + + again: + r = read (s, buf, count); + if (r == -1 && errno == EWOULDBLOCK) + { + block (s, REACTOR_READ); + goto again; + } + + return r; +} + +ssize_t +pth_write (int s, const void *buf, size_t count) +{ + int r; + + again: + r = write (s, buf, count); + if (r == -1 && errno == EWOULDBLOCK) + { + block (s, REACTOR_WRITE); + goto again; + } + + return r; +} + +int +pth_sleep (int seconds) +{ + _sleep (seconds * 1000); + return seconds; +} + +int +pth_nanosleep (const struct timespec *req, + struct timespec *rem) +{ + int timeout = req->tv_sec * 1000 + req->tv_nsec / 1000000; + + _sleep (timeout); + return 0; +} + +int +pth_millisleep (int millis) +{ + _sleep (millis); + return 0; +} + +void +pth_timeout (int seconds) +{ + /* Unregister any previously registered alarm. */ + if (current_pth->alarm_timer) + reactor_unset_timer_early (current_pth->alarm_timer); + current_pth->alarm_timer = 0; + + if (seconds != 0) + current_pth->alarm_timer = + reactor_set_timer (current_pth->pool, seconds * 1000, + return_from_alarm, current_pth); +} + +int +pth_send (int s, const void *msg, int len, unsigned int flags) +{ + int r; + + again: + r = send (s, msg, len, flags); + if (r == -1 && errno == EWOULDBLOCK) + { + block (s, REACTOR_WRITE); + goto again; + } + + return r; +} + +int +pth_sendto (int s, const void *msg, int len, unsigned int flags, + const struct sockaddr *to, int tolen) +{ + int r; + + again: + r = sendto (s, msg, len, flags, to, tolen); + if (r == -1 && errno == EWOULDBLOCK) + { + block (s, REACTOR_WRITE); + goto again; + } + + return r; +} + +int +pth_sendmsg (int s, const struct msghdr *msg, unsigned int flags) +{ + int r; + + again: + r = sendmsg (s, msg, flags); + if (r == -1 && errno == EWOULDBLOCK) + { + block (s, REACTOR_WRITE); + goto again; + } + + return r; +} + +int +pth_recv (int s, void *buf, int len, unsigned int flags) +{ + int r; + + again: + r = recv (s, buf, len, flags); + if (r == -1 && errno == EWOULDBLOCK) + { + block (s, REACTOR_READ); + goto again; + } + + return r; +} + +int +pth_recvfrom (int s, void *buf, int len, unsigned int flags, + struct sockaddr *from, int *fromlen) +{ + int r; + + again: + r = recvfrom (s, buf, len, flags, from, fromlen); + if (r == -1 && errno == EWOULDBLOCK) + { + block (s, REACTOR_READ); + goto again; + } + + return r; +} + +int +pth_recvmsg (int s, struct msghdr *msg, unsigned int flags) +{ + int r; + + again: + r = recvmsg (s, msg, flags); + if (r == -1 && errno == EWOULDBLOCK) + { + block (s, REACTOR_READ); + goto again; + } + + return r; +} + +/* NB. Although it may appear that this version of poll is + * inefficient because it makes extra real poll(2) system calls, + * in the case where there are many fds being polled, and they + * are frequently ready (ie. high load cases ...), this will + * be much more efficient than alternatives. + */ +int +pth_poll (struct pollfd *fds, unsigned int n, int timeout) +{ + int r; + + again: + r = poll (fds, n, 0); + if (r == 0) + { + _poll (fds, n, timeout); + if (current_pth->poll_timeout) return 0; + goto again; + } + + return r; +} + +/* Select is inefficiently implemented just as a library on top of + * the pth_poll call. Rewrite your code so it doesn't use pth_select. + */ +int +pth_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) +{ + pool p = new_subpool (current_pth->pool); + vector v = new_vector (p, struct pollfd); + int i, to, r; + struct pollfd pfd, *fds; + + /* Convert the sets into an array of poll descriptors. */ + for (i = 0; i < n; ++i) + { + if (readfds && FD_ISSET (i, readfds)) + { + pfd.fd = i; + pfd.events = POLLIN; + pfd.revents = 0; + vector_push_back (v, pfd); + } + if (writefds && FD_ISSET (i, writefds)) + { + pfd.fd = i; + pfd.events = POLLOUT; + pfd.revents = 0; + vector_push_back (v, pfd); + } + if (exceptfds && FD_ISSET (i, exceptfds)) + { + pfd.fd = i; + pfd.events = POLLERR; + pfd.revents = 0; + vector_push_back (v, pfd); + } + } + + /* Call the underlying poll function. */ + to = timeout ? timeout->tv_sec * 1000 + timeout->tv_usec / 1000 : -1; + vector_get_ptr (v, 0, fds); + n = vector_size (v); + + r = pth_poll (fds, n, to); + + /* Error. */ + if (r == -1) + { + delete_pool (p); + return -1; + } + + if (readfds) FD_ZERO (readfds); + if (writefds) FD_ZERO (writefds); + if (exceptfds) FD_ZERO (exceptfds); + + /* Timeout. */ + if (r == 0) + { + delete_pool (p); + return 0; + } + + /* Convert the returned events into sets. */ + for (i = 0; i < n; ++i) + { + if (fds[i].revents & POLLIN) + FD_SET (fds[i].fd, readfds); + if (fds[i].revents & POLLOUT) + FD_SET (fds[i].fd, writefds); + if (fds[i].revents & POLLERR) + FD_SET (fds[i].fd, exceptfds); + } + + delete_pool (p); + return r; +} + +int +pth_wait_readable (int fd) +{ + int r; + struct pollfd fds[1]; + + fds[0].fd = fd; + fds[0].events = POLLIN; + fds[0].revents = 0; /* Don't care. */ + + again: + r = poll (fds, 1, -1); + if (r == 0) + { + block (fd, REACTOR_READ); + goto again; + } + + return r >= 0 ? 1 : -1; +} + +int +pth_wait_writable (int fd) +{ + int r; + struct pollfd fds[1]; + + fds[0].fd = fd; + fds[0].events = POLLOUT; + fds[0].revents = 0; /* Don't care. */ + + again: + r = poll (fds, 1, -1); + if (r == 0) + { + block (fd, REACTOR_WRITE); + goto again; + } + + return r >= 0 ? 1 : -1; +} + +#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV) +#define do_setenv(name,value) setenv ((name), (value), 1) +#define do_unsetenv(name) unsetenv ((name)) +#elif defined(HAVE_PUTENV) +/* This implementation is ugly, but then putenv is an ugly function. */ + +/* This array maps environment variable names to the 'putenv'-ed env + * string. First time round we add a new env string to this array and + * putenv it into the environment. Subsequently we modify the env + * string. + */ +static struct putenv_map { + struct putenv_map *next; + const char *name; + char env[256]; +} *putenv_map = 0; + +static inline void +do_setenv (const char *name, const char *value) +{ + struct putenv_map *m; + + for (m = putenv_map; m; m = m->next) + if (strcmp (m->name, name) == 0) + { + /* Modify the env in-place. */ + finish: + snprintf (m->env, sizeof m->env, "%s=%s", name, value); + putenv (m->env); + return; + } + + /* Create a new entry. */ + m = pmalloc (global_pool, sizeof *m); + m->next = putenv_map; + m->name = name; + goto finish; +} + +#define do_unsetenv(name) putenv ((name)) + +#else +#error "no setenv/unsetenv or putenv in your libc" +#endif + +static inline void +_restore_lang () +{ + if (current_pth->lang == 0) + do_unsetenv ("LANGUAGE"); + else + do_setenv ("LANGUAGE", current_pth->lang); + +#ifdef __GLIBC__ + { + /* Please see gettext info file, node ``Being a `gettext' grok'', section + * ``Changing the language at runtime''. + */ + extern int _nl_msg_cat_cntr; + _nl_msg_cat_cntr++; + } +#endif +} + +static inline void +_restore_tz () +{ + if (current_pth->tz == 0) + do_unsetenv ("TZ"); + else + do_setenv ("TZ", current_pth->tz); +} + +void +pth_set_language (const char *lang) +{ + current_pth->lang = pstrdup (current_pth->pool, lang); + _restore_lang (); +} + +void +pth_set_tz (const char *tz) +{ + current_pth->tz = pstrdup (current_pth->pool, tz); + _restore_tz (); +} + +inline void +_pth_switch_thread_to_calling_context () +{ + mctx_switch (¤t_pth->thread_ctx, ¤t_pth->calling_ctx); +} + +inline void +_pth_switch_calling_to_thread_context (pseudothread pth) +{ + current_pth = pth; /* Set current thread. */ + mctx_switch (¤t_pth->calling_ctx, ¤t_pth->thread_ctx); +} + +inline int +_pth_alarm_received () +{ + return current_pth->alarm_received; +} + +static void +block (int sock, int operations) +{ + /* Register a read event in the reactor. */ + reactor_handle handle + = reactor_register (sock, operations, return_from_block, current_pth); + + /* Swap context back to the calling context. */ + _pth_switch_thread_to_calling_context (); + + /* Unregister the handle. */ + reactor_unregister (handle); + + /* Received alarm signal? - Exit. */ + if (_pth_alarm_received ()) + pth_exit (); + + /* Restore environment. */ + _restore_lang (); + _restore_tz (); +} + +static void +return_from_block (int sock, int events, void *vpth) +{ + /* Swap back to the thread context. */ + _pth_switch_calling_to_thread_context ((pseudothread) vpth); +} + +static void +_sleep (int timeout) +{ + /* Register a timer in the reactor. */ + reactor_timer timer + = reactor_set_timer (current_pth->pool, timeout, + return_from_sleep, current_pth); + + /* Swap context back to the calling context. */ + _pth_switch_thread_to_calling_context (); + + /* Received alarm signal? - Exit. */ + if (_pth_alarm_received ()) + { + reactor_unset_timer_early (timer); + pth_exit (); + } + + /* Note: no need to unregister the timer, since it has fired. */ + + /* Restore environment. */ + _restore_lang (); + _restore_tz (); +} + +static void +return_from_sleep (void *vpth) +{ + /* Swap back to the thread context. */ + _pth_switch_calling_to_thread_context ((pseudothread) vpth); +} + +static void +_poll (struct pollfd *fds, unsigned int n, int timeout) +{ + reactor_timer timer = 0; + reactor_handle handle[n]; + int i; + + (void) &timer; /* Tell gcc not to put it in a register. */ + current_pth->poll_timeout = 0; + + /* Register all events in the reactor. */ + for (i = 0; i < n; ++i) + /* NB: Poll operations == reactor operations. */ + handle[i] = reactor_register (fds[i].fd, fds[i].events, + return_from_poll, current_pth); + + /* Timeout? */ + if (timeout >= 0) + timer = reactor_set_timer (current_pth->pool, timeout, + return_from_poll_timeout, current_pth); + + /* Swap context back to calling context. */ + _pth_switch_thread_to_calling_context (); + + /* Unregister all the handles. */ + for (i = 0; i < n; ++i) + reactor_unregister (handle[i]); + + /* Delete the timer, if it exists but hasn't fired. */ + if (timer && !current_pth->poll_timeout) + reactor_unset_timer_early (timer); + + /* Received alarm signal? Exit. */ + if (_pth_alarm_received ()) + pth_exit (); + + /* Restore environment. */ + _restore_lang (); + _restore_tz (); +} + +static void +return_from_poll (int sock, int events, void *vpth) +{ + /* Swap back to the thread context. */ + _pth_switch_calling_to_thread_context ((pseudothread) vpth); +} + +static void +return_from_poll_timeout (void *vpth) +{ + pseudothread pth = (pseudothread) vpth; + + pth->poll_timeout = 1; + + /* Swap back to the thread context. */ + _pth_switch_calling_to_thread_context (pth); +} + +static void +return_from_alarm (void *vpth) +{ + pseudothread pth = (pseudothread) vpth; + + pth->alarm_received = 1; + pth->alarm_timer = 0; + + /* Swap back to the thread context. */ + _pth_switch_calling_to_thread_context (pth); +} + +vector +pseudothread_get_threads (pool pool) +{ + vector v = new_vector (pool, struct pseudothread); + int i; + + for (i = 0; i < vector_size (threads); ++i) + { + pseudothread pth; + struct pseudothread pth_copy; + + vector_get (threads, i, pth); + + if (pth) + { + /* Perform a deep copy of the structure. */ + memcpy (&pth_copy, pth, sizeof pth_copy); + if (pth_copy.name) + pth_copy.name = pstrdup (pool, pth_copy.name); + if (pth_copy.lang) + pth_copy.lang = pstrdup (pool, pth_copy.lang); + if (pth_copy.tz) + pth_copy.tz = pstrdup (pool, pth_copy.tz); + + vector_push_back (v, pth_copy); + } + } + + return v; +} + +int +pseudothread_count_threads (void) +{ + int count = 0; + int i; + + for (i = 0; i < vector_size (threads); ++i) + { + pseudothread pth; + + vector_get (threads, i, pth); + + if (pth) count++; + } + + return count; +} diff --git a/src/pthr_pseudothread.h b/src/pthr_pseudothread.h new file mode 100644 index 0000000..900a247 --- /dev/null +++ b/src/pthr_pseudothread.h @@ -0,0 +1,320 @@ +/* Pseudothread handler. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_pseudothread.h,v 1.13 2002/12/01 14:29:30 rich Exp $ + */ + +#ifndef PTHR_PSEUDOTHREAD_H +#define PTHR_PSEUDOTHREAD_H + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "pthr_reactor.h" +#include "pthr_context.h" + +struct pseudothread; +typedef struct pseudothread *pseudothread; + +/* Function: current_pth - current pseudothread + * + * @code{current_pth} is a global variable containing the currently + * running thread. + */ +extern pseudothread current_pth; + +/* Function: pseudothread_set_stack_size - set and get default stack size + * Function: pseudothread_get_stack_size + * + * @code{pseudothread_set_stack_size} sets the stack + * size for newly created threads. The default stack size + * is 64 KBytes. + * + * @code{pseudothread_get_stack_size} returns the current + * stack size setting. + * + * See also: @ref{new_pseudothread(3)}. + */ +extern int pseudothread_set_stack_size (int size); +extern int pseudothread_get_stack_size (void); + +/* Function: new_pseudothread - lightweight "pseudothreads" library + * Function: pth_start + * Function: pseudothread_get_threads + * Function: pseudothread_count_threads + * + * Pseudothreads are lightweight, cooperatively scheduled threads. + * + * @code{new_pseudothread} creates a new pseudothread. The thread + * only starts running when you call @code{pth_start}. The @code{pool} + * argument passed is used for all allocations within the thread. + * This pool is automatically deleted when the thread exits. + * The @code{name} argument is the name of the thread (used in + * thread listings -- see @ref{pseudothread_get_threads(3)}). You + * may change the name later. The @code{run} and @code{data} + * arguments are the entry point into the thread. The entry + * point is called as @code{run (data)}. + * + * @code{pseudothread_get_threads} returns a list of all the + * currently running pseudothreads. This allows you to implement + * a "process listing" for a program. The returned value + * is a vector of pseudothread structures (not pointers). These + * structures are opaque to you, but you can call the pth_get_* + * functions on the address of each one. + * + * @code{pseudothread_count_threads} counts the number of currently + * running threads. + */ +extern pseudothread new_pseudothread (pool, void (*run) (void *), void *data, const char *name); +extern void pth_start (pseudothread pth); +extern vector pseudothread_get_threads (pool); +extern int pseudothread_count_threads (void); + +/* Function: pth_accept - pseudothread system calls + * Function: pth_connect + * Function: pth_read + * Function: pth_write + * Function: pth_sleep + * Function: pth_millisleep + * Function: pth_nanosleep + * Function: pth_timeout + * + * @code{pth_accept}, @code{pth_connect}, @code{pth_read}, @code{pth_write}, + * @code{pth_sleep} and @code{pth_nanosleep} behave just like the + * corresponding system calls. However these calls handle non-blocking + * sockets and cause the thread to sleep on the reactor if it would + * block. For general I/O you will probably wish to wrap up your + * sockets in I/O handle objects, which give you a higher-level + * buffered interface to sockets (see @ref{io_fdopen(3)}). + * + * @code{pth_millisleep} sleeps for a given number of milliseconds. + * + * @code{pth_timeout} is similar to the @ref{alarm(2)} system call: it + * registers a timeout (in seconds). The thread will exit automatically + * (even in the middle of a system call) if the timeout is reached. + * To reset the timeout, call @code{pth_timeout} with a timeout of 0. + * + * See also: @ref{pth_poll(3)}, @ref{pth_select(3)}. + */ +extern int pth_accept (int, struct sockaddr *, int *); +extern int pth_connect (int, struct sockaddr *, int); +extern ssize_t pth_read (int, void *, size_t); +extern ssize_t pth_write (int, const void *, size_t); +extern int pth_sleep (int seconds); +extern int pth_millisleep (int millis); +extern int pth_nanosleep (const struct timespec *req, struct timespec *rem); +extern void pth_timeout (int seconds); + +/* Function: pth_send - pseudothread network system calls + * Function: pth_sendto + * Function: pth_sendmsg + * Function: pth_recv + * Function: pth_recvfrom + * Function: pth_recvmsg + * + * @code{pth_send}, @code{pth_sendto}, @code{pth_sendmsg}, + * @code{pth_recv}, @code{pth_recvfrom} and @code{pth_recvmsg} + * behave just like the corresponding system calls. However + * these calls handle non-blocking sockets and cause the + * thread to sleep on the reactor if it would block. + * + * See also: @ref{pth_poll(3)}, @ref{pth_read(3)}, @ref{pth_write(3)}. + */ +extern int pth_send (int s, const void *msg, int len, unsigned int flags); +extern int pth_sendto (int s, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen); +extern int pth_sendmsg (int s, const struct msghdr *msg, unsigned int flags); +extern int pth_recv (int s, void *buf, int len, unsigned int flags); +extern int pth_recvfrom (int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen); +extern int pth_recvmsg (int s, struct msghdr *msg, unsigned int flags); + +/* Function: pth_poll - pseudothread poll and select system calls + * Function: pth_select + * + * @code{pth_poll} behaves like the @ref{poll(2)} system call. It + * specifies an array @code{n} of @code{fds} file descriptor structures + * each of which is checked for an I/O event. If @code{timeout} is + * greater than or equal to zero, then the call will return after + * this many milliseconds if no I/O events are detected. If @code{timeout} + * is negative, then the timeout is infinite. + * + * @code{pth_select} behaves like the @ref{select(2)} system call. + * + * Note that @code{pth_select} is implemented as a library on top + * of @code{pth_poll}, and is considerably less efficient than + * @code{pth_poll}. It is recommended that you rewrite any code + * which uses @code{pth_select}/@code{select} to use + * @code{pth_poll}/@code{poll} instead. + * + * See also: @ref{pth_timeout(3)}, @ref{pth_wait_readable(3)}. + */ +extern int pth_poll (struct pollfd *fds, unsigned int n, int timeout); +extern int pth_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); + +/* Function: pth_wait_readable - wait for a file descriptor to become readable or writable + * Function: pth_wait_writable + * + * @code{pth_wait_readable} waits until a given file descriptor (or + * socket) has data to read. + * + * @code{pth_wait_writable} waits until a given file descriptor (or + * socket) can be written without blocking. + * + * The @code{fd} must be set to non-blocking. + * + * Returns: Both functions return 0 if OK, or -1 if there was an error. + * + * See also: @ref{pth_poll(3)}. + */ +extern int pth_wait_readable (int fd); +extern int pth_wait_writable (int fd); + +/* Function: pth_exit - exit a pseudothread and exception handling + * Function: pth_die + * Function: pth_catch + * + * @code{pth_exit} causes the current thread to exit immediately. + * + * @code{pth_die} is similar in concept to @code{pth_exit}, except + * that it throws an exception which may be caught by using the + * @code{pth_catch} function. The distinction between @code{pth_die} + * and @code{pth_exit} is the same as the distinction between + * the Perl functions @code{die} and @code{exit}, in as much as + * @code{exit} in Perl always exits the process immediately, and + * @code{die} in Perl generally exits the process immediately + * unless the programmer catches the exception with @code{eval} + * and handles it appropriately. + * + * @code{pth_catch} is used to catch exceptions thrown by + * @code{pth_die}. You give @code{fn} (function) and @code{data} + * arguments, and the function calls @code{fn (data)}. If, during + * this call, the code calls @code{pth_die}, then the exception + * message is immediately returned from @code{pth_catch}. If + * the code runs successfully to completion, then @code{pth_catch} + * will return @code{NULL}. + * + * Exceptions may be nested. + * + * Note that @code{pth_catch} will not catch calls to @code{pth_exit}. + */ +extern void pth_exit (void) __attribute__((noreturn)); +#define pth_die(msg) _pth_die ((msg), __FILE__, __LINE__) +extern const char *pth_catch (void (*fn) (void *), void *data); + +extern void _pth_die (const char *msg, + const char *filename, int lineno) + __attribute__((noreturn)); + +/* Function: pth_get_pool - get and set status of pseudothreads + * Function: pth_get_name + * Function: pth_get_thread_num + * Function: pth_get_run + * Function: pth_get_data + * Function: pth_get_language + * Function: pth_get_tz + * Function: pth_get_stack + * Function: pth_get_stack_size + * Function: pth_get_PC + * Function: pth_get_SP + * Function: pth_set_name + * Function: pth_set_language + * Function: pth_set_tz + * + * @code{pth_get_pool} returns the pool associated with this thread. + * The thread should use this pool for all allocations, ensuring that + * they are cleaned up correctly when the thread is deleted. See + * @ref{new_pool(3)}. + * + * @code{pth_get_name} returns the name of the thread. Note that + * this string is normally stored in thread's local pool, and therefore + * the string memory may be unexpected deallocated if the thread + * exits. Callers should take a copy of the string in their own pool + * if they are likely to need the string for a long period of time. + * + * @code{pth_get_thread_num} returns the thread number (roughly + * the equivalent of a process ID). + * + * @code{pth_get_run} and @code{pth_get_data} return the original + * entry point information of the thread. + * + * @code{pth_get_language} returns the value of the @code{LANGUAGE} + * environment variable for this thread. See the discussion + * of @code{pth_set_language} below. + * + * @code{pth_get_language} returns the value of the @code{TZ} + * environment variable for this thread. See the discussion + * of @code{pth_set_tz} below. + * + * @code{pth_get_stack} returns the top of the stack for this + * thread. + * + * @code{pth_get_stack_size} returns the maximum size of the stack for + * this thread. + * + * @code{pth_get_PC} returns the current program counter (PC) for + * this thread. Obviously it only makes sense to call this from + * another thread. + * + * @code{pth_get_SP} returns the current stack pointer (SP) for + * this thread. Obviously it only makes sense to call this from + * another thread. + * + * @code{pth_set_name} changes the name of the thread. The string + * @code{name} argument passed must be either statically allocated, + * or allocated in the thread's own pool (the normal case), or else + * allocated in another pool with a longer life than the current + * thread. + * + * @code{pth_set_language} changes the per-thread @code{LANGUAGE} + * environment variable. This is useful when serving clients from + * multiple locales, and using GNU gettext for translation. + * + * @code{pth_set_tz} changes the per-thread @code{TZ} + * environment variable. This is useful when serving clients from + * multiple timezones, and using @code{localtime} and related + * functions. + */ +extern pool pth_get_pool (pseudothread pth); +extern const char *pth_get_name (pseudothread pth); +extern int pth_get_thread_num (pseudothread pth); +extern void (*pth_get_run (pseudothread pth)) (void *); +extern void *pth_get_data (pseudothread pth); +extern const char *pth_get_language (pseudothread pth); +extern const char *pth_get_tz (pseudothread pth); +extern void *pth_get_stack (pseudothread pth); +extern int pth_get_stack_size (pseudothread pth); +extern unsigned long pth_get_PC (pseudothread pth); +extern unsigned long pth_get_SP (pseudothread pth); +extern void pth_set_name (const char *name); +extern void pth_set_language (const char *lang); +extern void pth_set_tz (const char *tz); + +/* These low-level functions are used by other parts of the pthrlib library. + * Do not use them from user programs. They switch thread context with the + * calling context and v.v. + */ +extern void _pth_switch_thread_to_calling_context (void); +extern void _pth_switch_calling_to_thread_context (pseudothread new_pth); +extern int _pth_alarm_received (void); + +#endif /* PTHR_PSEUDOTHREAD_H */ diff --git a/src/pthr_reactor.c b/src/pthr_reactor.c new file mode 100644 index 0000000..db8973e --- /dev/null +++ b/src/pthr_reactor.c @@ -0,0 +1,556 @@ +/* A specialized Reactor. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_reactor.c,v 1.6 2002/08/21 10:42:19 rich Exp $ + */ + +#include "config.h" + +#include +#include + +#ifdef HAVE_SYS_POLL_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SIGNAL_H +#include +#endif + +#ifdef HAVE_SYSLOG_H +#include +#endif + +#ifdef HAVE_ASSERT_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#include "pthr_reactor.h" + +#define REACTOR_DEBUG 0 + +struct reactor_handle +{ + int offset; /* Points into internal poll fds array. */ + void (*fn) (int, int, void *); + void *data; +}; + +struct reactor_timer +{ + pool pool; + struct reactor_timer *prev, *next; + unsigned long long delta; + void (*fn) (void *); + void *data; +}; + +struct reactor_prepoll +{ + pool pool; + struct reactor_prepoll *next; + void (*fn) (void *); + void *data; + int fired; +}; + +/* This is how HANDLES and POLL_ARRAY work: + * + * HANDLES is a straightforward list of reactor handle objects. When + * a user registers an event handler in the reactor, they get back + * an integer which is in fact an offset into the HANDLES array. + * + * POLL_ARRAY is the array which actually gets passed to the poll(2) + * system call. + * + * HANDLES and POLL_ARRAY are related through the OFFSET field in + * a reactor handle. Thus: + * + * HANDLES: +------+------+------+------+------+ + * offset: | 2 | 1 | 3 | 3 | 0 | + * | | | | | | + * | | | | | \ | | | | | + * | | | | | \ | | | | | + * +-|----+-|----+----\-+-|----+-|----+ + * \_____|_____ \ | | + * POLL_ARRAY: +------+-|----+\-----\-|----+ | + * | | | | \ |\ | | + * | | | | | | + * | _______________________/ + * | | | | | + * +------+------+------+------+ + * + * Note that you may have two handles pointing to the same offset + * in POLL_ARRAY: this happens when we share pollfds because they + * both contain the same fd and event information. + * + * If the OFFSET field of a handle contains -1, then the handle + * is unused. Notice that we never decrease the size of the HANDLES + * array. + * + * Adding (registering) a handle is easy enough: firstly we look for + * an unused (offset == -1) handle, and if not found we reallocate + * the HANDLES array to make it larger. Then we search through the + * POLL_ARRAY to see if it's possible to share. If not, we extend + * the POLL_ARRAY by 1 and set the offset accordingly. + * + * Deleting (unregistering) a handle is not so easy: deleting the + * handle is OK -- just set the offset to -1. But we must also + * check the POLL_ARRAY entry, and if it is not shared, delete it, + * shrink the POLL_ARRAY and update all handles which point to + * an offset larger than the deleted element in POLL_ARRAY. + */ + + +/* The list of reactor handles registered. */ +static struct reactor_handle *handles = 0; +static struct pollfd *poll_array = 0; +static int nr_handles_allocated = 0; +static int nr_array_allocated = 0; +static int nr_array_used = 0; + +/* The list of timers, stored in time order (in a delta queue). */ +static struct reactor_timer *head_timer = 0; + +/* The list of prepoll handlers in no particular order. */ +static struct reactor_prepoll *head_prepoll = 0; + +/* The current time, or near as dammit, in milliseconds from Unix epoch. */ +unsigned long long reactor_time; + +/* Function prototypes. */ +static void remove_timer (void *timerp); +static void remove_prepoll (void *timerp); + +/* Cause reactor_init / reactor_stop to be called automatically. */ +static void reactor_init (void) __attribute__ ((constructor)); +static void reactor_stop (void) __attribute__ ((destructor)); + +static void +reactor_init () +{ + struct timeval tv; + struct sigaction sa; + + /* Catch EPIPE errors rather than sending a signal. */ + memset (&sa, 0, sizeof sa); + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_RESTART; + sigaction (SIGPIPE, &sa, 0); + + /* Update the reactor time. */ + gettimeofday (&tv, 0); + reactor_time = tv.tv_sec * 1000LL + tv.tv_usec / 1000; +} + +static void +reactor_stop () +{ + int i; + reactor_timer p, p_next; + reactor_prepoll prepoll, prepoll_next; + + /* There should be no prepoll handlers registered. Check it and free them.*/ + for (prepoll = head_prepoll; prepoll; prepoll = prepoll_next) + { + syslog (LOG_WARNING, "prepoll handler left registered in reactor: fn=%p, data=%p", + prepoll->fn, prepoll->data); + prepoll_next = prepoll; + delete_pool (prepoll->pool); + } + + /* There should be no timers registered. Check it and free them up. */ + for (p = head_timer; p; p = p_next) + { + syslog (LOG_WARNING, "timer left registered in reactor: fn=%p, data=%p", + p->fn, p->data); + p_next = p->next; + delete_pool (p->pool); + } + + /* There should be no handles registered. Check for this. */ + for (i = 0; i < nr_handles_allocated; ++i) + if (handles[i].offset >= 0) + syslog (LOG_WARNING, "handle left registered in reactor: fn=%p, data=%p", + handles[i].fn, handles[i].data); + + /* Free up memory used by handles. */ + if (handles) free (handles); + if (poll_array) free (poll_array); +} + +reactor_handle +reactor_register (int socket, int operations, + void (*fn) (int socket, int events, void *), void *data) +{ + int i, h, a; + + /* Find an unused handle. */ + for (i = 0; i < nr_handles_allocated; ++i) + if (handles[i].offset == -1) + { + h = i; + goto found_handle; + } + + /* No free handles: allocate a new handle. */ + h = nr_handles_allocated; + nr_handles_allocated += 8; + handles = realloc (handles, + nr_handles_allocated * sizeof (struct reactor_handle)); + for (i = h; i < nr_handles_allocated; ++i) + handles[i].offset = -1; + + found_handle: + /* Examine the poll descriptors to see if we can share with an + * existing descriptor. + */ + for (a = 0; a < nr_array_used; ++a) + if (poll_array[a].fd == socket && + poll_array[a].events == operations) + goto found_poll_descriptor; + + /* Allocate space in the array of poll descriptors. */ + if (nr_array_used >= nr_array_allocated) + { + nr_array_allocated += 8; + poll_array = realloc (poll_array, + nr_array_allocated * sizeof (struct pollfd)); + } + a = nr_array_used++; + + /* Create the poll descriptor. */ + poll_array[a].fd = socket; + poll_array[a].events = operations; + + found_poll_descriptor: + /* Create the handle. */ + handles[h].offset = a; + handles[h].fn = fn; + handles[h].data = data; + +#if REACTOR_DEBUG + fprintf (stderr, + "reactor_register (fd=%d, ops=0x%x, fn=%p, data=%p) = %p\n", + socket, operations, fn, data, &handles[h]); +#endif + + /* Return the handle. */ + return h; +} + +void +reactor_unregister (reactor_handle handle) +{ + int i, a = handles[handle].offset; + +#if REACTOR_DEBUG + fprintf (stderr, + "reactor_unregister (handle=%d [fd=%d, ops=0x%x])\n", + handle, poll_array[a].fd, poll_array[a].events); +#endif + + handles[handle].offset = -1; + + /* Does any other handle share this element? If so, leave POLL_ARRAY alone. + */ + for (i = 0; i < nr_handles_allocated; ++i) + if (handles[i].offset == a) + return; + + /* Not shared. Remove this element from poll_array. */ + memcpy (&poll_array[a], &poll_array[a+1], + (nr_array_used - a - 1) * sizeof (struct pollfd)); + nr_array_used --; + + /* Any handles in used which use offset > a should be updated. */ + for (i = 0; i < nr_handles_allocated; ++i) + if (handles[i].offset > a) + handles[i].offset --; +} + +reactor_timer +reactor_set_timer (pool pp, + int timeout, + void (*fn) (void *data), + void *data) +{ + pool sp; + reactor_timer timer, p, last_p; + unsigned long long trigger_time, this_time; + + sp = new_subpool (pp); + + timer = pmalloc (sp, sizeof *timer); + + timer->pool = sp; + timer->fn = fn; + timer->data = data; + + /* Register a function to clean up this timer when the subpool is deleted.*/ + pool_register_cleanup_fn (sp, remove_timer, timer); + + /* Calculate the trigger time. */ + trigger_time = reactor_time + timeout; + + if (head_timer == 0) /* List is empty. */ + { + timer->prev = timer->next = 0; + timer->delta = trigger_time; + return head_timer = timer; + } + + /* Find out where to insert this handle in the delta queue. */ + this_time = 0; + last_p = 0; + for (p = head_timer; p; last_p = p, p = p->next) + { + this_time += p->delta; + + if (this_time >= trigger_time) /* Insert before element p. */ + { + timer->prev = p->prev; + timer->next = p; + if (p->prev) /* Not the first element. */ + p->prev->next = timer; + else /* First element in list. */ + head_timer = timer; + p->prev = timer; + timer->delta = trigger_time - (this_time - p->delta); + p->delta = this_time - trigger_time; + return timer; + } + } + + /* Insert at the end of the list. */ + last_p->next = timer; + timer->prev = last_p; + timer->next = 0; + timer->delta = trigger_time - this_time; + return timer; +} + +static void +remove_timer (void *timerp) +{ + struct reactor_timer *timer = (struct reactor_timer *) timerp; + + /* Remove this timer from the list. */ + if (timer->prev != 0) + timer->prev->next = timer->next; + else + head_timer = timer->next; + + if (timer->next != 0) + { + timer->next->prev = timer->prev; + timer->next->delta += timer->delta; + } +} + +void +reactor_unset_timer_early (reactor_timer timer) +{ + delete_pool (timer->pool); +} + +reactor_prepoll +reactor_register_prepoll (pool pp, + void (*fn) (void *data), + void *data) +{ + pool sp; + reactor_prepoll p; + + sp = new_subpool (pp); + + p = pmalloc (sp, sizeof *p); + + p->pool = sp; + p->fn = fn; + p->data = data; + + pool_register_cleanup_fn (sp, remove_prepoll, p); + + p->next = head_prepoll; + head_prepoll = p; + + return p; +} + +static void +remove_prepoll (void *handlep) +{ + reactor_prepoll handle = (reactor_prepoll) handlep, prev = 0, this; + + /* Find this handle in the list. */ + for (this = head_prepoll; + this && this != handle; + prev = this, this = this->next) + ; + + assert (this == handle); + + if (prev == 0) { /* Remove head handler. */ + head_prepoll = head_prepoll->next; + } else { /* Remove inner handler. */ + prev->next = this->next; + } +} + +void +reactor_unregister_prepoll (reactor_prepoll handle) +{ + delete_pool (handle->pool); +} + +void +reactor_invoke () +{ + int i, r, a; + reactor_prepoll prepoll; + struct timeval tv; + +#if 0 + /* Update the reactor time. */ + gettimeofday (&tv, 0); + reactor_time = tv.tv_sec * 1000LL + tv.tv_usec / 1000; +#endif + + /* Fire any timers which are ready. */ + while (head_timer != 0 && head_timer->delta <= reactor_time) + { + reactor_timer timer; + void (*fn) (void *); + void *data; + + /* Calling fn might change head_timer. */ + timer = head_timer; + fn = timer->fn; + data = timer->data; + + /* Remove the timer from the queue now (this avoids a rare race + * condition exposed if code calls pth_sleep followed immediately + * by pth_exit). + */ + reactor_unset_timer_early (timer); + + fn (data); + } + + /* Run the prepoll handlers. This is tricky -- we have to check + * (a) that we run every prepoll handler, even if new ones are + * added while we are running them, and (b) that we don't accidentally + * hit a prepoll handler which has been removed. The only thing we + * can be sure of is that HEAD_PREPOLL is always valid. Anything it + * points to can change any time we call a handler. Therefore this + * is how we do it: (1) go through the list, marked all of the handlers + * as not fired (ie. clearing the FIRED flag); (2) go through the list + * looking for the first non-fired handle, mark it as fired and run + * it; (3) repeat step (2) until there are no more non-fired handles. + */ + for (prepoll = head_prepoll; prepoll; prepoll = prepoll->next) + prepoll->fired = 0; + + prepoll_again: + for (prepoll = head_prepoll; + prepoll && prepoll->fired; + prepoll = prepoll->next) + ; + + if (prepoll) + { + prepoll->fired = 1; + prepoll->fn (prepoll->data); + goto prepoll_again; + } + + /* Poll file descriptors. */ + if (nr_array_used >= 0) + { +#if REACTOR_DEBUG + fprintf (stderr, "reactor_invoke: poll ["); + for (i = 0; i < nr_array_used; ++i) + fprintf (stderr, "(fd=%d, ops=0x%x)", + poll_array[i].fd, poll_array[i].events); + fprintf (stderr, "]\n"); +#endif + + r = poll (poll_array, nr_array_used, + head_timer ? head_timer->delta - reactor_time : -1); + + /* Update the reactor time. */ + gettimeofday (&tv, 0); + reactor_time = tv.tv_sec * 1000LL + tv.tv_usec / 1000; + +#if REACTOR_DEBUG + fprintf (stderr, "reactor_invoke: poll returned %d [", r); + for (i = 0; i < nr_array_used; ++i) + if (poll_array[i].revents != 0) + fprintf (stderr, "(fd=%d, ops=0x%x)", + poll_array[i].fd, poll_array[i].revents); + fprintf (stderr, "]\n"); +#endif + + if (r > 0) /* Some descriptors are ready. */ + { + /* Check for events happening in the array, but go via the + * handles because there is no back pointer back to the + * handles from the array. Surprisingly enough, this code + * appears to be free of race conditions (note that calling + * fn can register/unregister handles). + */ + for (i = 0; i < nr_handles_allocated; ++i) + { + a = handles[i].offset; + + if (a >= 0 && poll_array[a].revents != 0) + { + handles[i].fn (poll_array[a].fd, poll_array[a].revents, + handles[i].data); + } + } + } + else if (r == 0) /* The head timer has fired. */ + { + reactor_timer timer; + void (*fn) (void *); + void *data; + + /* Calling fn might change head_timer. */ + timer = head_timer; + fn = timer->fn; + data = timer->data; + + /* Remove the timer from the queue now (this avoids a rare race + * condition exposed if code calls pth_sleep followed immediately + * by pth_exit). + */ + reactor_unset_timer_early (timer); + + fn (data); + } + } +} diff --git a/src/pthr_reactor.h b/src/pthr_reactor.h new file mode 100644 index 0000000..365fd48 --- /dev/null +++ b/src/pthr_reactor.h @@ -0,0 +1,68 @@ +/* A specialized Reactor. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_reactor.h,v 1.3 2002/08/21 10:42:20 rich Exp $ + */ + +#ifndef PTHR_REACTOR_H +#define PTHR_REACTOR_H + +#include + +#include + +/* A reactor handle. */ +struct reactor_handle; +typedef int reactor_handle; /* These are offsets into a private array + * of struct reactor_handle. + */ + +/* A timer. */ +struct reactor_timer; +typedef struct reactor_timer *reactor_timer; + +/* Pre-poll handlers. */ +struct reactor_prepoll; +typedef struct reactor_prepoll *reactor_prepoll; + +/* Reactor operations. */ +#define REACTOR_READ POLLIN +#define REACTOR_WRITE POLLOUT + +/* Reactor time types. */ +typedef unsigned long long reactor_time_t; +typedef signed long long reactor_timediff_t; + +/* Reactor time in milliseconds from Unix epoch. */ +extern reactor_time_t reactor_time; + +/* Reactor functions. */ +extern reactor_handle reactor_register (int socket, int operations, + void (*fn) (int socket, int events, + void *data), + void *data); +extern void reactor_unregister (reactor_handle handle); +extern reactor_timer reactor_set_timer (pool, int timeout, + void (*fn) (void *data), + void *data); +extern void reactor_unset_timer_early (reactor_timer timer); +extern reactor_prepoll reactor_register_prepoll (pool, void (*fn) (void *data), + void *data); +extern void reactor_unregister_prepoll (reactor_prepoll handle); +extern void reactor_invoke (void); + +#endif /* PTHR_REACTOR_H */ diff --git a/src/pthr_rwlock.c b/src/pthr_rwlock.c new file mode 100644 index 0000000..27816d5 --- /dev/null +++ b/src/pthr_rwlock.c @@ -0,0 +1,306 @@ +/* Multiple reader / single writer locks for pthrlib. + * by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_rwlock.c,v 1.3 2002/12/01 14:29:30 rich Exp $ + */ + +#include "config.h" + +#include + +#ifdef HAVE_ASSERT_H +#include +#endif + +#include +#include + +#include "pthr_pseudothread.h" +#include "pthr_wait_queue.h" +#include "pthr_rwlock.h" + +/* I added this while I was trying to pin down a possible memory corruption + * problem. It can be disabled in normal operations. + */ +/* #define RWLOCK_MEM_DEBUG 1 */ +#define RWLOCK_MEM_DEBUG 0 + +#if RWLOCK_MEM_DEBUG +#define RWLOCK_MEM_MAGIC 0x99775533 +#endif + +struct rwlock +{ +#if RWLOCK_MEM_DEBUG + unsigned magic; +#endif + int n; /* If N == 0, lock is free. + * If N > 0, lock is held by N readers. + * If N == -1, lock is held by 1 writer. + */ + wait_queue writers_wq; /* Writers wait on this queue. */ + wait_queue readers_wq; /* Readers wait on this queue. */ + + /* A hash from pth pointer -> subpool. The keys of this hash are + * pseudothreads which are currently in the critical section. The + * values are subpools of the appropriate pth pool. If a thread + * exits without releasing the lock, then the subpool is deleted, + * which causes our callback to run, releasing the lock. + */ + hash pools; + + unsigned writers_have_priority:1; +}; + +static void _do_enter (rwlock); +static void _do_release (void *); +static void _delete_rwlock (void *); + +rwlock +new_rwlock (pool p) +{ + rwlock rw = pmalloc (p, sizeof *rw); + +#if RWLOCK_MEM_DEBUG + rw->magic = RWLOCK_MEM_MAGIC; +#endif + + rw->n = 0; + rw->readers_wq = new_wait_queue (p); + rw->writers_wq = new_wait_queue (p); + rw->writers_have_priority = 1; + rw->pools = new_hash (p, pseudothread, pool); + + /* The purpose of this cleanup is just to check that the rwlock + * isn't released with threads in the critical section. + */ + pool_register_cleanup_fn (p, _delete_rwlock, rw); + + return rw; +} + +static void +_delete_rwlock (void *vrw) +{ + rwlock rw = (rwlock) vrw; +#if RWLOCK_MEM_DEBUG + assert (rw->magic == RWLOCK_MEM_MAGIC); +#endif + assert (rw->n == 0); +} + +/* Calling this function changes the nature of the lock so that + * writers have priority over readers. If this is the case then + * new readers will not be able to enter a critical section if + * there are writers waiting to enter. + * [NB: This is the default.] + */ +void +rwlock_writers_have_priority (rwlock rw) +{ +#if RWLOCK_MEM_DEBUG + assert (rw->magic == RWLOCK_MEM_MAGIC); +#endif + rw->writers_have_priority = 1; +} + +/* Calling this function changes the nature of the lock so that + * readers have priority over writers. Note that if this is the case + * then writers are likely to be starved if the lock is frequently + * read. + */ +void +rwlock_readers_have_priority (rwlock rw) +{ +#if RWLOCK_MEM_DEBUG + assert (rw->magic == RWLOCK_MEM_MAGIC); +#endif + rw->writers_have_priority = 0; +} + +/* This function is identical to RWLOCK_ENTER_READ, but it + * does not block. It returns TRUE if the lock was successfully + * acquired, or FALSE if the operation would block. + */ +inline int +rwlock_try_enter_read (rwlock rw) +{ +#if RWLOCK_MEM_DEBUG + assert (rw->magic == RWLOCK_MEM_MAGIC); +#endif + + if (rw->n >= 0 && + (!rw->writers_have_priority || + wq_nr_sleepers (rw->writers_wq) == 0)) + { + _do_enter (rw); + rw->n++; + return 1; + } + else + return 0; +} + +/* This function is identical to RWLOCK_ENTER_WRITE, but it + * does not block. It returns TRUE if the lock was successfully + * acquired, or FALSE if the operation would block. + */ +inline int +rwlock_try_enter_write (rwlock rw) +{ +#if RWLOCK_MEM_DEBUG + assert (rw->magic == RWLOCK_MEM_MAGIC); +#endif + + if (rw->n == 0) + { + _do_enter (rw); + rw->n = -1; + return 1; + } + else + return 0; +} + +/* Enter a critical section as a reader. Any number of readers + * are allowed to enter a critical section at the same time. This + * function may block. + */ +void +rwlock_enter_read (rwlock rw) +{ +#if RWLOCK_MEM_DEBUG + assert (rw->magic == RWLOCK_MEM_MAGIC); +#endif + + while (rwlock_try_enter_read (rw) == 0) + wq_sleep_on (rw->readers_wq); +} + +/* Enter a critical section as a writer. Only a single writer + * is allowed to enter a critical section, and then only if + * there are no readers. This function may block. + */ +void +rwlock_enter_write (rwlock rw) +{ +#if RWLOCK_MEM_DEBUG + assert (rw->magic == RWLOCK_MEM_MAGIC); +#endif + + while (rwlock_try_enter_write (rw) == 0) + wq_sleep_on (rw->writers_wq); +} + +/* Leave a critical section. */ +void +rwlock_leave (rwlock rw) +{ + pool pool; + +#if RWLOCK_MEM_DEBUG + assert (rw->magic == RWLOCK_MEM_MAGIC); +#endif + + /* If this core dumps, it's probably because the pth didn't actually + * hold a lock. + */ + if (!hash_get (rw->pools, current_pth, pool)) abort (); + + /* Force _DO_RELEASE to run. */ + delete_pool (pool); +} + +struct cleanup_data +{ + pseudothread pth; + rwlock rw; +}; + +/* This function registers a clean-up function which deals with the + * case when a thread exits early without releasing the lock. + */ +static void +_do_enter (rwlock rw) +{ + struct cleanup_data *data; + pool pool; + +#if RWLOCK_MEM_DEBUG + assert (rw->magic == RWLOCK_MEM_MAGIC); +#endif + + /* Create a subpool. If the thread exits early, then this subpool + * with be deleted implicitly. If, on the other hand, we release + * the lock in RWLOCK_LEAVE, then we will delete this pool + * explicitly. Either way, _DO_RELEASE will be called. + */ + pool = new_subpool (pth_get_pool (current_pth)); + + /* Save it in the hash. */ + hash_insert (rw->pools, current_pth, pool); + + /* Register a clean-up function in the subpool to call _DO_RELEASE. */ + data = pmalloc (pool, sizeof (struct cleanup_data)); + data->pth = current_pth; + data->rw = rw; + pool_register_cleanup_fn (pool, _do_release, data); +} + +/* This function is called to do the actual work of releasing a lock. */ +static void +_do_release (void *vdata) +{ + struct cleanup_data *data = (struct cleanup_data *)vdata; + pseudothread pth = data->pth; + rwlock rw = data->rw; + +#if RWLOCK_MEM_DEBUG + assert (rw->magic == RWLOCK_MEM_MAGIC); +#endif + + assert (rw->n != 0); + + /* Remove this pseudothread from rw->pools. */ + if (!hash_erase (rw->pools, pth)) abort (); + + if (rw->n > 0) /* Reader leaving critical section? */ + { + rw->n --; + if (rw->n == 0) + { + /* Any writers waiting? */ + if (wq_nr_sleepers (rw->writers_wq) > 0) + wq_wake_up_one (rw->writers_wq); + + /* This can't happen (probably). */ + /* XXX It does happen -- but I believe it's not a mistake. */ + /*assert (wq_nr_sleepers (rw->readers_wq) == 0);*/ + } + } + else /* Writer leaving critical section? */ + { + rw->n = 0; + + /* Any writers waiting? */ + if (wq_nr_sleepers (rw->writers_wq) > 0) + wq_wake_up_one (rw->writers_wq); + /* Any readers waiting? */ + else if (wq_nr_sleepers (rw->readers_wq) > 0) + wq_wake_up_one (rw->readers_wq); + } +} diff --git a/src/pthr_rwlock.h b/src/pthr_rwlock.h new file mode 100644 index 0000000..d95894d --- /dev/null +++ b/src/pthr_rwlock.h @@ -0,0 +1,103 @@ +/* Multiple reader / single writer locks for pthrlib. + * by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_rwlock.h,v 1.3 2002/12/01 14:29:30 rich Exp $ + */ + +#ifndef PTHR_RWLOCK_H +#define PTHR_RWLOCK_H + +#include +#include + +#include "pthr_pseudothread.h" +#include "pthr_wait_queue.h" + +struct rwlock; +typedef struct rwlock *rwlock; + +/* Function: new_rwlock - multiple reader / single writer locks (rwlocks) + * Function: rwlock_writers_have_priority + * Function: rwlock_readers_have_priority + * Function: rwlock_enter_read + * Function: rwlock_enter_write + * Function: rwlock_try_enter_read + * Function: rwlock_try_enter_write + * Function: rwlock_leave + * + * Multiple reader / single writer locks (rwlocks) do what they + * say. They allow either many readers to access a critical section + * or a single writer (but not both). If instead you require simple + * mutex semantics, then please see . + * + * RWlocks are automatically released if they are being held when + * the thread exits. + * + * RWlocks are not ``upgradable''. The implementation is too + * complex to justify that. + * + * Note that there are possible deadlocks when using locks. To + * avoid deadlocks, always ensure every thread acquires locks + * in the same order. + * + * @code{new_rwlock} creates a new rwlock object. + * + * @code{rwlock_writers_have_priority} changes the nature of the lock so that + * writers have priority over readers. If this is the case then + * new readers will not be able to enter a critical section if + * there are writers waiting to enter. + * [NB: This is the default.] + * + * @code{rwlock_readers_have_priority} changes the nature of the lock so that + * readers have priority over writers. Note that if this is the case + * then writers are likely to be starved if the lock is frequently + * read. + * + * @code{rwlock_enter_read} enters the critical section as a reader. + * Any number of readers are allowed to enter a critical section at + * the same time. This function may block. + * + * @code{rwlock_enter_write} enters the critical section as a writer. + * Only a single writer is allowed to enter a critical section, and + * then only if there are no readers. This function may block. + * + * @code{rwlock_try_enter_read} is identical to + * @code{rwlock_enter_read}, but it + * does not block. It returns true if the lock was successfully + * acquired, or false if the operation would block. + * + * @code{rwlock_try_enter_write} is identical to + * @code{rwlock_enter_write}, but it + * does not block. It returns true if the lock was successfully + * acquired, or false if the operation would block. + * + * @code{rwlock_leave} leaves the critical section. + * + * Bugs: A common mistake is to accidentally call @code{rwlock_leave} + * when you are not holding the lock. This generally causes the + * library to crash. + */ +extern rwlock new_rwlock (pool); +extern void rwlock_writers_have_priority (rwlock); +extern void rwlock_readers_have_priority (rwlock); +extern void rwlock_enter_read (rwlock); +extern void rwlock_enter_write (rwlock); +extern int rwlock_try_enter_read (rwlock); +extern int rwlock_try_enter_write (rwlock); +extern void rwlock_leave (rwlock); + +#endif /* PTHR_RWLOCK_H */ diff --git a/src/pthr_server.c b/src/pthr_server.c new file mode 100644 index 0000000..d8cbe5c --- /dev/null +++ b/src/pthr_server.c @@ -0,0 +1,389 @@ +/* Generic server process. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_server.c,v 1.10 2003/02/05 22:13:33 rich Exp $ + */ + +#include "config.h" + +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_SYSLOG_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#ifdef HAVE_PWD_H +#include +#endif + +#ifdef HAVE_GRP_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_EXECINFO_H +#include +#endif + +#ifdef HAVE_SIGNAL_H +#include +#endif + +#include "pthr_listener.h" +#include "pthr_server.h" + +static int default_port = 80; +static char port_option_name = 'p'; +static in_addr_t default_address = INADDR_ANY; +static char address_option_name = 'a'; +static int disable_syslog = 0; +static const char *package_name = PACKAGE " " VERSION; +static int disable_fork = 0; +static int disable_chdir = 0; +static int disable_close = 0; +static const char *root = 0; +static const char *username = 0; +static const char *stderr_file = 0; +static void (*startup_fn)(int argc, char *argv[]) = 0; +static int enable_stack_trace_on_segv = 0; + +#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) +#define CAN_CATCH_SIGSEGV +#endif + +#ifdef CAN_CATCH_SIGSEGV +static void catch_sigsegv (int); +#endif + +extern char *optarg; +extern int optind; +extern int opterr; + +#ifdef __OpenBSD__ +extern int optreset; +#endif + +#ifndef INADDR_NONE +#define INADDR_NONE ((in_addr_t) 0xffffffff) +#endif + +void +pthr_server_main_loop (int argc, char *argv[], + void (*processor_fn) (int sock, void *)) +{ + struct sockaddr_in addr; + int port = default_port; + in_addr_t address = default_address; + int sock, one = 1; + int c; + char getopt_scr[10]; + + /* Reset the getopt library. */ + optind = 1; + opterr = 0; + +#ifdef __OpenBSD__ + optreset = 1; +#endif + + /* Handle command-line arguments. Get the correct port and address to + * listen on. + */ + snprintf (getopt_scr, sizeof getopt_scr, + "%c:%c:", port_option_name, address_option_name); + + while ((c = getopt (argc, argv, getopt_scr)) != -1) + { + if (port_option_name == c) + { + if (sscanf (optarg, "%d", &port) != 1) + { + fprintf (stderr, "invalid port option: %s\n", optarg); + exit (1); + } + } + else if (address_option_name == c) + { + /* Should really use inet_aton() (or even inet_pton()) + * but inet_addr() is more widely supported. + */ + address = inet_addr (optarg); + if (INADDR_NONE == address) + { + fprintf (stderr, "invalid address: %s\n", optarg); + exit(1); + } + } + } + + /* Bind a socket to the appropriate port. */ + sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock < 0) abort (); + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof (one)); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = address; + addr.sin_port = htons (port); + if (bind (sock, (struct sockaddr *) &addr, sizeof addr) < 0) + { + /* Generally this means that the port is already bound. */ + perror ("bind"); + exit (1); + } + + /* Put the socket into listen mode. */ + if (listen (sock, 10) < 0) abort (); + + /* Set the new socket to non-blocking. */ + if (fcntl (sock, F_SETFL, O_NONBLOCK) < 0) abort (); + + /* If running as root, and asked to chroot, then chroot. */ + if (root && geteuid () == 0) + { + if (chroot (root) == -1) + { + perror (root); + exit (1); + } + } + + /* If running as root, and asked to change user, do so now. */ + if (username && geteuid () == 0) + { + struct passwd *pw = getpwnam (username); + + if (pw == 0) + { + fprintf (stderr, "username not found: %s", username); + exit (1); + } + + if (initgroups (username, pw->pw_gid) == -1 || + setgid (pw->pw_gid) == -1 || + setuid (pw->pw_uid) == -1) + { + perror ("setuid"); + exit (1); + } + } + + if (!disable_chdir) + chdir ("/"); + + if (!disable_close) + { + /* Close connections to the terminal. */ + close (0); + close (1); + close (2); + open ("/dev/null", O_RDWR); + dup2 (0, 1); + dup2 (0, 2); + setsid (); + } + + if (stderr_file) + { + /* Reopen stderr on a log file. */ + close (2); + if (open (stderr_file, O_WRONLY|O_CREAT|O_APPEND, 0644) == -1) + { + /* Hard to output an error message at this point ... */ + abort (); + } + } + + if (!disable_fork) + { + /* Fork into background. */ + int pid = fork (); + + if (pid < 0) { perror ("fork"); exit (1); } + else if (pid > 0) + { + /* Parent process: exit normally. */ + exit (0); + } + } + + if (!disable_syslog) + { + /* Open connection to syslog. */ + openlog (package_name, LOG_PID|LOG_NDELAY, LOG_USER); + syslog (LOG_INFO, + "%s starting up on port %d", package_name, port); + } + + if (enable_stack_trace_on_segv) + { +#ifdef CAN_CATCH_SIGSEGV + struct sigaction sa; + + /* Print a stack trace to stderr if we get SIGSEGV. */ + memset (&sa, 0, sizeof sa); + sa.sa_handler = catch_sigsegv; + sa.sa_flags = 0; + sigaction (SIGSEGV, &sa, 0); +#endif + } + + /* Run the startup function, if any. */ + if (startup_fn) + startup_fn (argc, argv); + + /* Start the listener thread. */ + (void) new_listener (sock, processor_fn, 0); + + /* Run the reactor. */ + while (pseudothread_count_threads () > 0) + reactor_invoke (); +} + +#ifdef CAN_CATCH_SIGSEGV +static void +catch_sigsegv (int sig) +{ +#define MAX_ADDRS 50 + const char msg[] = "** Segmentation fault **\n\nStack trace:\n\n"; + void *addr[MAX_ADDRS]; + int n; + + write (2, msg, sizeof (msg) - 1); + + /* Write the stack trace to stderr. */ + n = backtrace (addr, MAX_ADDRS); + backtrace_symbols_fd (addr, n, 2); + + /* Really abort. */ + abort (); +#undef MAX_ADDRS +} +#endif + +void +pthr_server_default_port (int _default_port) +{ + default_port = _default_port; +} + +void +pthr_server_port_option_name (char _port_option_name) +{ + port_option_name = _port_option_name; +} + +void +pthr_server_default_address (in_addr_t _default_address) +{ + default_address = _default_address; +} + +void +pthr_server_address_option_name (char _address_option_name) +{ + address_option_name = _address_option_name; +} + +void +pthr_server_disable_syslog (void) +{ + disable_syslog = 1; +} + +void +pthr_server_package_name (const char *_package_name) +{ + package_name = _package_name; +} + +void +pthr_server_disable_fork (void) +{ + disable_fork = 1; +} + +void +pthr_server_disable_chdir (void) +{ + disable_chdir = 1; +} + +void +pthr_server_disable_close (void) +{ + disable_close = 1; +} + +void +pthr_server_chroot (const char *_root) +{ + root = _root; +} + +void +pthr_server_username (const char *_username) +{ + username = _username; +} + +void +pthr_server_stderr_file (const char *_pathname) +{ + stderr_file = _pathname; +} + +void +pthr_server_startup_fn (void (*_startup_fn) (int argc, char *argv[])) +{ + startup_fn = _startup_fn; +} + +void +pthr_server_enable_stack_trace_on_segv (void) +{ + enable_stack_trace_on_segv = 1; +} diff --git a/src/pthr_server.h b/src/pthr_server.h new file mode 100644 index 0000000..dec17f1 --- /dev/null +++ b/src/pthr_server.h @@ -0,0 +1,123 @@ +/* Generic server process. + * - by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_server.h,v 1.4 2002/11/20 14:22:20 rich Exp $ + */ + +#ifndef PTHR_SERVER_H +#define PTHR_SERVER_H + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +/* Function: pthr_server_main_loop - Enter server main loop. + * Function: pthr_server_default_port + * Function: pthr_server_port_option_name + * Function: pthr_server_default_address + * Function: pthr_server_address_option_name + * Function: pthr_server_disable_syslog + * Function: pthr_server_package_name + * Function: pthr_server_disable_fork + * Function: pthr_server_disable_chdir + * Function: pthr_server_disable_close + * Function: pthr_server_chroot + * Function: pthr_server_username + * Function: pthr_server_stderr_file + * Function: pthr_server_startup_fn + * Function: pthr_server_enable_stack_trace_on_segv + * + * The function @code{pthr_server_main_loop} is a helper function which + * allows you to write very simple servers quickly using @code{pthrlib}. + * Normally your @code{main} should just contain a call to + * @code{pthr_server_main_loop (argc, argv, processor)} + * and you would include a function called @code{processor} which is + * called on every connection. + * + * The other functions allow you to customise the behaviour of + * @code{pthr_server_main_loop}. You should call any of these once + * in your @code{main} before calling @code{pthr_server_main_loop}. + * + * By default the server listens on port 80 and allows you to + * override this on the command line using the @code{-p} option. + * To change this, call @code{pthr_server_default_port} and/or + * @code{pthr_server_port_option_name}. + * + * By default the server listens on address INADDR_ANY (all local + * addresses) and allows you to override this on the command line + * using the @code{-a} option. To change this, call + * @code{pthr_server_default_address} and/or + * @code{pthr_server_address_option_name}. + * + * By default the server writes package and version information to + * syslog (although it will not be able to correctly determine the + * package). Use @code{pthr_server_disable_syslog} to disable syslogging + * entirely, or @code{pthr_server_package_name} to change the name displayed + * in the logs. + * + * By default the server forks into the background, changes to the + * root directory and disconnects entirely from the terminal. Use + * @code{pthr_server_disable_fork}, @code{pthr_server_disable_chdir} and + * @code{pthr_server_disable_close} to change these respectively. + * + * If you wish to have the server chroot after start up, then call + * @code{pthr_server_chroot}. This is only possible if the server + * is run as root, otherwise the chroot is silently ignored. + * + * If you wish to have the server change user and group after start up, + * then call @code{pthr_server_username} (the group is taken from + * the password file and @code{initgroups(3)} is also called). This + * is only possible if the server is run as root, otherwise the + * change user is silently ignored. + * + * If you wish to have @code{stderr} connected to a file (this is done after + * changing user, so make sure the file is accessible or owned by the + * user, not by root), then call @code{pthr_server_stderr_file}. Note + * that unless you call this function or @code{pthr_server_disable_close} + * then @code{stderr} is sent to @code{/dev/null}! + * + * The @code{startup_fn} is called after all of the above operations + * have completed, but before the listener thread is created. You + * do miscellaneous start-of-day functions from here, in particular + * starting up other global threads. The command line arguments are also + * passed to this function. + * + * If @code{pthr_server_enable_stack_trace_on_segv} is called, then + * the server will attempt to dump a full stack trace to @code{stderr} + * if it receives a @code{SIGSEGV} (segmentation fault). This is useful + * for diagnosis of errors in production systems, but relies on some + * esoteric GLIBC functions (if these functions don't exist, then this + * setting does nothing). If the executable is linked with @code{-rdynamic} + * then symbolic names will be given in the stack trace, if available. + */ +extern void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *)); +extern void pthr_server_default_port (int default_port); +extern void pthr_server_port_option_name (char port_option_name); +extern void pthr_server_default_address (in_addr_t default_address); +extern void pthr_server_address_option_name (char address_option_name); +extern void pthr_server_disable_syslog (void); +extern void pthr_server_package_name (const char *package_name); +extern void pthr_server_disable_fork (void); +extern void pthr_server_disable_chdir (void); +extern void pthr_server_disable_close (void); +extern void pthr_server_chroot (const char *root); +extern void pthr_server_username (const char *username); +extern void pthr_server_stderr_file (const char *pathname); +extern void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[])); +extern void pthr_server_enable_stack_trace_on_segv (void); + +#endif /* PTHR_SERVER_H */ diff --git a/src/pthr_stack.c b/src/pthr_stack.c new file mode 100644 index 0000000..9755fd5 --- /dev/null +++ b/src/pthr_stack.c @@ -0,0 +1,119 @@ +/* Pseudothread stacks. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_stack.c,v 1.4 2003/02/05 22:13:33 rich Exp $ + */ + +/* The _PTH_GET_STACK and _PTH_RETURN_STACK functions are responsible + * for allocating new stacks and returning them when they are finished. + * + * We are careful to avoid freeing up a stack while we are actually + * using it. This means in particular that the _PTH_RETURN_STACK + * function doesn't directly free up the current stack. It will + * be freed up instead next time this function is called. + * + * Linux has the ability to allocate stacks from anonymous memory. + * We use this ability if available. However, Linux also has the + * ability to allocate "grows down" stack segments using the + * non-portable MAP_GROWSDOWN flag. We *don't* use this feature + * because (a) LinuxThreads itself doesn't use it any more and + * (b) it can cause intermittent failures if something else + * happens to allocate memory in the middle of our (as yet + * unallocated) stack. + * + * On Linux we also allocate a guard page to protect against + * stack overflow. + */ + +#include "config.h" + +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#include "pthr_stack.h" + +#define GUARD_PAGE_SIZE 8192 + +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif + +static inline void * +alloc_stack (int size) +{ + void *base; + + /* Allocate the actual stack memory. */ + base = mmap (0, size, PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (base == MAP_FAILED) return 0; + + /* Allocate a guard page right at the bottom of the stack. */ + if (mprotect (base, GUARD_PAGE_SIZE, 0) == -1) abort (); + + return base; +} + +static inline void +free_stack (void *base, int size) +{ + munmap (base, size); +} + +/* Stack pending deallocation (see notes above). */ +static void *pending_stack_base = 0; +static int pending_stack_size = 0; + +void * +_pth_get_stack (int size) +{ + void *base; + + /* Is there a stack waiting to be freed up? If so, free it now. */ + if (pending_stack_base) + { + free_stack (pending_stack_base, pending_stack_size); + pending_stack_base = 0; + } + + /* Allocate a stack of the appropriate size, if available. */ + base = alloc_stack (size); + if (!base) return 0; + + return base; +} + +void +_pth_return_stack (void *base, int size) +{ + /* Is there a stack waiting to be freed up? If so, free it now. */ + if (pending_stack_base) + { + free_stack (pending_stack_base, pending_stack_size); + pending_stack_base = 0; + } + + /* Don't actually free the stack right now. We're still using it. */ + pending_stack_base = base; + pending_stack_size = size; +} diff --git a/src/pthr_stack.h b/src/pthr_stack.h new file mode 100644 index 0000000..a5ef02e --- /dev/null +++ b/src/pthr_stack.h @@ -0,0 +1,26 @@ +/* Pseudothread stacks. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_stack.h,v 1.2 2001/06/06 12:34:38 rich Exp $ + */ + +#ifndef PTHR_STACK_H +#define PTHR_STACK_H + +extern void *_pth_get_stack (int size); +extern void _pth_return_stack (void *, int size); + +#endif /* PTHR_STACK_H */ diff --git a/src/pthr_version.h.in b/src/pthr_version.h.in new file mode 100644 index 0000000..c243a86 --- /dev/null +++ b/src/pthr_version.h.in @@ -0,0 +1,4 @@ +/* -*- C -*- @configure_input@ */ + +#define PTHRLIB_PACKAGE "@PACKAGE@" +#define PTHRLIB_VERSION "@VERSION@" diff --git a/src/pthr_wait_queue.c b/src/pthr_wait_queue.c new file mode 100644 index 0000000..10ea097 --- /dev/null +++ b/src/pthr_wait_queue.c @@ -0,0 +1,203 @@ +/* Wait queues. + * by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_wait_queue.c,v 1.5 2002/12/01 14:29:30 rich Exp $ + */ + +#include "config.h" + +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_ASSERT_H +#include +#endif + +#include +#include + +#include "pthr_pseudothread.h" +#include "pthr_wait_queue.h" + +/* See implementation notes in . */ +struct wait_queue +{ + /* List of threads currently sleeping on the queue. */ + vector sleepers; +}; + +wait_queue +new_wait_queue (pool pool) +{ + wait_queue wq = pmalloc (pool, sizeof *wq); + + wq->sleepers = new_vector (pool, pseudothread); + return wq; +} + +int +wq_nr_sleepers (wait_queue wq) +{ + return vector_size (wq->sleepers); +} + +/* To sleep on the wait queue, we register ourselves, then we swap back + * into the reactor context. + */ +void +wq_sleep_on (wait_queue wq) +{ + vector_push_back (wq->sleepers, current_pth); + + /* Swap context back to the calling context. */ + _pth_switch_thread_to_calling_context (); + + /* When we get here, we have been woken up ... */ + + /* Have we been signalled? */ + if (_pth_alarm_received ()) + { + int i; + pseudothread p; + + /* Remove self from sleepers list. */ + for (i = 0; i < vector_size (wq->sleepers); ++i) + { + vector_get (wq->sleepers, i, p); + if (p == current_pth) + { + vector_erase (wq->sleepers, i); + goto found; + } + } + + /* Oops - not found on sleepers list. */ + abort (); + + found: + /* Exit. */ + pth_exit (); + } +} + +/* This is the prepoll handler which actually wakes up the threads. */ +struct wake_up_info +{ + pool pool; + vector sleepers; /* Pseudothreads to wake up. */ + reactor_prepoll handler; /* Handler (must be unregistered at end). */ +}; + +static void +do_wake_up (void *infop) +{ + struct wake_up_info *info = (struct wake_up_info *) infop; + int i; + + for (i = 0; i < vector_size (info->sleepers); ++i) + { + pseudothread pth; + + vector_get (info->sleepers, i, pth); + + /* Swap into the thread context. */ + _pth_switch_calling_to_thread_context (pth); + } + + reactor_unregister_prepoll (info->handler); + delete_pool (info->pool); +} + +/* To wake up we take a private copy of the wait queue, clear the + * sleepers list, then register a prepoll handler which will eventually + * run and wake up each sleeper in turn. + */ +static inline void +wake_up (wait_queue wq, int n) +{ + pool pool; + vector v; + reactor_prepoll handler; + struct wake_up_info *wake_up_info; + + /* Added this experimentally to get around a bug when rws running monolith + * apps which have database connections open is killed. It seems to be + * something to do with having prepoll handlers registered when the + * reactor exits. Avoid this entirely here - there is no need, as far as + * I can see, to do anything in this function if no one is actually sleeping + * on the queue. + * - RWMJ 2002/10/15 + */ + if (vector_size (wq->sleepers) == 0) return; + + /* This will be freed up by the prepoll handler. */ + pool = new_subpool (global_pool); + + /* Take a private copy, either of the whole queue, or just part of it, + * and also clear the list. + */ + if (n == -1) + { + v = copy_vector (pool, wq->sleepers); + vector_clear (wq->sleepers); + } + else + { + v = new_vector (pool, pseudothread); + + while (n > 0) + { + pseudothread pth; + + vector_pop_front (wq->sleepers, pth); + vector_push_back (v, pth); + n--; + } + } + + /* Register a prepoll handler to wake up these sleepin' bewts. */ + wake_up_info = pmalloc (pool, sizeof *wake_up_info); + wake_up_info->pool = pool; + wake_up_info->sleepers = v; + handler = reactor_register_prepoll (pool, do_wake_up, wake_up_info); + wake_up_info->handler = handler; +} + +void +wq_wake_up (wait_queue wq) +{ + wake_up (wq, -1); +} + +void +wq_wake_up_one (wait_queue wq) +{ + /* If there is nothing on the wait queue, but we were instructed to + * wake one, then there is probably a bug in the code. + */ + if (vector_size (wq->sleepers) < 1) abort (); + + wake_up (wq, 1); +} diff --git a/src/pthr_wait_queue.h b/src/pthr_wait_queue.h new file mode 100644 index 0000000..a9a4271 --- /dev/null +++ b/src/pthr_wait_queue.h @@ -0,0 +1,124 @@ +/* Wait queues. + * by Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: pthr_wait_queue.h,v 1.3 2002/12/01 14:29:31 rich Exp $ + */ + +#ifndef PTHR_WAIT_QUEUE_H +#define PTHR_WAIT_QUEUE_H + +#include + +#include "pthr_pseudothread.h" + +struct wait_queue; +typedef struct wait_queue *wait_queue; + +/* Function: new_wait_queue - wait queues + * Function: wq_wake_up + * Function: wq_wake_up_one + * Function: wq_sleep_on + * Function: wq_nr_sleepers + * + * @code{new_wait_queue} creates a wait queue object. + * + * @code{wq_wake_up} wakes up all the threads which are currently + * sleeping on the wait queue. Note that this function does not block, + * and because pseudothreads are non-preemptive, none of the sleeping threads + * will actually begin running until at least the current thread + * blocks somewhere else. + * + * @code{wq_wake_up_one} wakes up just the first thread at the head + * of the wait queue (the one which has been waiting the longest). + * + * @code{wq_sleep_on} sends the current thread to sleep on the wait + * queue. This call blocks (obviously). + * + * @code{wq_nr_sleepers} returns the number of threads which are + * currently asleep on the wait queue. + * + * Please read the HISTORY section below for some background into + * how wait queues are implemented. This may help if you find there + * are tricky race conditions in your code. + * + * History: + * + * Originally, wait queues were implemented using underlying Unix + * pipes. This worked (to some extent) but the overhead of requiring + * one pipe (ie. one inode, two file descriptors) per wait queue + * made this implementation unacceptably heavyweight. + * + * Wait queues are now implemented using a simple hack in the reactor + * which will be described below. + * + * Wait queues are subtle. Consider this example: Threads 1, 2 and 3 are + * sleeping on a wait queue. Now thread 4 wakes up the queue. You would + * expect (probably) threads 1, 2 and 3 to each be woken up and allowed + * to start running. However, since this is a cooperatively multitasking + * environment, it may happen that thread 1 wakes up first, does some + * work and then goes back to sleep on the wait queue, all before threads + * 2 and 3 have woken up. With a naive implementation of wait queues, + * thread 4 might end up waking up thread 1 *again* (and even again after + * that), never waking up threads 2 and 3 and ultimately starving those + * threads. + * + * To avoid this situation, we might consider two possible alternatives: + * either when thread 1 goes back to sleep, it goes to sleep on a + * 'different' queue, or else thread 4 might take a copy of the wait + * queue and delete the queue before it wakes any of the threads up. + * + * Another nasty situation which might arise in real life is this: + * Again, threads 1, 2 and 3 are sleeping. Thread 4 wakes them up. + * Thread 1, while processing its work, happens also to wake up the + * same wait queue. What should happen to this second wake-up event? + * Should it be ignored? Should it wake up threads 2 and 3? Should + * it wake up any other threads which happen to have gone to sleep + * on the queue after 1, 2 and 3? Or perhaps some combination of + * these? + * + * The solution that we have come up with is as follows. A wait queue + * consists of a simple list of threads which are sleeping on it. When + * a thread wishes to sleep on the wait queue, it is added to this list, + * and it switches back into the reactor context. When a thread wishes + * to wake up all sleepers, it: + * (a) copies the list of sleeping pseudothreads into its own private + * space + * (b) clears the list of sleeping pseudothreads + * (c) registers a prepoll handler to run which will wake up (ie. switch + * into the context of) each of these threads in turn + * (d) continues to run to completion. + * A thread which wishes to wake just one pseudothread works similarly + * except that it only copies (and removes) a single item off the list. + * + * Note various invariant conditions: A thread cannot be entered on the + * wait queue sleeping list more than once (because it cannot call + * sleep_on when it is already sleeping). For similar reasons, a thread + * cannot be entered on any of the multiple lists at the same time. + * This implies that a thread cannot be woken up multiple times. + * + * The reader should satisfy themselves that this algorithm is free + * of races, and solves all the problems outlined above. In addition, it + * has the desirable property that wake_up* never sleeps. + * + */ +extern wait_queue new_wait_queue (pool); +extern void wq_wake_up (wait_queue); +extern void wq_wake_up_one (wait_queue); +extern void wq_sleep_on (wait_queue); +extern int wq_nr_sleepers (wait_queue); + +#endif /* PTHR_WAIT_QUEUE_H */ diff --git a/src/test_bigstack.c b/src/test_bigstack.c new file mode 100644 index 0000000..82dcf9a --- /dev/null +++ b/src/test_bigstack.c @@ -0,0 +1,62 @@ +/* Test big stack usage. + * Copyright (C) 2001 Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: test_bigstack.c,v 1.3 2002/08/21 10:42:20 rich Exp $ + */ + +#include "config.h" + +#include +#include + +#include "pthr_reactor.h" +#include "pthr_pseudothread.h" + +static pool test_pool; +static pseudothread test_pth; + +/* XXX I suspect that gcc will just turn this into tail recursion, + * proving nothing. + */ +static void +recurse (int n) +{ + char s[1024]; + s[1023] = 'a'; + if (n > 0) recurse (n-1); +} + +static void +do_test (void *data) +{ + recurse (100); +} + +int +main () +{ + pseudothread_set_stack_size (512 * 1024); + + test_pool = new_pool (); + test_pth = new_pseudothread (test_pool, do_test, 0, "testing thread"); + pth_start (test_pth); + + while (pseudothread_count_threads () > 0) + reactor_invoke (); + + exit (0); +} diff --git a/src/test_context.c b/src/test_context.c new file mode 100644 index 0000000..8c54646 --- /dev/null +++ b/src/test_context.c @@ -0,0 +1,99 @@ +/* Test context switching. + * Copyright (C) 2001-2003 Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: test_context.c,v 1.4 2003/02/05 22:13:33 rich Exp $ + */ + +#include "config.h" + +#include +#include + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_SETJMP_H +#include +#endif + +#include "pthr_context.h" + +#define STACK_SIZE 4096 +#define MARKER_SIZE 16 + +static unsigned char stack [MARKER_SIZE+STACK_SIZE+MARKER_SIZE]; +static mctx_t ctx, calling_ctx; +static int called = 0; +static void fn (void *); +static void setjmp_test (void *); +static jmp_buf jb; + +#define DATA ((void *) 0x12546731) + +int +main () +{ + int i; + + /* Place magic numbers into the marker areas at each end of the stack, so + * we can detect stack overrun. + */ + memset (stack, 0xeb, MARKER_SIZE); + memset (stack + MARKER_SIZE+STACK_SIZE, 0xeb, MARKER_SIZE); + mctx_set (&ctx, fn, DATA, stack + MARKER_SIZE, STACK_SIZE); + mctx_switch (&calling_ctx, &ctx); + assert (called == 1); + + /* Verify the ends of the stack haven't been overrun. */ + for (i = 0; i < MARKER_SIZE; ++i) + assert (stack[i] == 0xeb && stack[i + MARKER_SIZE+STACK_SIZE] == 0xeb); + + /* Check setjmp/longjmp work on the alternate stack. This was found to be + * a problem on Solaris. + */ + mctx_set (&ctx, setjmp_test, DATA, stack + MARKER_SIZE, STACK_SIZE); + mctx_switch (&calling_ctx, &ctx); + + exit (0); +} + +static void +fn (void *data) +{ + assert (data == DATA); + called = 1; + mctx_restore (&calling_ctx); + abort (); +} + +static void call_longjmp (void); + +static void +setjmp_test (void *data) +{ + if (setjmp (jb) == 0) + call_longjmp (); + + mctx_restore (&calling_ctx); +} + +static void +call_longjmp () +{ + longjmp (jb, 1); +} diff --git a/src/test_dbi.c b/src/test_dbi.c new file mode 100644 index 0000000..c661d54 --- /dev/null +++ b/src/test_dbi.c @@ -0,0 +1,316 @@ +/* Test the database interface. + * Copyright (C) 2002 Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: test_dbi.c,v 1.6 2002/12/09 10:43:27 rich Exp $ + */ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_STRING_H +#include +#endif + +#include + +#include "pthr_pseudothread.h" +#include "pthr_dbi.h" + +static pool test_pool; +static pseudothread test_pth; + +static void +do_test (void *data) +{ + db_handle dbh; + st_handle sth; + int userid, rownum; + char *alias, *username; + struct dbi_timestamp ts; + struct dbi_interval inv; + + /* Open a connection to the database. */ + dbh = new_db_handle (test_pool, "", DBI_THROW_ERRORS); + if (!dbh) + pth_die ("failed to connect to the database, check PGHOST, etc."); + + /* Create some tables and some data. */ + sth = st_prepare_cached + (dbh, + "create temporary table tdbi_users " + " (userid int4, " + " username text not null, " + " age int2 not null, " + " last_login date, " + " unique (userid), " + " unique (username))"); + st_execute (sth); + + sth = st_prepare_cached + (dbh, + "create temporary table tdbi_aliases " + " (userid int4 references tdbi_users (userid), " + " alias text not null)"); + st_execute (sth); + + sth = st_prepare_cached + (dbh, + "insert into tdbi_users (userid, username, age) values (?, ?, ?)", + DBI_INT, DBI_STRING, DBI_INT); + st_execute (sth, 1, "rich", 30); + st_execute (sth, 2, "anna", 45); + st_execute (sth, 3, "bob", 55); + st_execute (sth, 4, "dan", 24); + + sth = st_prepare_cached + (dbh, + "insert into tdbi_aliases (userid, alias) values (?, ?)", + DBI_INT, DBI_STRING); + st_execute (sth, 1, "richard"); + st_execute (sth, 1, "richie"); + st_execute (sth, 1, "richy"); + st_execute (sth, 2, "ann"); + st_execute (sth, 2, "annie"); + st_execute (sth, 3, "robert"); + st_execute (sth, 3, "bobbie"); + st_execute (sth, 3, "bobby"); + + /* Select out some results. */ + sth = st_prepare_cached + (dbh, + "select u.userid, u.username, a.alias " + "from tdbi_users u, tdbi_aliases a " + "where u.userid = a.userid " + "order by 3"); + st_execute (sth); + + st_bind (sth, 0, userid, DBI_INT); + st_bind (sth, 1, username, DBI_STRING); + st_bind (sth, 2, alias, DBI_STRING); + + rownum = 0; + while (st_fetch (sth)) + { + switch (rownum) + { + case 0: + assert (userid == 2 && + strcmp (username, "anna") == 0 && + strcmp (alias, "ann") == 0); + break; + case 1: + assert (userid == 2 && + strcmp (username, "anna") == 0 && + strcmp (alias, "annie") == 0); + break; + case 2: + assert (userid == 3 && + strcmp (username, "bob") == 0 && + strcmp (alias, "bobbie") == 0); + break; + case 3: + assert (userid == 3 && + strcmp (username, "bob") == 0 && + strcmp (alias, "bobby") == 0); + break; + case 4: + assert (userid == 1 && + strcmp (username, "rich") == 0 && + strcmp (alias, "richard") == 0); + break; + case 5: + assert (userid == 1 && + strcmp (username, "rich") == 0 && + strcmp (alias, "richie") == 0); + break; + case 6: + assert (userid == 1 && + strcmp (username, "rich") == 0 && + strcmp (alias, "richy") == 0); + break; + case 7: + assert (userid == 3 && + strcmp (username, "bob") == 0 && + strcmp (alias, "robert") == 0); + break; + default: + abort (); + } + + rownum++; + } + + sth = st_prepare_cached + (dbh, + "select username from tdbi_users where age > 40 order by 1"); + st_execute (sth); + + st_bind (sth, 0, username, DBI_STRING); + + rownum = 0; + while (st_fetch (sth)) + { + switch (rownum) + { + case 0: + assert (strcmp (username, "anna") == 0); + break; + case 1: + assert (strcmp (username, "bob") == 0); + break; + default: + abort (); + } + + rownum++; + } + + /* Select out one row, no rows. */ + sth = st_prepare_cached + (dbh, + "select userid from tdbi_users where username = ?", DBI_STRING); + st_execute (sth, "rich"); + + st_bind (sth, 0, userid, DBI_INT); + + assert (st_fetch (sth) != 0); + assert (userid == 1); + assert (st_fetch (sth) == 0); + assert (userid == 1); /* Hasn't splatted userid. */ + + st_execute (sth, "fred"); + + assert (st_fetch (sth) == 0); + + /* Check the st_finish function does nothing bad. */ + st_finish (sth); + + /* Drop the tables. */ + sth = st_prepare_cached + (dbh, + "drop table tdbi_aliases; drop table tdbi_users"); + st_execute (sth); + + /* Test timestamps and intervals. + * XXX Retrieval only tested/supported at present. + */ + sth = st_prepare_cached + (dbh, + "create temporary table tdbi_times " + " (ord int2 not null, ts timestamp, inv interval)"); + st_execute (sth); + + sth = st_prepare_cached + (dbh, + "insert into tdbi_times (ord, ts, inv) values (?, ?, ?)", + DBI_INT, DBI_STRING, DBI_STRING); + st_execute (sth, 0, "2002/11/09 01:02", 0); + st_execute (sth, 1, "2002/10/07 03:04:05", "1 year 1 day"); + st_execute (sth, 2, "2002/09/04 06:07:08.999", "01:00"); + st_execute (sth, 3, 0, "30 mins"); + st_execute (sth, 4, 0, "1 year 2 months 6 days 8 hours 9 mins"); + + sth = st_prepare_cached + (dbh, "select ord, ts, inv from tdbi_times order by 1"); + st_execute (sth); + + st_bind (sth, 1, ts, DBI_TIMESTAMP); + st_bind (sth, 2, inv, DBI_INTERVAL); + + assert (st_fetch (sth)); + assert (!ts.is_null); + assert (ts.year == 2002 && ts.month == 11 && ts.day == 9 && + ts.hour == 1 && ts.min == 2 && ts.sec == 0 && + ts.microsecs == 0); + assert (inv.is_null); + + assert (st_fetch (sth)); + assert (!ts.is_null); + assert (ts.year == 2002 && ts.month == 10 && ts.day == 7 && + ts.hour == 3 && ts.min == 4 && ts.sec == 5 && + ts.microsecs == 0); + assert (!inv.is_null); + assert (inv.years == 1 && inv.months == 0 && + inv.days == 1 && inv.hours == 0 && inv.mins == 0 && + inv.secs == 0); + + assert (st_fetch (sth)); + assert (!ts.is_null); + assert (ts.year == 2002 && ts.month == 9 && ts.day == 4 && + ts.hour == 6 && ts.min == 7 && ts.sec == 8 && + ts.microsecs == 999); + assert (!inv.is_null); + assert (inv.years == 0 && inv.months == 0 && + inv.days == 0 && inv.hours == 1 && inv.mins == 0 && + inv.secs == 0); + + assert (st_fetch (sth)); + assert (ts.is_null); + assert (!inv.is_null); + assert (inv.years == 0 && inv.months == 0 && + inv.days == 0 && inv.hours == 0 && inv.mins == 30 && + inv.secs == 0); + + assert (st_fetch (sth)); + assert (ts.is_null); + assert (!inv.is_null); + assert (inv.years == 1 && inv.months == 2 && + inv.days == 6 && inv.hours == 8 && inv.mins == 9 && + inv.secs == 0); + + /* Drop the table. */ + sth = st_prepare_cached + (dbh, + "drop table tdbi_times"); + st_execute (sth); + + /* Try rolling back the database. */ + db_rollback (dbh); +} + +int +main () +{ + char *env = getenv ("TEST_DBI"); + + /* Skip the test unless the 'TEST_DBI' environment variable is set. */ + if (!env || strcmp (env, "1") != 0) + { + fprintf (stderr, +"WARNING: DBI test skipped. If you want to run the DBI test, then you must\n" +"have:\n" +" (a) A working PostgreSQL >= 7.1 database.\n" +" (b) postgresql-devel packages installed (ie. libpq, header files).\n" +" (c) PGHOST, etc., set up to provide access to a database where I can\n" +" create temporary tables.\n" +"Set the TEST_DBI environment variable to 1 and run this test again.\n"); + + exit (0); + } + + test_pool = new_pool (); + test_pth = new_pseudothread (test_pool, do_test, 0, "testing thread"); + pth_start (test_pth); + + while (pseudothread_count_threads () > 0) + reactor_invoke (); + + exit (0); +} diff --git a/src/test_except1.c b/src/test_except1.c new file mode 100644 index 0000000..3701fab --- /dev/null +++ b/src/test_except1.c @@ -0,0 +1,67 @@ +/* Test the exception handling. + * Copyright (C) 2001 Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: test_except1.c,v 1.4 2002/12/01 14:29:31 rich Exp $ + */ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_STRING_H +#include +#endif + +#include + +#include "pthr_pseudothread.h" + +static pool test_pool; +static pseudothread test_pth; + +static void +do_die (void *data) +{ + pth_die ("this is the message"); +} + +static void +do_test (void *data) +{ + const char *msg; + + /* Check that catch can correctly catch a die. */ + msg = pth_catch (do_die, 0); + + if (!msg || strcmp (msg, "this is the message") != 0) + abort (); +} + +int +main () +{ + test_pool = new_pool (); + test_pth = new_pseudothread (test_pool, do_test, 0, "testing thread"); + pth_start (test_pth); + + while (pseudothread_count_threads () > 0) + reactor_invoke (); + + exit (0); +} diff --git a/src/test_except2.c b/src/test_except2.c new file mode 100644 index 0000000..de97057 --- /dev/null +++ b/src/test_except2.c @@ -0,0 +1,54 @@ +/* Test the exception handling. + * Copyright (C) 2001 Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: test_except2.c,v 1.3 2002/12/01 14:29:31 rich Exp $ + */ + +#include "config.h" + +#include +#include +#include + +#include +#include + +#include "pthr_pseudothread.h" + +static pool test_pool; +static pseudothread test_pth; + +static void +do_test (void *data) +{ + /* Just die, no catch. */ + pth_die ("you can just ignore this message"); + abort (); +} + +int +main () +{ + test_pool = new_pool (); + test_pth = new_pseudothread (test_pool, do_test, 0, "testing thread"); + pth_start (test_pth); + + while (pseudothread_count_threads () > 0) + reactor_invoke (); + + exit (0); +} diff --git a/src/test_except3.c b/src/test_except3.c new file mode 100644 index 0000000..aaad60c --- /dev/null +++ b/src/test_except3.c @@ -0,0 +1,76 @@ +/* Test the exception handling. + * Copyright (C) 2001 Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: test_except3.c,v 1.4 2002/12/01 14:29:31 rich Exp $ + */ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_STRING_H +#include +#endif + +#include + +#include "pthr_pseudothread.h" + +static pool test_pool; +static pseudothread test_pth; + +static void +do_die (void *data) +{ + pth_die ("this is do_die"); +} + +static void +do_nested (void *data) +{ + const char *msg; + + msg = pth_catch (do_die, 0); + + if (!msg || strcmp (msg, "this is do_die") != 0) + abort (); +} + +static void +do_test (void *data) +{ + const char *msg; + + msg = pth_catch (do_nested, 0); + + if (msg) abort (); +} + +int +main () +{ + test_pool = new_pool (); + test_pth = new_pseudothread (test_pool, do_test, 0, "testing thread"); + pth_start (test_pth); + + while (pseudothread_count_threads () > 0) + reactor_invoke (); + + exit (0); +} diff --git a/src/test_mutex.c b/src/test_mutex.c new file mode 100644 index 0000000..d8cbb9d --- /dev/null +++ b/src/test_mutex.c @@ -0,0 +1,122 @@ +/* Test mutexes. + * Copyright (C) 2001 Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: test_mutex.c,v 1.3 2002/12/01 14:29:31 rich Exp $ + */ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_FCNTL_H +#include +#endif + +#include +#include + +#include "pthr_mutex.h" + +#define NR_THREADS 50 +#define NR_INCREMENTS 50 + +static int var = 0; /* The contended variable. */ +static mutex lock; /* The lock. */ +static int nr_threads = NR_THREADS; + +static void +start_monitor_thread (void *data) +{ + int p = 0, v; + + printf ("[ ]\r["); + + while (nr_threads > 0) + { + /* Get value of contended variable and draw a scale. */ + v = 72 * var / (NR_THREADS * NR_INCREMENTS); + while (v > p) + { + printf ("."); + fflush (stdout); + p++; + } + + pth_millisleep (100); + } + + printf ("\n"); + + /* Check v is correct at the end. */ + assert (var == NR_THREADS * NR_INCREMENTS); + exit (0); +} + +static void +start_thread (void *data) +{ + int i; + + for (i = 0; i < NR_INCREMENTS; ++i) + { + int v; + + mutex_enter (lock); + v = var; /* Do a slow R/M/W. */ + pth_millisleep (1); + var = v + 1; + mutex_leave (lock); + + pth_millisleep (1); + } + + nr_threads--; +} + +int +main () +{ + pseudothread pth[NR_THREADS]; + pseudothread monitor_pth; + pool p; + int i; + + /* Create the lock. */ + lock = new_mutex (global_pool); + + /* Create the monitoring thread. */ + p = new_subpool (global_pool); + monitor_pth = new_pseudothread (p, start_monitor_thread, 0, + "monitor"); + pth_start (monitor_pth); + + /* Create the threads. */ + for (i = 0; i < NR_THREADS; ++i) + { + p = new_subpool (global_pool); + pth[i] = new_pseudothread (p, start_thread, 0, + psprintf (p, "thread %d", i)); + } + + /* Start all the threads running. */ + for (i = 0; i < NR_THREADS; ++i) + pth_start (pth[i]); + + for (;;) reactor_invoke (); +} diff --git a/src/test_pseudothread.c b/src/test_pseudothread.c new file mode 100644 index 0000000..547074e --- /dev/null +++ b/src/test_pseudothread.c @@ -0,0 +1,130 @@ +/* Test the pseudothreads. + * Copyright (C) 2001 Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: test_pseudothread.c,v 1.5 2002/12/01 14:29:31 rich Exp $ + */ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#include + +#include "pthr_pseudothread.h" + +static void set_flag (void *data) { int *flag = (int *) data; *flag = 1; } + +static pool test_pool; +static pseudothread test_pth; + +static pool pool1; +static pseudothread pth1; +static int pool_gone = 0; +static int thread_has_run = 0; + +static void +test_exiting (void *data) +{ + assert (current_pth == pth1); + thread_has_run = 1; + pth_exit (); + abort (); +} + +static void +test_timeout (void *data) +{ + assert (current_pth == pth1); + thread_has_run = 1; + pth_timeout (1); + pth_sleep (1000); +} + +static void +do_test (void *data) +{ + /* Check current_pth set correctly on thread start. */ + assert (current_pth == test_pth); + + /* Launch a thread and check that the thread runs and the pool is + * deleted at the end. + */ + pool1 = new_pool (); + pool_register_cleanup_fn (pool1, set_flag, &pool_gone); + pth1 = new_pseudothread (pool1, set_flag, &thread_has_run, "pth1"); + assert (pool_gone == 0); + assert (thread_has_run == 0); + pth_start (pth1); /* The thread actually runs and exits here. */ + assert (pool_gone == 1); + assert (thread_has_run == 1); + assert (current_pth == test_pth); + + /* Check pth_get_* functions. */ + assert (pth_get_pool (test_pth) == test_pool); + assert (strcmp (pth_get_name (test_pth), "testing thread") == 0); + assert (pth_get_thread_num (test_pth) == 0); + assert (pth_get_run (test_pth) == do_test); + assert (pth_get_data (test_pth) == 0); + assert (pth_get_language (test_pth) == 0); + + /* Check pth_exit function. */ + pool1 = new_pool (); + pth1 = new_pseudothread (pool1, test_exiting, 0, "exiting thread"); + thread_has_run = 0; + pth_start (pth1); + assert (thread_has_run == 1); + assert (current_pth == test_pth); + + /* Check timeout for system calls. */ + pool1 = new_pool (); + pool_register_cleanup_fn (pool1, set_flag, &pool_gone); + pth1 = new_pseudothread (pool1, test_timeout, 0, "timeout thread"); + thread_has_run = 0; + pool_gone = 0; + pth_start (pth1); + assert (thread_has_run == 1); + assert (current_pth == test_pth); + while (!pool_gone) { pth_millisleep (100); } +} + +int +main () +{ + test_pool = new_pool (); + test_pth = new_pseudothread (test_pool, do_test, 0, "testing thread"); + + /* Check that pth_start restores current_pth correctly. */ + current_pth = (pseudothread) 0x1234; + pth_start (test_pth); + + assert (current_pth == (pseudothread) 0x1234); + + while (pseudothread_count_threads () > 0) + reactor_invoke (); + + exit (0); +} diff --git a/src/test_reactor.c b/src/test_reactor.c new file mode 100644 index 0000000..1d7e206 --- /dev/null +++ b/src/test_reactor.c @@ -0,0 +1,97 @@ +/* Test the reactor. + * Copyright (C) 2001 Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: test_reactor.c,v 1.2 2002/08/21 10:42:21 rich Exp $ + */ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#include "pthr_reactor.h" + +static void set_flag (void *data) { int *flag = (int *) data; *flag = 1; } +static void set_flag_h (int s, int e, void *data) { int *flag = (int *) data; *flag = 1; } + +int +main () +{ + int p1[2], p2[2]; + reactor_handle h1, h2; + int flag1 = 0, flag2 = 0, flag3 = 0; + char c = '\0'; + reactor_timer t1; + reactor_prepoll pre1; + + /* Create some pipes. */ + if (pipe (p1) < 0) { perror ("pipe"); exit (1); } + if (pipe (p2) < 0) { perror ("pipe"); exit (1); } + + if (fcntl (p1[0], F_SETFL, O_NONBLOCK) < 0) { perror ("fcntl"); exit (1); } + if (fcntl (p1[1], F_SETFL, O_NONBLOCK) < 0) { perror ("fcntl"); exit (1); } + if (fcntl (p2[0], F_SETFL, O_NONBLOCK) < 0) { perror ("fcntl"); exit (1); } + if (fcntl (p2[1], F_SETFL, O_NONBLOCK) < 0) { perror ("fcntl"); exit (1); } + + /* Register read handlers. */ + h1 = reactor_register (p1[0], REACTOR_READ, set_flag_h, &flag1); + h2 = reactor_register (p2[0], REACTOR_READ, set_flag_h, &flag2); + + /* Register a prepoll handler. */ + pre1 = reactor_register_prepoll (global_pool, set_flag, &flag3); + + /* Write something and check the flags. */ + write (p1[1], &c, 1); + reactor_invoke (); + assert (flag1 == 1); + assert (flag2 == 0); + assert (flag3 == 1); + flag1 = flag3 = 0; + read (p1[0], &c, 1); + write (p2[1], &c, 1); + reactor_invoke (); + assert (flag1 == 0); + assert (flag2 == 1); + assert (flag3 == 1); + flag2 = flag3 = 0; + read (p1[0], &c, 1); + + reactor_unregister (h1); + reactor_unregister (h2); + reactor_unregister_prepoll (pre1); + + /* Register a timer function. */ + t1 = reactor_set_timer (global_pool, 1000, set_flag, &flag1); + sleep (2); + reactor_invoke (); + assert (flag1 == 1); + assert (flag3 == 0); + flag1 = 0; + + exit (0); +} diff --git a/src/test_rwlock.c b/src/test_rwlock.c new file mode 100644 index 0000000..66f57fd --- /dev/null +++ b/src/test_rwlock.c @@ -0,0 +1,152 @@ +/* Test rwlocks. + * Copyright (C) 2001 Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: test_rwlock.c,v 1.3 2002/12/01 14:29:31 rich Exp $ + */ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_FCNTL_H +#include +#endif + +#include +#include + +#include "pthr_rwlock.h" + +#define NR_WRITER_THREADS 1 +#define NR_READER_THREADS 500 +#define NR_INCREMENTS 1000 + +static int var = 0; /* The contended variable. */ +static rwlock lock; /* The lock. */ +static int nr_writer_threads = NR_WRITER_THREADS; +static int nr_reader_threads = NR_READER_THREADS; + +static void +start_monitor_thread (void *data) +{ + int p = 0, v; + + printf ("[ ]\r["); + + while (nr_writer_threads > 0 && nr_reader_threads > 0) + { + /* Get value of contended variable and draw a scale. */ + v = 72 * var / (NR_WRITER_THREADS * NR_INCREMENTS); + while (v > p) + { + printf ("."); + fflush (stdout); + p++; + } + + pth_millisleep (100); + } + + printf ("\n"); + + /* Check v is correct at the end. */ + assert (var == NR_WRITER_THREADS * NR_INCREMENTS); + exit (0); +} + +static void +start_writer_thread (void *data) +{ + int i; + + for (i = 0; i < NR_INCREMENTS; ++i) + { + int v; + + rwlock_enter_write (lock); + v = var; /* Do a slow R/M/W. */ + pth_millisleep (1); + var = v + 1; + rwlock_leave (lock); + + pth_millisleep (1); + } + + nr_writer_threads--; +} + +static void +start_reader_thread (void *data) +{ + while (nr_writer_threads > 0) + { + rwlock_enter_read (lock); + pth_millisleep (1); + rwlock_leave (lock); + + pth_millisleep (1); + } + + nr_reader_threads--; +} + +int +main () +{ + pseudothread writer_pth[NR_WRITER_THREADS]; + pseudothread reader_pth[NR_READER_THREADS]; + pseudothread monitor_pth; + pool p; + int i; + + /* Create the lock. */ + lock = new_rwlock (global_pool); + + /* Create the monitoring thread. */ + p = new_subpool (global_pool); + monitor_pth = new_pseudothread (p, start_monitor_thread, 0, + "monitor"); + pth_start (monitor_pth); + + /* Create the writer threads. */ + for (i = 0; i < NR_WRITER_THREADS; ++i) + { + p = new_subpool (global_pool); + writer_pth[i] = new_pseudothread (p, + start_writer_thread, 0, + psprintf (p, "writer thread %d", i)); + } + + /* Create the reader threads. */ + for (i = 0; i < NR_READER_THREADS; ++i) + { + p = new_subpool (global_pool); + reader_pth[i] = new_pseudothread (p, + start_reader_thread, 0, + psprintf (p, "reader thread %d", i)); + } + + /* Start all the threads running. */ + for (i = 0; i < NR_WRITER_THREADS; ++i) + pth_start (writer_pth[i]); + for (i = 0; i < NR_READER_THREADS; ++i) + pth_start (reader_pth[i]); + + for (;;) reactor_invoke (); +} diff --git a/src/test_select.c b/src/test_select.c new file mode 100644 index 0000000..ecf3cbb --- /dev/null +++ b/src/test_select.c @@ -0,0 +1,148 @@ +/* Test pth_select call (this also tests pth_poll, implicitly). + * Copyright (C) 2001 Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: test_select.c,v 1.4 2003/02/05 22:13:33 rich Exp $ + */ + +#include "config.h" + +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#include "pthr_reactor.h" +#include "pthr_pseudothread.h" + +#define NR_WRITERS 4 +#define NR_CHARS 100 + +static pool reader_pool; +static pseudothread reader_pth; +static pool writer_pool[NR_WRITERS]; +static pseudothread writer_pth[NR_WRITERS]; + +static int fds[NR_WRITERS][2]; + +static void +writer (void *vp) +{ + int id = *(int *)vp; + int i, fd = fds[id][1]; + char c[1] = { '0' + id }; + + for (i = 0; i < NR_CHARS; ++i) + { + pth_write (fd, c, 1); + pth_millisleep (3); + } + + c[0] = '\xff'; + pth_write (fd, c, 1); + close (fd); +} + +static void +reader (void *vp) +{ + int i, running = NR_WRITERS, r, max_fd = -1; + fd_set readfds, returnfds; + struct timeval tv; + + FD_ZERO (&readfds); + + for (i = 0; i < NR_WRITERS; ++i) + { + int fd = fds[i][0]; + + if (fd > max_fd) max_fd = fd; + FD_SET (fd, &readfds); + } + + while (running) + { + tv.tv_sec = 0; + tv.tv_usec = 1000; + returnfds = readfds; + r = pth_select (max_fd+1, &returnfds, 0, 0, &tv); + + if (r == -1) abort (); + if (r > 0) + { + for (i = 0; i <= max_fd; ++i) + { + if (FD_ISSET (i, &returnfds)) + { + char c[1]; + + pth_read (i, c, 1); + if (c[0] == '\xff') + { + running--; + close (i); + } + else + { + putchar (c[0]); putchar ('\r'); fflush (stdout); + } + } + } + } + } +} + +int +main () +{ + int i; + + for (i = 0; i < NR_WRITERS; ++i) + { + if (pipe (fds[i]) == -1) abort (); + + if (fcntl (fds[i][0], F_SETFL, O_NONBLOCK) == -1) abort (); + if (fcntl (fds[i][1], F_SETFL, O_NONBLOCK) == -1) abort (); + + writer_pool[i] = new_subpool (global_pool); + writer_pth[i] = new_pseudothread (writer_pool[i], writer, &i, + "writer"); + pth_start (writer_pth[i]); + } + + reader_pool = new_subpool (global_pool); + reader_pth = new_pseudothread (reader_pool, reader, 0, "reader"); + pth_start (reader_pth); + + while (pseudothread_count_threads () > 0) + reactor_invoke (); + + exit (0); +} -- 1.8.3.1