ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/thttpd/thttpd.c
Revision: 1.1.2.1
Committed: Mon Jun 18 21:19:51 2001 UTC (25 years, 3 months ago) by root
Content type: text/plain
Branch: dirpatch
CVS Tags: dp_j
Changes since 1.1: +14 -1 lines
Log Message:
*** empty log message ***

File Contents

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