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