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