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>