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