File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / thttpd / thttpd.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 17:21:13 2012 UTC (12 years, 3 months ago) by misho
Branches: thttpd, MAIN
CVS tags: v2_25b, HEAD
thttpd

    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>