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