ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/thttpd/thttpd.c
Revision: 1.1.2.1
Committed: Mon Jun 18 21:19:51 2001 UTC (25 years, 3 months ago) by root
Content type: text/plain
Branch: dirpatch
CVS Tags: dp_j
Changes since 1.1: +14 -1 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 /* thttpd.c - tiny/turbo/throttling HTTP server
2     **
3     ** Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer <jef@acme.com>.
4     ** All rights reserved.
5     **
6     ** Redistribution and use in source and binary forms, with or without
7     ** modification, are permitted provided that the following conditions
8     ** are met:
9     ** 1. Redistributions of source code must retain the above copyright
10     ** notice, this list of conditions and the following disclaimer.
11     ** 2. Redistributions in binary form must reproduce the above copyright
12     ** notice, this list of conditions and the following disclaimer in the
13     ** documentation and/or other materials provided with the distribution.
14     **
15     ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16     ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17     ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18     ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19     ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20     ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21     ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22     ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23     ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24     ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25     ** SUCH DAMAGE.
26     */
27    
28    
29     #include "config.h"
30     #include "version.h"
31    
32     #include <sys/param.h>
33     #include <sys/types.h>
34     #include <sys/time.h>
35     #include <sys/stat.h>
36     #include <sys/uio.h>
37    
38     #include <errno.h>
39     #ifdef HAVE_FCNTL_H
40     #include <fcntl.h>
41     #endif
42     #include <pwd.h>
43     #ifdef HAVE_GRP_H
44     #include <grp.h>
45     #endif
46     #include <signal.h>
47     #include <stdio.h>
48     #include <stdlib.h>
49     #include <string.h>
50     #include <syslog.h>
51     #ifdef TIME_WITH_SYS_TIME
52     #include <time.h>
53     #endif
54     #include <unistd.h>
55    
56     #include "fdwatch.h"
57     #include "libhttpd.h"
58     #include "mmc.h"
59     #include "timers.h"
60     #include "match.h"
61    
62    
63     static char* argv0;
64     static int debug;
65     static int port;
66     static char* dir;
67     static int do_chroot, no_log, no_symlink, do_vhost, do_global_passwd;
68     static char* cgi_pattern;
69 root 1.1.2.1 static char* autoindex_prog;
70 root 1.1 static char* url_pattern;
71     static int no_empty_referers;
72     static char* local_pattern;
73     static char* logfile;
74     static char* throttlefile;
75     static char* hostname;
76     static char* pidfile;
77     static char* user;
78     static char* charset;
79    
80    
81     typedef struct {
82     char* pattern;
83     long limit;
84     long rate;
85     off_t bytes_since_avg;
86     int num_sending;
87     } throttletab;
88     static throttletab* throttles;
89     static int numthrottles, maxthrottles;
90    
91     #define THROTTLE_NOLIMIT 1234567890L /* default limit (if none) */
92    
93    
94     typedef struct {
95     int conn_state;
96     httpd_conn* hc;
97     int tnums[MAXTHROTTLENUMS]; /* throttle indexes */
98     int numtnums;
99     long limit;
100     time_t started_at;
101     Timer* idle_read_timer;
102     Timer* idle_send_timer;
103     Timer* wakeup_timer;
104     Timer* linger_timer;
105     long wouldblock_delay;
106     off_t bytes;
107     off_t bytes_sent;
108     off_t bytes_to_send;
109     } connecttab;
110     static connecttab* connects;
111     static int numconnects, maxconnects;
112     static int httpd_conn_count;
113    
114     /* The connection states. */
115     #define CNST_FREE 0
116     #define CNST_READING 1
117     #define CNST_SENDING 2
118     #define CNST_PAUSING 3
119     #define CNST_LINGERING 4
120    
121    
122     static httpd_server* hs = (httpd_server*) 0;
123     int terminate = 0;
124     time_t start_time, stats_time;
125     long stats_connections, stats_bytes;
126     int stats_simultaneous;
127    
128     static int got_usr1;
129    
130    
131     /* Forwards. */
132     static void parse_args( int argc, char** argv );
133     static void usage( void );
134     static void read_config( char* filename );
135     static void value_required( char* name, char* value );
136     static void no_value_required( char* name, char* value );
137     static char* e_strdup( char* oldstr );
138     static void lookup_hostname( httpd_sockaddr* sa4P, size_t sa4_len, int* gotv4P, httpd_sockaddr* sa6P, size_t sa6_len, int* gotv6P );
139     static void read_throttlefile( char* throttlefile );
140     static void shut_down( void );
141     static int handle_newconnect( struct timeval* tvP, int listen_fd );
142     static void handle_read( connecttab* c, struct timeval* tvP );
143     static void handle_send( connecttab* c, struct timeval* tvP );
144     static void handle_linger( connecttab* c, struct timeval* tvP );
145     static int check_throttles( connecttab* c );
146     static void clear_throttles( connecttab* c, struct timeval* tvP );
147     static void update_throttles( ClientData client_data, struct timeval* nowP );
148     static void clear_connection( connecttab* c, struct timeval* tvP );
149     static void really_clear_connection( connecttab* c, struct timeval* tvP );
150     static void idle_read_connection( ClientData client_data, struct timeval* nowP );
151     static void idle_send_connection( ClientData client_data, struct timeval* nowP );
152     static void wakeup_connection( ClientData client_data, struct timeval* nowP );
153     static void linger_clear_connection( ClientData client_data, struct timeval* nowP );
154     static void occasional( ClientData client_data, struct timeval* nowP );
155     #ifdef STATS_TIME
156     static void show_stats( ClientData client_data, struct timeval* nowP );
157     #endif /* STATS_TIME */
158     static void logstats( struct timeval* nowP );
159     static void thttpd_logstats( long secs );
160    
161    
162     static void
163     handle_term( int sig )
164     {
165     shut_down();
166     syslog( LOG_NOTICE, "exiting due to signal %d", sig );
167     closelog();
168     exit( 1 );
169     }
170    
171    
172     static void
173     handle_hup( int sig )
174     {
175     FILE* logfp;
176    
177     if ( no_log )
178     return;
179    
180     /* Re-open the log file. */
181     if ( logfile != (char*) 0 )
182     {
183     logfp = fopen( logfile, "a" );
184     if ( logfp == (FILE*) 0 )
185     {
186     syslog( LOG_CRIT, "reopening %.80s - %m", logfile );
187     return;
188     }
189     (void) fcntl( fileno( logfp ), F_SETFD, 1 );
190     httpd_set_logfp( hs, logfp );
191     }
192     }
193    
194    
195     static void
196     handle_usr1( int sig )
197     {
198     got_usr1 = 1;
199     }
200    
201    
202     static void
203     handle_usr2( int sig )
204     {
205     logstats( (struct timeval*) 0 );
206     }
207    
208    
209     int
210     main( int argc, char** argv )
211     {
212     char* cp;
213     struct passwd* pwd;
214     uid_t uid;
215     gid_t gid;
216     char cwd[MAXPATHLEN];
217     FILE* logfp;
218     int num_ready;
219     int cnum, ridx;
220     connecttab* c;
221     httpd_conn* hc;
222     httpd_sockaddr sa4;
223     httpd_sockaddr sa6;
224     int gotv4, gotv6;
225     struct timeval tv;
226    
227     argv0 = argv[0];
228    
229     cp = strrchr( argv0, '/' );
230     if ( cp != (char*) 0 )
231     ++cp;
232     else
233     cp = argv0;
234     openlog( cp, LOG_NDELAY|LOG_PID, LOG_FACILITY );
235    
236     /* Handle command-line arguments. */
237     parse_args( argc, argv );
238    
239     /* Check port number. */
240     if ( port <= 0 )
241     {
242     syslog( LOG_CRIT, "illegal port number" );
243     (void) fprintf( stderr, "%s: illegal port number\n", argv0 );
244     exit( 1 );
245     }
246    
247     /* Read zone info now, in case we chroot(). */
248     tzset();
249    
250     /* Look up hostname now, in case we chroot(). */
251     lookup_hostname( &sa4, sizeof(sa4), &gotv4, &sa6, sizeof(sa6), &gotv6 );
252     if ( ! ( gotv4 || gotv6 ) )
253     {
254     syslog( LOG_ERR, "can't find any valid address" );
255     (void) fprintf( stderr, "%s: can't find any valid address\n", argv0 );
256     exit( 1 );
257     }
258    
259     /* Throttle file. */
260     numthrottles = 0;
261     maxthrottles = 0;
262     throttles = (throttletab*) 0;
263     if ( throttlefile != (char*) 0 )
264     read_throttlefile( throttlefile );
265    
266     /* Log file. */
267     if ( logfile != (char*) 0 )
268     {
269     if ( strcmp( logfile, "/dev/null" ) == 0 )
270     {
271     no_log = 1;
272     logfp = (FILE*) 0;
273     }
274     else
275     {
276     logfp = fopen( logfile, "a" );
277     if ( logfp == (FILE*) 0 )
278     {
279     syslog( LOG_CRIT, "%.80s - %m", logfile );
280     perror( logfile );
281     exit( 1 );
282     }
283     (void) fcntl( fileno( logfp ), F_SETFD, 1 );
284     }
285     }
286     else
287     logfp = (FILE*) 0;
288    
289     /* Figure out uid/gid from user. */
290     pwd = getpwnam( user );
291     if ( pwd == (struct passwd*) 0 )
292     {
293     syslog( LOG_CRIT, "unknown user - '%.80s'", user );
294     (void) fprintf( stderr, "%s: unknown user - '%s'\n", argv0, user );
295     exit( 1 );
296     }
297     uid = pwd->pw_uid;
298     gid = pwd->pw_gid;
299    
300     /* Switch directories if requested. */
301     if ( dir != (char*) 0 )
302     {
303     if ( chdir( dir ) < 0 )
304     {
305     syslog( LOG_CRIT, "chdir - %m" );
306     perror( "chdir" );
307     exit( 1 );
308     }
309     }
310     #ifdef USE_USER_DIR
311     else if ( getuid() == 0 )
312     {
313     /* No explicit directory was specified, we're root, and the
314     ** USE_USER_DIR option is set - switch to the specified user's
315     ** home dir.
316     */
317     if ( chdir( pwd->pw_dir ) < 0 )
318     {
319     syslog( LOG_CRIT, "chdir - %m" );
320     perror( "chdir" );
321     exit( 1 );
322     }
323     }
324     #endif /* USE_USER_DIR */
325    
326     /* Get current directory. */
327     (void) getcwd( cwd, sizeof(cwd) - 1 );
328     if ( cwd[strlen( cwd ) - 1] != '/' )
329     (void) strcat( cwd, "/" );
330    
331     if ( ! debug )
332     {
333     /* We're not going to use stdin stdout or stderr from here on, so close
334     ** them to save file descriptors.
335     */
336     (void) fclose( stdin );
337     (void) fclose( stdout );
338     (void) fclose( stderr );
339    
340     /* Daemonize - make ourselves a subprocess. */
341     #ifdef HAVE_DAEMON
342     if ( daemon( 1, 1 ) < 0 )
343     {
344     syslog( LOG_CRIT, "daemon - %m" );
345     exit( 1 );
346     }
347     #else /* HAVE_DAEMON */
348     switch ( fork() )
349     {
350     case 0:
351     break;
352     case -1:
353     syslog( LOG_CRIT, "fork - %m" );
354     exit( 1 );
355     default:
356     exit( 0 );
357     }
358     #ifdef HAVE_SETSID
359     (void) setsid();
360     #endif /* HAVE_SETSID */
361     #endif /* HAVE_DAEMON */
362     }
363     else
364     {
365     /* Even if we don't daemonize, we still want to disown our parent
366     ** process.
367     */
368     #ifdef HAVE_SETSID
369     (void) setsid();
370     #endif /* HAVE_SETSID */
371     }
372    
373     if ( pidfile != (char*) 0 )
374     {
375     /* Write the PID file. */
376     FILE* pidfp = fopen( pidfile, "w" );
377     if ( pidfp == (FILE*) 0 )
378     {
379     syslog( LOG_CRIT, "%.80s - %m", pidfile );
380     exit( 1 );
381     }
382     (void) fprintf( pidfp, "%d\n", (int) getpid() );
383     (void) fclose( pidfp );
384     }
385    
386     /* Chroot if requested. */
387     if ( do_chroot )
388     {
389     if ( chroot( cwd ) < 0 )
390     {
391     syslog( LOG_CRIT, "chroot - %m" );
392     perror( "chroot" );
393     exit( 1 );
394     }
395     (void) strcpy( cwd, "/" );
396     /* Always chdir to / after a chroot. */
397     if ( chdir( cwd ) < 0 )
398     {
399     syslog( LOG_CRIT, "chroot chdir - %m" );
400     perror( "chroot chdir" );
401     exit( 1 );
402     }
403     }
404    
405     /* Set up to catch signals. */
406     (void) signal( SIGTERM, handle_term );
407     (void) signal( SIGINT, handle_term );
408     (void) signal( SIGPIPE, SIG_IGN ); /* get EPIPE instead */
409     (void) signal( SIGHUP, handle_hup );
410     got_usr1 = 0;
411     (void) signal( SIGUSR1, handle_usr1 );
412     (void) signal( SIGUSR2, handle_usr2 );
413    
414     /* Initialize the timer package. */
415     tmr_init();
416    
417     /* Initialize the HTTP layer. Got to do this before giving up root,
418     ** so that we can bind to a privileged port.
419     */
420     hs = httpd_initialize(
421     hostname,
422     gotv4 ? &sa4 : (httpd_sockaddr*) 0, gotv6 ? &sa6 : (httpd_sockaddr*) 0,
423     port, cgi_pattern, charset, cwd, no_log, logfp, no_symlink, do_vhost,
424 root 1.1.2.1 do_global_passwd, url_pattern, local_pattern, no_empty_referers,
425     autoindex_prog );
426 root 1.1 if ( hs == (httpd_server*) 0 )
427     exit( 1 );
428    
429     /* Set up the occasional timer. */
430     if ( tmr_create( (struct timeval*) 0, occasional, JunkClientData, OCCASIONAL_TIME * 1000L, 1 ) == (Timer*) 0 )
431     {
432     syslog( LOG_CRIT, "tmr_create(occasional) failed" );
433     exit( 1 );
434     }
435     if ( numthrottles > 0 )
436     {
437     /* Set up the throttles timer. */
438     if ( tmr_create( (struct timeval*) 0, update_throttles, JunkClientData, THROTTLE_TIME * 1000L, 1 ) == (Timer*) 0 )
439     {
440     syslog( LOG_CRIT, "tmr_create(update_throttles) failed" );
441     exit( 1 );
442     }
443     }
444     #ifdef STATS_TIME
445     /* Set up the stats timer. */
446     if ( tmr_create( (struct timeval*) 0, show_stats, JunkClientData, STATS_TIME * 1000L, 1 ) == (Timer*) 0 )
447     {
448     syslog( LOG_CRIT, "tmr_create(show_stats) failed" );
449     exit( 1 );
450     }
451     #endif /* STATS_TIME */
452     start_time = stats_time = time( (time_t*) 0 );
453     stats_connections = stats_bytes = 0L;
454     stats_simultaneous = 0;
455    
456     /* If we're root, try to become someone else. */
457     if ( getuid() == 0 )
458     {
459     /* Set aux groups to null. */
460     if ( setgroups( 0, (const gid_t*) 0 ) < 0 )
461     {
462     syslog( LOG_CRIT, "setgroups - %m" );
463     exit( 1 );
464     }
465     /* Set primary group. */
466     if ( setgid( gid ) < 0 )
467     {
468     syslog( LOG_CRIT, "setgid - %m" );
469     exit( 1 );
470     }
471     /* Try setting aux groups correctly - not critical if this fails. */
472     if ( initgroups( user, gid ) < 0 )
473     syslog( LOG_WARNING, "initgroups - %m" );
474     #ifdef HAVE_SETLOGIN
475     /* Set login name. */
476     (void) setlogin( user );
477     #endif /* HAVE_SETLOGIN */
478     /* Set uid. */
479     if ( setuid( uid ) < 0 )
480     {
481     syslog( LOG_CRIT, "setuid - %m" );
482     exit( 1 );
483     }
484     /* Check for unnecessary security exposure. */
485     if ( ! do_chroot )
486     syslog(
487     LOG_CRIT,
488     "started as root without requesting chroot(), warning only" );
489     }
490    
491     /* Initialize our connections table. */
492     maxconnects = fdwatch_get_nfiles();
493     if ( maxconnects < 0 )
494     {
495     syslog( LOG_CRIT, "fdwatch initialization failure" );
496     exit( 1 );
497     }
498     maxconnects -= SPARE_FDS;
499     connects = NEW( connecttab, maxconnects );
500     if ( connects == (connecttab*) 0 )
501     {
502     syslog( LOG_CRIT, "out of memory allocating a connecttab" );
503     exit( 1 );
504     }
505     for ( cnum = 0; cnum < maxconnects; ++cnum )
506     {
507     connects[cnum].conn_state = CNST_FREE;
508     connects[cnum].hc = (httpd_conn*) 0;
509     }
510     numconnects = 0;
511     httpd_conn_count = 0;
512    
513     if ( hs != (httpd_server*) 0 )
514     {
515     if ( hs->listen4_fd != -1 )
516     fdwatch_add_fd( hs->listen4_fd, (void*) 0, FDW_READ );
517     if ( hs->listen6_fd != -1 )
518     fdwatch_add_fd( hs->listen6_fd, (void*) 0, FDW_READ );
519     }
520    
521     /* Main loop. */
522     (void) gettimeofday( &tv, (struct timezone*) 0 );
523     while ( ( ! terminate ) || numconnects > 0 )
524     {
525     /* Do the fd watch. */
526     num_ready = fdwatch( tmr_mstimeout( &tv ) );
527     if ( num_ready < 0 )
528     {
529     if ( errno == EINTR )
530     continue; /* try again */
531     syslog( LOG_ERR, "fdwatch - %m" );
532     exit( 1 );
533     }
534     (void) gettimeofday( &tv, (struct timezone*) 0 );
535     if ( num_ready == 0 )
536     {
537     /* No fd's are ready - run the timers. */
538     tmr_run( &tv );
539     continue;
540     }
541    
542     /* Is it a new connection? */
543     if ( hs != (httpd_server*) 0 && hs->listen6_fd != -1 &&
544     fdwatch_check_fd( hs->listen6_fd ) )
545     {
546     if ( handle_newconnect( &tv, hs->listen6_fd ) )
547     /* Go around the loop and do another fdwatch, rather than
548     ** dropping through and processing existing connections.
549     ** New connections always get priority.
550     */
551     continue;
552     }
553     if ( hs != (httpd_server*) 0 && hs->listen4_fd != -1 &&
554     fdwatch_check_fd( hs->listen4_fd ) )
555     {
556     if ( handle_newconnect( &tv, hs->listen4_fd ) )
557     /* Go around the loop and do another fdwatch, rather than
558     ** dropping through and processing existing connections.
559     ** New connections always get priority.
560     */
561     continue;
562     }
563    
564     /* Find the connections that need servicing. */
565     for ( ridx = 0; ridx < num_ready; ++ridx )
566     {
567     c = (connecttab*) fdwatch_get_client_data( ridx );
568     if ( c == (connecttab*) 0 )
569     continue;
570     hc = c->hc;
571     if ( c->conn_state == CNST_READING &&
572     fdwatch_check_fd( hc->conn_fd ) )
573     handle_read( c, &tv );
574     else if ( c->conn_state == CNST_SENDING &&
575     fdwatch_check_fd( hc->conn_fd ) )
576     handle_send( c, &tv );
577     else if ( c->conn_state == CNST_LINGERING &&
578     fdwatch_check_fd( hc->conn_fd ) )
579     handle_linger( c, &tv );
580     }
581     tmr_run( &tv );
582    
583     if ( got_usr1 && ! terminate )
584     {
585     terminate = 1;
586     if ( hs != (httpd_server*) 0 )
587     {
588     httpd_terminate( hs );
589     hs = (httpd_server*) 0;
590     }
591     }
592     }
593    
594     /* The main loop terminated. */
595     shut_down();
596     syslog( LOG_NOTICE, "exiting" );
597     closelog();
598     exit( 0 );
599     }
600    
601    
602     static void
603     parse_args( int argc, char** argv )
604     {
605     int argn;
606    
607     debug = 0;
608     port = DEFAULT_PORT;
609     dir = (char*) 0;
610     #ifdef ALWAYS_CHROOT
611     do_chroot = 1;
612     #else /* ALWAYS_CHROOT */
613     do_chroot = 0;
614     #endif /* ALWAYS_CHROOT */
615     no_log = 0;
616     no_symlink = do_chroot;
617     #ifdef ALWAYS_VHOST
618     do_vhost = 1;
619     #else /* ALWAYS_VHOST */
620     do_vhost = 0;
621     #endif /* ALWAYS_VHOST */
622     #ifdef ALWAYS_GLOBAL_PASSWD
623     do_global_passwd = 1;
624     #else /* ALWAYS_GLOBAL_PASSWD */
625     do_global_passwd = 0;
626     #endif /* ALWAYS_GLOBAL_PASSWD */
627     #ifdef CGI_PATTERN
628     cgi_pattern = CGI_PATTERN;
629     #else /* CGI_PATTERN */
630     cgi_pattern = (char*) 0;
631     #endif /* CGI_PATTERN */
632 root 1.1.2.1 autoindex_prog = (char*) 0;
633 root 1.1 url_pattern = (char*) 0;
634     no_empty_referers = 0;
635     local_pattern = (char*) 0;
636     throttlefile = (char*) 0;
637     hostname = (char*) 0;
638     logfile = (char*) 0;
639     pidfile = (char*) 0;
640     user = DEFAULT_USER;
641     charset = DEFAULT_CHARSET;
642     argn = 1;
643     while ( argn < argc && argv[argn][0] == '-' )
644     {
645     if ( strcmp( argv[argn], "-C" ) == 0 && argn + 1 < argc )
646     {
647     ++argn;
648     read_config( argv[argn] );
649     }
650     else if ( strcmp( argv[argn], "-p" ) == 0 && argn + 1 < argc )
651     {
652     ++argn;
653     port = atoi( argv[argn] );
654     }
655     else if ( strcmp( argv[argn], "-d" ) == 0 && argn + 1 < argc )
656     {
657     ++argn;
658     dir = argv[argn];
659     }
660     else if ( strcmp( argv[argn], "-r" ) == 0 )
661     {
662     do_chroot = 1;
663     no_symlink = 1;
664     }
665     else if ( strcmp( argv[argn], "-nor" ) == 0 )
666     {
667     do_chroot = 0;
668     no_symlink = 0;
669     }
670     else if ( strcmp( argv[argn], "-s" ) == 0 )
671     no_symlink = 0;
672     else if ( strcmp( argv[argn], "-nos" ) == 0 )
673     no_symlink = 1;
674     else if ( strcmp( argv[argn], "-u" ) == 0 && argn + 1 < argc )
675     {
676     ++argn;
677     user = argv[argn];
678     }
679     else if ( strcmp( argv[argn], "-c" ) == 0 && argn + 1 < argc )
680     {
681     ++argn;
682     cgi_pattern = argv[argn];
683     }
684 root 1.1.2.1 else if ( strcmp( argv[argn], "-x" ) == 0 && argn + 1 < argc )
685     {
686     ++argn;
687     autoindex_prog = argv[argn];
688     }
689 root 1.1 else if ( strcmp( argv[argn], "-t" ) == 0 && argn + 1 < argc )
690     {
691     ++argn;
692     throttlefile = argv[argn];
693     }
694     else if ( strcmp( argv[argn], "-h" ) == 0 && argn + 1 < argc )
695     {
696     ++argn;
697     hostname = argv[argn];
698     }
699     else if ( strcmp( argv[argn], "-l" ) == 0 && argn + 1 < argc )
700     {
701     ++argn;
702     logfile = argv[argn];
703     }
704     else if ( strcmp( argv[argn], "-v" ) == 0 )
705     do_vhost = 1;
706     else if ( strcmp( argv[argn], "-nov" ) == 0 )
707     do_vhost = 0;
708     else if ( strcmp( argv[argn], "-g" ) == 0 )
709     do_global_passwd = 1;
710     else if ( strcmp( argv[argn], "-nog" ) == 0 )
711     do_global_passwd = 0;
712     else if ( strcmp( argv[argn], "-i" ) == 0 && argn + 1 < argc )
713     {
714     ++argn;
715     pidfile = argv[argn];
716     }
717     else if ( strcmp( argv[argn], "-T" ) == 0 && argn + 1 < argc )
718     {
719     ++argn;
720     charset = argv[argn];
721     }
722     else if ( strcmp( argv[argn], "-V" ) == 0 )
723     {
724     (void) fprintf( stderr, "%s\n", SERVER_SOFTWARE );
725     exit( 0 );
726     }
727     else if ( strcmp( argv[argn], "-D" ) == 0 )
728     debug = 1;
729     else
730     usage();
731     ++argn;
732     }
733     if ( argn != argc )
734     usage();
735     }
736    
737    
738     static void
739     usage( void )
740     {
741     (void) fprintf( stderr,
742     "usage: %s [-C configfile] [-p port] [-d dir] [-r|-nor] [-v|-nov] [-g|-nog] [-u user] [-c cgipat] [-t throttles] [-h host] [-l logfile] [-i pidfile] [-T charset] [-V] [-D]\n",
743     argv0 );
744     exit( 1 );
745     }
746    
747    
748     static void
749     read_config( char* filename )
750     {
751     FILE* fp;
752     char line[10000];
753     char* cp;
754     char* cp2;
755     char* name;
756     char* value;
757    
758     fp = fopen( filename, "r" );
759     if ( fp == (FILE*) 0 )
760     {
761     perror( filename );
762     exit( 1 );
763     }
764    
765     while ( fgets( line, sizeof(line), fp ) != (char*) 0 )
766     {
767     /* Trim comments. */
768     if ( ( cp = strchr( line, '#' ) ) != (char*) 0 )
769     *cp = '\0';
770    
771     /* Split line into words. */
772     for ( cp = line; *cp != '\0'; cp = cp2 )
773     {
774     /* Skip leading whitespace. */
775     cp += strspn( cp, " \t\n\r" );
776     /* Find next whitespace. */
777     cp2 = cp + strcspn( cp, " \t\n\r" );
778     /* Insert EOS and advance next-word pointer. */
779     while ( *cp2 == ' ' || *cp2 == '\t' || *cp2 == '\n' || *cp2 == '\r' )
780     *cp2++ = '\0';
781     /* Split into name and value. */
782     name = cp;
783     value = strchr( name, '=' );
784     if ( value != (char*) 0 )
785     *value++ = '\0';
786     /* Interpret. */
787     if ( strcasecmp( name, "debug" ) == 0 )
788     {
789     no_value_required( name, value );
790     debug = 1;
791     }
792     else if ( strcasecmp( name, "port" ) == 0 )
793     {
794     value_required( name, value );
795     port = atoi( value );
796     }
797     else if ( strcasecmp( name, "dir" ) == 0 )
798     {
799     value_required( name, value );
800     dir = e_strdup( value );
801     }
802     else if ( strcasecmp( name, "chroot" ) == 0 )
803     {
804     no_value_required( name, value );
805     do_chroot = 1;
806     no_symlink = 1;
807     }
808     else if ( strcasecmp( name, "nochroot" ) == 0 )
809     {
810     no_value_required( name, value );
811     do_chroot = 0;
812     no_symlink = 0;
813     }
814     else if ( strcasecmp( name, "symlink" ) == 0 )
815     {
816     no_value_required( name, value );
817     no_symlink = 0;
818     }
819     else if ( strcasecmp( name, "nosymlink" ) == 0 )
820     {
821     no_value_required( name, value );
822     no_symlink = 1;
823     }
824     else if ( strcasecmp( name, "symlinks" ) == 0 )
825     {
826     no_value_required( name, value );
827     no_symlink = 0;
828     }
829     else if ( strcasecmp( name, "nosymlinks" ) == 0 )
830     {
831     no_value_required( name, value );
832     no_symlink = 1;
833     }
834     else if ( strcasecmp( name, "user" ) == 0 )
835     {
836     value_required( name, value );
837     user = e_strdup( value );
838     }
839     else if ( strcasecmp( name, "cgipat" ) == 0 )
840     {
841     value_required( name, value );
842     cgi_pattern = e_strdup( value );
843 root 1.1.2.1 }
844     else if ( strcasecmp( name, "indexprog" ) == 0 )
845     {
846     value_required( name, value );
847     autoindex_prog = e_strdup( value );
848 root 1.1 }
849     else if ( strcasecmp( name, "urlpat" ) == 0 )
850     {
851     value_required( name, value );
852     url_pattern = e_strdup( value );
853     }
854     else if ( strcasecmp( name, "noemptyreferers" ) == 0 )
855     {
856     no_value_required( name, value );
857     no_empty_referers = 1;
858     }
859     else if ( strcasecmp( name, "localpat" ) == 0 )
860     {
861     value_required( name, value );
862     local_pattern = e_strdup( value );
863     }
864     else if ( strcasecmp( name, "throttles" ) == 0 )
865     {
866     value_required( name, value );
867     throttlefile = e_strdup( value );
868     }
869     else if ( strcasecmp( name, "host" ) == 0 )
870     {
871     value_required( name, value );
872     hostname = e_strdup( value );
873     }
874     else if ( strcasecmp( name, "logfile" ) == 0 )
875     {
876     value_required( name, value );
877     logfile = e_strdup( value );
878     }
879     else if ( strcasecmp( name, "vhost" ) == 0 )
880     {
881     no_value_required( name, value );
882     do_vhost = 1;
883     }
884     else if ( strcasecmp( name, "novhost" ) == 0 )
885     {
886     no_value_required( name, value );
887     do_vhost = 0;
888     }
889     else if ( strcasecmp( name, "globalpasswd" ) == 0 )
890     {
891     no_value_required( name, value );
892     do_global_passwd = 1;
893     }
894     else if ( strcasecmp( name, "noglobalpasswd" ) == 0 )
895     {
896     no_value_required( name, value );
897     do_global_passwd = 0;
898     }
899     else if ( strcasecmp( name, "pidfile" ) == 0 )
900     {
901     value_required( name, value );
902     pidfile = e_strdup( value );
903     }
904     else if ( strcasecmp( name, "charset" ) == 0 )
905     {
906     value_required( name, value );
907     charset = e_strdup( value );
908     }
909     else
910     {
911     (void) fprintf(
912     stderr, "%s: unknown config option '%s'\n", argv0, name );
913     exit( 1 );
914     }
915     }
916     }
917    
918     (void) fclose( fp );
919     }
920    
921    
922     static void
923     value_required( char* name, char* value )
924     {
925     if ( value == (char*) 0 )
926     {
927     (void) fprintf(
928     stderr, "%s: value required for %s option\n", argv0, name );
929     exit( 1 );
930     }
931     }
932    
933    
934     static void
935     no_value_required( char* name, char* value )
936     {
937     if ( value != (char*) 0 )
938     {
939     (void) fprintf(
940     stderr, "%s: no value required for %s option\n",
941     argv0, name );
942     exit( 1 );
943     }
944     }
945    
946    
947     static char*
948     e_strdup( char* oldstr )
949     {
950     char* newstr;
951    
952     newstr = strdup( oldstr );
953     if ( newstr == (char*) 0 )
954     {
955     syslog( LOG_CRIT, "out of memory copying a string" );
956     (void) fprintf( stderr, "%s: out of memory copying a string\n", argv0 );
957     exit( 1 );
958     }
959     return newstr;
960     }
961    
962    
963     static void
964     lookup_hostname( httpd_sockaddr* sa4P, size_t sa4_len, int* gotv4P, httpd_sockaddr* sa6P, size_t sa6_len, int* gotv6P )
965     {
966     #if defined(HAVE_GETADDRINFO) && defined(HAVE_GAI_STRERROR)
967    
968     struct addrinfo hints;
969     struct addrinfo* ai;
970     struct addrinfo* ai2;
971     struct addrinfo* aiv4;
972     struct addrinfo* aiv6;
973     int gaierr;
974     char strport[10];
975    
976     memset( &hints, 0, sizeof(hints) );
977     hints.ai_family = AF_UNSPEC;
978     hints.ai_flags = AI_PASSIVE;
979     hints.ai_socktype = SOCK_STREAM;
980     (void) snprintf( strport, sizeof(strport), "%d", port );
981     if ( (gaierr = getaddrinfo( hostname, strport, &hints, &ai )) != 0 )
982     {
983     syslog(
984     LOG_CRIT, "getaddrinfo %.80s - %.80s",
985     hostname, gai_strerror( gaierr ) );
986     (void) fprintf(
987     stderr, "%s: getaddrinfo %s - %s\n",
988     argv0, hostname, gai_strerror( gaierr ) );
989     exit( 1 );
990     }
991    
992     /* Find the first IPv4 and IPv6 entries. */
993     aiv4 = (struct addrinfo*) 0;
994     aiv6 = (struct addrinfo*) 0;
995     for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next )
996     {
997     switch ( ai2->ai_family )
998     {
999     case AF_INET:
1000     if ( aiv4 == (struct addrinfo*) 0 )
1001     aiv4 = ai2;
1002     break;
1003     #if defined(AF_INET6) && defined(HAVE_SOCKADDR_IN6)
1004     case AF_INET6:
1005     if ( aiv6 == (struct addrinfo*) 0 )
1006     aiv6 = ai2;
1007     break;
1008     #endif /* AF_INET6 && HAVE_SOCKADDR_IN6 */
1009     }
1010     }
1011    
1012     if ( aiv4 == (struct addrinfo*) 0 )
1013     *gotv4P = 0;
1014     else
1015     {
1016     if ( sa4_len < aiv4->ai_addrlen )
1017     {
1018     syslog(
1019     LOG_CRIT, "%.80s - sockaddr too small (%d < %d)",
1020     hostname, sa4_len, aiv4->ai_addrlen );
1021     exit( 1 );
1022     }
1023     memset( sa4P, 0, sa4_len );
1024     memcpy( sa4P, aiv4->ai_addr, aiv4->ai_addrlen );
1025     *gotv4P = 1;
1026     }
1027     if ( aiv6 == (struct addrinfo*) 0 )
1028     *gotv6P = 0;
1029     else
1030     {
1031     if ( sa6_len < aiv6->ai_addrlen )
1032     {
1033     syslog(
1034     LOG_CRIT, "%.80s - sockaddr too small (%d < %d)",
1035     hostname, sa6_len, aiv6->ai_addrlen );
1036     exit( 1 );
1037     }
1038     memset( sa6P, 0, sa6_len );
1039     memcpy( sa6P, aiv6->ai_addr, aiv6->ai_addrlen );
1040     *gotv6P = 1;
1041     }
1042    
1043     freeaddrinfo( ai );
1044    
1045     #else /* HAVE_GETADDRINFO && HAVE_GAI_STRERROR */
1046    
1047     struct hostent* he;
1048    
1049     *gotv6P = 0;
1050    
1051     memset( sa4P, 0, sa4_len );
1052     #ifdef notdef
1053     /* We don't really need to set sa_len. */
1054     #ifdef HAVE_SA_LEN
1055     sa4P->sa_len = sa4_len;
1056     #endif /* HAVE_SA_LEN */
1057     #endif
1058     sa4P->sa.sa_family = AF_INET;
1059     if ( hostname == (char*) 0 )
1060     sa4P->sa_in.sin_addr.s_addr = htonl( INADDR_ANY );
1061     else
1062     {
1063     sa4P->sa_in.sin_addr.s_addr = inet_addr( hostname );
1064     if ( (int) sa4P->sa_in.sin_addr.s_addr == -1 )
1065     {
1066     he = gethostbyname( hostname );
1067     if ( he == (struct hostent*) 0 )
1068     {
1069     #ifdef HAVE_HSTRERROR
1070     syslog(
1071     LOG_CRIT, "gethostbyname %.80s - %.80s",
1072     hostname, hstrerror( h_errno ) );
1073     (void) fprintf(
1074     stderr, "%s: gethostbyname %s - %s\n",
1075     argv0, hostname, hstrerror( h_errno ) );
1076     #else /* HAVE_HSTRERROR */
1077     syslog( LOG_CRIT, "gethostbyname %.80s failed", hostname );
1078     (void) fprintf(
1079     stderr, "%s: gethostbyname %s failed\n", argv0, hostname );
1080     #endif /* HAVE_HSTRERROR */
1081     exit( 1 );
1082     }
1083     if ( he->h_addrtype != AF_INET )
1084     {
1085     syslog( LOG_CRIT, "%.80s - non-IP network address", hostname );
1086     (void) fprintf(
1087     stderr, "%s: %s - non-IP network address\n",
1088     argv0, hostname );
1089     exit( 1 );
1090     }
1091     (void) memcpy(
1092     &sa4P->sa_in.sin_addr.s_addr, he->h_addr, he->h_length );
1093     }
1094     }
1095     sa4P->sa_in.sin_port = htons( port );
1096     *gotv4P = 1;
1097    
1098     #endif /* HAVE_GETADDRINFO && HAVE_GAI_STRERROR */
1099     }
1100    
1101    
1102     static void
1103     read_throttlefile( char* throttlefile )
1104     {
1105     FILE* fp;
1106     char buf[5000];
1107     char* cp;
1108     int len;
1109     char pattern[5000];
1110     long limit;
1111     struct timeval tv;
1112    
1113     fp = fopen( throttlefile, "r" );
1114     if ( fp == (FILE*) 0 )
1115     {
1116     syslog( LOG_CRIT, "%.80s - %m", throttlefile );
1117     perror( throttlefile );
1118     exit( 1 );
1119     }
1120    
1121     (void) gettimeofday( &tv, (struct timezone*) 0 );
1122    
1123     while ( fgets( buf, sizeof(buf), fp ) != (char*) 0 )
1124     {
1125     /* Nuke comments. */
1126     cp = strchr( buf, '#' );
1127     if ( cp != (char*) 0 )
1128     *cp = '\0';
1129    
1130     /* Nuke trailing whitespace. */
1131     len = strlen( buf );
1132     while ( len > 0 &&
1133     ( buf[len-1] == ' ' || buf[len-1] == '\t' ||
1134     buf[len-1] == '\n' || buf[len-1] == '\r' ) )
1135     buf[--len] = '\0';
1136    
1137     /* Ignore empty lines. */
1138     if ( len == 0 )
1139     continue;
1140    
1141     /* Parse line. */
1142     if ( sscanf( buf, " %4900[^ \t] %ld", pattern, &limit ) != 2 || limit <= 0 )
1143     {
1144     syslog( LOG_CRIT,
1145     "unparsable line in %.80s - %.80s", throttlefile, buf );
1146     (void) fprintf( stderr,
1147     "%s: unparsable line in %.80s - %.80s\n",
1148     argv0, throttlefile, buf );
1149     continue;
1150     }
1151    
1152     /* Nuke any leading slashes in pattern. */
1153     if ( pattern[0] == '/' )
1154     (void) strcpy( pattern, &pattern[1] );
1155     while ( ( cp = strstr( pattern, "|/" ) ) != (char*) 0 )
1156     (void) strcpy( cp + 1, cp + 2 );
1157    
1158     /* Check for room in throttles. */
1159     if ( numthrottles >= maxthrottles )
1160     {
1161     if ( maxthrottles == 0 )
1162     {
1163     maxthrottles = 100; /* arbitrary */
1164     throttles = NEW( throttletab, maxthrottles );
1165     }
1166     else
1167     {
1168     maxthrottles *= 2;
1169     throttles = RENEW( throttles, throttletab, maxthrottles );
1170     }
1171     if ( throttles == (throttletab*) 0 )
1172     {
1173     syslog( LOG_CRIT, "out of memory allocating a throttletab" );
1174     (void) fprintf(
1175     stderr, "%s: out of memory allocating a throttletab\n",
1176     argv0 );
1177     exit( 1 );
1178     }
1179     }
1180    
1181     /* Add to table. */
1182     throttles[numthrottles].pattern = strdup( pattern );
1183     if ( throttles[numthrottles].pattern == (char*) 0 )
1184     {
1185     syslog( LOG_CRIT, "out of memory copying a throttle pattern" );
1186     (void) fprintf(
1187     stderr, "%s: out of memory copying a throttle pattern\n",
1188     argv0 );
1189     exit( 1 );
1190     }
1191     throttles[numthrottles].limit = limit;
1192     throttles[numthrottles].rate = 0;
1193     throttles[numthrottles].bytes_since_avg = 0;
1194     throttles[numthrottles].num_sending = 0;
1195    
1196     ++numthrottles;
1197     }
1198     (void) fclose( fp );
1199     }
1200    
1201    
1202     static void
1203     shut_down( void )
1204     {
1205     int cnum;
1206     struct timeval tv;
1207    
1208     (void) gettimeofday( &tv, (struct timezone*) 0 );
1209     logstats( &tv );
1210     for ( cnum = 0; cnum < maxconnects; ++cnum )
1211     {
1212     if ( connects[cnum].conn_state != CNST_FREE )
1213     httpd_close_conn( connects[cnum].hc, &tv );
1214     if ( connects[cnum].hc != (httpd_conn*) 0 )
1215     {
1216     httpd_destroy_conn( connects[cnum].hc );
1217     free( (void*) connects[cnum].hc );
1218     --httpd_conn_count;
1219     connects[cnum].hc = (httpd_conn*) 0;
1220     }
1221     }
1222     if ( hs != (httpd_server*) 0 )
1223     {
1224     httpd_server* ths = hs;
1225     hs = (httpd_server*) 0;
1226     httpd_terminate( ths );
1227     }
1228     mmc_destroy();
1229     tmr_destroy();
1230     free( (void*) connects );
1231     if ( throttles != (throttletab*) 0 )
1232     free( (void*) throttles );
1233     }
1234    
1235    
1236     static int
1237     handle_newconnect( struct timeval* tvP, int listen_fd )
1238     {
1239     int cnum;
1240     connecttab* c;
1241     ClientData client_data;
1242    
1243     /* This loops until the accept() fails, trying to start new
1244     ** connections as fast as possible so we don't overrun the
1245     ** listen queue.
1246     */
1247     for (;;)
1248     {
1249     /* Is there room in the connection table? */
1250     if ( numconnects >= maxconnects )
1251     {
1252     /* Out of connection slots. Run the timers, then the
1253     ** existing connections, and maybe we'll free up a slot
1254     ** by the time we get back here.
1255     **/
1256     syslog( LOG_WARNING, "too many connections!" );
1257     tmr_run( tvP );
1258     return 0;
1259     }
1260     /* Find a free connection entry. */
1261     for ( cnum = 0; cnum < maxconnects; ++cnum )
1262     if ( connects[cnum].conn_state == CNST_FREE )
1263     break;
1264     c = &connects[cnum];
1265     /* Make the httpd_conn if necessary. */
1266     if ( c->hc == (httpd_conn*) 0 )
1267     {
1268     c->hc = NEW( httpd_conn, 1 );
1269     if ( c->hc == (httpd_conn*) 0 )
1270     {
1271     syslog( LOG_CRIT, "out of memory allocating an httpd_conn" );
1272     exit( 1 );
1273     }
1274     c->hc->initialized = 0;
1275     ++httpd_conn_count;
1276     }
1277    
1278     /* Get the connection. */
1279     switch ( httpd_get_conn( hs, listen_fd, c->hc ) )
1280     {
1281     case GC_FAIL:
1282     case GC_NO_MORE:
1283     return 1;
1284     }
1285     c->conn_state = CNST_READING;
1286     ++numconnects;
1287     client_data.p = c;
1288     c->idle_read_timer = tmr_create(
1289     tvP, idle_read_connection, client_data, IDLE_READ_TIMELIMIT * 1000L,
1290     0 );
1291     if ( c->idle_read_timer == (Timer*) 0 )
1292     {
1293     syslog( LOG_CRIT, "tmr_create(idle_read_connection) failed" );
1294     exit( 1 );
1295     }
1296     c->idle_send_timer = (Timer*) 0;
1297     c->wakeup_timer = (Timer*) 0;
1298     c->linger_timer = (Timer*) 0;
1299     c->bytes_sent = 0;
1300     c->numtnums = 0;
1301    
1302     /* Set the connection file descriptor to no-delay mode. */
1303     httpd_set_ndelay( c->hc->conn_fd );
1304    
1305     fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ );
1306    
1307     ++stats_connections;
1308     if ( numconnects > stats_simultaneous )
1309     stats_simultaneous = numconnects;
1310     }
1311     }
1312    
1313    
1314     static void
1315     handle_read( connecttab* c, struct timeval* tvP )
1316     {
1317     int sz;
1318     ClientData client_data;
1319     httpd_conn* hc = c->hc;
1320    
1321     /* Is there room in our buffer to read more bytes? */
1322     if ( hc->read_idx >= hc->read_size )
1323     {
1324     if ( hc->read_size > 5000 )
1325     {
1326     httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1327     clear_connection( c, tvP );
1328     return;
1329     }
1330     httpd_realloc_str(
1331     &hc->read_buf, &hc->read_size, hc->read_size + 1000 );
1332     }
1333    
1334     /* Read some more bytes. */
1335     sz = read(
1336     hc->conn_fd, &(hc->read_buf[hc->read_idx]),
1337     hc->read_size - hc->read_idx );
1338     /* Ignore EWOULDBLOCK errors. At first glance you would think that
1339     ** connections returned by fdwatch as readable should never give an
1340     ** EWOULDBLOCK; however, this apparently can happen if a packet gets
1341     ** garbled.
1342     */
1343     if ( sz == 0 || ( sz < 0 && ( errno != EWOULDBLOCK ) ) )
1344     {
1345     httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1346     clear_connection( c, tvP );
1347     return;
1348     }
1349     hc->read_idx += sz;
1350    
1351     /* Do we have a complete request yet? */
1352     switch ( httpd_got_request( hc ) )
1353     {
1354     case GR_NO_REQUEST:
1355     return;
1356     case GR_BAD_REQUEST:
1357     httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1358     clear_connection( c, tvP );
1359     return;
1360     }
1361    
1362     /* Yes. Try parsing and resolving it. */
1363     if ( httpd_parse_request( hc ) < 0 )
1364     {
1365     clear_connection( c, tvP );
1366     return;
1367     }
1368    
1369     /* Check the throttle table */
1370     if ( ! check_throttles( c ) )
1371     {
1372     httpd_send_err(
1373     hc, 503, httpd_err503title, "", httpd_err503form, hc->encodedurl );
1374     clear_connection( c, tvP );
1375     return;
1376     }
1377    
1378     /* Start the connection going. */
1379     if ( httpd_start_request( hc, tvP ) < 0 )
1380     {
1381     /* Something went wrong. Close down the connection. */
1382     clear_connection( c, tvP );
1383     return;
1384     }
1385    
1386     /* Fill in bytes_to_send. */
1387     if ( hc->got_range )
1388     {
1389     c->bytes_sent = hc->init_byte_loc;
1390     c->bytes_to_send = hc->end_byte_loc + 1;
1391     }
1392     else
1393     c->bytes_to_send = hc->bytes_to_send;
1394    
1395     /* Check if it's already handled. */
1396     if ( hc->file_address == (char*) 0 )
1397     {
1398     /* No file address means someone else is handling it. */
1399     c->bytes_sent = hc->bytes_sent;
1400     clear_connection( c, tvP );
1401     return;
1402     }
1403     if ( c->bytes_sent >= c->bytes_to_send )
1404     {
1405     /* There's nothing to send. */
1406     clear_connection( c, tvP );
1407     return;
1408     }
1409    
1410     /* Cool, we have a valid connection and a file to send to it. */
1411     c->conn_state = CNST_SENDING;
1412     c->started_at = tvP->tv_sec;
1413     c->wouldblock_delay = 0;
1414     client_data.p = c;
1415     tmr_cancel( c->idle_read_timer );
1416     c->idle_read_timer = (Timer*) 0;
1417     c->idle_send_timer = tmr_create(
1418     tvP, idle_send_connection, client_data, IDLE_SEND_TIMELIMIT * 1000L,
1419     0 );
1420     if ( c->idle_send_timer == (Timer*) 0 )
1421     {
1422     syslog( LOG_CRIT, "tmr_create(idle_send_connection) failed" );
1423     exit( 1 );
1424     }
1425    
1426     fdwatch_del_fd( hc->conn_fd );
1427     fdwatch_add_fd( hc->conn_fd, c, FDW_WRITE );
1428     }
1429    
1430    
1431     static void
1432     handle_send( connecttab* c, struct timeval* tvP )
1433     {
1434     int sz, coast;
1435     ClientData client_data;
1436     time_t elapsed;
1437     httpd_conn* hc = c->hc;
1438    
1439     /* Do we need to write the headers first? */
1440     if ( hc->responselen == 0 )
1441     {
1442     /* No, just write the file. */
1443     sz = write(
1444     hc->conn_fd, &(hc->file_address[c->bytes_sent]),
1445     MIN( c->bytes_to_send - c->bytes_sent, c->limit ) );
1446     }
1447     else
1448     {
1449     /* Yes. We'll combine headers and file into a single writev(),
1450     ** hoping that this generates a single packet.
1451     */
1452     struct iovec iv[2];
1453    
1454     iv[0].iov_base = hc->response;
1455     iv[0].iov_len = hc->responselen;
1456     iv[1].iov_base = &(hc->file_address[c->bytes_sent]);
1457     iv[1].iov_len = MIN( c->bytes_to_send - c->bytes_sent, c->limit );
1458     sz = writev( hc->conn_fd, iv, 2 );
1459     }
1460    
1461     if ( sz == 0 ||
1462     ( sz < 0 && ( errno == EWOULDBLOCK || errno == EAGAIN ) ) )
1463     {
1464     /* This shouldn't happen, but some kernels, e.g.
1465     ** SunOS 4.1.x, are broken and select() says that
1466     ** O_NDELAY sockets are always writable even when
1467     ** they're actually not.
1468     **
1469     ** Current workaround is to block sending on this
1470     ** socket for a brief adaptively-tuned period.
1471     ** Fortunately we already have all the necessary
1472     ** blocking code, for use with throttling.
1473     */
1474     c->wouldblock_delay += MIN_WOULDBLOCK_DELAY;
1475     c->conn_state = CNST_PAUSING;
1476     fdwatch_del_fd( hc->conn_fd );
1477     client_data.p = c;
1478     c->wakeup_timer = tmr_create(
1479     tvP, wakeup_connection, client_data, c->wouldblock_delay, 0 );
1480     if ( c->wakeup_timer == (Timer*) 0 )
1481     {
1482     syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" );
1483     exit( 1 );
1484     }
1485     return;
1486     }
1487     if ( sz < 0 )
1488     {
1489     /* Something went wrong, close this connection.
1490     **
1491     ** If it's just an EPIPE, don't bother logging, that
1492     ** just means the client hung up on us.
1493     **
1494     ** On some systems, write() occasionally gives an EINVAL.
1495     ** Dunno why, something to do with the socket going
1496     ** bad. Anyway, we don't log those either.
1497     **
1498     ** And ECONNRESET isn't interesting either.
1499     */
1500     if ( errno != EPIPE && errno != EINVAL && errno != ECONNRESET )
1501     syslog( LOG_ERR, "write - %m sending %.80s", hc->encodedurl );
1502     clear_connection( c, tvP );
1503     return;
1504     }
1505    
1506     /* Ok, we wrote something. */
1507     tmr_reset( tvP, c->idle_send_timer );
1508     /* Was this a headers + file writev()? */
1509     if ( hc->responselen > 0 )
1510     {
1511     /* Yes; did we write only part of the headers? */
1512     if ( sz < hc->responselen )
1513     {
1514     /* Yes; move the unwritten part to the front of the buffer. */
1515     int newlen = hc->responselen - sz;
1516     (void) memcpy( hc->response, &(hc->response[sz]), newlen );
1517     hc->responselen = newlen;
1518     sz = 0;
1519     }
1520     else
1521     {
1522     /* Nope, we wrote the full headers, so adjust accordingly. */
1523     sz -= hc->responselen;
1524     hc->responselen = 0;
1525     }
1526     }
1527     /* And update how much of the file we wrote. */
1528     c->bytes_sent += sz;
1529     c->hc->bytes_sent += sz;
1530    
1531     /* Are we done? */
1532     if ( c->bytes_sent >= c->bytes_to_send )
1533     {
1534     /* This conection is finished! */
1535     clear_connection( c, tvP );
1536     return;
1537     }
1538    
1539     /* Tune the (blockheaded) wouldblock delay. */
1540     if ( c->wouldblock_delay > MIN_WOULDBLOCK_DELAY )
1541     c->wouldblock_delay -= MIN_WOULDBLOCK_DELAY;
1542    
1543     /* If we're throttling, check if we're sending too fast. */
1544     if ( c->limit != THROTTLE_NOLIMIT )
1545     {
1546     elapsed = tvP->tv_sec - c->started_at;
1547     if ( elapsed == 0 || c->hc->bytes_sent / elapsed > c->limit )
1548     {
1549     c->conn_state = CNST_PAUSING;
1550     fdwatch_del_fd( hc->conn_fd );
1551     /* When should we send the next c->limit bytes
1552     ** to get back on schedule? If less than a second
1553     ** (integer math rounding), use 1/8 second.
1554     */
1555     coast = ( c->hc->bytes_sent + c->limit ) / c->limit - elapsed;
1556     client_data.p = c;
1557     c->wakeup_timer = tmr_create(
1558     tvP, wakeup_connection, client_data,
1559     coast ? ( coast * 1000L ) : 125L, 0 );
1560     if ( c->wakeup_timer == (Timer*) 0 )
1561     {
1562     syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" );
1563     exit( 1 );
1564     }
1565     }
1566     }
1567     }
1568    
1569    
1570     static void
1571     handle_linger( connecttab* c, struct timeval* tvP )
1572     {
1573     char buf[1024];
1574     int r;
1575    
1576     /* In lingering-close mode we just read and ignore bytes. An error
1577     ** or EOF ends things, otherwise we go until a timeout.
1578     */
1579     r = read( c->hc->conn_fd, buf, sizeof(buf) );
1580     if ( r <= 0 )
1581     really_clear_connection( c, tvP );
1582     }
1583    
1584    
1585     static int
1586     check_throttles( connecttab* c )
1587     {
1588     int tnum;
1589    
1590     c->numtnums = 0;
1591     c->limit = THROTTLE_NOLIMIT;
1592     for ( tnum = 0; tnum < numthrottles && c->numtnums < MAXTHROTTLENUMS;
1593     ++tnum )
1594     if ( match( throttles[tnum].pattern, c->hc->expnfilename ) )
1595     {
1596     /* If we're way over the limit, don't even start. */
1597     if ( throttles[tnum].rate > throttles[tnum].limit * 2 )
1598     return 0;
1599     if ( throttles[tnum].num_sending < 0 )
1600     {
1601     syslog( LOG_ERR, "throttle sending count was negative - shouldn't happen!" );
1602     throttles[tnum].num_sending = 0;
1603     }
1604     c->tnums[c->numtnums++] = tnum;
1605     ++throttles[tnum].num_sending;
1606     c->limit = MIN(
1607     c->limit, throttles[tnum].limit / throttles[tnum].num_sending );
1608     }
1609     return 1;
1610     }
1611    
1612    
1613     static void
1614     clear_throttles( connecttab* c, struct timeval* tvP )
1615     {
1616     int i, tnum;
1617    
1618     for ( i = 0; i < c->numtnums; ++i )
1619     {
1620     tnum = c->tnums[i];
1621     --throttles[tnum].num_sending;
1622     throttles[tnum].bytes_since_avg += c->hc->bytes_sent;
1623     }
1624     }
1625    
1626    
1627     static void
1628     update_throttles( ClientData client_data, struct timeval* nowP )
1629     {
1630     int tnum;
1631    
1632     for ( tnum = 0; tnum < numthrottles; ++tnum )
1633     {
1634     throttles[tnum].rate =
1635     ( 3 * throttles[tnum].rate +
1636     throttles[tnum].bytes_since_avg / THROTTLE_TIME ) / 4;
1637     throttles[tnum].bytes_since_avg = 0;
1638     /* Log a warning message if necessary. */
1639     if ( throttles[tnum].rate > throttles[tnum].limit )
1640     {
1641     if ( throttles[tnum].rate > throttles[tnum].limit * 2 )
1642     syslog( LOG_NOTICE, "throttle #%d '%.80s' rate %ld GREATLY exceeding limit %ld", tnum, throttles[tnum].pattern, throttles[tnum].rate, throttles[tnum].limit );
1643     else
1644     syslog( LOG_NOTICE, "throttle #%d '%.80s' rate %ld exceeding limit %ld", tnum, throttles[tnum].pattern, throttles[tnum].rate, throttles[tnum].limit );
1645     }
1646     }
1647     }
1648    
1649    
1650     static void
1651     clear_connection( connecttab* c, struct timeval* tvP )
1652     {
1653     ClientData client_data;
1654    
1655     /* If we haven't actually sent the buffered response yet, do so now. */
1656     httpd_write_response( c->hc );
1657    
1658     if ( c->idle_read_timer != (Timer*) 0 )
1659     {
1660     tmr_cancel( c->idle_read_timer );
1661     c->idle_read_timer = 0;
1662     }
1663     if ( c->idle_send_timer != (Timer*) 0 )
1664     {
1665     tmr_cancel( c->idle_send_timer );
1666     c->idle_send_timer = 0;
1667     }
1668     if ( c->wakeup_timer != (Timer*) 0 )
1669     {
1670     tmr_cancel( c->wakeup_timer );
1671     c->wakeup_timer = 0;
1672     }
1673    
1674     /* This is our version of Apache's lingering_close() routine, which is
1675     ** their version of the often-broken SO_LINGER socket option. For why
1676     ** this is necessary, see http://www.apache.org/docs/misc/fin_wait_2.html
1677     ** What we do is delay the actual closing for a few seconds, while reading
1678     ** any bytes that come over the connection. However, we don't want to do
1679     ** this unless it's necessary, because it ties up a connection slot and
1680     ** file descriptor which means our maximum connection-handling rate
1681     ** is lower. So, elsewhere we set a flag when we detect the few
1682     ** circumstances that make a lingering close necessary. If the flag
1683     ** isn't set we do the real close now.
1684     */
1685     if ( c->hc->should_linger )
1686     {
1687     c->conn_state = CNST_LINGERING;
1688     fdwatch_del_fd( c->hc->conn_fd );
1689     fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ );
1690     /* Make sure we are still in no-delay mode. */
1691     httpd_set_ndelay( c->hc->conn_fd );
1692     client_data.p = c;
1693     c->linger_timer = tmr_create(
1694     tvP, linger_clear_connection, client_data, LINGER_TIME * 1000L, 0 );
1695     if ( c->linger_timer == (Timer*) 0 )
1696     {
1697     syslog( LOG_CRIT, "tmr_create(linger_clear_connection) failed" );
1698     exit( 1 );
1699     }
1700     }
1701     else
1702     really_clear_connection( c, tvP );
1703     }
1704    
1705    
1706     static void
1707     really_clear_connection( connecttab* c, struct timeval* tvP )
1708     {
1709     stats_bytes += c->bytes_to_send;
1710     fdwatch_del_fd( c->hc->conn_fd );
1711     httpd_close_conn( c->hc, tvP );
1712     clear_throttles( c, tvP );
1713     if ( c->linger_timer != (Timer*) 0 )
1714     {
1715     tmr_cancel( c->linger_timer );
1716     c->linger_timer = 0;
1717     }
1718     c->conn_state = CNST_FREE;
1719     --numconnects;
1720     }
1721    
1722    
1723     static void
1724     idle_read_connection( ClientData client_data, struct timeval* nowP )
1725     {
1726     connecttab* c;
1727    
1728     c = (connecttab*) client_data.p;
1729     c->idle_read_timer = (Timer*) 0;
1730     if ( c->conn_state != CNST_FREE )
1731     {
1732     syslog( LOG_INFO,
1733     "%.80s connection timed out reading",
1734     httpd_ntoa( &c->hc->client_addr ) );
1735     httpd_send_err( c->hc, 408, httpd_err408title, "", httpd_err408form, "" );
1736     clear_connection( c, nowP );
1737     }
1738     }
1739    
1740    
1741     static void
1742     idle_send_connection( ClientData client_data, struct timeval* nowP )
1743     {
1744     connecttab* c;
1745    
1746     c = (connecttab*) client_data.p;
1747     c->idle_send_timer = (Timer*) 0;
1748     if ( c->conn_state != CNST_FREE )
1749     {
1750     syslog( LOG_INFO,
1751     "%.80s connection timed out sending",
1752     httpd_ntoa( &c->hc->client_addr ) );
1753     clear_connection( c, nowP );
1754     }
1755     }
1756    
1757    
1758     static void
1759     wakeup_connection( ClientData client_data, struct timeval* nowP )
1760     {
1761     connecttab* c;
1762    
1763     c = (connecttab*) client_data.p;
1764     c->wakeup_timer = (Timer*) 0;
1765     if ( c->conn_state == CNST_PAUSING )
1766     {
1767     c->conn_state = CNST_SENDING;
1768     fdwatch_add_fd( c->hc->conn_fd, c, FDW_WRITE );
1769     }
1770     }
1771    
1772     static void
1773     linger_clear_connection( ClientData client_data, struct timeval* nowP )
1774     {
1775     connecttab* c;
1776    
1777     c = (connecttab*) client_data.p;
1778     c->linger_timer = (Timer*) 0;
1779     really_clear_connection( c, nowP );
1780     }
1781    
1782    
1783     static void
1784     occasional( ClientData client_data, struct timeval* nowP )
1785     {
1786     mmc_cleanup( nowP );
1787     tmr_cleanup();
1788     }
1789    
1790    
1791     #ifdef STATS_TIME
1792     static void
1793     show_stats( ClientData client_data, struct timeval* nowP )
1794     {
1795     logstats( nowP );
1796     }
1797     #endif /* STATS_TIME */
1798    
1799    
1800     /* Generate debugging statistics syslog messages for all packages. */
1801     static void
1802     logstats( struct timeval* nowP )
1803     {
1804     struct timeval tv;
1805     time_t now;
1806     long up_secs, stats_secs;
1807    
1808     if ( nowP == (struct timeval*) 0 )
1809     {
1810     (void) gettimeofday( &tv, (struct timezone*) 0 );
1811     nowP = &tv;
1812     }
1813     now = nowP->tv_sec;
1814     up_secs = now - start_time;
1815     stats_secs = now - stats_time;
1816     if ( stats_secs == 0 )
1817     stats_secs = 1; /* fudge */
1818     stats_time = now;
1819     syslog( LOG_NOTICE,
1820     "up %ld seconds, stats for %ld seconds:", up_secs, stats_secs );
1821    
1822     thttpd_logstats( stats_secs );
1823     httpd_logstats( stats_secs );
1824     mmc_logstats( stats_secs );
1825     fdwatch_logstats( stats_secs );
1826     tmr_logstats( stats_secs );
1827     }
1828    
1829    
1830     /* Generate debugging statistics syslog message. */
1831     static void
1832     thttpd_logstats( long secs )
1833     {
1834     syslog( LOG_NOTICE,
1835     " thttpd - %ld connections (%g/sec), %d max simultaneous, %ld bytes (%g/sec), %d httpd_conns allocated",
1836     stats_connections, (float) stats_connections / secs,
1837     stats_simultaneous, stats_bytes, (float) stats_bytes / secs,
1838     httpd_conn_count );
1839     stats_connections = stats_bytes = 0L;
1840     stats_simultaneous = 0;
1841     }