initial capture of my stuff
[rrq/thttpd.git] / thttpd.c
1 /* thttpd.c - tiny/turbo/throttling HTTP server
2 **
3 ** Copyright © 1995,1998,1999,2000,2001,2015 by
4 ** Jef Poskanzer <jef@mail.acme.com>. 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/wait.h>
37 #include <sys/uio.h>
38
39 #include <errno.h>
40 #ifdef HAVE_FCNTL_H
41 #include <fcntl.h>
42 #endif
43 #include <pwd.h>
44 #ifdef HAVE_GRP_H
45 #include <grp.h>
46 #endif
47 #include <signal.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <syslog.h>
52 #ifdef TIME_WITH_SYS_TIME
53 #include <time.h>
54 #endif
55 #include <unistd.h>
56
57 #include "fdwatch.h"
58 #include "libhttpd.h"
59 #include "mmc.h"
60 #include "timers.h"
61 #include "match.h"
62
63 #ifndef SHUT_WR
64 #define SHUT_WR 1
65 #endif
66
67 #ifndef HAVE_INT64T
68 typedef long long int64_t;
69 #endif
70
71
72 static char* argv0;
73 static int debug;
74 static unsigned short port;
75 static char* dir;
76 static char* data_dir;
77 static int do_chroot, no_log, no_symlink_check, do_vhost, do_global_passwd;
78 static char* cgi_pattern;
79 static int cgi_limit;
80 static char* url_pattern;
81 static int no_empty_referrers;
82 static char* local_pattern;
83 static char* logfile;
84 static char* throttlefile;
85 static char* hostname;
86 static char* pidfile;
87 static char* user;
88 static char* charset;
89 static char* p3p;
90 static int max_age;
91
92
93 typedef struct {
94     char* pattern;
95     long max_limit, min_limit;
96     long rate;
97     off_t bytes_since_avg;
98     int num_sending;
99     } throttletab;
100 static throttletab* throttles;
101 static int numthrottles, maxthrottles;
102
103 #define THROTTLE_NOLIMIT -1
104
105
106 typedef struct {
107     int conn_state;
108     int next_free_connect;
109     httpd_conn* hc;
110     int tnums[MAXTHROTTLENUMS];         /* throttle indexes */
111     int numtnums;
112     long max_limit, min_limit;
113     time_t started_at, active_at;
114     Timer* wakeup_timer;
115     Timer* linger_timer;
116     long wouldblock_delay;
117     off_t bytes;
118     off_t end_byte_index;
119     off_t next_byte_index;
120     } connecttab;
121 static connecttab* connects;
122 static int num_connects, max_connects, first_free_connect;
123 static int httpd_conn_count;
124
125 /* The connection states. */
126 #define CNST_FREE 0
127 #define CNST_READING 1
128 #define CNST_SENDING 2
129 #define CNST_PAUSING 3
130 #define CNST_LINGERING 4
131
132
133 static httpd_server* hs = (httpd_server*) 0;
134 int terminate = 0;
135 time_t start_time, stats_time;
136 long stats_connections;
137 off_t stats_bytes;
138 int stats_simultaneous;
139
140 static volatile int got_hup, got_usr1, watchdog_flag;
141
142
143 /* Forwards. */
144 static void parse_args( int argc, char** argv );
145 static void usage( void );
146 static void read_config( char* filename );
147 static void value_required( char* name, char* value );
148 static void no_value_required( char* name, char* value );
149 static char* e_strdup( char* oldstr );
150 static void lookup_hostname( httpd_sockaddr* sa4P, size_t sa4_len, int* gotv4P, httpd_sockaddr* sa6P, size_t sa6_len, int* gotv6P );
151 static void read_throttlefile( char* tf );
152 static void shut_down( void );
153 static int handle_newconnect( struct timeval* tvP, int listen_fd );
154 static void handle_read( connecttab* c, struct timeval* tvP );
155 static void handle_send( connecttab* c, struct timeval* tvP );
156 static void handle_linger( connecttab* c, struct timeval* tvP );
157 static int check_throttles( connecttab* c );
158 static void clear_throttles( connecttab* c, struct timeval* tvP );
159 static void update_throttles( ClientData client_data, struct timeval* nowP );
160 static void finish_connection( connecttab* c, struct timeval* tvP );
161 static void clear_connection( connecttab* c, struct timeval* tvP );
162 static void really_clear_connection( connecttab* c, struct timeval* tvP );
163 static void idle( ClientData client_data, struct timeval* nowP );
164 static void wakeup_connection( ClientData client_data, struct timeval* nowP );
165 static void linger_clear_connection( ClientData client_data, struct timeval* nowP );
166 static void occasional( ClientData client_data, struct timeval* nowP );
167 #ifdef STATS_TIME
168 static void show_stats( ClientData client_data, struct timeval* nowP );
169 #endif /* STATS_TIME */
170 static void logstats( struct timeval* nowP );
171 static void thttpd_logstats( long secs );
172
173
174 /* SIGTERM and SIGINT say to exit immediately. */
175 static void
176 handle_term( int sig )
177     {
178     /* Don't need to set up the handler again, since it's a one-shot. */
179
180     shut_down();
181     syslog( LOG_NOTICE, "exiting due to signal %d", sig );
182     closelog();
183     exit( 1 );
184     }
185
186
187 /* SIGCHLD - a chile process exitted, so we need to reap the zombie */
188 static void
189 handle_chld( int sig )
190     {
191     const int oerrno = errno;
192     pid_t pid;
193     int status;
194
195 #ifndef HAVE_SIGSET
196     /* Set up handler again. */
197     (void) signal( SIGCHLD, handle_chld );
198 #endif /* ! HAVE_SIGSET */
199
200     /* Reap defunct children until there aren't any more. */
201     for (;;)
202         {
203 #ifdef HAVE_WAITPID
204         pid = waitpid( (pid_t) -1, &status, WNOHANG );
205 #else /* HAVE_WAITPID */
206         pid = wait3( &status, WNOHANG, (struct rusage*) 0 );
207 #endif /* HAVE_WAITPID */
208         if ( (int) pid == 0 )           /* none left */
209             break;
210         if ( (int) pid < 0 )
211             {
212             if ( errno == EINTR || errno == EAGAIN )
213                 continue;
214             /* ECHILD shouldn't happen with the WNOHANG option,
215             ** but with some kernels it does anyway.  Ignore it.
216             */
217             if ( errno != ECHILD )
218                 syslog( LOG_ERR, "child wait - %m" );
219             break;
220             }
221         /* Decrement the CGI count.  Note that this is not accurate, since
222         ** each CGI can involve two or even three child processes.
223         ** Decrementing for each child means that when there is heavy CGI
224         ** activity, the count will be lower than it should be, and therefore
225         ** more CGIs will be allowed than should be.
226         */
227         if ( hs != (httpd_server*) 0 )
228             {
229             --hs->cgi_count;
230             if ( hs->cgi_count < 0 )
231                 hs->cgi_count = 0;
232             }
233         }
234
235     /* Restore previous errno. */
236     errno = oerrno;
237     }
238
239
240 /* SIGHUP says to re-open the log file. */
241 static void
242 handle_hup( int sig )
243     {
244     const int oerrno = errno;
245
246 #ifndef HAVE_SIGSET
247     /* Set up handler again. */
248     (void) signal( SIGHUP, handle_hup );
249 #endif /* ! HAVE_SIGSET */
250
251     /* Just set a flag that we got the signal. */
252     got_hup = 1;
253
254     /* Restore previous errno. */
255     errno = oerrno;
256     }
257
258
259 /* SIGUSR1 says to exit as soon as all current connections are done. */
260 static void
261 handle_usr1( int sig )
262     {
263     /* Don't need to set up the handler again, since it's a one-shot. */
264
265     if ( num_connects == 0 )
266         {
267         /* If there are no active connections we want to exit immediately
268         ** here.  Not only is it faster, but without any connections the
269         ** main loop won't wake up until the next new connection.
270         */
271         shut_down();
272         syslog( LOG_NOTICE, "exiting" );
273         closelog();
274         exit( 0 );
275         }
276
277     /* Otherwise, just set a flag that we got the signal. */
278     got_usr1 = 1;
279
280     /* Don't need to restore old errno, since we didn't do any syscalls. */
281     }
282
283
284 /* SIGUSR2 says to generate the stats syslogs immediately. */
285 static void
286 handle_usr2( int sig )
287     {
288     const int oerrno = errno;
289
290 #ifndef HAVE_SIGSET
291     /* Set up handler again. */
292     (void) signal( SIGUSR2, handle_usr2 );
293 #endif /* ! HAVE_SIGSET */
294
295     logstats( (struct timeval*) 0 );
296
297     /* Restore previous errno. */
298     errno = oerrno;
299     }
300
301
302 /* SIGALRM is used as a watchdog. */
303 static void
304 handle_alrm( int sig )
305     {
306     const int oerrno = errno;
307
308     /* If nothing has been happening */
309     if ( ! watchdog_flag )
310         {
311         /* Try changing dirs to someplace we can write. */
312         (void) chdir( "/tmp" );
313         /* Dump core. */
314         abort();
315         }
316     watchdog_flag = 0;
317
318 #ifndef HAVE_SIGSET
319     /* Set up handler again. */
320     (void) signal( SIGALRM, handle_alrm );
321 #endif /* ! HAVE_SIGSET */
322     /* Set up alarm again. */
323     (void) alarm( OCCASIONAL_TIME * 3 );
324
325     /* Restore previous errno. */
326     errno = oerrno;
327     }
328
329
330 static void
331 re_open_logfile( void )
332     {
333     FILE* logfp;
334
335     if ( no_log || hs == (httpd_server*) 0 )
336         return;
337
338     /* Re-open the log file. */
339     if ( logfile != (char*) 0 && strcmp( logfile, "-" ) != 0 )
340         {
341         syslog( LOG_NOTICE, "re-opening logfile" );
342         logfp = fopen( logfile, "a" );
343         if ( logfp == (FILE*) 0 )
344             {
345             syslog( LOG_CRIT, "re-opening %.80s - %m", logfile );
346             return;
347             }
348         (void) fcntl( fileno( logfp ), F_SETFD, 1 );
349         httpd_set_logfp( hs, logfp );
350         }
351     }
352
353
354 int
355 main( int argc, char** argv )
356     {
357     char* cp;
358     struct passwd* pwd;
359     uid_t uid = 32767;
360     gid_t gid = 32767;
361     char cwd[MAXPATHLEN+1];
362     FILE* logfp;
363     int num_ready;
364     int cnum;
365     connecttab* c;
366     httpd_conn* hc;
367     httpd_sockaddr sa4;
368     httpd_sockaddr sa6;
369     int gotv4, gotv6;
370     struct timeval tv;
371
372     argv0 = argv[0];
373
374     cp = strrchr( argv0, '/' );
375     if ( cp != (char*) 0 )
376         ++cp;
377     else
378         cp = argv0;
379     openlog( cp, LOG_NDELAY|LOG_PID, LOG_FACILITY );
380
381     /* Handle command-line arguments. */
382     parse_args( argc, argv );
383
384     /* Read zone info now, in case we chroot(). */
385     tzset();
386
387     /* Look up hostname now, in case we chroot(). */
388     lookup_hostname( &sa4, sizeof(sa4), &gotv4, &sa6, sizeof(sa6), &gotv6 );
389     if ( ! ( gotv4 || gotv6 ) )
390         {
391         syslog( LOG_ERR, "can't find any valid address" );
392         (void) fprintf( stderr, "%s: can't find any valid address\n", argv0 );
393         exit( 1 );
394         }
395
396     /* Throttle file. */
397     numthrottles = 0;
398     maxthrottles = 0;
399     throttles = (throttletab*) 0;
400     if ( throttlefile != (char*) 0 )
401         read_throttlefile( throttlefile );
402
403     /* If we're root and we're going to become another user, get the uid/gid
404     ** now.
405     */
406     if ( getuid() == 0 )
407         {
408         pwd = getpwnam( user );
409         if ( pwd == (struct passwd*) 0 )
410             {
411             syslog( LOG_CRIT, "unknown user - '%.80s'", user );
412             (void) fprintf( stderr, "%s: unknown user - '%s'\n", argv0, user );
413             exit( 1 );
414             }
415         uid = pwd->pw_uid;
416         gid = pwd->pw_gid;
417         }
418
419     /* Log file. */
420     if ( logfile != (char*) 0 )
421         {
422         if ( strcmp( logfile, "/dev/null" ) == 0 )
423             {
424             no_log = 1;
425             logfp = (FILE*) 0;
426             }
427         else if ( strcmp( logfile, "-" ) == 0 )
428             logfp = stdout;
429         else
430             {
431             logfp = fopen( logfile, "a" );
432             if ( logfp == (FILE*) 0 )
433                 {
434                 syslog( LOG_CRIT, "%.80s - %m", logfile );
435                 perror( logfile );
436                 exit( 1 );
437                 }
438             if ( logfile[0] != '/' )
439                 {
440                 syslog( LOG_WARNING, "logfile is not an absolute path, you may not be able to re-open it" );
441                 (void) fprintf( stderr, "%s: logfile is not an absolute path, you may not be able to re-open it\n", argv0 );
442                 }
443             (void) fcntl( fileno( logfp ), F_SETFD, 1 );
444             if ( getuid() == 0 )
445                 {
446                 /* If we are root then we chown the log file to the user we'll
447                 ** be switching to.
448                 */
449                 if ( fchown( fileno( logfp ), uid, gid ) < 0 )
450                     {
451                     syslog( LOG_WARNING, "fchown logfile - %m" );
452                     perror( "fchown logfile" );
453                     }
454                 }
455             }
456         }
457     else
458         logfp = (FILE*) 0;
459
460     /* Switch directories if requested. */
461     if ( dir != (char*) 0 )
462         {
463         if ( chdir( dir ) < 0 )
464             {
465             syslog( LOG_CRIT, "chdir - %m" );
466             perror( "chdir" );
467             exit( 1 );
468             }
469         }
470 #ifdef USE_USER_DIR
471     else if ( getuid() == 0 )
472         {
473         /* No explicit directory was specified, we're root, and the
474         ** USE_USER_DIR option is set - switch to the specified user's
475         ** home dir.
476         */
477         if ( chdir( pwd->pw_dir ) < 0 )
478             {
479             syslog( LOG_CRIT, "chdir - %m" );
480             perror( "chdir" );
481             exit( 1 );
482             }
483         }
484 #endif /* USE_USER_DIR */
485
486     /* Get current directory. */
487     (void) getcwd( cwd, sizeof(cwd) - 1 );
488     if ( cwd[strlen( cwd ) - 1] != '/' )
489         (void) strcat( cwd, "/" );
490
491     if ( ! debug )
492         {
493         /* We're not going to use stdin stdout or stderr from here on, so close
494         ** them to save file descriptors.
495         */
496         (void) fclose( stdin );
497         if ( logfp != stdout )
498             (void) fclose( stdout );
499         (void) fclose( stderr );
500
501         /* Daemonize - make ourselves a subprocess. */
502 #ifdef HAVE_DAEMON
503         if ( daemon( 1, 1 ) < 0 )
504             {
505             syslog( LOG_CRIT, "daemon - %m" );
506             exit( 1 );
507             }
508 #else /* HAVE_DAEMON */
509         switch ( fork() )
510             {
511             case 0:
512             break;
513             case -1:
514             syslog( LOG_CRIT, "fork - %m" );
515             exit( 1 );
516             default:
517             exit( 0 );
518             }
519 #ifdef HAVE_SETSID
520         (void) setsid();
521 #endif /* HAVE_SETSID */
522 #endif /* HAVE_DAEMON */
523         }
524     else
525         {
526         /* Even if we don't daemonize, we still want to disown our parent
527         ** process.
528         */
529 #ifdef HAVE_SETSID
530         (void) setsid();
531 #endif /* HAVE_SETSID */
532         }
533
534     if ( pidfile != (char*) 0 )
535         {
536         /* Write the PID file. */
537         FILE* pidfp = fopen( pidfile, "w" );
538         if ( pidfp == (FILE*) 0 )
539             {
540             syslog( LOG_CRIT, "%.80s - %m", pidfile );
541             exit( 1 );
542             }
543         (void) fprintf( pidfp, "%d\n", (int) getpid() );
544         (void) fclose( pidfp );
545         }
546
547     /* Initialize the fdwatch package.  Have to do this before chroot,
548     ** if /dev/poll is used.
549     */
550     max_connects = fdwatch_get_nfiles();
551     if ( max_connects < 0 )
552         {
553         syslog( LOG_CRIT, "fdwatch initialization failure" );
554         exit( 1 );
555         }
556     max_connects -= SPARE_FDS;
557
558     /* Chroot if requested. */
559     if ( do_chroot )
560         {
561         if ( chroot( cwd ) < 0 )
562             {
563             syslog( LOG_CRIT, "chroot - %m" );
564             perror( "chroot" );
565             exit( 1 );
566             }
567         /* If we're logging and the logfile's pathname begins with the
568         ** chroot tree's pathname, then elide the chroot pathname so
569         ** that the logfile pathname still works from inside the chroot
570         ** tree.
571         */
572         if ( logfile != (char*) 0 && strcmp( logfile, "-" ) != 0 )
573             {
574             if ( strncmp( logfile, cwd, strlen( cwd ) ) == 0 )
575                 {
576                 (void) ol_strcpy( logfile, &logfile[strlen( cwd ) - 1] );
577                 /* (We already guaranteed that cwd ends with a slash, so leaving
578                 ** that slash in logfile makes it an absolute pathname within
579                 ** the chroot tree.)
580                 */
581                 }
582             else
583                 {
584                 syslog( LOG_WARNING, "logfile is not within the chroot tree, you will not be able to re-open it" );
585                 (void) fprintf( stderr, "%s: logfile is not within the chroot tree, you will not be able to re-open it\n", argv0 );
586                 }
587             }
588         (void) strcpy( cwd, "/" );
589         /* Always chdir to / after a chroot. */
590         if ( chdir( cwd ) < 0 )
591             {
592             syslog( LOG_CRIT, "chroot chdir - %m" );
593             perror( "chroot chdir" );
594             exit( 1 );
595             }
596         }
597
598     /* Switch directories again if requested. */
599     if ( data_dir != (char*) 0 )
600         {
601         if ( chdir( data_dir ) < 0 )
602             {
603             syslog( LOG_CRIT, "data_dir chdir - %m" );
604             perror( "data_dir chdir" );
605             exit( 1 );
606             }
607         }
608
609     /* Set up to catch signals. */
610 #ifdef HAVE_SIGSET
611     (void) sigset( SIGTERM, handle_term );
612     (void) sigset( SIGINT, handle_term );
613     (void) sigset( SIGCHLD, handle_chld );
614     (void) sigset( SIGPIPE, SIG_IGN );          /* get EPIPE instead */
615     (void) sigset( SIGHUP, handle_hup );
616     (void) sigset( SIGUSR1, handle_usr1 );
617     (void) sigset( SIGUSR2, handle_usr2 );
618     (void) sigset( SIGALRM, handle_alrm );
619 #else /* HAVE_SIGSET */
620     (void) signal( SIGTERM, handle_term );
621     (void) signal( SIGINT, handle_term );
622     (void) signal( SIGCHLD, handle_chld );
623     (void) signal( SIGPIPE, SIG_IGN );          /* get EPIPE instead */
624     (void) signal( SIGHUP, handle_hup );
625     (void) signal( SIGUSR1, handle_usr1 );
626     (void) signal( SIGUSR2, handle_usr2 );
627     (void) signal( SIGALRM, handle_alrm );
628 #endif /* HAVE_SIGSET */
629     got_hup = 0;
630     got_usr1 = 0;
631     watchdog_flag = 0;
632     (void) alarm( OCCASIONAL_TIME * 3 );
633
634     /* Initialize the timer package. */
635     tmr_init();
636
637     /* Initialize the HTTP layer.  Got to do this before giving up root,
638     ** so that we can bind to a privileged port.
639     */
640     hs = httpd_initialize(
641         hostname,
642         gotv4 ? &sa4 : (httpd_sockaddr*) 0, gotv6 ? &sa6 : (httpd_sockaddr*) 0,
643         port, cgi_pattern, cgi_limit, charset, p3p, max_age, cwd, no_log, logfp,
644         no_symlink_check, do_vhost, do_global_passwd, url_pattern,
645         local_pattern, no_empty_referrers );
646     if ( hs == (httpd_server*) 0 )
647         exit( 1 );
648
649     /* Set up the occasional timer. */
650     if ( tmr_create( (struct timeval*) 0, occasional, JunkClientData, OCCASIONAL_TIME * 1000L, 1 ) == (Timer*) 0 )
651         {
652         syslog( LOG_CRIT, "tmr_create(occasional) failed" );
653         exit( 1 );
654         }
655     /* Set up the idle timer. */
656     if ( tmr_create( (struct timeval*) 0, idle, JunkClientData, 5 * 1000L, 1 ) == (Timer*) 0 )
657         {
658         syslog( LOG_CRIT, "tmr_create(idle) failed" );
659         exit( 1 );
660         }
661     if ( numthrottles > 0 )
662         {
663         /* Set up the throttles timer. */
664         if ( tmr_create( (struct timeval*) 0, update_throttles, JunkClientData, THROTTLE_TIME * 1000L, 1 ) == (Timer*) 0 )
665             {
666             syslog( LOG_CRIT, "tmr_create(update_throttles) failed" );
667             exit( 1 );
668             }
669         }
670 #ifdef STATS_TIME
671     /* Set up the stats timer. */
672     if ( tmr_create( (struct timeval*) 0, show_stats, JunkClientData, STATS_TIME * 1000L, 1 ) == (Timer*) 0 )
673         {
674         syslog( LOG_CRIT, "tmr_create(show_stats) failed" );
675         exit( 1 );
676         }
677 #endif /* STATS_TIME */
678     start_time = stats_time = time( (time_t*) 0 );
679     stats_connections = 0;
680     stats_bytes = 0;
681     stats_simultaneous = 0;
682
683     /* If we're root, try to become someone else. */
684     if ( getuid() == 0 )
685         {
686         /* Set aux groups to null. */
687         if ( setgroups( 0, (const gid_t*) 0 ) < 0 )
688             {
689             syslog( LOG_CRIT, "setgroups - %m" );
690             exit( 1 );
691             }
692         /* Set primary group. */
693         if ( setgid( gid ) < 0 )
694             {
695             syslog( LOG_CRIT, "setgid - %m" );
696             exit( 1 );
697             }
698         /* Try setting aux groups correctly - not critical if this fails. */
699         if ( initgroups( user, gid ) < 0 )
700             syslog( LOG_WARNING, "initgroups - %m" );
701 #ifdef HAVE_SETLOGIN
702         /* Set login name. */
703         (void) setlogin( user );
704 #endif /* HAVE_SETLOGIN */
705         /* Set uid. */
706         if ( setuid( uid ) < 0 )
707             {
708             syslog( LOG_CRIT, "setuid - %m" );
709             exit( 1 );
710             }
711         /* Check for unnecessary security exposure. */
712         if ( ! do_chroot )
713             syslog(
714                 LOG_WARNING,
715                 "started as root without requesting chroot(), warning only" );
716         }
717
718     /* Initialize our connections table. */
719     connects = NEW( connecttab, max_connects );
720     if ( connects == (connecttab*) 0 )
721         {
722         syslog( LOG_CRIT, "out of memory allocating a connecttab" );
723         exit( 1 );
724         }
725     for ( cnum = 0; cnum < max_connects; ++cnum )
726         {
727         connects[cnum].conn_state = CNST_FREE;
728         connects[cnum].next_free_connect = cnum + 1;
729         connects[cnum].hc = (httpd_conn*) 0;
730         }
731     connects[max_connects - 1].next_free_connect = -1;  /* end of link list */
732     first_free_connect = 0;
733     num_connects = 0;
734     httpd_conn_count = 0;
735
736     if ( hs != (httpd_server*) 0 )
737         {
738         if ( hs->listen4_fd != -1 )
739             fdwatch_add_fd( hs->listen4_fd, (void*) 0, FDW_READ );
740         if ( hs->listen6_fd != -1 )
741             fdwatch_add_fd( hs->listen6_fd, (void*) 0, FDW_READ );
742         }
743
744     /* Main loop. */
745     (void) gettimeofday( &tv, (struct timezone*) 0 );
746     while ( ( ! terminate ) || num_connects > 0 )
747         {
748         /* Do we need to re-open the log file? */
749         if ( got_hup )
750             {
751             re_open_logfile();
752             got_hup = 0;
753             }
754
755         /* Do the fd watch. */
756         num_ready = fdwatch( tmr_mstimeout( &tv ) );
757         if ( num_ready < 0 )
758             {
759             if ( errno == EINTR || errno == EAGAIN )
760                 continue;       /* try again */
761             syslog( LOG_ERR, "fdwatch - %m" );
762             exit( 1 );
763             }
764         (void) gettimeofday( &tv, (struct timezone*) 0 );
765
766         if ( num_ready == 0 )
767             {
768             /* No fd's are ready - run the timers. */
769             tmr_run( &tv );
770             continue;
771             }
772
773         /* Is it a new connection? */
774         if ( hs != (httpd_server*) 0 && hs->listen6_fd != -1 &&
775              fdwatch_check_fd( hs->listen6_fd ) )
776             {
777             if ( handle_newconnect( &tv, hs->listen6_fd ) )
778                 /* Go around the loop and do another fdwatch, rather than
779                 ** dropping through and processing existing connections.
780                 ** New connections always get priority.
781                 */
782                 continue;
783             }
784         if ( hs != (httpd_server*) 0 && hs->listen4_fd != -1 &&
785              fdwatch_check_fd( hs->listen4_fd ) )
786             {
787             if ( handle_newconnect( &tv, hs->listen4_fd ) )
788                 /* Go around the loop and do another fdwatch, rather than
789                 ** dropping through and processing existing connections.
790                 ** New connections always get priority.
791                 */
792                 continue;
793             }
794
795         /* Find the connections that need servicing. */
796         while ( ( c = (connecttab*) fdwatch_get_next_client_data() ) != (connecttab*) -1 )
797             {
798             if ( c == (connecttab*) 0 )
799                 continue;
800             hc = c->hc;
801             if ( ! fdwatch_check_fd( hc->conn_fd ) )
802                 /* Something went wrong. */
803                 clear_connection( c, &tv );
804             else
805                 switch ( c->conn_state )
806                     {
807                     case CNST_READING: handle_read( c, &tv ); break;
808                     case CNST_SENDING: handle_send( c, &tv ); break;
809                     case CNST_LINGERING: handle_linger( c, &tv ); break;
810                     }
811             }
812         tmr_run( &tv );
813
814         if ( got_usr1 && ! terminate )
815             {
816             terminate = 1;
817             if ( hs != (httpd_server*) 0 )
818                 {
819                 if ( hs->listen4_fd != -1 )
820                     fdwatch_del_fd( hs->listen4_fd );
821                 if ( hs->listen6_fd != -1 )
822                     fdwatch_del_fd( hs->listen6_fd );
823                 httpd_unlisten( hs );
824                 }
825             }
826         }
827
828     /* The main loop terminated. */
829     shut_down();
830     syslog( LOG_NOTICE, "exiting" );
831     closelog();
832     exit( 0 );
833     }
834
835
836 static void
837 parse_args( int argc, char** argv )
838     {
839     int argn;
840
841     debug = 0;
842     port = DEFAULT_PORT;
843     dir = (char*) 0;
844     data_dir = (char*) 0;
845 #ifdef ALWAYS_CHROOT
846     do_chroot = 1;
847 #else /* ALWAYS_CHROOT */
848     do_chroot = 0;
849 #endif /* ALWAYS_CHROOT */
850     no_log = 0;
851     no_symlink_check = do_chroot;
852 #ifdef ALWAYS_VHOST
853     do_vhost = 1;
854 #else /* ALWAYS_VHOST */
855     do_vhost = 0;
856 #endif /* ALWAYS_VHOST */
857 #ifdef ALWAYS_GLOBAL_PASSWD
858     do_global_passwd = 1;
859 #else /* ALWAYS_GLOBAL_PASSWD */
860     do_global_passwd = 0;
861 #endif /* ALWAYS_GLOBAL_PASSWD */
862 #ifdef CGI_PATTERN
863     cgi_pattern = CGI_PATTERN;
864 #else /* CGI_PATTERN */
865     cgi_pattern = (char*) 0;
866 #endif /* CGI_PATTERN */
867 #ifdef CGI_LIMIT
868     cgi_limit = CGI_LIMIT;
869 #else /* CGI_LIMIT */
870     cgi_limit = 0;
871 #endif /* CGI_LIMIT */
872     url_pattern = (char*) 0;
873     no_empty_referrers = 0;
874     local_pattern = (char*) 0;
875     throttlefile = (char*) 0;
876     hostname = (char*) 0;
877     logfile = (char*) 0;
878     pidfile = (char*) 0;
879     user = DEFAULT_USER;
880     charset = DEFAULT_CHARSET;
881     p3p = "";
882     max_age = -1;
883     argn = 1;
884     while ( argn < argc && argv[argn][0] == '-' )
885         {
886         if ( strcmp( argv[argn], "-V" ) == 0 )
887             {
888             (void) printf( "%s\n", SERVER_SOFTWARE );
889             exit( 0 );
890             }
891         else if ( strcmp( argv[argn], "-C" ) == 0 && argn + 1 < argc )
892             {
893             ++argn;
894             read_config( argv[argn] );
895             }
896         else if ( strcmp( argv[argn], "-p" ) == 0 && argn + 1 < argc )
897             {
898             ++argn;
899             port = (unsigned short) atoi( argv[argn] );
900             }
901         else if ( strcmp( argv[argn], "-d" ) == 0 && argn + 1 < argc )
902             {
903             ++argn;
904             dir = argv[argn];
905             }
906         else if ( strcmp( argv[argn], "-r" ) == 0 )
907             {
908             do_chroot = 1;
909             no_symlink_check = 1;
910             }
911         else if ( strcmp( argv[argn], "-nor" ) == 0 )
912             {
913             do_chroot = 0;
914             no_symlink_check = 0;
915             }
916         else if ( strcmp( argv[argn], "-dd" ) == 0 && argn + 1 < argc )
917             {
918             ++argn;
919             data_dir = argv[argn];
920             }
921         else if ( strcmp( argv[argn], "-s" ) == 0 )
922             no_symlink_check = 0;
923         else if ( strcmp( argv[argn], "-nos" ) == 0 )
924             no_symlink_check = 1;
925         else if ( strcmp( argv[argn], "-u" ) == 0 && argn + 1 < argc )
926             {
927             ++argn;
928             user = argv[argn];
929             }
930         else if ( strcmp( argv[argn], "-c" ) == 0 && argn + 1 < argc )
931             {
932             ++argn;
933             cgi_pattern = argv[argn];
934             }
935         else if ( strcmp( argv[argn], "-t" ) == 0 && argn + 1 < argc )
936             {
937             ++argn;
938             throttlefile = argv[argn];
939             }
940         else if ( strcmp( argv[argn], "-h" ) == 0 && argn + 1 < argc )
941             {
942             ++argn;
943             hostname = argv[argn];
944             }
945         else if ( strcmp( argv[argn], "-l" ) == 0 && argn + 1 < argc )
946             {
947             ++argn;
948             logfile = argv[argn];
949             }
950         else if ( strcmp( argv[argn], "-v" ) == 0 )
951             do_vhost = 1;
952         else if ( strcmp( argv[argn], "-nov" ) == 0 )
953             do_vhost = 0;
954         else if ( strcmp( argv[argn], "-g" ) == 0 )
955             do_global_passwd = 1;
956         else if ( strcmp( argv[argn], "-nog" ) == 0 )
957             do_global_passwd = 0;
958         else if ( strcmp( argv[argn], "-i" ) == 0 && argn + 1 < argc )
959             {
960             ++argn;
961             pidfile = argv[argn];
962             }
963         else if ( strcmp( argv[argn], "-T" ) == 0 && argn + 1 < argc )
964             {
965             ++argn;
966             charset = argv[argn];
967             }
968         else if ( strcmp( argv[argn], "-P" ) == 0 && argn + 1 < argc )
969             {
970             ++argn;
971             p3p = argv[argn];
972             }
973         else if ( strcmp( argv[argn], "-M" ) == 0 && argn + 1 < argc )
974             {
975             ++argn;
976             max_age = atoi( argv[argn] );
977             }
978         else if ( strcmp( argv[argn], "-D" ) == 0 )
979             debug = 1;
980         else
981             usage();
982         ++argn;
983         }
984     if ( argn != argc )
985         usage();
986     }
987
988
989 static void
990 usage( void )
991     {
992     (void) fprintf( stderr,
993 "usage:  %s [-C configfile] [-p port] [-d dir] [-r|-nor] [-dd data_dir] [-s|-nos] [-v|-nov] [-g|-nog] [-u user] [-c cgipat] [-t throttles] [-h host] [-l logfile] [-i pidfile] [-T charset] [-P P3P] [-M maxage] [-V] [-D]\n",
994         argv0 );
995     exit( 1 );
996     }
997
998
999 static void
1000 read_config( char* filename )
1001     {
1002     FILE* fp;
1003     char line[10000];
1004     char* cp;
1005     char* cp2;
1006     char* name;
1007     char* value;
1008
1009     fp = fopen( filename, "r" );
1010     if ( fp == (FILE*) 0 )
1011         {
1012         perror( filename );
1013         exit( 1 );
1014         }
1015
1016     while ( fgets( line, sizeof(line), fp ) != (char*) 0 )
1017         {
1018         /* Trim comments. */
1019         if ( ( cp = strchr( line, '#' ) ) != (char*) 0 )
1020             *cp = '\0';
1021
1022         /* Skip leading whitespace. */
1023         cp = line;
1024         cp += strspn( cp, " \t\n\r" );
1025
1026         /* Split line into words. */
1027         while ( *cp != '\0' )
1028             {
1029             /* Find next whitespace. */
1030             cp2 = cp + strcspn( cp, " \t\n\r" );
1031             /* Insert EOS and advance next-word pointer. */
1032             while ( *cp2 == ' ' || *cp2 == '\t' || *cp2 == '\n' || *cp2 == '\r' )
1033                 *cp2++ = '\0';
1034             /* Split into name and value. */
1035             name = cp;
1036             value = strchr( name, '=' );
1037             if ( value != (char*) 0 )
1038                 *value++ = '\0';
1039             /* Interpret. */
1040             if ( strcasecmp( name, "debug" ) == 0 )
1041                 {
1042                 no_value_required( name, value );
1043                 debug = 1;
1044                 }
1045             else if ( strcasecmp( name, "port" ) == 0 )
1046                 {
1047                 value_required( name, value );
1048                 port = (unsigned short) atoi( value );
1049                 }
1050             else if ( strcasecmp( name, "dir" ) == 0 )
1051                 {
1052                 value_required( name, value );
1053                 dir = e_strdup( value );
1054                 }
1055             else if ( strcasecmp( name, "chroot" ) == 0 )
1056                 {
1057                 no_value_required( name, value );
1058                 do_chroot = 1;
1059                 no_symlink_check = 1;
1060                 }
1061             else if ( strcasecmp( name, "nochroot" ) == 0 )
1062                 {
1063                 no_value_required( name, value );
1064                 do_chroot = 0;
1065                 no_symlink_check = 0;
1066                 }
1067             else if ( strcasecmp( name, "data_dir" ) == 0 )
1068                 {
1069                 value_required( name, value );
1070                 data_dir = e_strdup( value );
1071                 }
1072             else if ( strcasecmp( name, "nosymlinkcheck" ) == 0 )
1073                 {
1074                 no_value_required( name, value );
1075                 no_symlink_check = 1;
1076                 }
1077             else if ( strcasecmp( name, "symlinkcheck" ) == 0 )
1078                 {
1079                 no_value_required( name, value );
1080                 no_symlink_check = 0;
1081                 }
1082             else if ( strcasecmp( name, "user" ) == 0 )
1083                 {
1084                 value_required( name, value );
1085                 user = e_strdup( value );
1086                 }
1087             else if ( strcasecmp( name, "cgipat" ) == 0 )
1088                 {
1089                 value_required( name, value );
1090                 cgi_pattern = e_strdup( value );
1091                 }
1092             else if ( strcasecmp( name, "cgilimit" ) == 0 )
1093                 {
1094                 value_required( name, value );
1095                 cgi_limit = atoi( value );
1096                 }
1097             else if ( strcasecmp( name, "urlpat" ) == 0 )
1098                 {
1099                 value_required( name, value );
1100                 url_pattern = e_strdup( value );
1101                 }
1102             else if ( strcasecmp( name, "noemptyreferers" ) == 0 ||
1103                       strcasecmp( name, "noemptyreferrers" ) == 0 )
1104                 {
1105                 no_value_required( name, value );
1106                 no_empty_referrers = 1;
1107                 }
1108             else if ( strcasecmp( name, "localpat" ) == 0 )
1109                 {
1110                 value_required( name, value );
1111                 local_pattern = e_strdup( value );
1112                 }
1113             else if ( strcasecmp( name, "throttles" ) == 0 )
1114                 {
1115                 value_required( name, value );
1116                 throttlefile = e_strdup( value );
1117                 }
1118             else if ( strcasecmp( name, "host" ) == 0 )
1119                 {
1120                 value_required( name, value );
1121                 hostname = e_strdup( value );
1122                 }
1123             else if ( strcasecmp( name, "logfile" ) == 0 )
1124                 {
1125                 value_required( name, value );
1126                 logfile = e_strdup( value );
1127                 }
1128             else if ( strcasecmp( name, "vhost" ) == 0 )
1129                 {
1130                 no_value_required( name, value );
1131                 do_vhost = 1;
1132                 }
1133             else if ( strcasecmp( name, "novhost" ) == 0 )
1134                 {
1135                 no_value_required( name, value );
1136                 do_vhost = 0;
1137                 }
1138             else if ( strcasecmp( name, "globalpasswd" ) == 0 )
1139                 {
1140                 no_value_required( name, value );
1141                 do_global_passwd = 1;
1142                 }
1143             else if ( strcasecmp( name, "noglobalpasswd" ) == 0 )
1144                 {
1145                 no_value_required( name, value );
1146                 do_global_passwd = 0;
1147                 }
1148             else if ( strcasecmp( name, "pidfile" ) == 0 )
1149                 {
1150                 value_required( name, value );
1151                 pidfile = e_strdup( value );
1152                 }
1153             else if ( strcasecmp( name, "charset" ) == 0 )
1154                 {
1155                 value_required( name, value );
1156                 charset = e_strdup( value );
1157                 }
1158             else if ( strcasecmp( name, "p3p" ) == 0 )
1159                 {
1160                 value_required( name, value );
1161                 p3p = e_strdup( value );
1162                 }
1163             else if ( strcasecmp( name, "max_age" ) == 0 )
1164                 {
1165                 value_required( name, value );
1166                 max_age = atoi( value );
1167                 }
1168             else
1169                 {
1170                 (void) fprintf(
1171                     stderr, "%s: unknown config option '%s'\n", argv0, name );
1172                 exit( 1 );
1173                 }
1174
1175             /* Advance to next word. */
1176             cp = cp2;
1177             cp += strspn( cp, " \t\n\r" );
1178             }
1179         }
1180
1181     (void) fclose( fp );
1182     }
1183
1184
1185 static void
1186 value_required( char* name, char* value )
1187     {
1188     if ( value == (char*) 0 )
1189         {
1190         (void) fprintf(
1191             stderr, "%s: value required for %s option\n", argv0, name );
1192         exit( 1 );
1193         }
1194     }
1195
1196
1197 static void
1198 no_value_required( char* name, char* value )
1199     {
1200     if ( value != (char*) 0 )
1201         {
1202         (void) fprintf(
1203             stderr, "%s: no value required for %s option\n",
1204             argv0, name );
1205         exit( 1 );
1206         }
1207     }
1208
1209
1210 static char*
1211 e_strdup( char* oldstr )
1212     {
1213     char* newstr;
1214
1215     newstr = strdup( oldstr );
1216     if ( newstr == (char*) 0 )
1217         {
1218         syslog( LOG_CRIT, "out of memory copying a string" );
1219         (void) fprintf( stderr, "%s: out of memory copying a string\n", argv0 );
1220         exit( 1 );
1221         }
1222     return newstr;
1223     }
1224
1225
1226 static void
1227 lookup_hostname( httpd_sockaddr* sa4P, size_t sa4_len, int* gotv4P, httpd_sockaddr* sa6P, size_t sa6_len, int* gotv6P )
1228     {
1229 #ifdef USE_IPV6
1230
1231     struct addrinfo hints;
1232     char portstr[10];
1233     int gaierr;
1234     struct addrinfo* ai;
1235     struct addrinfo* ai2;
1236     struct addrinfo* aiv6;
1237     struct addrinfo* aiv4;
1238
1239     (void) memset( &hints, 0, sizeof(hints) );
1240     hints.ai_family = PF_UNSPEC;
1241     hints.ai_flags = AI_PASSIVE;
1242     hints.ai_socktype = SOCK_STREAM;
1243     (void) snprintf( portstr, sizeof(portstr), "%d", (int) port );
1244     if ( (gaierr = getaddrinfo( hostname, portstr, &hints, &ai )) != 0 )
1245         {
1246         syslog(
1247             LOG_CRIT, "getaddrinfo %.80s - %.80s",
1248             hostname, gai_strerror( gaierr ) );
1249         (void) fprintf(
1250             stderr, "%s: getaddrinfo %s - %s\n",
1251             argv0, hostname, gai_strerror( gaierr ) );
1252         exit( 1 );
1253         }
1254
1255     /* Find the first IPv6 and IPv4 entries. */
1256     aiv6 = (struct addrinfo*) 0;
1257     aiv4 = (struct addrinfo*) 0;
1258     for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next )
1259         {
1260         switch ( ai2->ai_family )
1261             {
1262             case AF_INET6:
1263             if ( aiv6 == (struct addrinfo*) 0 )
1264                 aiv6 = ai2;
1265             break;
1266             case AF_INET:
1267             if ( aiv4 == (struct addrinfo*) 0 )
1268                 aiv4 = ai2;
1269             break;
1270             }
1271         }
1272
1273     if ( aiv6 == (struct addrinfo*) 0 )
1274         *gotv6P = 0;
1275     else
1276         {
1277         if ( sa6_len < aiv6->ai_addrlen )
1278             {
1279             syslog(
1280                 LOG_CRIT, "%.80s - sockaddr too small (%lu < %lu)",
1281                 hostname, (unsigned long) sa6_len,
1282                 (unsigned long) aiv6->ai_addrlen );
1283             exit( 1 );
1284             }
1285         (void) memset( sa6P, 0, sa6_len );
1286         (void) memmove( sa6P, aiv6->ai_addr, aiv6->ai_addrlen );
1287         *gotv6P = 1;
1288         }
1289
1290     if ( aiv4 == (struct addrinfo*) 0 )
1291         *gotv4P = 0;
1292     else
1293         {
1294         if ( sa4_len < aiv4->ai_addrlen )
1295             {
1296             syslog(
1297                 LOG_CRIT, "%.80s - sockaddr too small (%lu < %lu)",
1298                 hostname, (unsigned long) sa4_len,
1299                 (unsigned long) aiv4->ai_addrlen );
1300             exit( 1 );
1301             }
1302         (void) memset( sa4P, 0, sa4_len );
1303         (void) memmove( sa4P, aiv4->ai_addr, aiv4->ai_addrlen );
1304         *gotv4P = 1;
1305         }
1306
1307     freeaddrinfo( ai );
1308
1309 #else /* USE_IPV6 */
1310
1311     struct hostent* he;
1312
1313     *gotv6P = 0;
1314
1315     (void) memset( sa4P, 0, sa4_len );
1316     sa4P->sa.sa_family = AF_INET;
1317     if ( hostname == (char*) 0 )
1318         sa4P->sa_in.sin_addr.s_addr = htonl( INADDR_ANY );
1319     else
1320         {
1321         sa4P->sa_in.sin_addr.s_addr = inet_addr( hostname );
1322         if ( (int) sa4P->sa_in.sin_addr.s_addr == -1 )
1323             {
1324             he = gethostbyname( hostname );
1325             if ( he == (struct hostent*) 0 )
1326                 {
1327 #ifdef HAVE_HSTRERROR
1328                 syslog(
1329                     LOG_CRIT, "gethostbyname %.80s - %.80s",
1330                     hostname, hstrerror( h_errno ) );
1331                 (void) fprintf(
1332                     stderr, "%s: gethostbyname %s - %s\n",
1333                     argv0, hostname, hstrerror( h_errno ) );
1334 #else /* HAVE_HSTRERROR */
1335                 syslog( LOG_CRIT, "gethostbyname %.80s failed", hostname );
1336                 (void) fprintf(
1337                     stderr, "%s: gethostbyname %s failed\n", argv0, hostname );
1338 #endif /* HAVE_HSTRERROR */
1339                 exit( 1 );
1340                 }
1341             if ( he->h_addrtype != AF_INET )
1342                 {
1343                 syslog( LOG_CRIT, "%.80s - non-IP network address", hostname );
1344                 (void) fprintf(
1345                     stderr, "%s: %s - non-IP network address\n",
1346                     argv0, hostname );
1347                 exit( 1 );
1348                 }
1349             (void) memmove(
1350                 &sa4P->sa_in.sin_addr.s_addr, he->h_addr, he->h_length );
1351             }
1352         }
1353     sa4P->sa_in.sin_port = htons( port );
1354     *gotv4P = 1;
1355
1356 #endif /* USE_IPV6 */
1357     }
1358
1359
1360 static void
1361 read_throttlefile( char* tf )
1362     {
1363     FILE* fp;
1364     char buf[5000];
1365     char* cp;
1366     int len;
1367     char pattern[5000];
1368     long max_limit, min_limit;
1369     struct timeval tv;
1370
1371     fp = fopen( tf, "r" );
1372     if ( fp == (FILE*) 0 )
1373         {
1374         syslog( LOG_CRIT, "%.80s - %m", tf );
1375         perror( tf );
1376         exit( 1 );
1377         }
1378
1379     (void) gettimeofday( &tv, (struct timezone*) 0 );
1380
1381     while ( fgets( buf, sizeof(buf), fp ) != (char*) 0 )
1382         {
1383         /* Nuke comments. */
1384         cp = strchr( buf, '#' );
1385         if ( cp != (char*) 0 )
1386             *cp = '\0';
1387
1388         /* Nuke trailing whitespace. */
1389         len = strlen( buf );
1390         while ( len > 0 &&
1391                 ( buf[len-1] == ' ' || buf[len-1] == '\t' ||
1392                   buf[len-1] == '\n' || buf[len-1] == '\r' ) )
1393             buf[--len] = '\0';
1394
1395         /* Ignore empty lines. */
1396         if ( len == 0 )
1397             continue;
1398
1399         /* Parse line. */
1400         if ( sscanf( buf, " %4900[^ \t] %ld-%ld", pattern, &min_limit, &max_limit ) == 3 )
1401             {}
1402         else if ( sscanf( buf, " %4900[^ \t] %ld", pattern, &max_limit ) == 2 )
1403             min_limit = 0;
1404         else
1405             {
1406             syslog( LOG_CRIT,
1407                 "unparsable line in %.80s - %.80s", tf, buf );
1408             (void) fprintf( stderr,
1409                 "%s: unparsable line in %.80s - %.80s\n",
1410                 argv0, tf, buf );
1411             continue;
1412             }
1413
1414         /* Nuke any leading slashes in pattern. */
1415         if ( pattern[0] == '/' )
1416             (void) ol_strcpy( pattern, &pattern[1] );
1417         while ( ( cp = strstr( pattern, "|/" ) ) != (char*) 0 )
1418             (void) ol_strcpy( cp + 1, cp + 2 );
1419
1420         /* Check for room in throttles. */
1421         if ( numthrottles >= maxthrottles )
1422             {
1423             if ( maxthrottles == 0 )
1424                 {
1425                 maxthrottles = 100;     /* arbitrary */
1426                 throttles = NEW( throttletab, maxthrottles );
1427                 }
1428             else
1429                 {
1430                 maxthrottles *= 2;
1431                 throttles = RENEW( throttles, throttletab, maxthrottles );
1432                 }
1433             if ( throttles == (throttletab*) 0 )
1434                 {
1435                 syslog( LOG_CRIT, "out of memory allocating a throttletab" );
1436                 (void) fprintf(
1437                     stderr, "%s: out of memory allocating a throttletab\n",
1438                     argv0 );
1439                 exit( 1 );
1440                 }
1441             }
1442
1443         /* Add to table. */
1444         throttles[numthrottles].pattern = e_strdup( pattern );
1445         throttles[numthrottles].max_limit = max_limit;
1446         throttles[numthrottles].min_limit = min_limit;
1447         throttles[numthrottles].rate = 0;
1448         throttles[numthrottles].bytes_since_avg = 0;
1449         throttles[numthrottles].num_sending = 0;
1450
1451         ++numthrottles;
1452         }
1453     (void) fclose( fp );
1454     }
1455
1456
1457 static void
1458 shut_down( void )
1459     {
1460     int cnum;
1461     struct timeval tv;
1462
1463     (void) gettimeofday( &tv, (struct timezone*) 0 );
1464     logstats( &tv );
1465     for ( cnum = 0; cnum < max_connects; ++cnum )
1466         {
1467         if ( connects[cnum].conn_state != CNST_FREE )
1468             httpd_close_conn( connects[cnum].hc, &tv );
1469         if ( connects[cnum].hc != (httpd_conn*) 0 )
1470             {
1471             httpd_destroy_conn( connects[cnum].hc );
1472             free( (void*) connects[cnum].hc );
1473             --httpd_conn_count;
1474             connects[cnum].hc = (httpd_conn*) 0;
1475             }
1476         }
1477     if ( hs != (httpd_server*) 0 )
1478         {
1479         httpd_server* ths = hs;
1480         hs = (httpd_server*) 0;
1481         if ( ths->listen4_fd != -1 )
1482             fdwatch_del_fd( ths->listen4_fd );
1483         if ( ths->listen6_fd != -1 )
1484             fdwatch_del_fd( ths->listen6_fd );
1485         httpd_terminate( ths );
1486         }
1487     mmc_term();
1488     tmr_term();
1489     free( (void*) connects );
1490     if ( throttles != (throttletab*) 0 )
1491         free( (void*) throttles );
1492     }
1493
1494
1495 static int
1496 handle_newconnect( struct timeval* tvP, int listen_fd )
1497     {
1498     connecttab* c;
1499     ClientData client_data;
1500
1501     /* This loops until the accept() fails, trying to start new
1502     ** connections as fast as possible so we don't overrun the
1503     ** listen queue.
1504     */
1505     for (;;)
1506         {
1507         /* Is there room in the connection table? */
1508         if ( num_connects >= max_connects )
1509             {
1510             /* Out of connection slots.  Run the timers, then the
1511             ** existing connections, and maybe we'll free up a slot
1512             ** by the time we get back here.
1513             */
1514             syslog( LOG_WARNING, "too many connections!" );
1515             tmr_run( tvP );
1516             return 0;
1517             }
1518         /* Get the first free connection entry off the free list. */
1519         if ( first_free_connect == -1 || connects[first_free_connect].conn_state != CNST_FREE )
1520             {
1521             syslog( LOG_CRIT, "the connects free list is messed up" );
1522             exit( 1 );
1523             }
1524         c = &connects[first_free_connect];
1525         /* Make the httpd_conn if necessary. */
1526         if ( c->hc == (httpd_conn*) 0 )
1527             {
1528             c->hc = NEW( httpd_conn, 1 );
1529             if ( c->hc == (httpd_conn*) 0 )
1530                 {
1531                 syslog( LOG_CRIT, "out of memory allocating an httpd_conn" );
1532                 exit( 1 );
1533                 }
1534             c->hc->initialized = 0;
1535             ++httpd_conn_count;
1536             }
1537
1538         /* Get the connection. */
1539         switch ( httpd_get_conn( hs, listen_fd, c->hc ) )
1540             {
1541             /* Some error happened.  Run the timers, then the
1542             ** existing connections.  Maybe the error will clear.
1543             */
1544             case GC_FAIL:
1545             tmr_run( tvP );
1546             return 0;
1547
1548             /* No more connections to accept for now. */
1549             case GC_NO_MORE:
1550             return 1;
1551             }
1552         c->conn_state = CNST_READING;
1553         /* Pop it off the free list. */
1554         first_free_connect = c->next_free_connect;
1555         c->next_free_connect = -1;
1556         ++num_connects;
1557         client_data.p = c;
1558         c->active_at = tvP->tv_sec;
1559         c->wakeup_timer = (Timer*) 0;
1560         c->linger_timer = (Timer*) 0;
1561         c->next_byte_index = 0;
1562         c->numtnums = 0;
1563
1564         /* Set the connection file descriptor to no-delay mode. */
1565         httpd_set_ndelay( c->hc->conn_fd );
1566
1567         fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ );
1568
1569         ++stats_connections;
1570         if ( num_connects > stats_simultaneous )
1571             stats_simultaneous = num_connects;
1572         }
1573     }
1574
1575
1576 static void
1577 handle_read( connecttab* c, struct timeval* tvP )
1578     {
1579     int sz;
1580     ClientData client_data;
1581     httpd_conn* hc = c->hc;
1582
1583     /* Is there room in our buffer to read more bytes? */
1584     if ( hc->read_idx >= hc->read_size )
1585         {
1586         if ( hc->read_size > 5000 )
1587             {
1588             httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1589             finish_connection( c, tvP );
1590             return;
1591             }
1592         httpd_realloc_str(
1593             &hc->read_buf, &hc->read_size, hc->read_size + 1000 );
1594         }
1595
1596     /* Read some more bytes. */
1597     sz = read(
1598         hc->conn_fd, &(hc->read_buf[hc->read_idx]),
1599         hc->read_size - hc->read_idx );
1600     if ( sz == 0 )
1601         {
1602         httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1603         finish_connection( c, tvP );
1604         return;
1605         }
1606     if ( sz < 0 )
1607         {
1608         /* Ignore EINTR and EAGAIN.  Also ignore EWOULDBLOCK.  At first glance
1609         ** you would think that connections returned by fdwatch as readable
1610         ** should never give an EWOULDBLOCK; however, this apparently can
1611         ** happen if a packet gets garbled.
1612         */
1613         if ( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK )
1614             return;
1615         httpd_send_err(
1616             hc, 400, httpd_err400title, "", httpd_err400form, "" );
1617         finish_connection( c, tvP );
1618         return;
1619         }
1620     hc->read_idx += sz;
1621     c->active_at = tvP->tv_sec;
1622
1623     /* Do we have a complete request yet? */
1624     switch ( httpd_got_request( hc ) )
1625         {
1626         case GR_NO_REQUEST:
1627         return;
1628         case GR_BAD_REQUEST:
1629         httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1630         finish_connection( c, tvP );
1631         return;
1632         }
1633
1634     /* Yes.  Try parsing and resolving it. */
1635     if ( httpd_parse_request( hc ) < 0 )
1636         {
1637         finish_connection( c, tvP );
1638         return;
1639         }
1640
1641     /* Check the throttle table */
1642     if ( ! check_throttles( c ) )
1643         {
1644         httpd_send_err(
1645             hc, 503, httpd_err503title, "", httpd_err503form, hc->encodedurl );
1646         finish_connection( c, tvP );
1647         return;
1648         }
1649
1650     /* Start the connection going. */
1651     if ( httpd_start_request( hc, tvP ) < 0 )
1652         {
1653         /* Something went wrong.  Close down the connection. */
1654         finish_connection( c, tvP );
1655         return;
1656         }
1657
1658     /* Fill in end_byte_index. */
1659     if ( hc->got_range )
1660         {
1661         c->next_byte_index = hc->first_byte_index;
1662         c->end_byte_index = hc->last_byte_index + 1;
1663         }
1664     else if ( hc->bytes_to_send < 0 )
1665         c->end_byte_index = 0;
1666     else
1667         c->end_byte_index = hc->bytes_to_send;
1668
1669     /* Check if it's already handled. */
1670     if ( hc->file_address == (char*) 0 )
1671         {
1672         /* No file address means someone else is handling it. */
1673         int tind;
1674         for ( tind = 0; tind < c->numtnums; ++tind )
1675             throttles[c->tnums[tind]].bytes_since_avg += hc->bytes_sent;
1676         c->next_byte_index = hc->bytes_sent;
1677         finish_connection( c, tvP );
1678         return;
1679         }
1680     if ( c->next_byte_index >= c->end_byte_index )
1681         {
1682         /* There's nothing to send. */
1683         finish_connection( c, tvP );
1684         return;
1685         }
1686
1687     /* Cool, we have a valid connection and a file to send to it. */
1688     c->conn_state = CNST_SENDING;
1689     c->started_at = tvP->tv_sec;
1690     c->wouldblock_delay = 0;
1691     client_data.p = c;
1692
1693     fdwatch_del_fd( hc->conn_fd );
1694     fdwatch_add_fd( hc->conn_fd, c, FDW_WRITE );
1695     }
1696
1697
1698 static void
1699 handle_send( connecttab* c, struct timeval* tvP )
1700     {
1701     size_t max_bytes;
1702     int sz, coast;
1703     ClientData client_data;
1704     time_t elapsed;
1705     httpd_conn* hc = c->hc;
1706     int tind;
1707
1708     if ( c->max_limit == THROTTLE_NOLIMIT )
1709         max_bytes = 1000000000L;
1710     else
1711         max_bytes = c->max_limit / 4;   /* send at most 1/4 seconds worth */
1712
1713     /* Do we need to write the headers first? */
1714     if ( hc->responselen == 0 )
1715         {
1716         /* No, just write the file. */
1717         sz = write(
1718             hc->conn_fd, &(hc->file_address[c->next_byte_index]),
1719             MIN( c->end_byte_index - c->next_byte_index, max_bytes ) );
1720         }
1721     else
1722         {
1723         /* Yes.  We'll combine headers and file into a single writev(),
1724         ** hoping that this generates a single packet.
1725         */
1726         struct iovec iv[2];
1727
1728         iv[0].iov_base = hc->response;
1729         iv[0].iov_len = hc->responselen;
1730         iv[1].iov_base = &(hc->file_address[c->next_byte_index]);
1731         iv[1].iov_len = MIN( c->end_byte_index - c->next_byte_index, max_bytes );
1732         sz = writev( hc->conn_fd, iv, 2 );
1733         }
1734
1735     if ( sz < 0 && errno == EINTR )
1736         return;
1737
1738     if ( sz == 0 ||
1739          ( sz < 0 && ( errno == EWOULDBLOCK || errno == EAGAIN ) ) )
1740         {
1741         /* This shouldn't happen, but some kernels, e.g.
1742         ** SunOS 4.1.x, are broken and select() says that
1743         ** O_NDELAY sockets are always writable even when
1744         ** they're actually not.
1745         **
1746         ** Current workaround is to block sending on this
1747         ** socket for a brief adaptively-tuned period.
1748         ** Fortunately we already have all the necessary
1749         ** blocking code, for use with throttling.
1750         */
1751         c->wouldblock_delay += MIN_WOULDBLOCK_DELAY;
1752         c->conn_state = CNST_PAUSING;
1753         fdwatch_del_fd( hc->conn_fd );
1754         client_data.p = c;
1755         if ( c->wakeup_timer != (Timer*) 0 )
1756             syslog( LOG_ERR, "replacing non-null wakeup_timer!" );
1757         c->wakeup_timer = tmr_create(
1758             tvP, wakeup_connection, client_data, c->wouldblock_delay, 0 );
1759         if ( c->wakeup_timer == (Timer*) 0 )
1760             {
1761             syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" );
1762             exit( 1 );
1763             }
1764         return;
1765         }
1766
1767     if ( sz < 0 )
1768         {
1769         /* Something went wrong, close this connection.
1770         **
1771         ** If it's just an EPIPE, don't bother logging, that
1772         ** just means the client hung up on us.
1773         **
1774         ** On some systems, write() occasionally gives an EINVAL.
1775         ** Dunno why, something to do with the socket going
1776         ** bad.  Anyway, we don't log those either.
1777         **
1778         ** And ECONNRESET isn't interesting either.
1779         */
1780         if ( errno != EPIPE && errno != EINVAL && errno != ECONNRESET )
1781             syslog( LOG_ERR, "write - %m sending %.80s", hc->encodedurl );
1782         clear_connection( c, tvP );
1783         return;
1784         }
1785
1786     /* Ok, we wrote something. */
1787     c->active_at = tvP->tv_sec;
1788     /* Was this a headers + file writev()? */
1789     if ( hc->responselen > 0 )
1790         {
1791         /* Yes; did we write only part of the headers? */
1792         if ( sz < hc->responselen )
1793             {
1794             /* Yes; move the unwritten part to the front of the buffer. */
1795             int newlen = hc->responselen - sz;
1796             (void) memmove( hc->response, &(hc->response[sz]), newlen );
1797             hc->responselen = newlen;
1798             sz = 0;
1799             }
1800         else
1801             {
1802             /* Nope, we wrote the full headers, so adjust accordingly. */
1803             sz -= hc->responselen;
1804             hc->responselen = 0;
1805             }
1806         }
1807     /* And update how much of the file we wrote. */
1808     c->next_byte_index += sz;
1809     c->hc->bytes_sent += sz;
1810     for ( tind = 0; tind < c->numtnums; ++tind )
1811         throttles[c->tnums[tind]].bytes_since_avg += sz;
1812
1813     /* Are we done? */
1814     if ( c->next_byte_index >= c->end_byte_index )
1815         {
1816         /* This connection is finished! */
1817         finish_connection( c, tvP );
1818         return;
1819         }
1820
1821     /* Tune the (blockheaded) wouldblock delay. */
1822     if ( c->wouldblock_delay > MIN_WOULDBLOCK_DELAY )
1823         c->wouldblock_delay -= MIN_WOULDBLOCK_DELAY;
1824
1825     /* If we're throttling, check if we're sending too fast. */
1826     if ( c->max_limit != THROTTLE_NOLIMIT )
1827         {
1828         elapsed = tvP->tv_sec - c->started_at;
1829         if ( elapsed == 0 )
1830             elapsed = 1;        /* count at least one second */
1831         if ( c->hc->bytes_sent / elapsed > c->max_limit )
1832             {
1833             c->conn_state = CNST_PAUSING;
1834             fdwatch_del_fd( hc->conn_fd );
1835             /* How long should we wait to get back on schedule?  If less
1836             ** than a second (integer math rounding), use 1/2 second.
1837             */
1838             coast = c->hc->bytes_sent / c->max_limit - elapsed;
1839             client_data.p = c;
1840             if ( c->wakeup_timer != (Timer*) 0 )
1841                 syslog( LOG_ERR, "replacing non-null wakeup_timer!" );
1842             c->wakeup_timer = tmr_create(
1843                 tvP, wakeup_connection, client_data,
1844                 coast > 0 ? ( coast * 1000L ) : 500L, 0 );
1845             if ( c->wakeup_timer == (Timer*) 0 )
1846                 {
1847                 syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" );
1848                 exit( 1 );
1849                 }
1850             }
1851         }
1852     /* (No check on min_limit here, that only controls connection startups.) */
1853     }
1854
1855
1856 static void
1857 handle_linger( connecttab* c, struct timeval* tvP )
1858     {
1859     char buf[4096];
1860     int r;
1861
1862     /* In lingering-close mode we just read and ignore bytes.  An error
1863     ** or EOF ends things, otherwise we go until a timeout.
1864     */
1865     r = read( c->hc->conn_fd, buf, sizeof(buf) );
1866     if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
1867         return;
1868     if ( r <= 0 )
1869         really_clear_connection( c, tvP );
1870     }
1871
1872
1873 static int
1874 check_throttles( connecttab* c )
1875     {
1876     int tnum;
1877     long l;
1878
1879     c->numtnums = 0;
1880     c->max_limit = c->min_limit = THROTTLE_NOLIMIT;
1881     for ( tnum = 0; tnum < numthrottles && c->numtnums < MAXTHROTTLENUMS;
1882           ++tnum )
1883         if ( match( throttles[tnum].pattern, c->hc->expnfilename ) )
1884             {
1885             /* If we're way over the limit, don't even start. */
1886             if ( throttles[tnum].rate > throttles[tnum].max_limit * 2 )
1887                 return 0;
1888             /* Also don't start if we're under the minimum. */
1889             if ( throttles[tnum].rate < throttles[tnum].min_limit )
1890                 return 0;
1891             if ( throttles[tnum].num_sending < 0 )
1892                 {
1893                 syslog( LOG_ERR, "throttle sending count was negative - shouldn't happen!" );
1894                 throttles[tnum].num_sending = 0;
1895                 }
1896             c->tnums[c->numtnums++] = tnum;
1897             ++throttles[tnum].num_sending;
1898             l = throttles[tnum].max_limit / throttles[tnum].num_sending;
1899             if ( c->max_limit == THROTTLE_NOLIMIT )
1900                 c->max_limit = l;
1901             else
1902                 c->max_limit = MIN( c->max_limit, l );
1903             l = throttles[tnum].min_limit;
1904             if ( c->min_limit == THROTTLE_NOLIMIT )
1905                 c->min_limit = l;
1906             else
1907                 c->min_limit = MAX( c->min_limit, l );
1908             }
1909     return 1;
1910     }
1911
1912
1913 static void
1914 clear_throttles( connecttab* c, struct timeval* tvP )
1915     {
1916     int tind;
1917
1918     for ( tind = 0; tind < c->numtnums; ++tind )
1919         --throttles[c->tnums[tind]].num_sending;
1920     }
1921
1922
1923 static void
1924 update_throttles( ClientData client_data, struct timeval* nowP )
1925     {
1926     int tnum, tind;
1927     int cnum;
1928     connecttab* c;
1929     long l;
1930
1931     /* Update the average sending rate for each throttle.  This is only used
1932     ** when new connections start up.
1933     */
1934     for ( tnum = 0; tnum < numthrottles; ++tnum )
1935         {
1936         throttles[tnum].rate = ( 2 * throttles[tnum].rate + throttles[tnum].bytes_since_avg / THROTTLE_TIME ) / 3;
1937         throttles[tnum].bytes_since_avg = 0;
1938         /* Log a warning message if necessary. */
1939         if ( throttles[tnum].rate > throttles[tnum].max_limit && throttles[tnum].num_sending != 0 )
1940             {
1941             if ( throttles[tnum].rate > throttles[tnum].max_limit * 2 )
1942                 syslog( LOG_NOTICE, "throttle #%d '%.80s' rate %ld greatly exceeding limit %ld; %d sending", tnum, throttles[tnum].pattern, throttles[tnum].rate, throttles[tnum].max_limit, throttles[tnum].num_sending );
1943             else
1944                 syslog( LOG_INFO, "throttle #%d '%.80s' rate %ld exceeding limit %ld; %d sending", tnum, throttles[tnum].pattern, throttles[tnum].rate, throttles[tnum].max_limit, throttles[tnum].num_sending );
1945             }
1946         if ( throttles[tnum].rate < throttles[tnum].min_limit && throttles[tnum].num_sending != 0 )
1947             {
1948             syslog( LOG_NOTICE, "throttle #%d '%.80s' rate %ld lower than minimum %ld; %d sending", tnum, throttles[tnum].pattern, throttles[tnum].rate, throttles[tnum].min_limit, throttles[tnum].num_sending );
1949             }
1950         }
1951
1952     /* Now update the sending rate on all the currently-sending connections,
1953     ** redistributing it evenly.
1954     */
1955     for ( cnum = 0; cnum < max_connects; ++cnum )
1956         {
1957         c = &connects[cnum];
1958         if ( c->conn_state == CNST_SENDING || c->conn_state == CNST_PAUSING )
1959             {
1960             c->max_limit = THROTTLE_NOLIMIT;
1961             for ( tind = 0; tind < c->numtnums; ++tind )
1962                 {
1963                 tnum = c->tnums[tind];
1964                 l = throttles[tnum].max_limit / throttles[tnum].num_sending;
1965                 if ( c->max_limit == THROTTLE_NOLIMIT )
1966                     c->max_limit = l;
1967                 else
1968                     c->max_limit = MIN( c->max_limit, l );
1969                 }
1970             }
1971         }
1972     }
1973
1974
1975 static void
1976 finish_connection( connecttab* c, struct timeval* tvP )
1977     {
1978     /* If we haven't actually sent the buffered response yet, do so now. */
1979     httpd_write_response( c->hc );
1980
1981     /* And clear. */
1982     clear_connection( c, tvP );
1983     }
1984
1985
1986 static void
1987 clear_connection( connecttab* c, struct timeval* tvP )
1988     {
1989     ClientData client_data;
1990
1991     if ( c->wakeup_timer != (Timer*) 0 )
1992         {
1993         tmr_cancel( c->wakeup_timer );
1994         c->wakeup_timer = 0;
1995         }
1996
1997     /* This is our version of Apache's lingering_close() routine, which is
1998     ** their version of the often-broken SO_LINGER socket option.  For why
1999     ** this is necessary, see http://www.apache.org/docs/misc/fin_wait_2.html
2000     ** What we do is delay the actual closing for a few seconds, while reading
2001     ** any bytes that come over the connection.  However, we don't want to do
2002     ** this unless it's necessary, because it ties up a connection slot and
2003     ** file descriptor which means our maximum connection-handling rate
2004     ** is lower.  So, elsewhere we set a flag when we detect the few
2005     ** circumstances that make a lingering close necessary.  If the flag
2006     ** isn't set we do the real close now.
2007     */
2008     if ( c->conn_state == CNST_LINGERING )
2009         {
2010         /* If we were already lingering, shut down for real. */
2011         tmr_cancel( c->linger_timer );
2012         c->linger_timer = (Timer*) 0;
2013         c->hc->should_linger = 0;
2014         }
2015     if ( c->hc->should_linger )
2016         {
2017         if ( c->conn_state != CNST_PAUSING )
2018             fdwatch_del_fd( c->hc->conn_fd );
2019         c->conn_state = CNST_LINGERING;
2020         shutdown( c->hc->conn_fd, SHUT_WR );
2021         fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ );
2022         client_data.p = c;
2023         if ( c->linger_timer != (Timer*) 0 )
2024             syslog( LOG_ERR, "replacing non-null linger_timer!" );
2025         c->linger_timer = tmr_create(
2026             tvP, linger_clear_connection, client_data, LINGER_TIME, 0 );
2027         if ( c->linger_timer == (Timer*) 0 )
2028             {
2029             syslog( LOG_CRIT, "tmr_create(linger_clear_connection) failed" );
2030             exit( 1 );
2031             }
2032         }
2033     else
2034         really_clear_connection( c, tvP );
2035     }
2036
2037
2038 static void
2039 really_clear_connection( connecttab* c, struct timeval* tvP )
2040     {
2041     stats_bytes += c->hc->bytes_sent;
2042     if ( c->conn_state != CNST_PAUSING )
2043         fdwatch_del_fd( c->hc->conn_fd );
2044     httpd_close_conn( c->hc, tvP );
2045     clear_throttles( c, tvP );
2046     if ( c->linger_timer != (Timer*) 0 )
2047         {
2048         tmr_cancel( c->linger_timer );
2049         c->linger_timer = 0;
2050         }
2051     c->conn_state = CNST_FREE;
2052     c->next_free_connect = first_free_connect;
2053     first_free_connect = c - connects;  /* division by sizeof is implied */
2054     --num_connects;
2055     }
2056
2057
2058 static void
2059 idle( ClientData client_data, struct timeval* nowP )
2060     {
2061     int cnum;
2062     connecttab* c;
2063
2064     for ( cnum = 0; cnum < max_connects; ++cnum )
2065         {
2066         c = &connects[cnum];
2067         switch ( c->conn_state )
2068             {
2069             case CNST_READING:
2070             if ( nowP->tv_sec - c->active_at >= IDLE_READ_TIMELIMIT )
2071                 {
2072                 syslog( LOG_INFO,
2073                     "%.80s connection timed out reading",
2074                     httpd_ntoa( &c->hc->client_addr ) );
2075                 httpd_send_err(
2076                     c->hc, 408, httpd_err408title, "", httpd_err408form, "" );
2077                 finish_connection( c, nowP );
2078                 }
2079             break;
2080             case CNST_SENDING:
2081             case CNST_PAUSING:
2082             if ( nowP->tv_sec - c->active_at >= IDLE_SEND_TIMELIMIT )
2083                 {
2084                 syslog( LOG_INFO,
2085                     "%.80s connection timed out sending",
2086                     httpd_ntoa( &c->hc->client_addr ) );
2087                 clear_connection( c, nowP );
2088                 }
2089             break;
2090             }
2091         }
2092     }
2093
2094
2095 static void
2096 wakeup_connection( ClientData client_data, struct timeval* nowP )
2097     {
2098     connecttab* c;
2099
2100     c = (connecttab*) client_data.p;
2101     c->wakeup_timer = (Timer*) 0;
2102     if ( c->conn_state == CNST_PAUSING )
2103         {
2104         c->conn_state = CNST_SENDING;
2105         fdwatch_add_fd( c->hc->conn_fd, c, FDW_WRITE );
2106         }
2107     }
2108
2109 static void
2110 linger_clear_connection( ClientData client_data, struct timeval* nowP )
2111     {
2112     connecttab* c;
2113
2114     c = (connecttab*) client_data.p;
2115     c->linger_timer = (Timer*) 0;
2116     really_clear_connection( c, nowP );
2117     }
2118
2119
2120 static void
2121 occasional( ClientData client_data, struct timeval* nowP )
2122     {
2123     mmc_cleanup( nowP );
2124     tmr_cleanup();
2125     watchdog_flag = 1;          /* let the watchdog know that we are alive */
2126     }
2127
2128
2129 #ifdef STATS_TIME
2130 static void
2131 show_stats( ClientData client_data, struct timeval* nowP )
2132     {
2133     logstats( nowP );
2134     }
2135 #endif /* STATS_TIME */
2136
2137
2138 /* Generate debugging statistics syslog messages for all packages. */
2139 static void
2140 logstats( struct timeval* nowP )
2141     {
2142     struct timeval tv;
2143     time_t now;
2144     long up_secs, stats_secs;
2145
2146     if ( nowP == (struct timeval*) 0 )
2147         {
2148         (void) gettimeofday( &tv, (struct timezone*) 0 );
2149         nowP = &tv;
2150         }
2151     now = nowP->tv_sec;
2152     up_secs = now - start_time;
2153     stats_secs = now - stats_time;
2154     if ( stats_secs == 0 )
2155         stats_secs = 1; /* fudge */
2156     stats_time = now;
2157     syslog( LOG_NOTICE,
2158         "up %ld seconds, stats for %ld seconds:", up_secs, stats_secs );
2159
2160     thttpd_logstats( stats_secs );
2161     httpd_logstats( stats_secs );
2162     mmc_logstats( stats_secs );
2163     fdwatch_logstats( stats_secs );
2164     tmr_logstats( stats_secs );
2165     }
2166
2167
2168 /* Generate debugging statistics syslog message. */
2169 static void
2170 thttpd_logstats( long secs )
2171     {
2172     if ( secs > 0 )
2173         syslog( LOG_NOTICE,
2174             "  thttpd - %ld connections (%g/sec), %d max simultaneous, %lld bytes (%g/sec), %d httpd_conns allocated",
2175             stats_connections, (float) stats_connections / secs,
2176             stats_simultaneous, (long long) stats_bytes,
2177             (float) stats_bytes / secs, httpd_conn_count );
2178     stats_connections = 0;
2179     stats_bytes = 0;
2180     stats_simultaneous = 0;
2181     }