diff -urN user/thttpd/libhttpd.c user/thttpd/libhttpd.c --- user/thttpd/libhttpd.c 2008-06-11 21:27:34.000000000 -0400 +++ user/thttpd/libhttpd.c 2008-10-30 12:56:11.000000000 -0400 @@ -83,6 +83,11 @@ #include "timers.h" #include "match.h" #include "tdate_parse.h" +#if defined(__uClinux__) && defined(CONFIG_READ_TIMEOUT) +#include "fdwatch.h" +extern int read_timeout_flag; +extern long read_timeout; +#endif #ifndef STDIN_FILENO #define STDIN_FILENO 0 @@ -3316,8 +3321,19 @@ (void) httpd_write_fully( hc->conn_fd, headers, headers_len ); /* Echo the rest of the output. */ +#if defined(__uClinux__) && defined(CONFIG_READ_TIMEOUT) + if (read_timeout_flag) + fdwatch_add_fd(rfd, (void*) 0, FDW_READ); +#endif for (;;) { +#if defined(__uClinux__) && defined(CONFIG_READ_TIMEOUT) + if (read_timeout_flag) { + int num_ready = fdwatch(read_timeout); + if(num_ready == 0) break; + if( num_ready < 0 || !fdwatch_check_fd(rfd)) continue; + } +#endif r = read( rfd, buf, sizeof(buf) ); if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) ) { @@ -3329,9 +3345,80 @@ if ( httpd_write_fully( hc->conn_fd, buf, r ) != r ) break; } +#if defined(__uClinux__) && defined(CONFIG_READ_TIMEOUT) + if(read_timeout_flag) + fdwatch_del_fd(rfd); +#endif shutdown( hc->conn_fd, SHUT_WR ); } +static void +cgi_do_exec( httpd_conn* hc, char** argp ) + { + char** envp; + char* binary; + char* directory; + + /* Make the environment vector. */ + envp = make_envp( hc ); + + /* At this point we would like to set close-on-exec again for hc->conn_fd + ** (see previous comments on Linux's broken behavior re: close-on-exec + ** and dup.) Unfortunately there seems to be another Linux problem, or + ** perhaps a different aspect of the same problem - if we do this + ** close-on-exec in Linux, the socket stays open but stderr gets + ** closed - the last fd duped from the socket. What a mess. So we'll + ** just leave the socket as is, which under other OSs means an extra + ** file descriptor gets passed to the child process. Since the child + ** probably already has that file open via stdin stdout and/or stderr, + ** this is not a problem. + */ + /* (void) fcntl( hc->conn_fd, F_SETFD, 1 ); */ + +#ifdef CGI_NICE + /* Set priority. */ + (void) nice( CGI_NICE ); +#endif /* CGI_NICE */ + + /* Split the program into directory and binary, so we can chdir() + ** to the program's own directory. This isn't in the CGI 1.1 + ** spec, but it's what other HTTP servers do. + */ + directory = strdup( hc->expnfilename ); + if ( directory == (char*) 0 ) + binary = hc->expnfilename; /* ignore errors */ + else + { + binary = strrchr( directory, '/' ); + if ( binary == (char*) 0 ) + binary = hc->expnfilename; + else + { + *binary++ = '\0'; + (void) chdir( directory ); /* ignore errors */ + } + } + + /* Default behavior for SIGPIPE. */ +#ifdef HAVE_SIGSET + (void) sigset( SIGPIPE, SIG_DFL ); +#else /* HAVE_SIGSET */ + (void) signal( SIGPIPE, SIG_DFL ); +#endif /* HAVE_SIGSET */ + + /* Run the program. */ + (void) execve( binary, argp, envp ); + + /* Something went wrong. */ + syslog( LOG_ERR, "execve %.80s - %m", hc->expnfilename ); + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + httpd_write_response( hc ); +#ifdef __uClinux__ + _exit( 1 ); +#else + exit( 1 ); +#endif + } /* CGI child process. */ static void @@ -3339,9 +3426,6 @@ { int r; char** argp; - char** envp; - char* binary; - char* directory; /* Unset close-on-exec flag for this socket. This actually shouldn't ** be necessary, according to POSIX a dup()'d file descriptor does @@ -3352,6 +3436,7 @@ */ (void) fcntl( hc->conn_fd, F_SETFD, 0 ); +#ifndef __uClinux__ /* Close the syslog descriptor so that the CGI program can't ** mess with it. All other open descriptors should be either ** the listen socket(s), sockets from accept(), or the file-logging @@ -3359,6 +3444,7 @@ ** have to close anything else. */ closelog(); +#endif /* If the socket happens to be using one of the stdin/stdout/stderr ** descriptors, move it to another descriptor so that the dup2 calls @@ -3377,9 +3463,6 @@ */ } - /* Make the environment vector. */ - envp = make_envp( hc ); - /* Make the argument vector. */ argp = make_argp( hc ); @@ -3396,13 +3479,18 @@ syslog( LOG_ERR, "pipe - %m" ); httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); httpd_write_response( hc ); +#ifdef __uClinux__ + _exit( 1 ); +#else exit( 1 ); - } +#endif + } /* Need to schedule a kill for process r; but in the main process! */ + #ifdef __uClinux__ - r = vfork( ); + /* we can't fork in parallel under uClinux, so we'll need to process up front */ + cgi_interpose_input( hc, p[1] ); #else r = fork( ); -#endif if ( r < 0 ) { syslog( LOG_ERR, "fork - %m" ); @@ -3419,6 +3507,7 @@ exit( 0 ); } /* Need to schedule a kill for process r; but in the main process! */ +#endif (void) close( p[1] ); if ( p[0] != STDIN_FILENO ) { @@ -3445,13 +3534,48 @@ syslog( LOG_ERR, "pipe - %m" ); httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); httpd_write_response( hc ); +#ifdef __uClinux__ + _exit( 1 ); +#else exit( 1 ); +#endif } #ifdef __uClinux__ + /* Oohkay, this is a little tricky. Again, we can't parallel fork, so + ** we'll need to spawn the CGI app (with vfork) and then do our output + ** processing when we get control back. Sadly this means that the user + ** won't see any output at all until we're all done... + */ r = vfork( ); + if ( r < 0 ) + { + syslog( LOG_ERR, "vfork - %m" ); + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + httpd_write_response( hc ); + _exit( 1 ); + } + if ( r == 0 ) + { + /* CGI exec process. */ + (void) close( p[0] ); + if ( p[1] != STDOUT_FILENO ) + (void) dup2( p[1], STDOUT_FILENO ); + if ( p[1] != STDERR_FILENO ) + (void) dup2( p[1], STDERR_FILENO ); + if ( p[1] != STDOUT_FILENO && p[1] != STDERR_FILENO ) + (void) close( p[1] ); + sub_process = 1; + cgi_do_exec( hc, argp ); + _exit( 0 ); + } + + (void) close( p[1] ); + /* And finally we'll do the output processing */ + cgi_interpose_output( hc, p[0] ); + (void) close( p[0] ); + _exit( 0 ); #else r = fork( ); -#endif if ( r < 0 ) { syslog( LOG_ERR, "fork - %m" ); @@ -3475,6 +3599,7 @@ (void) dup2( p[1], STDERR_FILENO ); if ( p[1] != STDOUT_FILENO && p[1] != STDERR_FILENO ) (void) close( p[1] ); +#endif } else { @@ -3485,58 +3610,7 @@ (void) dup2( hc->conn_fd, STDERR_FILENO ); } - /* At this point we would like to set close-on-exec again for hc->conn_fd - ** (see previous comments on Linux's broken behavior re: close-on-exec - ** and dup.) Unfortunately there seems to be another Linux problem, or - ** perhaps a different aspect of the same problem - if we do this - ** close-on-exec in Linux, the socket stays open but stderr gets - ** closed - the last fd duped from the socket. What a mess. So we'll - ** just leave the socket as is, which under other OSs means an extra - ** file descriptor gets passed to the child process. Since the child - ** probably already has that file open via stdin stdout and/or stderr, - ** this is not a problem. - */ - /* (void) fcntl( hc->conn_fd, F_SETFD, 1 ); */ - -#ifdef CGI_NICE - /* Set priority. */ - (void) nice( CGI_NICE ); -#endif /* CGI_NICE */ - - /* Split the program into directory and binary, so we can chdir() - ** to the program's own directory. This isn't in the CGI 1.1 - ** spec, but it's what other HTTP servers do. - */ - directory = strdup( hc->expnfilename ); - if ( directory == (char*) 0 ) - binary = hc->expnfilename; /* ignore errors */ - else - { - binary = strrchr( directory, '/' ); - if ( binary == (char*) 0 ) - binary = hc->expnfilename; - else - { - *binary++ = '\0'; - (void) chdir( directory ); /* ignore errors */ - } - } - - /* Default behavior for SIGPIPE. */ -#ifdef HAVE_SIGSET - (void) sigset( SIGPIPE, SIG_DFL ); -#else /* HAVE_SIGSET */ - (void) signal( SIGPIPE, SIG_DFL ); -#endif /* HAVE_SIGSET */ - - /* Run the program. */ - (void) execve( binary, argp, envp ); - - /* Something went wrong. */ - syslog( LOG_ERR, "execve %.80s - %m", hc->expnfilename ); - httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); - httpd_write_response( hc ); - exit( 1 ); + cgi_do_exec( hc, argp ); } @@ -3545,6 +3619,15 @@ { int r; ClientData client_data; +#ifdef __uClinux__ + /* this is not super-safe, but we're relying on the pointers at least staying + ** read-only. hopefully the contained code only fiddles with the file descriptors + ** and other integer fields. + */ + httpd_server forked_hs = *hc->hs; + httpd_conn forked_hc = *hc; + forked_hc.hs = &forked_hs; +#endif if ( hc->method == METHOD_GET || hc->method == METHOD_POST ) { @@ -3573,11 +3656,17 @@ { /* Child process. */ sub_process = 1; +#ifdef __uClinux__ + httpd_unlisten( &forked_hs ); + cgi_child( &forked_hc ); +#else httpd_unlisten( hc->hs ); cgi_child( hc ); +#endif } /* Parent process. */ + sub_process = 0; syslog( LOG_INFO, "spawned CGI process %d for file '%.200s'", r, hc->expnfilename ); #ifdef CGI_TIMELIMIT /* Schedule a kill for the child process, in case it runs too long */ diff -urN user/thttpd/makefile user/thttpd/makefile --- user/thttpd/makefile 2007-03-13 23:07:20.000000000 -0400 +++ user/thttpd/makefile 2008-10-30 13:53:43.000000000 -0400 @@ -1,3 +1,6 @@ +ifneq (,$(CONFIG_READ_TIMEOUT)$(READ_TIMEOUT)) +CFLAGS += -DCONFIG_READ_TIMEOUT +endif CONFOPTS = diff -urN user/thttpd/thttpd.c user/thttpd/thttpd.c --- user/thttpd/thttpd.c 2008-06-11 21:27:34.000000000 -0400 +++ user/thttpd/thttpd.c 2008-10-30 13:55:47.000000000 -0400 @@ -68,6 +68,10 @@ typedef long long int64_t; #endif +#if defined(__uClinux__) && defined(CONFIG_READ_TIMEOUT) +int read_timeout_flag = 0; +long read_timeout = 0; +#endif static char* argv0; static int debug; @@ -979,6 +983,15 @@ } else if ( strcmp( argv[argn], "-D" ) == 0 ) debug = 1; + else if ( strcmp( argv[argn], "-e" ) == 0 ) + { + ++argn; +#if defined(__uClinux__) && defined(CONFIG_READ_TIMEOUT) + read_timeout = atol( argv[argn] ); + if (read_timeout > 0) + read_timeout_flag = 1; +#endif + } else usage(); ++argn; @@ -992,7 +1005,7 @@ usage( void ) { (void) fprintf( stderr, -"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", +"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] [-e timeout(ms)] [-T charset] [-P P3P] [-M maxage] [-V] [-D]\n", argv0 ); exit( 1 ); }