Annotation of embedaddon/thttpd/thttpd.c, revision 1.1.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>