ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/thttpd/thttpd.c
Revision: 1.1.6.2
Committed: Sat Jun 30 00:27:25 2001 UTC (25 years, 2 months ago) by root
Content type: text/plain
Branch: mmapppatch
Changes since 1.1.6.1: +3 -0 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 /* thttpd.c - tiny/turbo/throttling HTTP server
2     **
3     ** Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer <jef@acme.com>.
4     ** All rights reserved.
5     **
6     ** Redistribution and use in source and binary forms, with or without
7     ** modification, are permitted provided that the following conditions
8     ** are met:
9     ** 1. Redistributions of source code must retain the above copyright
10     ** notice, this list of conditions and the following disclaimer.
11     ** 2. Redistributions in binary form must reproduce the above copyright
12     ** notice, this list of conditions and the following disclaimer in the
13     ** documentation and/or other materials provided with the distribution.
14     **
15     ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16     ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17     ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18     ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19     ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20     ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21     ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22     ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23     ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24     ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25     ** SUCH DAMAGE.
26     */
27    
28    
29     #include "config.h"
30     #include "version.h"
31    
32     #include <sys/param.h>
33     #include <sys/types.h>
34     #include <sys/time.h>
35     #include <sys/stat.h>
36     #include <sys/uio.h>
37    
38     #include <errno.h>
39     #ifdef HAVE_FCNTL_H
40     #include <fcntl.h>
41     #endif
42     #include <pwd.h>
43     #ifdef HAVE_GRP_H
44     #include <grp.h>
45     #endif
46     #include <signal.h>
47     #include <stdio.h>
48     #include <stdlib.h>
49     #include <string.h>
50     #include <syslog.h>
51     #ifdef TIME_WITH_SYS_TIME
52     #include <time.h>
53     #endif
54     #include <unistd.h>
55    
56     #include "fdwatch.h"
57     #include "libhttpd.h"
58     #include "mmc.h"
59     #include "timers.h"
60     #include "match.h"
61    
62    
63     static char* argv0;
64     static int debug;
65     static int port;
66     static char* dir;
67     static int do_chroot, no_log, no_symlink, do_vhost, do_global_passwd;
68     static char* cgi_pattern;
69     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 root 1.1.6.2 #ifdef MMAP_MAX
498     maxconnects = maxconnects / 2;
499     #endif
500 root 1.1 connects = NEW( connecttab, maxconnects );
501     if ( connects == (connecttab*) 0 )
502     {
503     syslog( LOG_CRIT, "out of memory allocating a connecttab" );
504     exit( 1 );
505     }
506     for ( cnum = 0; cnum < maxconnects; ++cnum )
507     {
508     connects[cnum].conn_state = CNST_FREE;
509     connects[cnum].hc = (httpd_conn*) 0;
510     }
511     numconnects = 0;
512     httpd_conn_count = 0;
513    
514     if ( hs != (httpd_server*) 0 )
515     {
516     if ( hs->listen4_fd != -1 )
517     fdwatch_add_fd( hs->listen4_fd, (void*) 0, FDW_READ );
518     if ( hs->listen6_fd != -1 )
519     fdwatch_add_fd( hs->listen6_fd, (void*) 0, FDW_READ );
520     }
521    
522     /* Main loop. */
523     (void) gettimeofday( &tv, (struct timezone*) 0 );
524     while ( ( ! terminate ) || numconnects > 0 )
525     {
526     /* Do the fd watch. */
527     num_ready = fdwatch( tmr_mstimeout( &tv ) );
528     if ( num_ready < 0 )
529     {
530     if ( errno == EINTR )
531     continue; /* try again */
532     syslog( LOG_ERR, "fdwatch - %m" );
533     exit( 1 );
534     }
535     (void) gettimeofday( &tv, (struct timezone*) 0 );
536     if ( num_ready == 0 )
537     {
538     /* No fd's are ready - run the timers. */
539     tmr_run( &tv );
540     continue;
541     }
542    
543     /* Is it a new connection? */
544     if ( hs != (httpd_server*) 0 && hs->listen6_fd != -1 &&
545     fdwatch_check_fd( hs->listen6_fd ) )
546     {
547     if ( handle_newconnect( &tv, hs->listen6_fd ) )
548     /* Go around the loop and do another fdwatch, rather than
549     ** dropping through and processing existing connections.
550     ** New connections always get priority.
551     */
552     continue;
553     }
554     if ( hs != (httpd_server*) 0 && hs->listen4_fd != -1 &&
555     fdwatch_check_fd( hs->listen4_fd ) )
556     {
557     if ( handle_newconnect( &tv, hs->listen4_fd ) )
558     /* Go around the loop and do another fdwatch, rather than
559     ** dropping through and processing existing connections.
560     ** New connections always get priority.
561     */
562     continue;
563     }
564    
565     /* Find the connections that need servicing. */
566     for ( ridx = 0; ridx < num_ready; ++ridx )
567     {
568     c = (connecttab*) fdwatch_get_client_data( ridx );
569     if ( c == (connecttab*) 0 )
570     continue;
571     hc = c->hc;
572     if ( c->conn_state == CNST_READING &&
573     fdwatch_check_fd( hc->conn_fd ) )
574     handle_read( c, &tv );
575     else if ( c->conn_state == CNST_SENDING &&
576     fdwatch_check_fd( hc->conn_fd ) )
577     handle_send( c, &tv );
578     else if ( c->conn_state == CNST_LINGERING &&
579     fdwatch_check_fd( hc->conn_fd ) )
580     handle_linger( c, &tv );
581     }
582     tmr_run( &tv );
583    
584     if ( got_usr1 && ! terminate )
585     {
586     terminate = 1;
587     if ( hs != (httpd_server*) 0 )
588     {
589     httpd_terminate( hs );
590     hs = (httpd_server*) 0;
591     }
592     }
593     }
594    
595     /* The main loop terminated. */
596     shut_down();
597     syslog( LOG_NOTICE, "exiting" );
598     closelog();
599     exit( 0 );
600     }
601    
602    
603     static void
604     parse_args( int argc, char** argv )
605     {
606     int argn;
607    
608     debug = 0;
609     port = DEFAULT_PORT;
610     dir = (char*) 0;
611     #ifdef ALWAYS_CHROOT
612     do_chroot = 1;
613     #else /* ALWAYS_CHROOT */
614     do_chroot = 0;
615     #endif /* ALWAYS_CHROOT */
616     no_log = 0;
617     no_symlink = do_chroot;
618     #ifdef ALWAYS_VHOST
619     do_vhost = 1;
620     #else /* ALWAYS_VHOST */
621     do_vhost = 0;
622     #endif /* ALWAYS_VHOST */
623     #ifdef ALWAYS_GLOBAL_PASSWD
624     do_global_passwd = 1;
625     #else /* ALWAYS_GLOBAL_PASSWD */
626     do_global_passwd = 0;
627     #endif /* ALWAYS_GLOBAL_PASSWD */
628     #ifdef CGI_PATTERN
629     cgi_pattern = CGI_PATTERN;
630     #else /* CGI_PATTERN */
631     cgi_pattern = (char*) 0;
632     #endif /* CGI_PATTERN */
633     url_pattern = (char*) 0;
634     no_empty_referers = 0;
635     local_pattern = (char*) 0;
636     throttlefile = (char*) 0;
637     hostname = (char*) 0;
638     logfile = (char*) 0;
639     pidfile = (char*) 0;
640     user = DEFAULT_USER;
641     charset = DEFAULT_CHARSET;
642     argn = 1;
643     while ( argn < argc && argv[argn][0] == '-' )
644     {
645     if ( strcmp( argv[argn], "-C" ) == 0 && argn + 1 < argc )
646     {
647     ++argn;
648     read_config( argv[argn] );
649     }
650     else if ( strcmp( argv[argn], "-p" ) == 0 && argn + 1 < argc )
651     {
652     ++argn;
653     port = atoi( argv[argn] );
654     }
655     else if ( strcmp( argv[argn], "-d" ) == 0 && argn + 1 < argc )
656     {
657     ++argn;
658     dir = argv[argn];
659     }
660     else if ( strcmp( argv[argn], "-r" ) == 0 )
661     {
662     do_chroot = 1;
663     no_symlink = 1;
664     }
665     else if ( strcmp( argv[argn], "-nor" ) == 0 )
666     {
667     do_chroot = 0;
668     no_symlink = 0;
669     }
670     else if ( strcmp( argv[argn], "-s" ) == 0 )
671     no_symlink = 0;
672     else if ( strcmp( argv[argn], "-nos" ) == 0 )
673     no_symlink = 1;
674     else if ( strcmp( argv[argn], "-u" ) == 0 && argn + 1 < argc )
675     {
676     ++argn;
677     user = argv[argn];
678     }
679     else if ( strcmp( argv[argn], "-c" ) == 0 && argn + 1 < argc )
680     {
681     ++argn;
682     cgi_pattern = argv[argn];
683     }
684     else if ( strcmp( argv[argn], "-t" ) == 0 && argn + 1 < argc )
685     {
686     ++argn;
687     throttlefile = argv[argn];
688     }
689     else if ( strcmp( argv[argn], "-h" ) == 0 && argn + 1 < argc )
690     {
691     ++argn;
692     hostname = argv[argn];
693     }
694     else if ( strcmp( argv[argn], "-l" ) == 0 && argn + 1 < argc )
695     {
696     ++argn;
697     logfile = argv[argn];
698     }
699     else if ( strcmp( argv[argn], "-v" ) == 0 )
700     do_vhost = 1;
701     else if ( strcmp( argv[argn], "-nov" ) == 0 )
702     do_vhost = 0;
703     else if ( strcmp( argv[argn], "-g" ) == 0 )
704     do_global_passwd = 1;
705     else if ( strcmp( argv[argn], "-nog" ) == 0 )
706     do_global_passwd = 0;
707     else if ( strcmp( argv[argn], "-i" ) == 0 && argn + 1 < argc )
708     {
709     ++argn;
710     pidfile = argv[argn];
711     }
712     else if ( strcmp( argv[argn], "-T" ) == 0 && argn + 1 < argc )
713     {
714     ++argn;
715     charset = argv[argn];
716     }
717     else if ( strcmp( argv[argn], "-V" ) == 0 )
718     {
719     (void) fprintf( stderr, "%s\n", SERVER_SOFTWARE );
720     exit( 0 );
721     }
722     else if ( strcmp( argv[argn], "-D" ) == 0 )
723     debug = 1;
724     else
725     usage();
726     ++argn;
727     }
728     if ( argn != argc )
729     usage();
730     }
731    
732    
733     static void
734     usage( void )
735     {
736     (void) fprintf( stderr,
737     "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",
738     argv0 );
739     exit( 1 );
740     }
741    
742    
743     static void
744     read_config( char* filename )
745     {
746     FILE* fp;
747     char line[10000];
748     char* cp;
749     char* cp2;
750     char* name;
751     char* value;
752    
753     fp = fopen( filename, "r" );
754     if ( fp == (FILE*) 0 )
755     {
756     perror( filename );
757     exit( 1 );
758     }
759    
760     while ( fgets( line, sizeof(line), fp ) != (char*) 0 )
761     {
762     /* Trim comments. */
763     if ( ( cp = strchr( line, '#' ) ) != (char*) 0 )
764     *cp = '\0';
765    
766     /* Split line into words. */
767     for ( cp = line; *cp != '\0'; cp = cp2 )
768     {
769     /* Skip leading whitespace. */
770     cp += strspn( cp, " \t\n\r" );
771     /* Find next whitespace. */
772     cp2 = cp + strcspn( cp, " \t\n\r" );
773     /* Insert EOS and advance next-word pointer. */
774     while ( *cp2 == ' ' || *cp2 == '\t' || *cp2 == '\n' || *cp2 == '\r' )
775     *cp2++ = '\0';
776     /* Split into name and value. */
777     name = cp;
778     value = strchr( name, '=' );
779     if ( value != (char*) 0 )
780     *value++ = '\0';
781     /* Interpret. */
782     if ( strcasecmp( name, "debug" ) == 0 )
783     {
784     no_value_required( name, value );
785     debug = 1;
786     }
787     else if ( strcasecmp( name, "port" ) == 0 )
788     {
789     value_required( name, value );
790     port = atoi( value );
791     }
792     else if ( strcasecmp( name, "dir" ) == 0 )
793     {
794     value_required( name, value );
795     dir = e_strdup( value );
796     }
797     else if ( strcasecmp( name, "chroot" ) == 0 )
798     {
799     no_value_required( name, value );
800     do_chroot = 1;
801     no_symlink = 1;
802     }
803     else if ( strcasecmp( name, "nochroot" ) == 0 )
804     {
805     no_value_required( name, value );
806     do_chroot = 0;
807     no_symlink = 0;
808     }
809     else if ( strcasecmp( name, "symlink" ) == 0 )
810     {
811     no_value_required( name, value );
812     no_symlink = 0;
813     }
814     else if ( strcasecmp( name, "nosymlink" ) == 0 )
815     {
816     no_value_required( name, value );
817     no_symlink = 1;
818     }
819     else if ( strcasecmp( name, "symlinks" ) == 0 )
820     {
821     no_value_required( name, value );
822     no_symlink = 0;
823     }
824     else if ( strcasecmp( name, "nosymlinks" ) == 0 )
825     {
826     no_value_required( name, value );
827     no_symlink = 1;
828     }
829     else if ( strcasecmp( name, "user" ) == 0 )
830     {
831     value_required( name, value );
832     user = e_strdup( value );
833     }
834     else if ( strcasecmp( name, "cgipat" ) == 0 )
835     {
836     value_required( name, value );
837     cgi_pattern = e_strdup( value );
838     }
839     else if ( strcasecmp( name, "urlpat" ) == 0 )
840     {
841     value_required( name, value );
842     url_pattern = e_strdup( value );
843     }
844     else if ( strcasecmp( name, "noemptyreferers" ) == 0 )
845     {
846     no_value_required( name, value );
847     no_empty_referers = 1;
848     }
849     else if ( strcasecmp( name, "localpat" ) == 0 )
850     {
851     value_required( name, value );
852     local_pattern = e_strdup( value );
853     }
854     else if ( strcasecmp( name, "throttles" ) == 0 )
855     {
856     value_required( name, value );
857     throttlefile = e_strdup( value );
858     }
859     else if ( strcasecmp( name, "host" ) == 0 )
860     {
861     value_required( name, value );
862     hostname = e_strdup( value );
863     }
864     else if ( strcasecmp( name, "logfile" ) == 0 )
865     {
866     value_required( name, value );
867     logfile = e_strdup( value );
868     }
869     else if ( strcasecmp( name, "vhost" ) == 0 )
870     {
871     no_value_required( name, value );
872     do_vhost = 1;
873     }
874     else if ( strcasecmp( name, "novhost" ) == 0 )
875     {
876     no_value_required( name, value );
877     do_vhost = 0;
878     }
879     else if ( strcasecmp( name, "globalpasswd" ) == 0 )
880     {
881     no_value_required( name, value );
882     do_global_passwd = 1;
883     }
884     else if ( strcasecmp( name, "noglobalpasswd" ) == 0 )
885     {
886     no_value_required( name, value );
887     do_global_passwd = 0;
888     }
889     else if ( strcasecmp( name, "pidfile" ) == 0 )
890     {
891     value_required( name, value );
892     pidfile = e_strdup( value );
893     }
894     else if ( strcasecmp( name, "charset" ) == 0 )
895     {
896     value_required( name, value );
897     charset = e_strdup( value );
898     }
899     else
900     {
901     (void) fprintf(
902     stderr, "%s: unknown config option '%s'\n", argv0, name );
903     exit( 1 );
904     }
905     }
906     }
907    
908     (void) fclose( fp );
909     }
910    
911    
912     static void
913     value_required( char* name, char* value )
914     {
915     if ( value == (char*) 0 )
916     {
917     (void) fprintf(
918     stderr, "%s: value required for %s option\n", argv0, name );
919     exit( 1 );
920     }
921     }
922    
923    
924     static void
925     no_value_required( char* name, char* value )
926     {
927     if ( value != (char*) 0 )
928     {
929     (void) fprintf(
930     stderr, "%s: no value required for %s option\n",
931     argv0, name );
932     exit( 1 );
933     }
934     }
935    
936    
937     static char*
938     e_strdup( char* oldstr )
939     {
940     char* newstr;
941    
942     newstr = strdup( oldstr );
943     if ( newstr == (char*) 0 )
944     {
945     syslog( LOG_CRIT, "out of memory copying a string" );
946     (void) fprintf( stderr, "%s: out of memory copying a string\n", argv0 );
947     exit( 1 );
948     }
949     return newstr;
950     }
951    
952    
953     static void
954     lookup_hostname( httpd_sockaddr* sa4P, size_t sa4_len, int* gotv4P, httpd_sockaddr* sa6P, size_t sa6_len, int* gotv6P )
955     {
956     #if defined(HAVE_GETADDRINFO) && defined(HAVE_GAI_STRERROR)
957    
958     struct addrinfo hints;
959     struct addrinfo* ai;
960     struct addrinfo* ai2;
961     struct addrinfo* aiv4;
962     struct addrinfo* aiv6;
963     int gaierr;
964     char strport[10];
965    
966     memset( &hints, 0, sizeof(hints) );
967     hints.ai_family = AF_UNSPEC;
968     hints.ai_flags = AI_PASSIVE;
969     hints.ai_socktype = SOCK_STREAM;
970     (void) snprintf( strport, sizeof(strport), "%d", port );
971     if ( (gaierr = getaddrinfo( hostname, strport, &hints, &ai )) != 0 )
972     {
973     syslog(
974     LOG_CRIT, "getaddrinfo %.80s - %.80s",
975     hostname, gai_strerror( gaierr ) );
976     (void) fprintf(
977     stderr, "%s: getaddrinfo %s - %s\n",
978     argv0, hostname, gai_strerror( gaierr ) );
979     exit( 1 );
980     }
981    
982     /* Find the first IPv4 and IPv6 entries. */
983     aiv4 = (struct addrinfo*) 0;
984     aiv6 = (struct addrinfo*) 0;
985     for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next )
986     {
987     switch ( ai2->ai_family )
988     {
989     case AF_INET:
990     if ( aiv4 == (struct addrinfo*) 0 )
991     aiv4 = ai2;
992     break;
993     #if defined(AF_INET6) && defined(HAVE_SOCKADDR_IN6)
994     case AF_INET6:
995     if ( aiv6 == (struct addrinfo*) 0 )
996     aiv6 = ai2;
997     break;
998     #endif /* AF_INET6 && HAVE_SOCKADDR_IN6 */
999     }
1000     }
1001    
1002     if ( aiv4 == (struct addrinfo*) 0 )
1003     *gotv4P = 0;
1004     else
1005     {
1006     if ( sa4_len < aiv4->ai_addrlen )
1007     {
1008     syslog(
1009     LOG_CRIT, "%.80s - sockaddr too small (%d < %d)",
1010     hostname, sa4_len, aiv4->ai_addrlen );
1011     exit( 1 );
1012     }
1013     memset( sa4P, 0, sa4_len );
1014     memcpy( sa4P, aiv4->ai_addr, aiv4->ai_addrlen );
1015     *gotv4P = 1;
1016     }
1017     if ( aiv6 == (struct addrinfo*) 0 )
1018     *gotv6P = 0;
1019     else
1020     {
1021     if ( sa6_len < aiv6->ai_addrlen )
1022     {
1023     syslog(
1024     LOG_CRIT, "%.80s - sockaddr too small (%d < %d)",
1025     hostname, sa6_len, aiv6->ai_addrlen );
1026     exit( 1 );
1027     }
1028     memset( sa6P, 0, sa6_len );
1029     memcpy( sa6P, aiv6->ai_addr, aiv6->ai_addrlen );
1030     *gotv6P = 1;
1031     }
1032    
1033     freeaddrinfo( ai );
1034    
1035     #else /* HAVE_GETADDRINFO && HAVE_GAI_STRERROR */
1036    
1037     struct hostent* he;
1038    
1039     *gotv6P = 0;
1040    
1041     memset( sa4P, 0, sa4_len );
1042     #ifdef notdef
1043     /* We don't really need to set sa_len. */
1044     #ifdef HAVE_SA_LEN
1045     sa4P->sa_len = sa4_len;
1046     #endif /* HAVE_SA_LEN */
1047     #endif
1048     sa4P->sa.sa_family = AF_INET;
1049     if ( hostname == (char*) 0 )
1050     sa4P->sa_in.sin_addr.s_addr = htonl( INADDR_ANY );
1051     else
1052     {
1053     sa4P->sa_in.sin_addr.s_addr = inet_addr( hostname );
1054     if ( (int) sa4P->sa_in.sin_addr.s_addr == -1 )
1055     {
1056     he = gethostbyname( hostname );
1057     if ( he == (struct hostent*) 0 )
1058     {
1059     #ifdef HAVE_HSTRERROR
1060     syslog(
1061     LOG_CRIT, "gethostbyname %.80s - %.80s",
1062     hostname, hstrerror( h_errno ) );
1063     (void) fprintf(
1064     stderr, "%s: gethostbyname %s - %s\n",
1065     argv0, hostname, hstrerror( h_errno ) );
1066     #else /* HAVE_HSTRERROR */
1067     syslog( LOG_CRIT, "gethostbyname %.80s failed", hostname );
1068     (void) fprintf(
1069     stderr, "%s: gethostbyname %s failed\n", argv0, hostname );
1070     #endif /* HAVE_HSTRERROR */
1071     exit( 1 );
1072     }
1073     if ( he->h_addrtype != AF_INET )
1074     {
1075     syslog( LOG_CRIT, "%.80s - non-IP network address", hostname );
1076     (void) fprintf(
1077     stderr, "%s: %s - non-IP network address\n",
1078     argv0, hostname );
1079     exit( 1 );
1080     }
1081     (void) memcpy(
1082     &sa4P->sa_in.sin_addr.s_addr, he->h_addr, he->h_length );
1083     }
1084     }
1085     sa4P->sa_in.sin_port = htons( port );
1086     *gotv4P = 1;
1087    
1088     #endif /* HAVE_GETADDRINFO && HAVE_GAI_STRERROR */
1089     }
1090    
1091    
1092     static void
1093     read_throttlefile( char* throttlefile )
1094     {
1095     FILE* fp;
1096     char buf[5000];
1097     char* cp;
1098     int len;
1099     char pattern[5000];
1100     long limit;
1101     struct timeval tv;
1102    
1103     fp = fopen( throttlefile, "r" );
1104     if ( fp == (FILE*) 0 )
1105     {
1106     syslog( LOG_CRIT, "%.80s - %m", throttlefile );
1107     perror( throttlefile );
1108     exit( 1 );
1109     }
1110    
1111     (void) gettimeofday( &tv, (struct timezone*) 0 );
1112    
1113     while ( fgets( buf, sizeof(buf), fp ) != (char*) 0 )
1114     {
1115     /* Nuke comments. */
1116     cp = strchr( buf, '#' );
1117     if ( cp != (char*) 0 )
1118     *cp = '\0';
1119    
1120     /* Nuke trailing whitespace. */
1121     len = strlen( buf );
1122     while ( len > 0 &&
1123     ( buf[len-1] == ' ' || buf[len-1] == '\t' ||
1124     buf[len-1] == '\n' || buf[len-1] == '\r' ) )
1125     buf[--len] = '\0';
1126    
1127     /* Ignore empty lines. */
1128     if ( len == 0 )
1129     continue;
1130    
1131     /* Parse line. */
1132     if ( sscanf( buf, " %4900[^ \t] %ld", pattern, &limit ) != 2 || limit <= 0 )
1133     {
1134     syslog( LOG_CRIT,
1135     "unparsable line in %.80s - %.80s", throttlefile, buf );
1136     (void) fprintf( stderr,
1137     "%s: unparsable line in %.80s - %.80s\n",
1138     argv0, throttlefile, buf );
1139     continue;
1140     }
1141    
1142     /* Nuke any leading slashes in pattern. */
1143     if ( pattern[0] == '/' )
1144     (void) strcpy( pattern, &pattern[1] );
1145     while ( ( cp = strstr( pattern, "|/" ) ) != (char*) 0 )
1146     (void) strcpy( cp + 1, cp + 2 );
1147    
1148     /* Check for room in throttles. */
1149     if ( numthrottles >= maxthrottles )
1150     {
1151     if ( maxthrottles == 0 )
1152     {
1153     maxthrottles = 100; /* arbitrary */
1154     throttles = NEW( throttletab, maxthrottles );
1155     }
1156     else
1157     {
1158     maxthrottles *= 2;
1159     throttles = RENEW( throttles, throttletab, maxthrottles );
1160     }
1161     if ( throttles == (throttletab*) 0 )
1162     {
1163     syslog( LOG_CRIT, "out of memory allocating a throttletab" );
1164     (void) fprintf(
1165     stderr, "%s: out of memory allocating a throttletab\n",
1166     argv0 );
1167     exit( 1 );
1168     }
1169     }
1170    
1171     /* Add to table. */
1172     throttles[numthrottles].pattern = strdup( pattern );
1173     if ( throttles[numthrottles].pattern == (char*) 0 )
1174     {
1175     syslog( LOG_CRIT, "out of memory copying a throttle pattern" );
1176     (void) fprintf(
1177     stderr, "%s: out of memory copying a throttle pattern\n",
1178     argv0 );
1179     exit( 1 );
1180     }
1181     throttles[numthrottles].limit = limit;
1182     throttles[numthrottles].rate = 0;
1183     throttles[numthrottles].bytes_since_avg = 0;
1184     throttles[numthrottles].num_sending = 0;
1185    
1186     ++numthrottles;
1187     }
1188     (void) fclose( fp );
1189     }
1190    
1191    
1192     static void
1193     shut_down( void )
1194     {
1195     int cnum;
1196     struct timeval tv;
1197    
1198     (void) gettimeofday( &tv, (struct timezone*) 0 );
1199     logstats( &tv );
1200     for ( cnum = 0; cnum < maxconnects; ++cnum )
1201     {
1202     if ( connects[cnum].conn_state != CNST_FREE )
1203     httpd_close_conn( connects[cnum].hc, &tv );
1204     if ( connects[cnum].hc != (httpd_conn*) 0 )
1205     {
1206     httpd_destroy_conn( connects[cnum].hc );
1207     free( (void*) connects[cnum].hc );
1208     --httpd_conn_count;
1209     connects[cnum].hc = (httpd_conn*) 0;
1210     }
1211     }
1212     if ( hs != (httpd_server*) 0 )
1213     {
1214     httpd_server* ths = hs;
1215     hs = (httpd_server*) 0;
1216     httpd_terminate( ths );
1217     }
1218     mmc_destroy();
1219     tmr_destroy();
1220     free( (void*) connects );
1221     if ( throttles != (throttletab*) 0 )
1222     free( (void*) throttles );
1223     }
1224    
1225    
1226     static int
1227     handle_newconnect( struct timeval* tvP, int listen_fd )
1228     {
1229     int cnum;
1230     connecttab* c;
1231     ClientData client_data;
1232    
1233     /* This loops until the accept() fails, trying to start new
1234     ** connections as fast as possible so we don't overrun the
1235     ** listen queue.
1236     */
1237     for (;;)
1238     {
1239     /* Is there room in the connection table? */
1240     if ( numconnects >= maxconnects )
1241     {
1242     /* Out of connection slots. Run the timers, then the
1243     ** existing connections, and maybe we'll free up a slot
1244     ** by the time we get back here.
1245     **/
1246     syslog( LOG_WARNING, "too many connections!" );
1247     tmr_run( tvP );
1248     return 0;
1249     }
1250     /* Find a free connection entry. */
1251     for ( cnum = 0; cnum < maxconnects; ++cnum )
1252     if ( connects[cnum].conn_state == CNST_FREE )
1253     break;
1254     c = &connects[cnum];
1255     /* Make the httpd_conn if necessary. */
1256     if ( c->hc == (httpd_conn*) 0 )
1257     {
1258     c->hc = NEW( httpd_conn, 1 );
1259     if ( c->hc == (httpd_conn*) 0 )
1260     {
1261     syslog( LOG_CRIT, "out of memory allocating an httpd_conn" );
1262     exit( 1 );
1263     }
1264     c->hc->initialized = 0;
1265     ++httpd_conn_count;
1266     }
1267    
1268     /* Get the connection. */
1269     switch ( httpd_get_conn( hs, listen_fd, c->hc ) )
1270     {
1271     case GC_FAIL:
1272     case GC_NO_MORE:
1273     return 1;
1274     }
1275     c->conn_state = CNST_READING;
1276     ++numconnects;
1277     client_data.p = c;
1278     c->idle_read_timer = tmr_create(
1279     tvP, idle_read_connection, client_data, IDLE_READ_TIMELIMIT * 1000L,
1280     0 );
1281     if ( c->idle_read_timer == (Timer*) 0 )
1282     {
1283     syslog( LOG_CRIT, "tmr_create(idle_read_connection) failed" );
1284     exit( 1 );
1285     }
1286     c->idle_send_timer = (Timer*) 0;
1287     c->wakeup_timer = (Timer*) 0;
1288     c->linger_timer = (Timer*) 0;
1289     c->bytes_sent = 0;
1290     c->numtnums = 0;
1291    
1292     /* Set the connection file descriptor to no-delay mode. */
1293     httpd_set_ndelay( c->hc->conn_fd );
1294    
1295     fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ );
1296    
1297     ++stats_connections;
1298     if ( numconnects > stats_simultaneous )
1299     stats_simultaneous = numconnects;
1300     }
1301     }
1302    
1303    
1304     static void
1305     handle_read( connecttab* c, struct timeval* tvP )
1306     {
1307     int sz;
1308     ClientData client_data;
1309     httpd_conn* hc = c->hc;
1310    
1311     /* Is there room in our buffer to read more bytes? */
1312     if ( hc->read_idx >= hc->read_size )
1313     {
1314     if ( hc->read_size > 5000 )
1315     {
1316     httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1317     clear_connection( c, tvP );
1318     return;
1319     }
1320     httpd_realloc_str(
1321     &hc->read_buf, &hc->read_size, hc->read_size + 1000 );
1322     }
1323    
1324     /* Read some more bytes. */
1325     sz = read(
1326     hc->conn_fd, &(hc->read_buf[hc->read_idx]),
1327     hc->read_size - hc->read_idx );
1328     /* Ignore EWOULDBLOCK errors. At first glance you would think that
1329     ** connections returned by fdwatch as readable should never give an
1330     ** EWOULDBLOCK; however, this apparently can happen if a packet gets
1331     ** garbled.
1332     */
1333     if ( sz == 0 || ( sz < 0 && ( errno != EWOULDBLOCK ) ) )
1334     {
1335     httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1336     clear_connection( c, tvP );
1337     return;
1338     }
1339     hc->read_idx += sz;
1340    
1341     /* Do we have a complete request yet? */
1342     switch ( httpd_got_request( hc ) )
1343     {
1344     case GR_NO_REQUEST:
1345     return;
1346     case GR_BAD_REQUEST:
1347     httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1348     clear_connection( c, tvP );
1349     return;
1350     }
1351    
1352     /* Yes. Try parsing and resolving it. */
1353     if ( httpd_parse_request( hc ) < 0 )
1354     {
1355     clear_connection( c, tvP );
1356     return;
1357     }
1358    
1359     /* Check the throttle table */
1360     if ( ! check_throttles( c ) )
1361     {
1362     httpd_send_err(
1363     hc, 503, httpd_err503title, "", httpd_err503form, hc->encodedurl );
1364     clear_connection( c, tvP );
1365     return;
1366     }
1367    
1368     /* Start the connection going. */
1369     if ( httpd_start_request( hc, tvP ) < 0 )
1370     {
1371     /* Something went wrong. Close down the connection. */
1372     clear_connection( c, tvP );
1373     return;
1374     }
1375    
1376     /* Fill in bytes_to_send. */
1377     if ( hc->got_range )
1378     {
1379     c->bytes_sent = hc->init_byte_loc;
1380     c->bytes_to_send = hc->end_byte_loc + 1;
1381     }
1382     else
1383     c->bytes_to_send = hc->bytes_to_send;
1384    
1385     /* Check if it's already handled. */
1386 root 1.1.6.1 if ( hc->file_address == (char*) 0
1387     #ifdef MMAP_MAX
1388     && hc->file_fd <= 0
1389     #endif
1390     )
1391 root 1.1 {
1392     /* No file address means someone else is handling it. */
1393     c->bytes_sent = hc->bytes_sent;
1394     clear_connection( c, tvP );
1395     return;
1396     }
1397     if ( c->bytes_sent >= c->bytes_to_send )
1398     {
1399     /* There's nothing to send. */
1400     clear_connection( c, tvP );
1401     return;
1402     }
1403    
1404     /* Cool, we have a valid connection and a file to send to it. */
1405     c->conn_state = CNST_SENDING;
1406     c->started_at = tvP->tv_sec;
1407     c->wouldblock_delay = 0;
1408     client_data.p = c;
1409     tmr_cancel( c->idle_read_timer );
1410     c->idle_read_timer = (Timer*) 0;
1411     c->idle_send_timer = tmr_create(
1412     tvP, idle_send_connection, client_data, IDLE_SEND_TIMELIMIT * 1000L,
1413     0 );
1414     if ( c->idle_send_timer == (Timer*) 0 )
1415     {
1416     syslog( LOG_CRIT, "tmr_create(idle_send_connection) failed" );
1417     exit( 1 );
1418     }
1419    
1420     fdwatch_del_fd( hc->conn_fd );
1421     fdwatch_add_fd( hc->conn_fd, c, FDW_WRITE );
1422     }
1423    
1424    
1425     static void
1426     handle_send( connecttab* c, struct timeval* tvP )
1427     {
1428     int sz, coast;
1429     ClientData client_data;
1430     time_t elapsed;
1431     httpd_conn* hc = c->hc;
1432    
1433     /* Do we need to write the headers first? */
1434     if ( hc->responselen == 0 )
1435     {
1436     /* No, just write the file. */
1437 root 1.1.6.1 #ifdef MMAP_MAX
1438     if (hc->file_address)
1439     {
1440     #endif
1441     sz = write(
1442     hc->conn_fd, &(hc->file_address[c->bytes_sent]),
1443     MIN( c->bytes_to_send - c->bytes_sent, c->limit ) );
1444     #ifdef MMAP_MAX
1445     }
1446     else
1447     {
1448     if (hc->write_ofs >= WRITE_BUFFER)
1449     {
1450     /* refill buffer */
1451     lseek (hc->file_fd, c->bytes_sent, SEEK_SET);
1452     read (hc->file_fd, hc->write_buf, WRITE_BUFFER);
1453     hc->write_ofs = 0;
1454     }
1455    
1456     sz = write(
1457     hc->conn_fd, hc->write_buf + hc->write_ofs,
1458     MIN( c->bytes_to_send - c->bytes_sent, MIN( WRITE_BUFFER - hc->write_ofs, c->limit ) ) );
1459    
1460     if (sz > 0)
1461     hc->write_ofs += sz;
1462    
1463     }
1464     #endif
1465 root 1.1 }
1466     else
1467     {
1468     /* Yes. We'll combine headers and file into a single writev(),
1469     ** hoping that this generates a single packet.
1470     */
1471     struct iovec iv[2];
1472 root 1.1.6.1 int ivc;
1473 root 1.1
1474     iv[0].iov_base = hc->response;
1475     iv[0].iov_len = hc->responselen;
1476 root 1.1.6.1
1477     #ifdef MMAP_MAX
1478     if (hc->file_address)
1479     #endif
1480     {
1481     iv[1].iov_base = &(hc->file_address[c->bytes_sent]);
1482     iv[1].iov_len = MIN( c->bytes_to_send - c->bytes_sent, c->limit );
1483     ivc = 2;
1484     }
1485     #ifdef MMAP_MAX
1486     else
1487     ivc = 1;
1488     #endif
1489     sz = writev( hc->conn_fd, iv, ivc );
1490 root 1.1 }
1491    
1492     if ( sz == 0 ||
1493     ( sz < 0 && ( errno == EWOULDBLOCK || errno == EAGAIN ) ) )
1494     {
1495     /* This shouldn't happen, but some kernels, e.g.
1496     ** SunOS 4.1.x, are broken and select() says that
1497     ** O_NDELAY sockets are always writable even when
1498     ** they're actually not.
1499     **
1500     ** Current workaround is to block sending on this
1501     ** socket for a brief adaptively-tuned period.
1502     ** Fortunately we already have all the necessary
1503     ** blocking code, for use with throttling.
1504     */
1505     c->wouldblock_delay += MIN_WOULDBLOCK_DELAY;
1506     c->conn_state = CNST_PAUSING;
1507     fdwatch_del_fd( hc->conn_fd );
1508     client_data.p = c;
1509     c->wakeup_timer = tmr_create(
1510     tvP, wakeup_connection, client_data, c->wouldblock_delay, 0 );
1511     if ( c->wakeup_timer == (Timer*) 0 )
1512     {
1513     syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" );
1514     exit( 1 );
1515     }
1516     return;
1517     }
1518     if ( sz < 0 )
1519     {
1520     /* Something went wrong, close this connection.
1521     **
1522     ** If it's just an EPIPE, don't bother logging, that
1523     ** just means the client hung up on us.
1524     **
1525     ** On some systems, write() occasionally gives an EINVAL.
1526     ** Dunno why, something to do with the socket going
1527     ** bad. Anyway, we don't log those either.
1528     **
1529     ** And ECONNRESET isn't interesting either.
1530     */
1531     if ( errno != EPIPE && errno != EINVAL && errno != ECONNRESET )
1532     syslog( LOG_ERR, "write - %m sending %.80s", hc->encodedurl );
1533     clear_connection( c, tvP );
1534     return;
1535     }
1536    
1537     /* Ok, we wrote something. */
1538     tmr_reset( tvP, c->idle_send_timer );
1539     /* Was this a headers + file writev()? */
1540     if ( hc->responselen > 0 )
1541     {
1542     /* Yes; did we write only part of the headers? */
1543     if ( sz < hc->responselen )
1544     {
1545     /* Yes; move the unwritten part to the front of the buffer. */
1546     int newlen = hc->responselen - sz;
1547     (void) memcpy( hc->response, &(hc->response[sz]), newlen );
1548     hc->responselen = newlen;
1549     sz = 0;
1550     }
1551     else
1552     {
1553     /* Nope, we wrote the full headers, so adjust accordingly. */
1554     sz -= hc->responselen;
1555     hc->responselen = 0;
1556     }
1557     }
1558     /* And update how much of the file we wrote. */
1559     c->bytes_sent += sz;
1560     c->hc->bytes_sent += sz;
1561    
1562     /* Are we done? */
1563     if ( c->bytes_sent >= c->bytes_to_send )
1564     {
1565     /* This conection is finished! */
1566     clear_connection( c, tvP );
1567     return;
1568     }
1569    
1570     /* Tune the (blockheaded) wouldblock delay. */
1571     if ( c->wouldblock_delay > MIN_WOULDBLOCK_DELAY )
1572     c->wouldblock_delay -= MIN_WOULDBLOCK_DELAY;
1573    
1574     /* If we're throttling, check if we're sending too fast. */
1575     if ( c->limit != THROTTLE_NOLIMIT )
1576     {
1577     elapsed = tvP->tv_sec - c->started_at;
1578     if ( elapsed == 0 || c->hc->bytes_sent / elapsed > c->limit )
1579     {
1580     c->conn_state = CNST_PAUSING;
1581     fdwatch_del_fd( hc->conn_fd );
1582     /* When should we send the next c->limit bytes
1583     ** to get back on schedule? If less than a second
1584     ** (integer math rounding), use 1/8 second.
1585     */
1586     coast = ( c->hc->bytes_sent + c->limit ) / c->limit - elapsed;
1587     client_data.p = c;
1588     c->wakeup_timer = tmr_create(
1589     tvP, wakeup_connection, client_data,
1590     coast ? ( coast * 1000L ) : 125L, 0 );
1591     if ( c->wakeup_timer == (Timer*) 0 )
1592     {
1593     syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" );
1594     exit( 1 );
1595     }
1596     }
1597     }
1598     }
1599    
1600    
1601     static void
1602     handle_linger( connecttab* c, struct timeval* tvP )
1603     {
1604     char buf[1024];
1605     int r;
1606    
1607     /* In lingering-close mode we just read and ignore bytes. An error
1608     ** or EOF ends things, otherwise we go until a timeout.
1609     */
1610     r = read( c->hc->conn_fd, buf, sizeof(buf) );
1611     if ( r <= 0 )
1612     really_clear_connection( c, tvP );
1613     }
1614    
1615    
1616     static int
1617     check_throttles( connecttab* c )
1618     {
1619     int tnum;
1620    
1621     c->numtnums = 0;
1622     c->limit = THROTTLE_NOLIMIT;
1623     for ( tnum = 0; tnum < numthrottles && c->numtnums < MAXTHROTTLENUMS;
1624     ++tnum )
1625     if ( match( throttles[tnum].pattern, c->hc->expnfilename ) )
1626     {
1627     /* If we're way over the limit, don't even start. */
1628     if ( throttles[tnum].rate > throttles[tnum].limit * 2 )
1629     return 0;
1630     if ( throttles[tnum].num_sending < 0 )
1631     {
1632     syslog( LOG_ERR, "throttle sending count was negative - shouldn't happen!" );
1633     throttles[tnum].num_sending = 0;
1634     }
1635     c->tnums[c->numtnums++] = tnum;
1636     ++throttles[tnum].num_sending;
1637     c->limit = MIN(
1638     c->limit, throttles[tnum].limit / throttles[tnum].num_sending );
1639     }
1640     return 1;
1641     }
1642    
1643    
1644     static void
1645     clear_throttles( connecttab* c, struct timeval* tvP )
1646     {
1647     int i, tnum;
1648    
1649     for ( i = 0; i < c->numtnums; ++i )
1650     {
1651     tnum = c->tnums[i];
1652     --throttles[tnum].num_sending;
1653     throttles[tnum].bytes_since_avg += c->hc->bytes_sent;
1654     }
1655     }
1656    
1657    
1658     static void
1659     update_throttles( ClientData client_data, struct timeval* nowP )
1660     {
1661     int tnum;
1662    
1663     for ( tnum = 0; tnum < numthrottles; ++tnum )
1664     {
1665     throttles[tnum].rate =
1666     ( 3 * throttles[tnum].rate +
1667     throttles[tnum].bytes_since_avg / THROTTLE_TIME ) / 4;
1668     throttles[tnum].bytes_since_avg = 0;
1669     /* Log a warning message if necessary. */
1670     if ( throttles[tnum].rate > throttles[tnum].limit )
1671     {
1672     if ( throttles[tnum].rate > throttles[tnum].limit * 2 )
1673     syslog( LOG_NOTICE, "throttle #%d '%.80s' rate %ld GREATLY exceeding limit %ld", tnum, throttles[tnum].pattern, throttles[tnum].rate, throttles[tnum].limit );
1674     else
1675     syslog( LOG_NOTICE, "throttle #%d '%.80s' rate %ld exceeding limit %ld", tnum, throttles[tnum].pattern, throttles[tnum].rate, throttles[tnum].limit );
1676     }
1677     }
1678     }
1679    
1680    
1681     static void
1682     clear_connection( connecttab* c, struct timeval* tvP )
1683     {
1684     ClientData client_data;
1685    
1686     /* If we haven't actually sent the buffered response yet, do so now. */
1687     httpd_write_response( c->hc );
1688    
1689     if ( c->idle_read_timer != (Timer*) 0 )
1690     {
1691     tmr_cancel( c->idle_read_timer );
1692     c->idle_read_timer = 0;
1693     }
1694     if ( c->idle_send_timer != (Timer*) 0 )
1695     {
1696     tmr_cancel( c->idle_send_timer );
1697     c->idle_send_timer = 0;
1698     }
1699     if ( c->wakeup_timer != (Timer*) 0 )
1700     {
1701     tmr_cancel( c->wakeup_timer );
1702     c->wakeup_timer = 0;
1703     }
1704    
1705     /* This is our version of Apache's lingering_close() routine, which is
1706     ** their version of the often-broken SO_LINGER socket option. For why
1707     ** this is necessary, see http://www.apache.org/docs/misc/fin_wait_2.html
1708     ** What we do is delay the actual closing for a few seconds, while reading
1709     ** any bytes that come over the connection. However, we don't want to do
1710     ** this unless it's necessary, because it ties up a connection slot and
1711     ** file descriptor which means our maximum connection-handling rate
1712     ** is lower. So, elsewhere we set a flag when we detect the few
1713     ** circumstances that make a lingering close necessary. If the flag
1714     ** isn't set we do the real close now.
1715     */
1716     if ( c->hc->should_linger )
1717     {
1718     c->conn_state = CNST_LINGERING;
1719     fdwatch_del_fd( c->hc->conn_fd );
1720     fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ );
1721     /* Make sure we are still in no-delay mode. */
1722     httpd_set_ndelay( c->hc->conn_fd );
1723     client_data.p = c;
1724     c->linger_timer = tmr_create(
1725     tvP, linger_clear_connection, client_data, LINGER_TIME * 1000L, 0 );
1726     if ( c->linger_timer == (Timer*) 0 )
1727     {
1728     syslog( LOG_CRIT, "tmr_create(linger_clear_connection) failed" );
1729     exit( 1 );
1730     }
1731     }
1732     else
1733     really_clear_connection( c, tvP );
1734     }
1735    
1736    
1737     static void
1738     really_clear_connection( connecttab* c, struct timeval* tvP )
1739     {
1740     stats_bytes += c->bytes_to_send;
1741     fdwatch_del_fd( c->hc->conn_fd );
1742     httpd_close_conn( c->hc, tvP );
1743     clear_throttles( c, tvP );
1744     if ( c->linger_timer != (Timer*) 0 )
1745     {
1746     tmr_cancel( c->linger_timer );
1747     c->linger_timer = 0;
1748     }
1749     c->conn_state = CNST_FREE;
1750     --numconnects;
1751     }
1752    
1753    
1754     static void
1755     idle_read_connection( ClientData client_data, struct timeval* nowP )
1756     {
1757     connecttab* c;
1758    
1759     c = (connecttab*) client_data.p;
1760     c->idle_read_timer = (Timer*) 0;
1761     if ( c->conn_state != CNST_FREE )
1762     {
1763     syslog( LOG_INFO,
1764     "%.80s connection timed out reading",
1765     httpd_ntoa( &c->hc->client_addr ) );
1766     httpd_send_err( c->hc, 408, httpd_err408title, "", httpd_err408form, "" );
1767     clear_connection( c, nowP );
1768     }
1769     }
1770    
1771    
1772     static void
1773     idle_send_connection( ClientData client_data, struct timeval* nowP )
1774     {
1775     connecttab* c;
1776    
1777     c = (connecttab*) client_data.p;
1778     c->idle_send_timer = (Timer*) 0;
1779     if ( c->conn_state != CNST_FREE )
1780     {
1781     syslog( LOG_INFO,
1782     "%.80s connection timed out sending",
1783     httpd_ntoa( &c->hc->client_addr ) );
1784     clear_connection( c, nowP );
1785     }
1786     }
1787    
1788    
1789     static void
1790     wakeup_connection( ClientData client_data, struct timeval* nowP )
1791     {
1792     connecttab* c;
1793    
1794     c = (connecttab*) client_data.p;
1795     c->wakeup_timer = (Timer*) 0;
1796     if ( c->conn_state == CNST_PAUSING )
1797     {
1798     c->conn_state = CNST_SENDING;
1799     fdwatch_add_fd( c->hc->conn_fd, c, FDW_WRITE );
1800     }
1801     }
1802    
1803     static void
1804     linger_clear_connection( ClientData client_data, struct timeval* nowP )
1805     {
1806     connecttab* c;
1807    
1808     c = (connecttab*) client_data.p;
1809     c->linger_timer = (Timer*) 0;
1810     really_clear_connection( c, nowP );
1811     }
1812    
1813    
1814     static void
1815     occasional( ClientData client_data, struct timeval* nowP )
1816     {
1817     mmc_cleanup( nowP );
1818     tmr_cleanup();
1819     }
1820    
1821    
1822     #ifdef STATS_TIME
1823     static void
1824     show_stats( ClientData client_data, struct timeval* nowP )
1825     {
1826     logstats( nowP );
1827     }
1828     #endif /* STATS_TIME */
1829    
1830    
1831     /* Generate debugging statistics syslog messages for all packages. */
1832     static void
1833     logstats( struct timeval* nowP )
1834     {
1835     struct timeval tv;
1836     time_t now;
1837     long up_secs, stats_secs;
1838    
1839     if ( nowP == (struct timeval*) 0 )
1840     {
1841     (void) gettimeofday( &tv, (struct timezone*) 0 );
1842     nowP = &tv;
1843     }
1844     now = nowP->tv_sec;
1845     up_secs = now - start_time;
1846     stats_secs = now - stats_time;
1847     if ( stats_secs == 0 )
1848     stats_secs = 1; /* fudge */
1849     stats_time = now;
1850     syslog( LOG_NOTICE,
1851     "up %ld seconds, stats for %ld seconds:", up_secs, stats_secs );
1852    
1853     thttpd_logstats( stats_secs );
1854     httpd_logstats( stats_secs );
1855     mmc_logstats( stats_secs );
1856     fdwatch_logstats( stats_secs );
1857     tmr_logstats( stats_secs );
1858     }
1859    
1860    
1861     /* Generate debugging statistics syslog message. */
1862     static void
1863     thttpd_logstats( long secs )
1864     {
1865     syslog( LOG_NOTICE,
1866     " thttpd - %ld connections (%g/sec), %d max simultaneous, %ld bytes (%g/sec), %d httpd_conns allocated",
1867     stats_connections, (float) stats_connections / secs,
1868     stats_simultaneous, stats_bytes, (float) stats_bytes / secs,
1869     httpd_conn_count );
1870     stats_connections = stats_bytes = 0L;
1871     stats_simultaneous = 0;
1872     }