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