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