1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
4 <title>pthrlib documentation index</title>
5 <style type="text/css"><!--
10 background-color: #eeeeff;
19 <body bgcolor="#ffffff">
20 <h1>pthrlib documentation index</h1>
23 <code>pthrlib</code> is a library for writing small, fast
24 and efficient servers in C. It offers a list of advanced
25 features. This library has been used to write a
26 <a href="http://www.annexia.org/freeware/rws/">very
27 tiny and fast web server called rws</a> and a closed
32 The primary aims of <code>pthrlib</code> are:
36 <li> Be very simple to use.
37 <li> Provide rich functionality for C programmers out of the box.
38 <li> Meticulous attention paid to the number of syscalls
39 issued and the efficiency of those syscalls, so typically
40 a <code>pthrlib</code> server will outperform any other
41 server architecture or language (on non-SMP).
42 <li> Tiny memory footprint for running servers.
43 <li> Cooperative threading reduces the danger and agony
44 of thread programming.
45 <li> Use of <code>c2lib</code> removes many risks of
49 <h2>Tutorial and programming examples</h2>
52 At the heart of <code>pthrlib</code> is a threading
53 library called <q><b>pseudothreads</b></q>. This library
54 is a typical lightweight threading library, written
55 from scratch to be as small and fast as possible (it
56 therefore lacks many of the unnecessary features
57 which complicate other lightweight threading libraries,
58 such as the ability to suspend threads).
62 A small <code>pthrlib</code> server will start off
63 with just a single listener thread, listening for
64 new connections on a socket. When connections come
65 in, a new thread is spun off to handle it:
69 <tr> <td> listener thread </td> </tr>
70 <tr> <td> processing thread, connected to client #1 </td> </tr>
71 <tr> <td> processing thread, connected to client #2 </td> </tr>
72 <tr> <td> processing thread, connected to client #3 </td> </tr>
73 <tr> <td align="center"> ... </td> </tr>
77 More complex <code>pthrlib</code> servers may contain
78 several core threads: for example our closed-source
79 chat server has one extra thread called <code>autoannounce</code>
80 which periodically sends out announcement messages to
81 all clients. They may also use more than one thread
82 per client. Since threads are very lightweight, you
83 should be able to create as many threads as necessary
87 <h3>Simple <q>echo</q> server</h3>
90 To help you create a server with a listener thread
91 spinning off threads for each incoming connection,
92 there is a helper function called <code>pthr_server_main_loop(3)</code>.
93 Almost all programs will want to use it, such as the
94 following simple <q>echo</q> program (I have split
95 the program into chunks for readability).
99 Standard includes for socket programs, and predeclare
104 #include <stdio.h>
105 #include <stdlib.h>
106 #include <sys/socket.h>
108 #include <pool.h>
110 #include <pthr_pseudothread.h>
111 #include <pthr_iolib.h>
112 #include <pthr_server.h>
114 static void start_processor (int sock, void *data);
115 static void run (void *);
119 Recall from the diagram above that we will start one
120 processing thread for each client. The following structure
121 is used to store the per-thread information about that
126 typedef struct processor_thread
128 pseudothread pth; /* Pseudothread handle. */
129 int sock; /* Socket. */
134 <code>main</code> is very simple, since
135 <code>pthr_server_main_loop</code> does all the hard work
136 of opening up a listening socket, forking into the
137 background, parsing command line arguments and so on.
138 Note that we pass a pointer to our
139 <code>start_processor</code> function.
144 main (int argc, char *argv[])
146 /* Start up the server. */
147 pthr_server_main_loop (argc, argv, start_processor);
154 Whenever a client makes a new connection to our server,
155 the listener thread is going to call <code>start_processor</code>.
156 This creates allocates the per-thread data structure
157 and starts the new thread. The <code>run</code>
158 function is the actual new processing thread running.
163 start_processor (int sock, void *data)
169 p = pmalloc (pool, sizeof *p);
172 p->pth = new_pseudothread (pool, run, p, "processor thread");
174 pth_start (p->pth);
180 processor_thread p = (processor_thread) vp;
184 io = io_fdopen (p->sock);
186 /* Sit in a loop reading strings and echoing them back. */
187 while (io_fgets (buffer, sizeof buffer, io, 1))
188 io_fputs (buffer, io);
197 Here is a typical run with this program (what I
198 typed is shown in <b>bold text</b>):
202 $ <b>./eg_echo -p 9000</b>
203 $ <b>telnet localhost 9000</b>
205 Connected to localhost.localnet (127.0.0.1).
206 Escape character is '^]'.
217 <h3>Simple HTTP server</h3>
220 <b>Note:</b> Although it is possible to write complete
221 mini webservers using just <code>pthrlib</code>, it is
222 often more flexible and just as fast to use
223 <a href="http://www.annexia.org/freeware/rws/">rws's</a>
224 shared object scripts. <code>rws</code> provides you
225 with the complete web serving framework. If you don't
226 use <code>rws</code> and you need to, say, serve an
227 image or a static page at some point in your application,
228 then you will need to either link to another web server
229 like Apache, or else write your own static file service
230 code (it can be done -- we did it for the chat server --
231 but it's unnecessary).
235 The following code comes from example 1 supplied with
236 <code>pthrlib</code>. You can find the working code
237 in the <code>examples/</code> directory. I have omitted
238 some parts of the code in order to concentrate on the
239 interesting and relevant bits.
243 First of all, the <code>main</code> function:
247 static void start_processor (int sock, void *);
250 main (int argc, char *argv[])
252 /* Start up the server. */
253 pthr_server_main_loop (argc, argv, start_processor);
259 start_processor (int sock, void *data)
261 (void) new_eg1_echo_processor (sock);
266 Again, we are using <code>pthr_server_main_loop</code> to
267 do the hard work. <code>start_processor</code> starts the
268 processor thread. The processor thread's <code>run</code>
269 function has the following outline:
276 eg1_echo_processor p = (eg1_echo_processor) vp;
280 io = io_fdopen (p->sock);
282 /* Sit in a loop reading HTTP requests. */
285 /* Parse the HTTP request. */
289 /* Form the HTTP response. */
301 The purpose of this loop is to deal with HTTP keepalives,
302 where a client (or perhaps many different clients through a
303 proxy) makes a series of requests over the same TCP connection.
304 For each request, we'll make an iteration of the <code>while</code>
305 loop. Each request is independent of the previous one.
309 At the beginning of the thread, the listening thread hands us
310 a socket file descriptor in <code>sock</code>. Doing I/O directly
311 on a file descriptor is inconvenient, and it can't be
312 wrapped up directly in a <code>stdio</code> <code>FILE *</code>
313 because these block, hanging the entire process (and all other
314 threads). <code>iolib</code> is a replacement for <code>stdio</code>
315 which works with pools and doesn't block. <code>io_fdopen</code>
316 wraps up a file descriptor in a full buffered <code>io_handle</code>.
320 Now lets look at the step which parses the HTTP request:
324 http_request http_request;
326 pool pool = pth_get_pool (p->pth);
330 /* ----- HTTP request ----- */
331 http_request = new_http_request (pool, io);
332 if (http_request == 0) /* Normal end of file. */
335 cgi = new_cgi (pool, http_request, io);
336 if (cgi == 0) /* XXX Should send an error here. */
341 The <code>new_http_request</code> function parses the
342 HTTP headers. It does pretty much the equivalent of what
343 Apache does just before it hands off to a normal CGI script.
344 You can think of <code>new_cgi</code> as being somewhat
345 equivalent to Perl's <code>CGI.pm</code>.
349 Here's the code which generates the HTTP response:
353 http_response http_response;
357 http_response = new_http_response (pool, http_request,
360 http_response_send_header (http_response,
361 "Content-Type", "text/plain");
362 close = http_response_end_headers (http_response);
364 if (!http_request_is_HEAD (http_request))
366 io_fprintf (io, "Hello. This is your server.\r\n\r\n");
367 io_fprintf (io, "Your browser sent the following headers:\r\n");
369 headers = http_request_get_headers (http_request);
370 for (i = 0; i < vector_size (headers); ++i)
372 vector_get (headers, i, header);
373 io_fprintf (io, "\t%s: %s\r\n", header.key, header.value);
376 io_fprintf (io, "----- end of headers -----\r\n");
378 io_fprintf (io, "The URL was: %s\r\n",
379 http_request_get_url (http_request));
380 io_fprintf (io, "The path component was: %s\r\n",
381 http_request_path (http_request));
382 io_fprintf (io, "The query string was: %s\r\n",
383 http_request_query_string (http_request));
384 io_fprintf (io, "The query arguments were:\r\n");
386 params = cgi_params (cgi);
387 for (i = 0; i < vector_size (params); ++i)
389 vector_get (params, i, name);
390 value = cgi_param (cgi, name);
391 io_fprintf (io, "\t%s=%s\r\n", name, value);
394 io_fprintf (io, "----- end of parameters -----\r\n");
399 <code>new_http_response</code>,
400 <code>http_response_send_header</code> and
401 <code>http_response_end_headers</code> generates the
402 HTTP headers for the response back to the client. We'll
403 see those headers in a minute.
404 Notice that we send back an explicit
405 <code>Content-Type: text/plain</code>
410 The rest of the code actually generates the page. The
411 simplest way to describe it is to show an actual interaction
412 with the server. What I typed is shown in <b>bold text</b>.
416 $ <b>./pthr_eg1_echo -p 9000</b>
417 $ <b>telnet localhost 9000</b>
419 Connected to localhost.
420 Escape character is '^]'.
421 <b>GET /path/here?abc=123&def=456 HTTP/1.0</b>
422 <b>Host: localhost:9000</b>
425 Content-Type: text/plain
426 Server: pthrlib-httpd/3.0.3
427 Date: Fri, 30 Aug 2002 17:04:03 GMT
430 Hello. This is your server.
432 Your browser sent the following headers:
434 ----- end of headers -----
435 The URL was: /path/here?abc=123&def=456
436 The path component was: /path/here
437 The query string was: abc=123&def=456
438 The query arguments were:
441 ----- end of parameters -----
442 Connection closed by foreign host.
445 <h3>Static file webserver</h3>
448 This following code is from example 2. You can find
449 the complete working program in the <code>examples/</code>
450 directory. It's a very minimal webserver which can
451 only serve static files from a single directory. If
452 you start the server up as <code>root</code>, then
453 the server will <code>chroot(2)</code> itself into
454 a configurable directory, and change its user ID to
455 <code>nobody.nobody</code>
459 Again the <code>main</code> function uses
460 <code>pthr_server_main_loop</code> for simplicity.
461 However one thing which <code>pthr_server_main_loop</code>
462 can't do (yet) is set up signal handlers, so we have
463 to do those by hand first:
468 main (int argc, char *argv[])
472 /* Intercept signals. */
473 memset (&sa, 0, sizeof sa);
474 sa.sa_handler = catch_quit_signal;
475 sa.sa_flags = SA_RESTART;
476 sigaction (SIGINT, &sa, 0);
477 sigaction (SIGQUIT, &sa, 0);
478 sigaction (SIGTERM, &sa, 0);
480 /* ... but ignore SIGPIPE errors. */
481 sa.sa_handler = SIG_IGN;
482 sa.sa_flags = SA_RESTART;
483 sigaction (SIGPIPE, &sa, 0);
485 /* Start up the server. */
486 pthr_server_chroot (root);
487 pthr_server_username (user);
488 pthr_server_main_loop (argc, argv, start_processor);
494 start_processor (int sock, void *data)
496 (void) new_eg2_server_processor (sock);
500 catch_quit_signal (int sig)
507 Notice that just before we actually call
508 <code>pthr_server_main_loop</code>, we configure
509 the main loop code first by telling it the
510 root directory (where we want to <code>chroot(2)</code>
511 to) and the username (<code>nobody</code>).
515 The <code>eg2_server_processor</code> thread
516 structure contains a little more data this time. It
517 contains most of the information about the current
522 struct eg2_server_processor
524 /* Pseudothread handle. */
530 /* Pool for memory allocations. */
534 http_request http_request;
542 The <code>run</code> function has the same basic outline,
543 ie. a <code>while</code> loop to process each request on
544 the same keep-alive connection, and a call to
545 <code>new_http_request</code> to parse the HTTP headers. The
546 outline code is shown in <span style="color: red">red text</span>
547 below. The code to handle the response is shown in
552 <span style="color: red">static void
555 eg2_server_processor p = (eg2_server_processor) vp;
560 p->io = io_fdopen (p->sock);
562 /* Sit in a loop reading HTTP requests. */
565 /* ----- HTTP request ----- */
566 p->http_request = new_http_request (pool, p->io);
567 if (p->http_request == 0) /* Normal end of file. */
570 /* Get the path and locate the file. */
571 path = http_request_path (p->http_request);
572 if (stat (path, &statbuf) == -1)
574 close = file_not_found_error (p);
578 /* File or directory? */
579 if (S_ISDIR (statbuf.st_mode))
581 close = serve_directory (p, path, &statbuf);
584 else if (S_ISREG (statbuf.st_mode))
586 close = serve_file (p, path, &statbuf);
591 close = file_not_found_error (p);
594 <span style="color: red">}
596 io_fclose (p->io);
603 This is a very simple webserver, so all it does is take the
604 <code>path</code> component of the request, and uses it directly
605 as a filename (note that it relies completely on the
606 <code>chroot(2)</code> environment for security).
610 Firstly it calls <code>stat</code> to find out if the filename
611 is a directory or a regular file. If it is neither, or if the
612 file doesn't exist, it calls <code>file_not_found_error</code>
613 which sends back a 404 FILE NOT FOUND error.
617 If the file is a regular file, we call <code>serve_file</code>,
618 which is a simple piece of code:
623 serve_file (eg2_server_processor p, const char *path,
624 const struct stat *statbuf)
626 http_response http_response;
628 char *buffer = alloca (n);
630 char *content_length = pitoa (p->pool, statbuf->st_size);
632 fd = open (path, O_RDONLY);
634 return file_not_found_error (p);
636 http_response = new_http_response (pool, p->http_request, p->io,
638 http_response_send_headers (http_response,
640 "Content-Type", "text/plain",
641 "Content-Length", content_length,
642 /* End of headers. */
644 cl = http_response_end_headers (http_response);
646 if (http_request_is_HEAD (p->http_request)) return cl;
648 while ((r = read (fd, buffer, n)) > 0)
650 io_fwrite (buffer, r, 1, p->io);
663 Firstly we work out the size of the file, using the
664 <code>statbuf.st_size</code> field. The
665 <a href="http://www.annexia.org/freeware/c2lib/">c2lib</a>
666 function <code>pitoa</code> turns this into a string (all
667 headers must be passed as strings). Next we open the
668 file. If this fails, then the file is inaccessible or
669 has just gone, so we return a 404 instead.
673 Next we generate our headers:
677 Content-Type: text/plain
678 Content-Length: <i>(size of the file in octets)</i>
682 <code>pthrlib</code> will generate other standard
687 If the request was a <code>HEAD</code> request, then
688 the client only wants to see the headers, so we stop
689 right there. Otherwise we copy the file back to our
694 Party question: Why is it OK to use <code>read(2)</code>
695 when reading the file, but not OK to use <code>write(2)</code>
696 when writing to the socket? Why will this <i>not</i> cause
697 the whole server process to block (on Linux at least)?
701 Serving a directory is more complicated, so we'll take it in
702 steps. Recall that to serve a directory, we actually need
703 to create an HTML page which lists the files, with information
704 about those files and links to the files themselves.
708 Firstly if the user requested the directory as:
712 http://your.hostname/path/to/directory
716 then we need to redirect them to:
720 http://your.hostname/path/to/directory<b>/</b>
724 (note the trailing slash). The reason for this is that
725 relative links within our page won't work otherwise. The
726 browser will request <code>/path/to/file</code> instead of
727 <code>/path/to/directory/file</code>. This is actually a
728 bit of webserver arcana which is often forgotten. If you
729 don't believe me, Apache does this too: go look at the source!
734 serve_directory (eg2_server_processor p, const char *path,
735 const struct stat *statbuf)
737 http_response http_response;
742 /* If the path doesn't end with a "/", then we need to send
743 * a redirect back to the client so it refetches the page
746 if (path[strlen (path)-1] != '/')
748 char *location = psprintf (p->pool, "%s/", path);
749 return moved_permanently (p, location);
754 <code>moved_permanently</code> sends back a 301 MOVED PERMANENTLY
755 page causing the browser to re-request the new location.
759 The next piece of code should be familiar boilerplate. We open
760 the directory, and send back headers. If the request is
761 <code>HEAD</code> we then drop out.
765 dir = opendir (path);
767 return file_not_found_error (p);
769 http_response = new_http_response (pool, p->http_request, p->io,
771 http_response_send_headers (http_response,
773 "Content-Type", "text/html",
775 /* End of headers. */
777 close = http_response_end_headers (http_response);
779 if (http_request_is_HEAD (p->http_request)) return close;
783 The next piece of code is the complicated bit which generates
784 the HTML page listing the files:
788 io_fprintf (p->io,
789 "<html><head><title>Directory: %s</title></head>" CRLF
790 "<body bgcolor=\"#ffffff\">" CRLF
791 "<h1>Directory: %s</h1>" CRLF
793 "<tr><td></td><td></td>"
794 "<td><a href=\"..\">Parent directory</a></td></tr>" CRLF,
797 while ((d = readdir (dir)) != 0)
799 if (d->d_name[0] != '.') /* Ignore hidden files. */
801 const char *filename;
802 struct stat fstatbuf;
804 /* Generate the full pathname to this file. */
805 filename = psprintf (p->pool, "%s/%s", path, d->d_name);
807 /* Stat the file to find out what it is. */
808 if (lstat (filename, &fstatbuf) == 0)
813 if (S_ISDIR (fstatbuf.st_mode))
815 else if (S_ISREG (fstatbuf.st_mode))
817 else if (S_ISLNK (fstatbuf.st_mode))
822 size = fstatbuf.st_size;
824 /* Print the details. */
825 io_fprintf (p->io,
826 "<tr><td>[ %s ]</td><td align=right>%d</td>"
827 "<td><a href=\"%s%s\">%s</a>",
830 S_ISDIR (fstatbuf.st_mode) ? "/" : "",
833 if (S_ISLNK (fstatbuf.st_mode))
835 char link[NAME_MAX+1];
838 r = readlink (filename, link, NAME_MAX);
839 if (r >= 0) link[r] = '\0';
840 else strcpy (link, "unknown");
842 io_fprintf (p->io, " -&gt; %s", link);
845 io_fputs ("</td></tr>" CRLF, p->io);
850 io_fprintf (p->io,
851 "</table></body></html>" CRLF);
857 We first send the top of the HTML page, and the beginning
858 of the table (the whole page is one large table, of course).
862 Next we loop over the directory entries using <code>readdir(3)</code>
863 to read each one. Ignoring files which start with a dot (.) we
864 <code>lstat(2)</code> each file to find out if it's a directory,
865 file or symbolic link, or some type of special device node.
869 Depending on the file type, we generate a different bit
870 of HTML containing a relative link to the file or
871 directory (if it's a directory we need to remember to
872 append a trailing slash to the name to avoid that extra
877 Finally after we reach the end of the directory we finish
878 of the table and the page and return.
881 <h2>Further examples</h2>
884 That's the end of this <code>pthrlib</code> tutorial, I
889 <code>pthrlib</code> isn't just about writing web servers.
890 You can use it to write all sorts of types of servers,
891 or even clients (it has an FTP client library which I
892 used to load-test <code>Net::FTPServer</code>).
896 If, however, you feel like using <code>pthrlib</code> to
897 write a web server, I strongly urge you to use
898 <a href="http://www.annexia.org/freeware/rws/">rws</a>
899 and shared object scripts. These are described in
900 the <a href="http://www.annexia.org/freeware/rws/doc/">rws
901 documentation</a>. (rws uses <code>pthrlib</code>).
904 <h2>Links to manual pages</h2>
907 (These manual pages are not always up to date. For the
908 latest documentation, always consult the manual pages
909 supplied with the latest <code>pthrlib</code> package!)
912 <h3>Pseudothreads</h3>
915 <li> <a href="new_pseudothread.3.html"><code>new_pseudothread(3)</code></a> </li>
916 <li> <a href="pseudothread_count_threads.3.html"><code>pseudothread_count_threads(3)</code></a> </li>
917 <li> <a href="pseudothread_get_stack_size.3.html"><code>pseudothread_get_stack_size(3)</code></a> </li>
918 <li> <a href="pseudothread_get_threads.3.html"><code>pseudothread_get_threads(3)</code></a> </li>
919 <li> <a href="pseudothread_set_stack_size.3.html"><code>pseudothread_set_stack_size(3)</code></a> </li>
920 <li> <a href="pth_accept.3.html"><code>pth_accept(3)</code></a> </li>
921 <li> <a href="pth_catch.3.html"><code>pth_catch(3)</code></a> </li>
922 <li> <a href="pth_connect.3.html"><code>pth_connect(3)</code></a> </li>
923 <li> <a href="pth_die.3.html"><code>pth_die(3)</code></a> </li>
924 <li> <a href="pth_exit.3.html"><code>pth_exit(3)</code></a> </li>
925 <li> <a href="pth_get_data.3.html"><code>pth_get_data(3)</code></a> </li>
926 <li> <a href="pth_get_language.3.html"><code>pth_get_language(3)</code></a> </li>
927 <li> <a href="pth_get_name.3.html"><code>pth_get_name(3)</code></a> </li>
928 <li> <a href="pth_get_PC.3.html"><code>pth_get_PC(3)</code></a> </li>
929 <li> <a href="pth_get_pool.3.html"><code>pth_get_pool(3)</code></a> </li>
930 <li> <a href="pth_get_run.3.html"><code>pth_get_run(3)</code></a> </li>
931 <li> <a href="pth_get_SP.3.html"><code>pth_get_SP(3)</code></a> </li>
932 <li> <a href="pth_get_stack.3.html"><code>pth_get_stack(3)</code></a> </li>
933 <li> <a href="pth_get_stack_size.3.html"><code>pth_get_stack_size(3)</code></a> </li>
934 <li> <a href="pth_get_thread_num.3.html"><code>pth_get_thread_num(3)</code></a> </li>
935 <li> <a href="pth_get_tz.3.html"><code>pth_get_tz(3)</code></a> </li>
936 <li> <a href="pth_millisleep.3.html"><code>pth_millisleep(3)</code></a> </li>
937 <li> <a href="pth_nanosleep.3.html"><code>pth_nanosleep(3)</code></a> </li>
938 <li> <a href="pth_poll.3.html"><code>pth_poll(3)</code></a> </li>
939 <li> <a href="pth_read.3.html"><code>pth_read(3)</code></a> </li>
940 <li> <a href="pth_recv.3.html"><code>pth_recv(3)</code></a> </li>
941 <li> <a href="pth_recvfrom.3.html"><code>pth_recvfrom(3)</code></a> </li>
942 <li> <a href="pth_recvmsg.3.html"><code>pth_recvmsg(3)</code></a> </li>
943 <li> <a href="pth_select.3.html"><code>pth_select(3)</code></a> </li>
944 <li> <a href="pth_send.3.html"><code>pth_send(3)</code></a> </li>
945 <li> <a href="pth_sendmsg.3.html"><code>pth_sendmsg(3)</code></a> </li>
946 <li> <a href="pth_sendto.3.html"><code>pth_sendto(3)</code></a> </li>
947 <li> <a href="pth_set_language.3.html"><code>pth_set_language(3)</code></a> </li>
948 <li> <a href="pth_set_name.3.html"><code>pth_set_name(3)</code></a> </li>
949 <li> <a href="pth_set_tz.3.html"><code>pth_set_tz(3)</code></a> </li>
950 <li> <a href="pth_sleep.3.html"><code>pth_sleep(3)</code></a> </li>
951 <li> <a href="pth_start.3.html"><code>pth_start(3)</code></a> </li>
952 <li> <a href="pth_timeout.3.html"><code>pth_timeout(3)</code></a> </li>
953 <li> <a href="pth_write.3.html"><code>pth_write(3)</code></a> </li>
956 <h3>Server main loop</h3>
959 <li> <a href="pthr_server_main_loop.3.html"><code>pthr_server_main_loop(3)</code></a> </li>
960 <li> <a href="pthr_server_default_port.3.html"><code>pthr_server_default_port(3)</code></a> </li>
961 <li> <a href="pthr_server_port_option_name.3.html"><code>pthr_server_port_option_name(3)</code></a> </li>
962 <li> <a href="pthr_server_disable_syslog.3.html"><code>pthr_server_disable_syslog(3)</code></a> </li>
963 <li> <a href="pthr_server_package_name.3.html"><code>pthr_server_package_name(3)</code></a> </li>
964 <li> <a href="pthr_server_disable_fork.3.html"><code>pthr_server_disable_fork(3)</code></a> </li>
965 <li> <a href="pthr_server_disable_chdir.3.html"><code>pthr_server_disable_chdir(3)</code></a> </li>
966 <li> <a href="pthr_server_disable_close.3.html"><code>pthr_server_disable_close(3)</code></a> </li>
967 <li> <a href="pthr_server_chroot.3.html"><code>pthr_server_chroot(3)</code></a> </li>
968 <li> <a href="pthr_server_username.3.html"><code>pthr_server_username(3)</code></a> </li>
969 <li> <a href="pthr_server_stderr_file.3.html"><code>pthr_server_stderr_file(3)</code></a> </li>
970 <li> <a href="pthr_server_startup_fn.3.html"><code>pthr_server_startup_fn(3)</code></a> </li>
973 <h3>Buffered I/O library</h3>
976 <li> <a href="io_copy.3.html"><code>io_copy(3)</code></a> </li>
977 <li> <a href="io_fclose.3.html"><code>io_fclose(3)</code></a> </li>
978 <li> <a href="io_fdopen.3.html"><code>io_fdopen(3)</code></a> </li>
979 <li> <a href="io_fflush.3.html"><code>io_fflush(3)</code></a> </li>
980 <li> <a href="io_fgetc.3.html"><code>io_fgetc(3)</code></a> </li>
981 <li> <a href="io_fgets.3.html"><code>io_fgets(3)</code></a> </li>
982 <li> <a href="io_fileno.3.html"><code>io_fileno(3)</code></a> </li>
983 <li> <a href="io_fprintf.3.html"><code>io_fprintf(3)</code></a> </li>
984 <li> <a href="io_fputc.3.html"><code>io_fputc(3)</code></a> </li>
985 <li> <a href="io_fputs.3.html"><code>io_fputs(3)</code></a> </li>
986 <li> <a href="io_fread.3.html"><code>io_fread(3)</code></a> </li>
987 <li> <a href="io_fwrite.3.html"><code>io_fwrite(3)</code></a> </li>
988 <li> <a href="io_get_inbufcount.3.html"><code>io_get_inbufcount(3)</code></a> </li>
989 <li> <a href="io_get_outbufcount.3.html"><code>io_get_outbufcount(3)</code></a> </li>
990 <li> <a href="io_pclose.3.html"><code>io_pclose(3)</code></a> </li>
991 <li> <a href="io_popen.3.html"><code>io_popen(3)</code></a> </li>
992 <li> <a href="io_setbufmode.3.html"><code>io_setbufmode(3)</code></a> </li>
993 <li> <a href="io_ungetc.3.html"><code>io_ungetc(3)</code></a> </li>
996 <h3>HTTP server library</h3>
999 <li> <a href="http_get_log_file.3.html"><code>http_get_log_file(3)</code></a> </li>
1000 <li> <a href="http_get_servername.3.html"><code>http_get_servername(3)</code></a> </li>
1001 <li> <a href="http_request_get_header.3.html"><code>http_request_get_header(3)</code></a> </li>
1002 <li> <a href="http_request_get_headers.3.html"><code>http_request_get_headers(3)</code></a> </li>
1003 <li> <a href="http_request_is_HEAD.3.html"><code>http_request_is_HEAD(3)</code></a> </li>
1004 <li> <a href="http_request_method.3.html"><code>http_request_method(3)</code></a> </li>
1005 <li> <a href="http_request_method_string.3.html"><code>http_request_method_string(3)</code></a> </li>
1006 <li> <a href="http_request_nr_headers.3.html"><code>http_request_nr_headers(3)</code></a> </li>
1007 <li> <a href="http_request_path.3.html"><code>http_request_path(3)</code></a> </li>
1008 <li> <a href="http_request_query_string.3.html"><code>http_request_query_string(3)</code></a> </li>
1009 <li> <a href="http_request_time.3.html"><code>http_request_time(3)</code></a> </li>
1010 <li> <a href="http_request_url.3.html"><code>http_request_url(3)</code></a> </li>
1011 <li> <a href="http_request_version.3.html"><code>http_request_version(3)</code></a> </li>
1012 <li> <a href="http_response_end_headers.3.html"><code>http_response_end_headers(3)</code></a> </li>
1013 <li> <a href="http_response_send_header.3.html"><code>http_response_send_header(3)</code></a> </li>
1014 <li> <a href="http_response_send_headers.3.html"><code>http_response_send_headers(3)</code></a> </li>
1015 <li> <a href="http_set_log_file.3.html"><code>http_set_log_file(3)</code></a> </li>
1016 <li> <a href="http_set_servername.3.html"><code>http_set_servername(3)</code></a> </li>
1017 <li> <a href="new_http_request.3.html"><code>new_http_request(3)</code></a> </li>
1018 <li> <a href="new_http_response.3.html"><code>new_http_response(3)</code></a> </li>
1021 <h3>CGI library</h3>
1024 <li> <a href="cgi_erase.3.html"><code>cgi_erase(3)</code></a> </li>
1025 <li> <a href="cgi_escape.3.html"><code>cgi_escape(3)</code></a> </li>
1026 <li> <a href="cgi_get_post_max.3.html"><code>cgi_get_post_max(3)</code></a> </li>
1027 <li> <a href="cgi_param.3.html"><code>cgi_param(3)</code></a> </li>
1028 <li> <a href="cgi_param_list.3.html"><code>cgi_param_list(3)</code></a> </li>
1029 <li> <a href="cgi_params.3.html"><code>cgi_params(3)</code></a> </li>
1030 <li> <a href="cgi_set_post_max.3.html"><code>cgi_set_post_max(3)</code></a> </li>
1031 <li> <a href="cgi_unescape.3.html"><code>cgi_unescape(3)</code></a> </li>
1032 <li> <a href="copy_cgi.3.html"><code>copy_cgi(3)</code></a> </li>
1033 <li> <a href="new_cgi.3.html"><code>new_cgi(3)</code></a> </li>
1036 <h3>Thread synchronisation (mutexes, R/W-locks, wait queues)</h3>
1039 <li> <a href="mutex_enter.3.html"><code>mutex_enter(3)</code></a> </li>
1040 <li> <a href="mutex_leave.3.html"><code>mutex_leave(3)</code></a> </li>
1041 <li> <a href="mutex_try_enter.3.html"><code>mutex_try_enter(3)</code></a> </li>
1042 <li> <a href="new_mutex.3.html"><code>new_mutex(3)</code></a> </li>
1043 <li> <a href="new_rwlock.3.html"><code>new_rwlock(3)</code></a> </li>
1044 <li> <a href="new_wait_queue.3.html"><code>new_wait_queue(3)</code></a> </li>
1045 <li> <a href="rwlock_enter_read.3.html"><code>rwlock_enter_read(3)</code></a> </li>
1046 <li> <a href="rwlock_enter_write.3.html"><code>rwlock_enter_write(3)</code></a> </li>
1047 <li> <a href="rwlock_leave.3.html"><code>rwlock_leave(3)</code></a> </li>
1048 <li> <a href="rwlock_readers_have_priority.3.html"><code>rwlock_readers_have_priority(3)</code></a> </li>
1049 <li> <a href="rwlock_try_enter_read.3.html"><code>rwlock_try_enter_read(3)</code></a> </li>
1050 <li> <a href="rwlock_try_enter_write.3.html"><code>rwlock_try_enter_write(3)</code></a> </li>
1051 <li> <a href="rwlock_writers_have_priority.3.html"><code>rwlock_writers_have_priority(3)</code></a> </li>
1052 <li> <a href="wq_nr_sleepers.3.html"><code>wq_nr_sleepers(3)</code></a> </li>
1053 <li> <a href="wq_sleep_on.3.html"><code>wq_sleep_on(3)</code></a> </li>
1054 <li> <a href="wq_wake_up.3.html"><code>wq_wake_up(3)</code></a> </li>
1055 <li> <a href="wq_wake_up_one.3.html"><code>wq_wake_up_one(3)</code></a> </li>
1058 <h3>FTP client library</h3>
1061 <li> <a href="ftpc_ascii.3.html"><code>ftpc_ascii(3)</code></a> </li>
1062 <li> <a href="ftpc_binary.3.html"><code>ftpc_binary(3)</code></a> </li>
1063 <li> <a href="ftpc_cdup.3.html"><code>ftpc_cdup(3)</code></a> </li>
1064 <li> <a href="ftpc_cwd.3.html"><code>ftpc_cwd(3)</code></a> </li>
1065 <li> <a href="ftpc_delete.3.html"><code>ftpc_delete(3)</code></a> </li>
1066 <li> <a href="ftpc_dir.3.html"><code>ftpc_dir(3)</code></a> </li>
1067 <li> <a href="ftpc_get.3.html"><code>ftpc_get(3)</code></a> </li>
1068 <li> <a href="ftpc_login.3.html"><code>ftpc_login(3)</code></a> </li>
1069 <li> <a href="ftpc_ls.3.html"><code>ftpc_ls(3)</code></a> </li>
1070 <li> <a href="ftpc_mkdir.3.html"><code>ftpc_mkdir(3)</code></a> </li>
1071 <li> <a href="ftpc_put.3.html"><code>ftpc_put(3)</code></a> </li>
1072 <li> <a href="ftpc_pwd.3.html"><code>ftpc_pwd(3)</code></a> </li>
1073 <li> <a href="ftpc_quit.3.html"><code>ftpc_quit(3)</code></a> </li>
1074 <li> <a href="ftpc_quote.3.html"><code>ftpc_quote(3)</code></a> </li>
1075 <li> <a href="ftpc_rmdir.3.html"><code>ftpc_rmdir(3)</code></a> </li>
1076 <li> <a href="ftpc_set_passive_mode.3.html"><code>ftpc_set_passive_mode(3)</code></a> </li>
1077 <li> <a href="ftpc_type.3.html"><code>ftpc_type(3)</code></a> </li>
1078 <li> <a href="new_ftpc.3.html"><code>new_ftpc(3)</code></a> </li>
1082 <address><a href="mailto:rich@annexia.org">Richard Jones</a></address>
1083 <!-- Created: Wed May 1 19:36:16 BST 2002 -->
1084 <!-- hhmts start -->
1085 Last modified: Sun Dec 1 14:44:00 GMT 2002