Add to git.
[pthrlib.git] / doc / index.html
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2 <html>
3   <head>
4     <title>pthrlib documentation index</title>
5     <style type="text/css"><!--
6       h1 {
7       text-align: center;
8       }
9       pre {
10       background-color: #eeeeff;
11       }
12       code {
13       color: green;
14       font-weight: bold;
15       }
16       --></style>
17   </head>
18
19   <body bgcolor="#ffffff">
20     <h1>pthrlib documentation index</h1>
21
22     <p>
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
28       source chat server.
29     </p>
30
31     <p>
32       The primary aims of <code>pthrlib</code> are:
33     </p>
34
35     <ul>
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
46         buffer overflows.
47     </ul>
48
49     <h2>Tutorial and programming examples</h2>
50
51     <p>
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).
59     </p>
60
61     <p>
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:
66     </p>
67
68     <table border="1">
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>
74     </table>
75
76     <p>
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
84       for your application.
85     </p>
86
87     <h3>Simple <q>echo</q> server</h3>
88
89     <p>
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).
96     </p>
97
98     <p>
99       Standard includes for socket programs, and predeclare
100       static functions:
101     </p>
102
103 <pre>
104 #include &lt;stdio.h&gt;
105 #include &lt;stdlib.h&gt;
106 #include &lt;sys/socket.h&gt;
107
108 #include &lt;pool.h&gt;
109
110 #include &lt;pthr_pseudothread.h&gt;
111 #include &lt;pthr_iolib.h&gt;
112 #include &lt;pthr_server.h&gt;
113
114 static void start_processor (int sock, void *data);
115 static void run (void *);
116 </pre>
117
118     <p>
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
122       processing thread:
123     </p>
124
125 <pre>
126 typedef struct processor_thread
127 {
128   pseudothread pth;             /* Pseudothread handle. */
129   int sock;                     /* Socket. */
130 } *processor_thread;
131 </pre>
132
133     <p>
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.
140     </p>
141
142 <pre>
143 int
144 main (int argc, char *argv[])
145 {
146   /* Start up the server. */
147   pthr_server_main_loop (argc, argv, start_processor);
148
149   exit (0);
150 }
151 </pre>
152
153     <p>
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.
159     </p>
160
161 <pre>
162 static void
163 start_processor (int sock, void *data)
164 {
165   pool pool;
166   processor_thread p;
167
168   pool = new_pool ();
169   p = pmalloc (pool, sizeof *p);
170
171   p-&gt;sock = sock;
172   p-&gt;pth = new_pseudothread (pool, run, p, "processor thread");
173
174   pth_start (p-&gt;pth);
175 }
176
177 static void
178 run (void *vp)
179 {
180   processor_thread p = (processor_thread) vp;
181   io_handle io;
182   char buffer[256];
183
184   io = io_fdopen (p-&gt;sock);
185
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);
189
190   io_fclose (io);
191
192   pth_exit ();
193 }
194 </pre>
195
196     <p>
197       Here is a typical run with this program (what I
198       typed is shown in <b>bold text</b>):
199     </p>
200
201 <pre>
202 $ <b>./eg_echo -p 9000</b>
203 $ <b>telnet localhost 9000</b>
204 Trying 127.0.0.1...
205 Connected to localhost.localnet (127.0.0.1).
206 Escape character is '^]'.
207 <b>hello</b>
208 hello
209 <b>goodbye</b>
210 goodbye
211 <b>^]</b>
212
213 telnet> <b>quit</b>
214 Connection closed.
215 </pre>
216
217     <h3>Simple HTTP server</h3>
218
219     <p>
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).
232     </p>
233
234     <p>
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.
240     </p>
241
242     <p>
243       First of all, the <code>main</code> function:
244     </p>
245
246 <pre>
247 static void start_processor (int sock, void *);
248
249 int
250 main (int argc, char *argv[])
251 {
252   /* Start up the server. */
253   pthr_server_main_loop (argc, argv, start_processor);
254
255   exit (0);
256 }
257
258 static void
259 start_processor (int sock, void *data)
260 {
261   (void) new_eg1_echo_processor (sock);
262 }
263 </pre>
264
265     <p>
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:
270     </p>
271
272 <pre>
273 static void
274 run (void *vp)
275 {
276   eg1_echo_processor p = (eg1_echo_processor) vp;
277   int close = 0;
278   io_handle io;
279
280   io = io_fdopen (p-&gt;sock);
281
282   /* Sit in a loop reading HTTP requests. */
283   while (!close)
284     {
285       /* Parse the HTTP request. */
286             :    :    :
287             :    :    :
288
289       /* Form the HTTP response. */
290             :    :    :
291             :    :    :
292     }
293
294   io_fclose (io);
295
296   pth_exit ();
297 }
298 </pre>
299
300     <p>
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.
306     </p>
307
308     <p>
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>.
317     </p>
318
319     <p>
320       Now lets look at the step which parses the HTTP request:
321     </p>
322
323 <pre>
324   http_request http_request;
325   cgi cgi;
326   pool pool = pth_get_pool (p-&gt;pth);
327             :    :    :
328             :    :    :
329
330       /* ----- HTTP request ----- */
331       http_request = new_http_request (pool, io);
332       if (http_request == 0)    /* Normal end of file. */
333         break;
334
335       cgi = new_cgi (pool, http_request, io);
336       if (cgi == 0)             /* XXX Should send an error here. */
337         break;
338 </pre>
339
340     <p>
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>.
346     </p>
347
348     <p>
349       Here's the code which generates the HTTP response:
350     </p>
351
352 <pre>
353   http_response http_response;
354             :    :    :
355             :    :    :
356
357       http_response = new_http_response (pool, http_request,
358                                          io,
359                                          200, "OK");
360       http_response_send_header (http_response,
361                                  "Content-Type", "text/plain");
362       close = http_response_end_headers (http_response);
363
364       if (!http_request_is_HEAD (http_request))
365         {
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");
368
369           headers = http_request_get_headers (http_request);
370           for (i = 0; i &lt; vector_size (headers); ++i)
371             {
372               vector_get (headers, i, header);
373               io_fprintf (io, "\t%s: %s\r\n", header.key, header.value);
374             }
375
376           io_fprintf (io, "----- end of headers -----\r\n");
377
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");
385
386           params = cgi_params (cgi);
387           for (i = 0; i &lt; vector_size (params); ++i)
388             {
389               vector_get (params, i, name);
390               value = cgi_param (cgi, name);
391               io_fprintf (io, "\t%s=%s\r\n", name, value);
392             }
393
394           io_fprintf (io, "----- end of parameters -----\r\n");
395         }
396 </pre>
397
398     <p>
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>
406       header.
407     </p>
408
409     <p>
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>.
413     </p>
414
415 <pre>
416 $ <b>./pthr_eg1_echo -p 9000</b>
417 $ <b>telnet localhost 9000</b>
418 Trying 127.0.0.1...
419 Connected to localhost.
420 Escape character is '^]'.
421 <b>GET /path/here?abc=123&amp;def=456 HTTP/1.0</b>
422 <b>Host: localhost:9000</b>
423
424 HTTP/1.1 200 OK
425 Content-Type: text/plain
426 Server: pthrlib-httpd/3.0.3
427 Date: Fri, 30 Aug 2002 17:04:03 GMT
428 Connection: close
429
430 Hello. This is your server.
431
432 Your browser sent the following headers:
433         host: localhost:9000
434 ----- end of headers -----
435 The URL was: /path/here?abc=123&amp;def=456
436 The path component was: /path/here
437 The query string was: abc=123&amp;def=456
438 The query arguments were:
439         abc=123
440         def=456
441 ----- end of parameters -----
442 Connection closed by foreign host.
443 </pre>
444
445     <h3>Static file webserver</h3>
446
447     <p>
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>
456     </p>
457
458     <p>
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:
464     </p>
465
466 <pre>
467 int
468 main (int argc, char *argv[])
469 {
470   struct sigaction sa;
471
472   /* Intercept signals. */
473   memset (&amp;sa, 0, sizeof sa);
474   sa.sa_handler = catch_quit_signal;
475   sa.sa_flags = SA_RESTART;
476   sigaction (SIGINT, &amp;sa, 0);
477   sigaction (SIGQUIT, &amp;sa, 0);
478   sigaction (SIGTERM, &amp;sa, 0);
479
480   /* ... but ignore SIGPIPE errors. */
481   sa.sa_handler = SIG_IGN;
482   sa.sa_flags = SA_RESTART;
483   sigaction (SIGPIPE, &amp;sa, 0);
484
485   /* Start up the server. */
486   pthr_server_chroot (root);
487   pthr_server_username (user);
488   pthr_server_main_loop (argc, argv, start_processor);
489
490   exit (0);
491 }
492
493 static void
494 start_processor (int sock, void *data)
495 {
496   (void) new_eg2_server_processor (sock);
497 }
498
499 static void
500 catch_quit_signal (int sig)
501 {
502   exit (0);
503 }
504 </pre>
505
506     <p>
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>).
512     </p>
513
514     <p>
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
518       request:
519     </p>
520
521 <pre>
522 struct eg2_server_processor
523 {
524   /* Pseudothread handle. */
525   pseudothread pth;
526
527   /* Socket. */
528   int sock;
529
530   /* Pool for memory allocations. */
531   struct pool *pool;
532
533   /* HTTP request. */
534   http_request http_request;
535
536   /* IO handle. */
537   io_handle io;
538 };
539 </pre>
540
541     <p>
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
548       black.
549     </p>
550
551 <pre>
552 <span style="color: red">static void
553 run (void *vp)
554 {
555   eg2_server_processor p = (eg2_server_processor) vp;
556   int close = 0;
557   const char *path;
558   struct stat statbuf;
559
560   p-&gt;io = io_fdopen (p-&gt;sock);
561
562   /* Sit in a loop reading HTTP requests. */
563   while (!close)
564     {
565       /* ----- HTTP request ----- */
566       p-&gt;http_request = new_http_request (pool, p-&gt;io);
567       if (p-&gt;http_request == 0)      /* Normal end of file. */
568         break;</span>
569
570       /* Get the path and locate the file. */
571       path = http_request_path (p-&gt;http_request);
572       if (stat (path, &statbuf) == -1)
573         {
574           close = file_not_found_error (p);
575           continue;
576         }
577
578       /* File or directory? */
579       if (S_ISDIR (statbuf.st_mode))
580         {
581           close = serve_directory (p, path, &statbuf);
582           continue;
583         }
584       else if (S_ISREG (statbuf.st_mode))
585         {
586           close = serve_file (p, path, &statbuf);
587           continue;
588         }
589       else
590         {
591           close = file_not_found_error (p);
592           continue;
593         }
594     <span style="color: red">}
595
596   io_fclose (p-&gt;io);
597
598   pth_exit ();
599 }</span>
600 </pre>
601
602     <p>
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).
607     </p>
608
609     <p>
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.
614     </p>
615
616     <p>
617       If the file is a regular file, we call <code>serve_file</code>,
618       which is a simple piece of code:
619     </p>
620
621 <pre>
622 static int
623 serve_file (eg2_server_processor p, const char *path,
624             const struct stat *statbuf)
625 {
626   http_response http_response;
627   const int n = 4096;
628   char *buffer = alloca (n);
629   int cl, fd, r;
630   char *content_length = pitoa (p-&gt;pool, statbuf-&gt;st_size);
631
632   fd = open (path, O_RDONLY);
633   if (fd &lt; 0)
634     return file_not_found_error (p);
635
636   http_response = new_http_response (pool, p-&gt;http_request, p-&gt;io,
637                                      200, "OK");
638   http_response_send_headers (http_response,
639                               /* Content type. */
640                               "Content-Type", "text/plain",
641                               "Content-Length", content_length,
642                               /* End of headers. */
643                               NULL);
644   cl = http_response_end_headers (http_response);
645
646   if (http_request_is_HEAD (p-&gt;http_request)) return cl;
647
648   while ((r = read (fd, buffer, n)) &gt; 0)
649     {
650       io_fwrite (buffer, r, 1, p-&gt;io);
651     }
652
653   if (r &lt; 0)
654     perror ("read");
655
656   close (fd);
657
658   return cl;
659 }
660 </pre>
661
662     <p>
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.
670     </p>
671
672     <p>
673       Next we generate our headers:
674     </p>
675
676 <pre>
677 Content-Type: text/plain
678 Content-Length: <i>(size of the file in octets)</i>
679 </pre>
680
681     <p>
682       <code>pthrlib</code> will generate other standard
683       headers as well.
684     </p>
685
686     <p>
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
690       user.
691     </p>
692
693     <p>
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)?
698     </p>
699
700     <p>
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.
705     </p>
706
707     <p>
708       Firstly if the user requested the directory as:
709     </p>
710
711 <pre>
712 http://your.hostname/path/to/directory
713 </pre>
714
715     <p>
716       then we need to redirect them to:
717     </p>
718
719 <pre>
720 http://your.hostname/path/to/directory<b>/</b>
721 </pre>
722
723     <p>
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!
730     </p>
731
732 <pre>
733 static int
734 serve_directory (eg2_server_processor p, const char *path,
735                  const struct stat *statbuf)
736 {
737   http_response http_response;
738   int close;
739   DIR *dir;
740   struct dirent *d;
741
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
744    * with "/" appended.
745    */
746   if (path[strlen (path)-1] != '/')
747     {
748       char *location = psprintf (p-&gt;pool, "%s/", path);
749       return moved_permanently (p, location);
750     }
751 </pre>
752
753     <p>
754       <code>moved_permanently</code> sends back a 301 MOVED PERMANENTLY
755       page causing the browser to re-request the new location.
756     </p>
757
758     <p>
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.
762     </p>
763
764 <pre>
765   dir = opendir (path);
766   if (dir == 0)
767     return file_not_found_error (p);
768
769   http_response = new_http_response (pool, p-&gt;http_request, p-&gt;io,
770                                      200, "OK");
771   http_response_send_headers (http_response,
772                               /* Content type. */
773                               "Content-Type", "text/html",
774                               NO_CACHE_HEADERS,
775                               /* End of headers. */
776                               NULL);
777   close = http_response_end_headers (http_response);
778
779   if (http_request_is_HEAD (p-&gt;http_request)) return close;
780 </pre>
781
782     <p>
783       The next piece of code is the complicated bit which generates
784       the HTML page listing the files:
785     </p>
786
787 <pre>
788   io_fprintf (p-&gt;io,
789               "&lt;html&gt;&lt;head&gt;&lt;title&gt;Directory: %s&lt;/title&gt;&lt;/head&gt;" CRLF
790               "&lt;body bgcolor=\"#ffffff\"&gt;" CRLF
791               "&lt;h1&gt;Directory: %s&lt;/h1&gt;" CRLF
792               "&lt;table&gt;" CRLF
793               "&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;"
794               "&lt;td&gt;&lt;a href=\"..\"&gt;Parent directory&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;" CRLF,
795               path, path);
796
797   while ((d = readdir (dir)) != 0)
798     {
799       if (d-&gt;d_name[0] != '.')       /* Ignore hidden files. */
800         {
801           const char *filename;
802           struct stat fstatbuf;
803
804           /* Generate the full pathname to this file. */
805           filename = psprintf (p-&gt;pool, "%s/%s", path, d-&gt;d_name);
806
807           /* Stat the file to find out what it is. */
808           if (lstat (filename, &amp;fstatbuf) == 0)
809             {
810               const char *type;
811               int size;
812
813               if (S_ISDIR (fstatbuf.st_mode))
814                 type = "dir";
815               else if (S_ISREG (fstatbuf.st_mode))
816                 type = "file";
817               else if (S_ISLNK (fstatbuf.st_mode))
818                 type = "link";
819               else
820                 type = "special";
821
822               size = fstatbuf.st_size;
823
824               /* Print the details. */
825               io_fprintf (p-&gt;io,
826                           "&lt;tr&gt;&lt;td&gt;[ %s ]&lt;/td&gt;&lt;td align=right&gt;%d&lt;/td&gt;"
827                           "&lt;td&gt;&lt;a href=\"%s%s\"&gt;%s&lt;/a&gt;",
828                           type, size,
829                           d-&gt;d_name,
830                           S_ISDIR (fstatbuf.st_mode) ? "/" : "",
831                           d-&gt;d_name);
832
833               if (S_ISLNK (fstatbuf.st_mode))
834                 {
835                   char link[NAME_MAX+1];
836                   int r;
837
838                   r = readlink (filename, link, NAME_MAX);
839                   if (r &gt;= 0) link[r] = '\0';
840                   else strcpy (link, "unknown");
841
842                   io_fprintf (p-&gt;io, " -&amp;gt; %s", link);
843                 }
844
845               io_fputs ("&lt;/td&gt;&lt;/tr&gt;" CRLF, p-&gt;io);
846             }
847         }
848     }
849
850   io_fprintf (p-&gt;io,
851               "&lt;/table&gt;&lt;/body&gt;&lt;/html&gt;" CRLF);
852
853   return close;
854 </pre>
855
856     <p>
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).
859     </p>
860
861     <p>
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.
866     </p>
867
868     <p>
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
873       301 redirect).
874     </p>
875
876     <p>
877       Finally after we reach the end of the directory we finish
878       of the table and the page and return.
879     </p>
880
881     <h2>Further examples</h2>
882
883     <p>
884       That's the end of this <code>pthrlib</code> tutorial, I
885       hope you enjoyed it.
886     </p>
887
888     <p>
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>).
893     </p>
894
895     <p>
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>).
902     </p>
903
904     <h2>Links to manual pages</h2>
905
906     <p>
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!)
910     </p>
911
912     <h3>Pseudothreads</h3>
913
914     <ul>
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>
954     </ul>
955
956     <h3>Server main loop</h3>
957
958     <ul>
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>
971     </ul>
972
973     <h3>Buffered I/O library</h3>
974
975     <ul>
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>
994     </ul>
995
996     <h3>HTTP server library</h3>
997
998     <ul>
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>
1019     </ul>
1020
1021     <h3>CGI library</h3>
1022
1023     <ul>
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>
1034     </ul>
1035
1036     <h3>Thread synchronisation (mutexes, R/W-locks, wait queues)</h3>
1037
1038     <ul>
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>
1056     </ul>
1057
1058     <h3>FTP client library</h3>
1059
1060     <ul>
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>
1079     </ul>
1080
1081     <hr>
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
1086 <!-- hhmts end -->
1087   </body>
1088 </html>