]>
git.gir.st - ttxd.git/blob - src/thttpd-2.27/thttpd.c
1 /* thttpd.c - tiny/turbo/throttling HTTP server
3 ** Copyright © 1995,1998,1999,2000,2001,2015 by
4 ** Jef Poskanzer <jef@mail.acme.com>. All rights reserved.
6 ** Redistribution and use in source and binary forms, with or without
7 ** modification, are permitted provided that the following conditions
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.
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
32 #include <sys/param.h>
33 #include <sys/types.h>
52 #ifdef TIME_WITH_SYS_TIME
68 typedef long long int64_t;
74 static unsigned short port
;
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
;
80 static char* url_pattern
;
81 static int no_empty_referrers
;
82 static char* local_pattern
;
84 static char* throttlefile
;
85 static char* hostname
;
95 long max_limit
, min_limit
;
97 off_t bytes_since_avg
;
100 static throttletab
* throttles
;
101 static int numthrottles
, maxthrottles
;
103 #define THROTTLE_NOLIMIT -1
108 int next_free_connect
;
110 int tnums
[MAXTHROTTLENUMS
]; /* throttle indexes */
112 long max_limit
, min_limit
;
113 time_t started_at
, active_at
;
116 long wouldblock_delay
;
118 off_t end_byte_index
;
119 off_t next_byte_index
;
121 static connecttab
* connects
;
122 static int num_connects
, max_connects
, first_free_connect
;
123 static int httpd_conn_count
;
125 /* The connection states. */
127 #define CNST_READING 1
128 #define CNST_SENDING 2
129 #define CNST_PAUSING 3
130 #define CNST_LINGERING 4
133 static httpd_server
* hs
= (httpd_server
*) 0;
135 time_t start_time
, stats_time
;
136 long stats_connections
;
138 int stats_simultaneous
;
140 static volatile int got_hup
, got_usr1
, watchdog_flag
;
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* tf
);
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
);
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
);
174 /* SIGTERM and SIGINT say to exit immediately. */
176 handle_term( int sig
)
178 /* Don't need to set up the handler again, since it's a one-shot. */
181 syslog( LOG_NOTICE
, "exiting due to signal %d", sig
);
187 /* SIGCHLD - a chile process exitted, so we need to reap the zombie */
189 handle_chld( int sig
)
191 const int oerrno
= errno
;
196 /* Set up handler again. */
197 (void) signal( SIGCHLD
, handle_chld
);
198 #endif /* ! HAVE_SIGSET */
200 /* Reap defunct children until there aren't any more. */
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 */
212 if ( errno
== EINTR
|| errno
== EAGAIN
)
214 /* ECHILD shouldn't happen with the WNOHANG option,
215 ** but with some kernels it does anyway. Ignore it.
217 if ( errno
!= ECHILD
)
218 syslog( LOG_ERR
, "child wait - %m" );
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.
227 if ( hs
!= (httpd_server
*) 0 )
230 if ( hs
->cgi_count
< 0 )
235 /* Restore previous errno. */
240 /* SIGHUP says to re-open the log file. */
242 handle_hup( int sig
)
244 const int oerrno
= errno
;
247 /* Set up handler again. */
248 (void) signal( SIGHUP
, handle_hup
);
249 #endif /* ! HAVE_SIGSET */
251 /* Just set a flag that we got the signal. */
254 /* Restore previous errno. */
259 /* SIGUSR1 says to exit as soon as all current connections are done. */
261 handle_usr1( int sig
)
263 /* Don't need to set up the handler again, since it's a one-shot. */
265 if ( num_connects
== 0 )
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.
272 syslog( LOG_NOTICE
, "exiting" );
277 /* Otherwise, just set a flag that we got the signal. */
280 /* Don't need to restore old errno, since we didn't do any syscalls. */
284 /* SIGUSR2 says to generate the stats syslogs immediately. */
286 handle_usr2( int sig
)
288 const int oerrno
= errno
;
291 /* Set up handler again. */
292 (void) signal( SIGUSR2
, handle_usr2
);
293 #endif /* ! HAVE_SIGSET */
295 logstats( (struct timeval
*) 0 );
297 /* Restore previous errno. */
302 /* SIGALRM is used as a watchdog. */
304 handle_alrm( int sig
)
306 const int oerrno
= errno
;
308 /* If nothing has been happening */
309 if ( ! watchdog_flag
)
311 /* Try changing dirs to someplace we can write. */
312 (void) chdir( "/tmp" );
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 );
325 /* Restore previous errno. */
331 re_open_logfile( void )
335 if ( no_log
|| hs
== (httpd_server
*) 0 )
338 /* Re-open the log file. */
339 if ( logfile
!= (char*) 0 && strcmp( logfile
, "-" ) != 0 )
341 syslog( LOG_NOTICE
, "re-opening logfile" );
342 logfp
= fopen( logfile
, "a" );
343 if ( logfp
== (FILE*) 0 )
345 syslog( LOG_CRIT
, "re-opening %.80s - %m", logfile
);
348 (void) fcntl( fileno( logfp
), F_SETFD
, 1 );
349 httpd_set_logfp( hs
, logfp
);
355 main( int argc
, char** argv
)
361 char cwd
[MAXPATHLEN
+1];
374 cp
= strrchr( argv0
, '/' );
375 if ( cp
!= (char*) 0 )
379 openlog( cp
, LOG_NDELAY
|LOG_PID
, LOG_FACILITY
);
381 /* Handle command-line arguments. */
382 parse_args( argc
, argv
);
384 /* Read zone info now, in case we chroot(). */
387 /* Look up hostname now, in case we chroot(). */
388 lookup_hostname( &sa4
, sizeof(sa4
), &gotv4
, &sa6
, sizeof(sa6
), &gotv6
);
389 if ( ! ( gotv4
|| gotv6
) )
391 syslog( LOG_ERR
, "can't find any valid address" );
392 (void) fprintf( stderr
, "%s: can't find any valid address\n", argv0
);
399 throttles
= (throttletab
*) 0;
400 if ( throttlefile
!= (char*) 0 )
401 read_throttlefile( throttlefile
);
403 /* If we're root and we're going to become another user, get the uid/gid
408 pwd
= getpwnam( user
);
409 if ( pwd
== (struct passwd
*) 0 )
411 syslog( LOG_CRIT
, "unknown user - '%.80s'", user
);
412 (void) fprintf( stderr
, "%s: unknown user - '%s'\n", argv0
, user
);
420 if ( logfile
!= (char*) 0 )
422 if ( strcmp( logfile
, "/dev/null" ) == 0 )
427 else if ( strcmp( logfile
, "-" ) == 0 )
431 logfp
= fopen( logfile
, "a" );
432 if ( logfp
== (FILE*) 0 )
434 syslog( LOG_CRIT
, "%.80s - %m", logfile
);
438 if ( logfile
[0] != '/' )
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
);
443 (void) fcntl( fileno( logfp
), F_SETFD
, 1 );
446 /* If we are root then we chown the log file to the user we'll
449 if ( fchown( fileno( logfp
), uid
, gid
) < 0 )
451 syslog( LOG_WARNING
, "fchown logfile - %m" );
452 perror( "fchown logfile" );
460 /* Switch directories if requested. */
461 if ( dir
!= (char*) 0 )
463 if ( chdir( dir
) < 0 )
465 syslog( LOG_CRIT
, "chdir - %m" );
471 else if ( getuid() == 0 )
473 /* No explicit directory was specified, we're root, and the
474 ** USE_USER_DIR option is set - switch to the specified user's
477 if ( chdir( pwd
->pw_dir
) < 0 )
479 syslog( LOG_CRIT
, "chdir - %m" );
484 #endif /* USE_USER_DIR */
486 /* Get current directory. */
487 (void) getcwd( cwd
, sizeof(cwd
) - 1 );
488 if ( cwd
[strlen( cwd
) - 1] != '/' )
489 (void) strcat( cwd
, "/" );
493 /* We're not going to use stdin stdout or stderr from here on, so close
494 ** them to save file descriptors.
496 (void) fclose( stdin
);
497 if ( logfp
!= stdout
)
498 (void) fclose( stdout
);
499 (void) fclose( stderr
);
501 /* Daemonize - make ourselves a subprocess. */
503 if ( daemon( 1, 1 ) < 0 )
505 syslog( LOG_CRIT
, "daemon - %m" );
508 #else /* HAVE_DAEMON */
514 syslog( LOG_CRIT
, "fork - %m" );
521 #endif /* HAVE_SETSID */
522 #endif /* HAVE_DAEMON */
526 /* Even if we don't daemonize, we still want to disown our parent
531 #endif /* HAVE_SETSID */
534 if ( pidfile
!= (char*) 0 )
536 /* Write the PID file. */
537 FILE* pidfp
= fopen( pidfile
, "w" );
538 if ( pidfp
== (FILE*) 0 )
540 syslog( LOG_CRIT
, "%.80s - %m", pidfile
);
543 (void) fprintf( pidfp
, "%d\n", (int) getpid() );
544 (void) fclose( pidfp
);
547 /* Initialize the fdwatch package. Have to do this before chroot,
548 ** if /dev/poll is used.
550 max_connects
= fdwatch_get_nfiles();
551 if ( max_connects
< 0 )
553 syslog( LOG_CRIT
, "fdwatch initialization failure" );
556 max_connects
-= SPARE_FDS
;
558 /* Chroot if requested. */
561 if ( chroot( cwd
) < 0 )
563 syslog( LOG_CRIT
, "chroot - %m" );
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
572 if ( logfile
!= (char*) 0 && strcmp( logfile
, "-" ) != 0 )
574 if ( strncmp( logfile
, cwd
, strlen( cwd
) ) == 0 )
576 (void) ol_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
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
);
588 (void) strcpy( cwd
, "/" );
589 /* Always chdir to / after a chroot. */
590 if ( chdir( cwd
) < 0 )
592 syslog( LOG_CRIT
, "chroot chdir - %m" );
593 perror( "chroot chdir" );
598 /* Switch directories again if requested. */
599 if ( data_dir
!= (char*) 0 )
601 if ( chdir( data_dir
) < 0 )
603 syslog( LOG_CRIT
, "data_dir chdir - %m" );
604 perror( "data_dir chdir" );
609 /* Set up to catch signals. */
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 */
632 (void) alarm( OCCASIONAL_TIME
* 3 );
634 /* Initialize the timer package. */
637 /* Initialize the HTTP layer. Got to do this before giving up root,
638 ** so that we can bind to a privileged port.
640 hs
= httpd_initialize(
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_referrers
);
646 if ( hs
== (httpd_server
*) 0 )
649 /* Set up the occasional timer. */
650 if ( tmr_create( (struct timeval
*) 0, occasional
, JunkClientData
, OCCASIONAL_TIME
* 1000L, 1 ) == (Timer
*) 0 )
652 syslog( LOG_CRIT
, "tmr_create(occasional) failed" );
655 /* Set up the idle timer. */
656 if ( tmr_create( (struct timeval
*) 0, idle
, JunkClientData
, 5 * 1000L, 1 ) == (Timer
*) 0 )
658 syslog( LOG_CRIT
, "tmr_create(idle) failed" );
661 if ( numthrottles
> 0 )
663 /* Set up the throttles timer. */
664 if ( tmr_create( (struct timeval
*) 0, update_throttles
, JunkClientData
, THROTTLE_TIME
* 1000L, 1 ) == (Timer
*) 0 )
666 syslog( LOG_CRIT
, "tmr_create(update_throttles) failed" );
671 /* Set up the stats timer. */
672 if ( tmr_create( (struct timeval
*) 0, show_stats
, JunkClientData
, STATS_TIME
* 1000L, 1 ) == (Timer
*) 0 )
674 syslog( LOG_CRIT
, "tmr_create(show_stats) failed" );
677 #endif /* STATS_TIME */
678 start_time
= stats_time
= time( (time_t*) 0 );
679 stats_connections
= 0;
681 stats_simultaneous
= 0;
683 /* If we're root, try to become someone else. */
686 /* Set aux groups to null. */
687 if ( setgroups( 0, (const gid_t
*) 0 ) < 0 )
689 syslog( LOG_CRIT
, "setgroups - %m" );
692 /* Set primary group. */
693 if ( setgid( gid
) < 0 )
695 syslog( LOG_CRIT
, "setgid - %m" );
698 /* Try setting aux groups correctly - not critical if this fails. */
699 if ( initgroups( user
, gid
) < 0 )
700 syslog( LOG_WARNING
, "initgroups - %m" );
702 /* Set login name. */
703 (void) setlogin( user
);
704 #endif /* HAVE_SETLOGIN */
706 if ( setuid( uid
) < 0 )
708 syslog( LOG_CRIT
, "setuid - %m" );
711 /* Check for unnecessary security exposure. */
715 "started as root without requesting chroot(), warning only" );
718 /* Initialize our connections table. */
719 connects
= NEW( connecttab
, max_connects
);
720 if ( connects
== (connecttab
*) 0 )
722 syslog( LOG_CRIT
, "out of memory allocating a connecttab" );
725 for ( cnum
= 0; cnum
< max_connects
; ++cnum
)
727 connects
[cnum
].conn_state
= CNST_FREE
;
728 connects
[cnum
].next_free_connect
= cnum
+ 1;
729 connects
[cnum
].hc
= (httpd_conn
*) 0;
731 connects
[max_connects
- 1].next_free_connect
= -1; /* end of link list */
732 first_free_connect
= 0;
734 httpd_conn_count
= 0;
736 if ( hs
!= (httpd_server
*) 0 )
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
);
745 (void) gettimeofday( &tv
, (struct timezone
*) 0 );
746 while ( ( ! terminate
) || num_connects
> 0 )
748 /* Do we need to re-open the log file? */
755 /* Do the fd watch. */
756 num_ready
= fdwatch( tmr_mstimeout( &tv
) );
759 if ( errno
== EINTR
|| errno
== EAGAIN
)
760 continue; /* try again */
761 syslog( LOG_ERR
, "fdwatch - %m" );
764 (void) gettimeofday( &tv
, (struct timezone
*) 0 );
766 if ( num_ready
== 0 )
768 /* No fd's are ready - run the timers. */
773 /* Is it a new connection? */
774 if ( hs
!= (httpd_server
*) 0 && hs
->listen6_fd
!= -1 &&
775 fdwatch_check_fd( hs
->listen6_fd
) )
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.
784 if ( hs
!= (httpd_server
*) 0 && hs
->listen4_fd
!= -1 &&
785 fdwatch_check_fd( hs
->listen4_fd
) )
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.
795 /* Find the connections that need servicing. */
796 while ( ( c
= (connecttab
*) fdwatch_get_next_client_data() ) != (connecttab
*) -1 )
798 if ( c
== (connecttab
*) 0 )
801 if ( ! fdwatch_check_fd( hc
->conn_fd
) )
802 /* Something went wrong. */
803 clear_connection( c
, &tv
);
805 switch ( c
->conn_state
)
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;
814 if ( got_usr1
&& ! terminate
)
817 if ( hs
!= (httpd_server
*) 0 )
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
);
828 /* The main loop terminated. */
830 syslog( LOG_NOTICE
, "exiting" );
837 parse_args( int argc
, char** argv
)
844 data_dir
= (char*) 0;
847 #else /* ALWAYS_CHROOT */
849 #endif /* ALWAYS_CHROOT */
851 no_symlink_check
= do_chroot
;
854 #else /* ALWAYS_VHOST */
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 */
863 cgi_pattern
= CGI_PATTERN
;
864 #else /* CGI_PATTERN */
865 cgi_pattern
= (char*) 0;
866 #endif /* CGI_PATTERN */
868 cgi_limit
= CGI_LIMIT
;
869 #else /* CGI_LIMIT */
871 #endif /* CGI_LIMIT */
872 url_pattern
= (char*) 0;
873 no_empty_referrers
= 0;
874 local_pattern
= (char*) 0;
875 throttlefile
= (char*) 0;
876 hostname
= (char*) 0;
880 charset
= DEFAULT_CHARSET
;
884 while ( argn
< argc
&& argv
[argn
][0] == '-' )
886 if ( strcmp( argv
[argn
], "-V" ) == 0 )
888 (void) printf( "%s\n", SERVER_SOFTWARE
);
891 else if ( strcmp( argv
[argn
], "-C" ) == 0 && argn
+ 1 < argc
)
894 read_config( argv
[argn
] );
896 else if ( strcmp( argv
[argn
], "-p" ) == 0 && argn
+ 1 < argc
)
899 port
= (unsigned short) atoi( argv
[argn
] );
901 else if ( strcmp( argv
[argn
], "-d" ) == 0 && argn
+ 1 < argc
)
906 else if ( strcmp( argv
[argn
], "-r" ) == 0 )
909 no_symlink_check
= 1;
911 else if ( strcmp( argv
[argn
], "-nor" ) == 0 )
914 no_symlink_check
= 0;
916 else if ( strcmp( argv
[argn
], "-dd" ) == 0 && argn
+ 1 < argc
)
919 data_dir
= argv
[argn
];
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
)
930 else if ( strcmp( argv
[argn
], "-c" ) == 0 && argn
+ 1 < argc
)
933 cgi_pattern
= argv
[argn
];
935 else if ( strcmp( argv
[argn
], "-t" ) == 0 && argn
+ 1 < argc
)
938 throttlefile
= argv
[argn
];
940 else if ( strcmp( argv
[argn
], "-h" ) == 0 && argn
+ 1 < argc
)
943 hostname
= argv
[argn
];
945 else if ( strcmp( argv
[argn
], "-l" ) == 0 && argn
+ 1 < argc
)
948 logfile
= argv
[argn
];
950 else if ( strcmp( argv
[argn
], "-v" ) == 0 )
952 else if ( strcmp( argv
[argn
], "-nov" ) == 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
)
961 pidfile
= argv
[argn
];
963 else if ( strcmp( argv
[argn
], "-T" ) == 0 && argn
+ 1 < argc
)
966 charset
= argv
[argn
];
968 else if ( strcmp( argv
[argn
], "-P" ) == 0 && argn
+ 1 < argc
)
973 else if ( strcmp( argv
[argn
], "-M" ) == 0 && argn
+ 1 < argc
)
976 max_age
= atoi( argv
[argn
] );
978 else if ( strcmp( argv
[argn
], "-D" ) == 0 )
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",
1000 read_config( char* filename
)
1009 fp
= fopen( filename
, "r" );
1010 if ( fp
== (FILE*) 0 )
1016 while ( fgets( line
, sizeof(line
), fp
) != (char*) 0 )
1018 /* Trim comments. */
1019 if ( ( cp
= strchr( line
, '#' ) ) != (char*) 0 )
1022 /* Skip leading whitespace. */
1024 cp
+= strspn( cp
, " \t\n\r" );
1026 /* Split line into words. */
1027 while ( *cp
!= '\0' )
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' )
1034 /* Split into name and value. */
1036 value
= strchr( name
, '=' );
1037 if ( value
!= (char*) 0 )
1040 if ( strcasecmp( name
, "debug" ) == 0 )
1042 no_value_required( name
, value
);
1045 else if ( strcasecmp( name
, "port" ) == 0 )
1047 value_required( name
, value
);
1048 port
= (unsigned short) atoi( value
);
1050 else if ( strcasecmp( name
, "dir" ) == 0 )
1052 value_required( name
, value
);
1053 dir
= e_strdup( value
);
1055 else if ( strcasecmp( name
, "chroot" ) == 0 )
1057 no_value_required( name
, value
);
1059 no_symlink_check
= 1;
1061 else if ( strcasecmp( name
, "nochroot" ) == 0 )
1063 no_value_required( name
, value
);
1065 no_symlink_check
= 0;
1067 else if ( strcasecmp( name
, "data_dir" ) == 0 )
1069 value_required( name
, value
);
1070 data_dir
= e_strdup( value
);
1072 else if ( strcasecmp( name
, "nosymlinkcheck" ) == 0 )
1074 no_value_required( name
, value
);
1075 no_symlink_check
= 1;
1077 else if ( strcasecmp( name
, "symlinkcheck" ) == 0 )
1079 no_value_required( name
, value
);
1080 no_symlink_check
= 0;
1082 else if ( strcasecmp( name
, "user" ) == 0 )
1084 value_required( name
, value
);
1085 user
= e_strdup( value
);
1087 else if ( strcasecmp( name
, "cgipat" ) == 0 )
1089 value_required( name
, value
);
1090 cgi_pattern
= e_strdup( value
);
1092 else if ( strcasecmp( name
, "cgilimit" ) == 0 )
1094 value_required( name
, value
);
1095 cgi_limit
= atoi( value
);
1097 else if ( strcasecmp( name
, "urlpat" ) == 0 )
1099 value_required( name
, value
);
1100 url_pattern
= e_strdup( value
);
1102 else if ( strcasecmp( name
, "noemptyreferers" ) == 0 ||
1103 strcasecmp( name
, "noemptyreferrers" ) == 0 )
1105 no_value_required( name
, value
);
1106 no_empty_referrers
= 1;
1108 else if ( strcasecmp( name
, "localpat" ) == 0 )
1110 value_required( name
, value
);
1111 local_pattern
= e_strdup( value
);
1113 else if ( strcasecmp( name
, "throttles" ) == 0 )
1115 value_required( name
, value
);
1116 throttlefile
= e_strdup( value
);
1118 else if ( strcasecmp( name
, "host" ) == 0 )
1120 value_required( name
, value
);
1121 hostname
= e_strdup( value
);
1123 else if ( strcasecmp( name
, "logfile" ) == 0 )
1125 value_required( name
, value
);
1126 logfile
= e_strdup( value
);
1128 else if ( strcasecmp( name
, "vhost" ) == 0 )
1130 no_value_required( name
, value
);
1133 else if ( strcasecmp( name
, "novhost" ) == 0 )
1135 no_value_required( name
, value
);
1138 else if ( strcasecmp( name
, "globalpasswd" ) == 0 )
1140 no_value_required( name
, value
);
1141 do_global_passwd
= 1;
1143 else if ( strcasecmp( name
, "noglobalpasswd" ) == 0 )
1145 no_value_required( name
, value
);
1146 do_global_passwd
= 0;
1148 else if ( strcasecmp( name
, "pidfile" ) == 0 )
1150 value_required( name
, value
);
1151 pidfile
= e_strdup( value
);
1153 else if ( strcasecmp( name
, "charset" ) == 0 )
1155 value_required( name
, value
);
1156 charset
= e_strdup( value
);
1158 else if ( strcasecmp( name
, "p3p" ) == 0 )
1160 value_required( name
, value
);
1161 p3p
= e_strdup( value
);
1163 else if ( strcasecmp( name
, "max_age" ) == 0 )
1165 value_required( name
, value
);
1166 max_age
= atoi( value
);
1171 stderr
, "%s: unknown config option '%s'\n", argv0
, name
);
1175 /* Advance to next word. */
1177 cp
+= strspn( cp
, " \t\n\r" );
1181 (void) fclose( fp
);
1186 value_required( char* name
, char* value
)
1188 if ( value
== (char*) 0 )
1191 stderr
, "%s: value required for %s option\n", argv0
, name
);
1198 no_value_required( char* name
, char* value
)
1200 if ( value
!= (char*) 0 )
1203 stderr
, "%s: no value required for %s option\n",
1211 e_strdup( char* oldstr
)
1215 newstr
= strdup( oldstr
);
1216 if ( newstr
== (char*) 0 )
1218 syslog( LOG_CRIT
, "out of memory copying a string" );
1219 (void) fprintf( stderr
, "%s: out of memory copying a string\n", argv0
);
1227 lookup_hostname( httpd_sockaddr
* sa4P
, size_t sa4_len
, int* gotv4P
, httpd_sockaddr
* sa6P
, size_t sa6_len
, int* gotv6P
)
1231 struct addrinfo hints
;
1234 struct addrinfo
* ai
;
1235 struct addrinfo
* ai2
;
1236 struct addrinfo
* aiv6
;
1237 struct addrinfo
* aiv4
;
1239 (void) memset( &hints
, 0, sizeof(hints
) );
1240 hints
.ai_family
= PF_UNSPEC
;
1241 hints
.ai_flags
= AI_PASSIVE
;
1242 hints
.ai_socktype
= SOCK_STREAM
;
1243 (void) snprintf( portstr
, sizeof(portstr
), "%d", (int) port
);
1244 if ( (gaierr
= getaddrinfo( hostname
, portstr
, &hints
, &ai
)) != 0 )
1247 LOG_CRIT
, "getaddrinfo %.80s - %.80s",
1248 hostname
, gai_strerror( gaierr
) );
1250 stderr
, "%s: getaddrinfo %s - %s\n",
1251 argv0
, hostname
, gai_strerror( gaierr
) );
1255 /* Find the first IPv6 and IPv4 entries. */
1256 aiv6
= (struct addrinfo
*) 0;
1257 aiv4
= (struct addrinfo
*) 0;
1258 for ( ai2
= ai
; ai2
!= (struct addrinfo
*) 0; ai2
= ai2
->ai_next
)
1260 switch ( ai2
->ai_family
)
1263 if ( aiv6
== (struct addrinfo
*) 0 )
1267 if ( aiv4
== (struct addrinfo
*) 0 )
1273 if ( aiv6
== (struct addrinfo
*) 0 )
1277 if ( sa6_len
< aiv6
->ai_addrlen
)
1280 LOG_CRIT
, "%.80s - sockaddr too small (%lu < %lu)",
1281 hostname
, (unsigned long) sa6_len
,
1282 (unsigned long) aiv6
->ai_addrlen
);
1285 (void) memset( sa6P
, 0, sa6_len
);
1286 (void) memmove( sa6P
, aiv6
->ai_addr
, aiv6
->ai_addrlen
);
1290 if ( aiv4
== (struct addrinfo
*) 0 )
1294 if ( sa4_len
< aiv4
->ai_addrlen
)
1297 LOG_CRIT
, "%.80s - sockaddr too small (%lu < %lu)",
1298 hostname
, (unsigned long) sa4_len
,
1299 (unsigned long) aiv4
->ai_addrlen
);
1302 (void) memset( sa4P
, 0, sa4_len
);
1303 (void) memmove( sa4P
, aiv4
->ai_addr
, aiv4
->ai_addrlen
);
1309 #else /* USE_IPV6 */
1315 (void) memset( sa4P
, 0, sa4_len
);
1316 sa4P
->sa
.sa_family
= AF_INET
;
1317 if ( hostname
== (char*) 0 )
1318 sa4P
->sa_in
.sin_addr
.s_addr
= htonl( INADDR_ANY
);
1321 sa4P
->sa_in
.sin_addr
.s_addr
= inet_addr( hostname
);
1322 if ( (int) sa4P
->sa_in
.sin_addr
.s_addr
== -1 )
1324 he
= gethostbyname( hostname
);
1325 if ( he
== (struct hostent
*) 0 )
1327 #ifdef HAVE_HSTRERROR
1329 LOG_CRIT
, "gethostbyname %.80s - %.80s",
1330 hostname
, hstrerror( h_errno
) );
1332 stderr
, "%s: gethostbyname %s - %s\n",
1333 argv0
, hostname
, hstrerror( h_errno
) );
1334 #else /* HAVE_HSTRERROR */
1335 syslog( LOG_CRIT
, "gethostbyname %.80s failed", hostname
);
1337 stderr
, "%s: gethostbyname %s failed\n", argv0
, hostname
);
1338 #endif /* HAVE_HSTRERROR */
1341 if ( he
->h_addrtype
!= AF_INET
)
1343 syslog( LOG_CRIT
, "%.80s - non-IP network address", hostname
);
1345 stderr
, "%s: %s - non-IP network address\n",
1350 &sa4P
->sa_in
.sin_addr
.s_addr
, he
->h_addr
, he
->h_length
);
1353 sa4P
->sa_in
.sin_port
= htons( port
);
1356 #endif /* USE_IPV6 */
1361 read_throttlefile( char* tf
)
1368 long max_limit
, min_limit
;
1371 fp
= fopen( tf
, "r" );
1372 if ( fp
== (FILE*) 0 )
1374 syslog( LOG_CRIT
, "%.80s - %m", tf
);
1379 (void) gettimeofday( &tv
, (struct timezone
*) 0 );
1381 while ( fgets( buf
, sizeof(buf
), fp
) != (char*) 0 )
1383 /* Nuke comments. */
1384 cp
= strchr( buf
, '#' );
1385 if ( cp
!= (char*) 0 )
1388 /* Nuke trailing whitespace. */
1389 len
= strlen( buf
);
1391 ( buf
[len
-1] == ' ' || buf
[len
-1] == '\t' ||
1392 buf
[len
-1] == '\n' || buf
[len
-1] == '\r' ) )
1395 /* Ignore empty lines. */
1400 if ( sscanf( buf
, " %4900[^ \t] %ld-%ld", pattern
, &min_limit
, &max_limit
) == 3 )
1402 else if ( sscanf( buf
, " %4900[^ \t] %ld", pattern
, &max_limit
) == 2 )
1407 "unparsable line in %.80s - %.80s", tf
, buf
);
1408 (void) fprintf( stderr
,
1409 "%s: unparsable line in %.80s - %.80s\n",
1414 /* Nuke any leading slashes in pattern. */
1415 if ( pattern
[0] == '/' )
1416 (void) ol_strcpy( pattern
, &pattern
[1] );
1417 while ( ( cp
= strstr( pattern
, "|/" ) ) != (char*) 0 )
1418 (void) ol_strcpy( cp
+ 1, cp
+ 2 );
1420 /* Check for room in throttles. */
1421 if ( numthrottles
>= maxthrottles
)
1423 if ( maxthrottles
== 0 )
1425 maxthrottles
= 100; /* arbitrary */
1426 throttles
= NEW( throttletab
, maxthrottles
);
1431 throttles
= RENEW( throttles
, throttletab
, maxthrottles
);
1433 if ( throttles
== (throttletab
*) 0 )
1435 syslog( LOG_CRIT
, "out of memory allocating a throttletab" );
1437 stderr
, "%s: out of memory allocating a throttletab\n",
1444 throttles
[numthrottles
].pattern
= e_strdup( pattern
);
1445 throttles
[numthrottles
].max_limit
= max_limit
;
1446 throttles
[numthrottles
].min_limit
= min_limit
;
1447 throttles
[numthrottles
].rate
= 0;
1448 throttles
[numthrottles
].bytes_since_avg
= 0;
1449 throttles
[numthrottles
].num_sending
= 0;
1453 (void) fclose( fp
);
1463 (void) gettimeofday( &tv
, (struct timezone
*) 0 );
1465 for ( cnum
= 0; cnum
< max_connects
; ++cnum
)
1467 if ( connects
[cnum
].conn_state
!= CNST_FREE
)
1468 httpd_close_conn( connects
[cnum
].hc
, &tv
);
1469 if ( connects
[cnum
].hc
!= (httpd_conn
*) 0 )
1471 httpd_destroy_conn( connects
[cnum
].hc
);
1472 free( (void*) connects
[cnum
].hc
);
1474 connects
[cnum
].hc
= (httpd_conn
*) 0;
1477 if ( hs
!= (httpd_server
*) 0 )
1479 httpd_server
* ths
= hs
;
1480 hs
= (httpd_server
*) 0;
1481 if ( ths
->listen4_fd
!= -1 )
1482 fdwatch_del_fd( ths
->listen4_fd
);
1483 if ( ths
->listen6_fd
!= -1 )
1484 fdwatch_del_fd( ths
->listen6_fd
);
1485 httpd_terminate( ths
);
1489 free( (void*) connects
);
1490 if ( throttles
!= (throttletab
*) 0 )
1491 free( (void*) throttles
);
1496 handle_newconnect( struct timeval
* tvP
, int listen_fd
)
1499 ClientData client_data
;
1501 /* This loops until the accept() fails, trying to start new
1502 ** connections as fast as possible so we don't overrun the
1507 /* Is there room in the connection table? */
1508 if ( num_connects
>= max_connects
)
1510 /* Out of connection slots. Run the timers, then the
1511 ** existing connections, and maybe we'll free up a slot
1512 ** by the time we get back here.
1514 syslog( LOG_WARNING
, "too many connections!" );
1518 /* Get the first free connection entry off the free list. */
1519 if ( first_free_connect
== -1 || connects
[first_free_connect
].conn_state
!= CNST_FREE
)
1521 syslog( LOG_CRIT
, "the connects free list is messed up" );
1524 c
= &connects
[first_free_connect
];
1525 /* Make the httpd_conn if necessary. */
1526 if ( c
->hc
== (httpd_conn
*) 0 )
1528 c
->hc
= NEW( httpd_conn
, 1 );
1529 if ( c
->hc
== (httpd_conn
*) 0 )
1531 syslog( LOG_CRIT
, "out of memory allocating an httpd_conn" );
1534 c
->hc
->initialized
= 0;
1538 /* Get the connection. */
1539 switch ( httpd_get_conn( hs
, listen_fd
, c
->hc
) )
1541 /* Some error happened. Run the timers, then the
1542 ** existing connections. Maybe the error will clear.
1548 /* No more connections to accept for now. */
1552 c
->conn_state
= CNST_READING
;
1553 /* Pop it off the free list. */
1554 first_free_connect
= c
->next_free_connect
;
1555 c
->next_free_connect
= -1;
1558 c
->active_at
= tvP
->tv_sec
;
1559 c
->wakeup_timer
= (Timer
*) 0;
1560 c
->linger_timer
= (Timer
*) 0;
1561 c
->next_byte_index
= 0;
1564 /* Set the connection file descriptor to no-delay mode. */
1565 httpd_set_ndelay( c
->hc
->conn_fd
);
1567 fdwatch_add_fd( c
->hc
->conn_fd
, c
, FDW_READ
);
1569 ++stats_connections
;
1570 if ( num_connects
> stats_simultaneous
)
1571 stats_simultaneous
= num_connects
;
1577 handle_read( connecttab
* c
, struct timeval
* tvP
)
1580 ClientData client_data
;
1581 httpd_conn
* hc
= c
->hc
;
1583 /* Is there room in our buffer to read more bytes? */
1584 if ( hc
->read_idx
>= hc
->read_size
)
1586 if ( hc
->read_size
> 5000 )
1588 httpd_send_err( hc
, 400, httpd_err400title
, "", httpd_err400form
, "" );
1589 finish_connection( c
, tvP
);
1593 &hc
->read_buf
, &hc
->read_size
, hc
->read_size
+ 1000 );
1596 /* Read some more bytes. */
1598 hc
->conn_fd
, &(hc
->read_buf
[hc
->read_idx
]),
1599 hc
->read_size
- hc
->read_idx
);
1602 httpd_send_err( hc
, 400, httpd_err400title
, "", httpd_err400form
, "" );
1603 finish_connection( c
, tvP
);
1608 /* Ignore EINTR and EAGAIN. Also ignore EWOULDBLOCK. At first glance
1609 ** you would think that connections returned by fdwatch as readable
1610 ** should never give an EWOULDBLOCK; however, this apparently can
1611 ** happen if a packet gets garbled.
1613 if ( errno
== EINTR
|| errno
== EAGAIN
|| errno
== EWOULDBLOCK
)
1616 hc
, 400, httpd_err400title
, "", httpd_err400form
, "" );
1617 finish_connection( c
, tvP
);
1621 c
->active_at
= tvP
->tv_sec
;
1623 /* Do we have a complete request yet? */
1624 switch ( httpd_got_request( hc
) )
1628 case GR_BAD_REQUEST
:
1629 httpd_send_err( hc
, 400, httpd_err400title
, "", httpd_err400form
, "" );
1630 finish_connection( c
, tvP
);
1634 /* Yes. Try parsing and resolving it. */
1635 if ( httpd_parse_request( hc
) < 0 )
1637 finish_connection( c
, tvP
);
1641 /* Check the throttle table */
1642 if ( ! check_throttles( c
) )
1645 hc
, 503, httpd_err503title
, "", httpd_err503form
, hc
->encodedurl
);
1646 finish_connection( c
, tvP
);
1650 /* Start the connection going. */
1651 if ( httpd_start_request( hc
, tvP
) < 0 )
1653 /* Something went wrong. Close down the connection. */
1654 finish_connection( c
, tvP
);
1658 /* Fill in end_byte_index. */
1659 if ( hc
->got_range
)
1661 c
->next_byte_index
= hc
->first_byte_index
;
1662 c
->end_byte_index
= hc
->last_byte_index
+ 1;
1664 else if ( hc
->bytes_to_send
< 0 )
1665 c
->end_byte_index
= 0;
1667 c
->end_byte_index
= hc
->bytes_to_send
;
1669 /* Check if it's already handled. */
1670 if ( hc
->file_address
== (char*) 0 )
1672 /* No file address means someone else is handling it. */
1674 for ( tind
= 0; tind
< c
->numtnums
; ++tind
)
1675 throttles
[c
->tnums
[tind
]].bytes_since_avg
+= hc
->bytes_sent
;
1676 c
->next_byte_index
= hc
->bytes_sent
;
1677 finish_connection( c
, tvP
);
1680 if ( c
->next_byte_index
>= c
->end_byte_index
)
1682 /* There's nothing to send. */
1683 finish_connection( c
, tvP
);
1687 /* Cool, we have a valid connection and a file to send to it. */
1688 c
->conn_state
= CNST_SENDING
;
1689 c
->started_at
= tvP
->tv_sec
;
1690 c
->wouldblock_delay
= 0;
1693 fdwatch_del_fd( hc
->conn_fd
);
1694 fdwatch_add_fd( hc
->conn_fd
, c
, FDW_WRITE
);
1699 handle_send( connecttab
* c
, struct timeval
* tvP
)
1703 ClientData client_data
;
1705 httpd_conn
* hc
= c
->hc
;
1708 if ( c
->max_limit
== THROTTLE_NOLIMIT
)
1709 max_bytes
= 1000000000L;
1711 max_bytes
= c
->max_limit
/ 4; /* send at most 1/4 seconds worth */
1713 /* Do we need to write the headers first? */
1714 if ( hc
->responselen
== 0 )
1716 /* No, just write the file. */
1718 hc
->conn_fd
, &(hc
->file_address
[c
->next_byte_index
]),
1719 MIN( c
->end_byte_index
- c
->next_byte_index
, max_bytes
) );
1723 /* Yes. We'll combine headers and file into a single writev(),
1724 ** hoping that this generates a single packet.
1728 iv
[0].iov_base
= hc
->response
;
1729 iv
[0].iov_len
= hc
->responselen
;
1730 iv
[1].iov_base
= &(hc
->file_address
[c
->next_byte_index
]);
1731 iv
[1].iov_len
= MIN( c
->end_byte_index
- c
->next_byte_index
, max_bytes
);
1732 sz
= writev( hc
->conn_fd
, iv
, 2 );
1735 if ( sz
< 0 && errno
== EINTR
)
1739 ( sz
< 0 && ( errno
== EWOULDBLOCK
|| errno
== EAGAIN
) ) )
1741 /* This shouldn't happen, but some kernels, e.g.
1742 ** SunOS 4.1.x, are broken and select() says that
1743 ** O_NDELAY sockets are always writable even when
1744 ** they're actually not.
1746 ** Current workaround is to block sending on this
1747 ** socket for a brief adaptively-tuned period.
1748 ** Fortunately we already have all the necessary
1749 ** blocking code, for use with throttling.
1751 c
->wouldblock_delay
+= MIN_WOULDBLOCK_DELAY
;
1752 c
->conn_state
= CNST_PAUSING
;
1753 fdwatch_del_fd( hc
->conn_fd
);
1755 if ( c
->wakeup_timer
!= (Timer
*) 0 )
1756 syslog( LOG_ERR
, "replacing non-null wakeup_timer!" );
1757 c
->wakeup_timer
= tmr_create(
1758 tvP
, wakeup_connection
, client_data
, c
->wouldblock_delay
, 0 );
1759 if ( c
->wakeup_timer
== (Timer
*) 0 )
1761 syslog( LOG_CRIT
, "tmr_create(wakeup_connection) failed" );
1769 /* Something went wrong, close this connection.
1771 ** If it's just an EPIPE, don't bother logging, that
1772 ** just means the client hung up on us.
1774 ** On some systems, write() occasionally gives an EINVAL.
1775 ** Dunno why, something to do with the socket going
1776 ** bad. Anyway, we don't log those either.
1778 ** And ECONNRESET isn't interesting either.
1780 if ( errno
!= EPIPE
&& errno
!= EINVAL
&& errno
!= ECONNRESET
)
1781 syslog( LOG_ERR
, "write - %m sending %.80s", hc
->encodedurl
);
1782 clear_connection( c
, tvP
);
1786 /* Ok, we wrote something. */
1787 c
->active_at
= tvP
->tv_sec
;
1788 /* Was this a headers + file writev()? */
1789 if ( hc
->responselen
> 0 )
1791 /* Yes; did we write only part of the headers? */
1792 if ( sz
< hc
->responselen
)
1794 /* Yes; move the unwritten part to the front of the buffer. */
1795 int newlen
= hc
->responselen
- sz
;
1796 (void) memmove( hc
->response
, &(hc
->response
[sz
]), newlen
);
1797 hc
->responselen
= newlen
;
1802 /* Nope, we wrote the full headers, so adjust accordingly. */
1803 sz
-= hc
->responselen
;
1804 hc
->responselen
= 0;
1807 /* And update how much of the file we wrote. */
1808 c
->next_byte_index
+= sz
;
1809 c
->hc
->bytes_sent
+= sz
;
1810 for ( tind
= 0; tind
< c
->numtnums
; ++tind
)
1811 throttles
[c
->tnums
[tind
]].bytes_since_avg
+= sz
;
1814 if ( c
->next_byte_index
>= c
->end_byte_index
)
1816 /* This connection is finished! */
1817 finish_connection( c
, tvP
);
1821 /* Tune the (blockheaded) wouldblock delay. */
1822 if ( c
->wouldblock_delay
> MIN_WOULDBLOCK_DELAY
)
1823 c
->wouldblock_delay
-= MIN_WOULDBLOCK_DELAY
;
1825 /* If we're throttling, check if we're sending too fast. */
1826 if ( c
->max_limit
!= THROTTLE_NOLIMIT
)
1828 elapsed
= tvP
->tv_sec
- c
->started_at
;
1830 elapsed
= 1; /* count at least one second */
1831 if ( c
->hc
->bytes_sent
/ elapsed
> c
->max_limit
)
1833 c
->conn_state
= CNST_PAUSING
;
1834 fdwatch_del_fd( hc
->conn_fd
);
1835 /* How long should we wait to get back on schedule? If less
1836 ** than a second (integer math rounding), use 1/2 second.
1838 coast
= c
->hc
->bytes_sent
/ c
->max_limit
- elapsed
;
1840 if ( c
->wakeup_timer
!= (Timer
*) 0 )
1841 syslog( LOG_ERR
, "replacing non-null wakeup_timer!" );
1842 c
->wakeup_timer
= tmr_create(
1843 tvP
, wakeup_connection
, client_data
,
1844 coast
> 0 ? ( coast
* 1000L ) : 500L, 0 );
1845 if ( c
->wakeup_timer
== (Timer
*) 0 )
1847 syslog( LOG_CRIT
, "tmr_create(wakeup_connection) failed" );
1852 /* (No check on min_limit here, that only controls connection startups.) */
1857 handle_linger( connecttab
* c
, struct timeval
* tvP
)
1862 /* In lingering-close mode we just read and ignore bytes. An error
1863 ** or EOF ends things, otherwise we go until a timeout.
1865 r
= read( c
->hc
->conn_fd
, buf
, sizeof(buf
) );
1866 if ( r
< 0 && ( errno
== EINTR
|| errno
== EAGAIN
) )
1869 really_clear_connection( c
, tvP
);
1874 check_throttles( connecttab
* c
)
1880 c
->max_limit
= c
->min_limit
= THROTTLE_NOLIMIT
;
1881 for ( tnum
= 0; tnum
< numthrottles
&& c
->numtnums
< MAXTHROTTLENUMS
;
1883 if ( match( throttles
[tnum
].pattern
, c
->hc
->expnfilename
) )
1885 /* If we're way over the limit, don't even start. */
1886 if ( throttles
[tnum
].rate
> throttles
[tnum
].max_limit
* 2 )
1888 /* Also don't start if we're under the minimum. */
1889 if ( throttles
[tnum
].rate
< throttles
[tnum
].min_limit
)
1891 if ( throttles
[tnum
].num_sending
< 0 )
1893 syslog( LOG_ERR
, "throttle sending count was negative - shouldn't happen!" );
1894 throttles
[tnum
].num_sending
= 0;
1896 c
->tnums
[c
->numtnums
++] = tnum
;
1897 ++throttles
[tnum
].num_sending
;
1898 l
= throttles
[tnum
].max_limit
/ throttles
[tnum
].num_sending
;
1899 if ( c
->max_limit
== THROTTLE_NOLIMIT
)
1902 c
->max_limit
= MIN( c
->max_limit
, l
);
1903 l
= throttles
[tnum
].min_limit
;
1904 if ( c
->min_limit
== THROTTLE_NOLIMIT
)
1907 c
->min_limit
= MAX( c
->min_limit
, l
);
1914 clear_throttles( connecttab
* c
, struct timeval
* tvP
)
1918 for ( tind
= 0; tind
< c
->numtnums
; ++tind
)
1919 --throttles
[c
->tnums
[tind
]].num_sending
;
1924 update_throttles( ClientData client_data
, struct timeval
* nowP
)
1931 /* Update the average sending rate for each throttle. This is only used
1932 ** when new connections start up.
1934 for ( tnum
= 0; tnum
< numthrottles
; ++tnum
)
1936 throttles
[tnum
].rate
= ( 2 * throttles
[tnum
].rate
+ throttles
[tnum
].bytes_since_avg
/ THROTTLE_TIME
) / 3;
1937 throttles
[tnum
].bytes_since_avg
= 0;
1938 /* Log a warning message if necessary. */
1939 if ( throttles
[tnum
].rate
> throttles
[tnum
].max_limit
&& throttles
[tnum
].num_sending
!= 0 )
1941 if ( throttles
[tnum
].rate
> throttles
[tnum
].max_limit
* 2 )
1942 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
);
1944 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
);
1946 if ( throttles
[tnum
].rate
< throttles
[tnum
].min_limit
&& throttles
[tnum
].num_sending
!= 0 )
1948 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
);
1952 /* Now update the sending rate on all the currently-sending connections,
1953 ** redistributing it evenly.
1955 for ( cnum
= 0; cnum
< max_connects
; ++cnum
)
1957 c
= &connects
[cnum
];
1958 if ( c
->conn_state
== CNST_SENDING
|| c
->conn_state
== CNST_PAUSING
)
1960 c
->max_limit
= THROTTLE_NOLIMIT
;
1961 for ( tind
= 0; tind
< c
->numtnums
; ++tind
)
1963 tnum
= c
->tnums
[tind
];
1964 l
= throttles
[tnum
].max_limit
/ throttles
[tnum
].num_sending
;
1965 if ( c
->max_limit
== THROTTLE_NOLIMIT
)
1968 c
->max_limit
= MIN( c
->max_limit
, l
);
1976 finish_connection( connecttab
* c
, struct timeval
* tvP
)
1978 /* If we haven't actually sent the buffered response yet, do so now. */
1979 httpd_write_response( c
->hc
);
1982 clear_connection( c
, tvP
);
1987 clear_connection( connecttab
* c
, struct timeval
* tvP
)
1989 ClientData client_data
;
1991 if ( c
->wakeup_timer
!= (Timer
*) 0 )
1993 tmr_cancel( c
->wakeup_timer
);
1994 c
->wakeup_timer
= 0;
1997 /* This is our version of Apache's lingering_close() routine, which is
1998 ** their version of the often-broken SO_LINGER socket option. For why
1999 ** this is necessary, see http://www.apache.org/docs/misc/fin_wait_2.html
2000 ** What we do is delay the actual closing for a few seconds, while reading
2001 ** any bytes that come over the connection. However, we don't want to do
2002 ** this unless it's necessary, because it ties up a connection slot and
2003 ** file descriptor which means our maximum connection-handling rate
2004 ** is lower. So, elsewhere we set a flag when we detect the few
2005 ** circumstances that make a lingering close necessary. If the flag
2006 ** isn't set we do the real close now.
2008 if ( c
->conn_state
== CNST_LINGERING
)
2010 /* If we were already lingering, shut down for real. */
2011 tmr_cancel( c
->linger_timer
);
2012 c
->linger_timer
= (Timer
*) 0;
2013 c
->hc
->should_linger
= 0;
2015 if ( c
->hc
->should_linger
)
2017 if ( c
->conn_state
!= CNST_PAUSING
)
2018 fdwatch_del_fd( c
->hc
->conn_fd
);
2019 c
->conn_state
= CNST_LINGERING
;
2020 shutdown( c
->hc
->conn_fd
, SHUT_WR
);
2021 fdwatch_add_fd( c
->hc
->conn_fd
, c
, FDW_READ
);
2023 if ( c
->linger_timer
!= (Timer
*) 0 )
2024 syslog( LOG_ERR
, "replacing non-null linger_timer!" );
2025 c
->linger_timer
= tmr_create(
2026 tvP
, linger_clear_connection
, client_data
, LINGER_TIME
, 0 );
2027 if ( c
->linger_timer
== (Timer
*) 0 )
2029 syslog( LOG_CRIT
, "tmr_create(linger_clear_connection) failed" );
2034 really_clear_connection( c
, tvP
);
2039 really_clear_connection( connecttab
* c
, struct timeval
* tvP
)
2041 stats_bytes
+= c
->hc
->bytes_sent
;
2042 if ( c
->conn_state
!= CNST_PAUSING
)
2043 fdwatch_del_fd( c
->hc
->conn_fd
);
2044 httpd_close_conn( c
->hc
, tvP
);
2045 clear_throttles( c
, tvP
);
2046 if ( c
->linger_timer
!= (Timer
*) 0 )
2048 tmr_cancel( c
->linger_timer
);
2049 c
->linger_timer
= 0;
2051 c
->conn_state
= CNST_FREE
;
2052 c
->next_free_connect
= first_free_connect
;
2053 first_free_connect
= c
- connects
; /* division by sizeof is implied */
2059 idle( ClientData client_data
, struct timeval
* nowP
)
2064 for ( cnum
= 0; cnum
< max_connects
; ++cnum
)
2066 c
= &connects
[cnum
];
2067 switch ( c
->conn_state
)
2070 if ( nowP
->tv_sec
- c
->active_at
>= IDLE_READ_TIMELIMIT
)
2073 "%.80s connection timed out reading",
2074 httpd_ntoa( &c
->hc
->client_addr
) );
2076 c
->hc
, 408, httpd_err408title
, "", httpd_err408form
, "" );
2077 finish_connection( c
, nowP
);
2082 if ( nowP
->tv_sec
- c
->active_at
>= IDLE_SEND_TIMELIMIT
)
2085 "%.80s connection timed out sending",
2086 httpd_ntoa( &c
->hc
->client_addr
) );
2087 clear_connection( c
, nowP
);
2096 wakeup_connection( ClientData client_data
, struct timeval
* nowP
)
2100 c
= (connecttab
*) client_data
.p
;
2101 c
->wakeup_timer
= (Timer
*) 0;
2102 if ( c
->conn_state
== CNST_PAUSING
)
2104 c
->conn_state
= CNST_SENDING
;
2105 fdwatch_add_fd( c
->hc
->conn_fd
, c
, FDW_WRITE
);
2110 linger_clear_connection( ClientData client_data
, struct timeval
* nowP
)
2114 c
= (connecttab
*) client_data
.p
;
2115 c
->linger_timer
= (Timer
*) 0;
2116 really_clear_connection( c
, nowP
);
2121 occasional( ClientData client_data
, struct timeval
* nowP
)
2123 mmc_cleanup( nowP
);
2125 watchdog_flag
= 1; /* let the watchdog know that we are alive */
2131 show_stats( ClientData client_data
, struct timeval
* nowP
)
2135 #endif /* STATS_TIME */
2138 /* Generate debugging statistics syslog messages for all packages. */
2140 logstats( struct timeval
* nowP
)
2144 long up_secs
, stats_secs
;
2146 if ( nowP
== (struct timeval
*) 0 )
2148 (void) gettimeofday( &tv
, (struct timezone
*) 0 );
2152 up_secs
= now
- start_time
;
2153 stats_secs
= now
- stats_time
;
2154 if ( stats_secs
== 0 )
2155 stats_secs
= 1; /* fudge */
2158 "up %ld seconds, stats for %ld seconds:", up_secs
, stats_secs
);
2160 thttpd_logstats( stats_secs
);
2161 httpd_logstats( stats_secs
);
2162 mmc_logstats( stats_secs
);
2163 fdwatch_logstats( stats_secs
);
2164 tmr_logstats( stats_secs
);
2168 /* Generate debugging statistics syslog message. */
2170 thttpd_logstats( long secs
)
2174 " thttpd - %ld connections (%g/sec), %d max simultaneous, %lld bytes (%g/sec), %d httpd_conns allocated",
2175 stats_connections
, (float) stats_connections
/ secs
,
2176 stats_simultaneous
, (long long) stats_bytes
,
2177 (float) stats_bytes
/ secs
, httpd_conn_count
);
2178 stats_connections
= 0;
2180 stats_simultaneous
= 0;