diff options
author | Arno Töll <arno@debian.org> | 2012-11-21 23:57:42 +0100 |
---|---|---|
committer | Arno Töll <arno@debian.org> | 2012-11-21 23:57:42 +0100 |
commit | 47236ea4c1d5601fc6bea1b280d36152ec5ff32b (patch) | |
tree | 89c109c0bef460fa747a3413610034002fac1cb4 /src/server.c | |
parent | dae92c799e85ec3b65f46da6a776e4386bc99d6c (diff) | |
download | lighttpd-47236ea4c1d5601fc6bea1b280d36152ec5ff32b.tar.gz |
Imported Upstream version 1.4.32
Diffstat (limited to 'src/server.c')
-rw-r--r-- | src/server.c | 900 |
1 files changed, 631 insertions, 269 deletions
diff --git a/src/server.c b/src/server.c index 9eb9eb4..590a9d5 100644 --- a/src/server.c +++ b/src/server.c @@ -1,3 +1,20 @@ +#include "server.h" +#include "buffer.h" +#include "network.h" +#include "log.h" +#include "keyvalue.h" +#include "response.h" +#include "request.h" +#include "chunk.h" +#include "http_chunk.h" +#include "fdevent.h" +#include "connections.h" +#include "stat_cache.h" +#include "plugin.h" +#include "joblist.h" +#include "network_backends.h" +#include "version.h" + #include <sys/types.h> #include <sys/time.h> #include <sys/stat.h> @@ -14,44 +31,33 @@ #include <stdio.h> -#include "server.h" -#include "buffer.h" -#include "network.h" -#include "log.h" -#include "keyvalue.h" -#include "response.h" -#include "request.h" -#include "chunk.h" -#include "http_chunk.h" -#include "fdevent.h" -#include "connections.h" -#include "stat_cache.h" -#include "plugin.h" -#include "joblist.h" - #ifdef HAVE_GETOPT_H -#include <getopt.h> +# include <getopt.h> #endif #ifdef HAVE_VALGRIND_VALGRIND_H -#include <valgrind/valgrind.h> +# include <valgrind/valgrind.h> #endif #ifdef HAVE_SYS_WAIT_H -#include <sys/wait.h> +# include <sys/wait.h> #endif #ifdef HAVE_PWD_H -#include <grp.h> -#include <pwd.h> +# include <grp.h> +# include <pwd.h> #endif #ifdef HAVE_SYS_RESOURCE_H -#include <sys/resource.h> +# include <sys/resource.h> #endif #ifdef HAVE_SYS_PRCTL_H -#include <sys/prctl.h> +# include <sys/prctl.h> +#endif + +#ifdef USE_OPENSSL +# include <openssl/err.h> #endif #ifndef __sgi @@ -59,35 +65,76 @@ /* #define USE_ALARM */ #endif +#ifdef HAVE_GETUID +# ifndef HAVE_ISSETUGID + +static int l_issetugid(void) { + return (geteuid() != getuid() || getegid() != getgid()); +} + +# define issetugid l_issetugid +# endif +#endif + static volatile sig_atomic_t srv_shutdown = 0; static volatile sig_atomic_t graceful_shutdown = 0; static volatile sig_atomic_t handle_sig_alarm = 1; static volatile sig_atomic_t handle_sig_hup = 0; +static volatile sig_atomic_t forwarded_sig_hup = 0; #if defined(HAVE_SIGACTION) && defined(SA_SIGINFO) +static volatile siginfo_t last_sigterm_info; +static volatile siginfo_t last_sighup_info; + static void sigaction_handler(int sig, siginfo_t *si, void *context) { - UNUSED(si); + static siginfo_t empty_siginfo; UNUSED(context); - switch (sig) { - case SIGTERM: srv_shutdown = 1; break; - case SIGINT: - if (graceful_shutdown) srv_shutdown = 1; - else graceful_shutdown = 1; + if (!si) si = &empty_siginfo; - break; - case SIGALRM: handle_sig_alarm = 1; break; - case SIGHUP: handle_sig_hup = 1; break; - case SIGCHLD: break; + switch (sig) { + case SIGTERM: + srv_shutdown = 1; + last_sigterm_info = *si; + break; + case SIGINT: + if (graceful_shutdown) { + srv_shutdown = 1; + } else { + graceful_shutdown = 1; + } + last_sigterm_info = *si; + + break; + case SIGALRM: + handle_sig_alarm = 1; + break; + case SIGHUP: + /** + * we send the SIGHUP to all procs in the process-group + * this includes ourself + * + * make sure we only send it once and don't create a + * infinite loop + */ + if (!forwarded_sig_hup) { + handle_sig_hup = 1; + last_sighup_info = *si; + } else { + forwarded_sig_hup = 0; + } + break; + case SIGCHLD: + break; } } #elif defined(HAVE_SIGNAL) || defined(HAVE_SIGACTION) static void signal_handler(int sig) { switch (sig) { case SIGTERM: srv_shutdown = 1; break; - case SIGINT: + case SIGINT: if (graceful_shutdown) srv_shutdown = 1; - else graceful_shutdown = 1; + else graceful_shutdown = 1; break; case SIGALRM: handle_sig_alarm = 1; break; @@ -109,27 +156,26 @@ static void daemonize(void) { signal(SIGTSTP, SIG_IGN); #endif if (0 != fork()) exit(0); - + if (-1 == setsid()) exit(0); signal(SIGHUP, SIG_IGN); if (0 != fork()) exit(0); - + if (0 != chdir("/")) exit(0); - - umask(0); } #endif static server *server_init(void) { int i; - + FILE *frandom = NULL; + server *srv = calloc(1, sizeof(*srv)); assert(srv); #define CLEAN(x) \ srv->x = buffer_init(); - + CLEAN(response_header); CLEAN(parse_full_path); CLEAN(ts_debug_str); @@ -139,66 +185,84 @@ static server *server_init(void) { CLEAN(tmp_buf); srv->empty_string = buffer_init_string(""); CLEAN(cond_check_buf); - + CLEAN(srvconf.errorlog_file); + CLEAN(srvconf.breakagelog_file); CLEAN(srvconf.groupname); CLEAN(srvconf.username); CLEAN(srvconf.changeroot); CLEAN(srvconf.bindhost); CLEAN(srvconf.event_handler); CLEAN(srvconf.pid_file); - + CLEAN(tmp_chunk_len); #undef CLEAN - + #define CLEAN(x) \ srv->x = array_init(); - + CLEAN(config_context); CLEAN(config_touched); CLEAN(status); #undef CLEAN - + for (i = 0; i < FILE_CACHE_MAX; i++) { + srv->mtime_cache[i].mtime = (time_t)-1; srv->mtime_cache[i].str = buffer_init(); } - + + if ((NULL != (frandom = fopen("/dev/urandom", "rb")) || NULL != (frandom = fopen("/dev/random", "rb"))) + && 1 == fread(srv->entropy, sizeof(srv->entropy), 1, frandom)) { + unsigned int e; + memcpy(&e, srv->entropy, sizeof(e) < sizeof(srv->entropy) ? sizeof(e) : sizeof(srv->entropy)); + srand(e); + srv->is_real_entropy = 1; + } else { + unsigned int j; + srand(time(NULL) ^ getpid()); + srv->is_real_entropy = 0; + for (j = 0; j < sizeof(srv->entropy); j++) + srv->entropy[j] = rand(); + } + if (frandom) fclose(frandom); + srv->cur_ts = time(NULL); srv->startup_ts = srv->cur_ts; - + srv->conns = calloc(1, sizeof(*srv->conns)); assert(srv->conns); - + srv->joblist = calloc(1, sizeof(*srv->joblist)); assert(srv->joblist); - + srv->fdwaitqueue = calloc(1, sizeof(*srv->fdwaitqueue)); assert(srv->fdwaitqueue); - + srv->srvconf.modules = array_init(); srv->srvconf.modules_dir = buffer_init_string(LIBRARY_DIR); srv->srvconf.network_backend = buffer_init(); srv->srvconf.upload_tempdirs = array_init(); - + srv->srvconf.reject_expect_100_with_417 = 1; + /* use syslog */ - srv->errorlog_fd = -1; - srv->errorlog_mode = ERRORLOG_STDERR; + srv->errorlog_fd = STDERR_FILENO; + srv->errorlog_mode = ERRORLOG_FD; srv->split_vals = array_init(); - + return srv; } static void server_free(server *srv) { size_t i; - + for (i = 0; i < FILE_CACHE_MAX; i++) { buffer_free(srv->mtime_cache[i].str); } - + #define CLEAN(x) \ buffer_free(srv->x); - + CLEAN(response_header); CLEAN(parse_full_path); CLEAN(ts_debug_str); @@ -208,8 +272,9 @@ static void server_free(server *srv) { CLEAN(tmp_buf); CLEAN(empty_string); CLEAN(cond_check_buf); - + CLEAN(srvconf.errorlog_file); + CLEAN(srvconf.breakagelog_file); CLEAN(srvconf.groupname); CLEAN(srvconf.username); CLEAN(srvconf.changeroot); @@ -217,7 +282,8 @@ static void server_free(server *srv) { CLEAN(srvconf.event_handler); CLEAN(srvconf.pid_file); CLEAN(srvconf.modules_dir); - + CLEAN(srvconf.network_backend); + CLEAN(tmp_chunk_len); #undef CLEAN @@ -225,48 +291,64 @@ static void server_free(server *srv) { fdevent_unregister(srv->ev, srv->fd); #endif fdevent_free(srv->ev); - + free(srv->conns); - + if (srv->config_storage) { for (i = 0; i < srv->config_context->used; i++) { specific_config *s = srv->config_storage[i]; if (!s) continue; - + buffer_free(s->document_root); buffer_free(s->server_name); buffer_free(s->server_tag); buffer_free(s->ssl_pemfile); buffer_free(s->ssl_ca_file); + buffer_free(s->ssl_cipher_list); + buffer_free(s->ssl_dh_file); + buffer_free(s->ssl_ec_curve); buffer_free(s->error_handler); buffer_free(s->errorfile_prefix); array_free(s->mimetypes); - + buffer_free(s->ssl_verifyclient_username); +#ifdef USE_OPENSSL + SSL_CTX_free(s->ssl_ctx); +#endif free(s); } free(srv->config_storage); srv->config_storage = NULL; } - + #define CLEAN(x) \ array_free(srv->x); - + CLEAN(config_context); CLEAN(config_touched); CLEAN(status); + CLEAN(srvconf.upload_tempdirs); #undef CLEAN - + joblist_free(srv, srv->joblist); fdwaitqueue_free(srv, srv->fdwaitqueue); - + if (srv->stat_cache) { stat_cache_free(srv->stat_cache); } array_free(srv->srvconf.modules); array_free(srv->split_vals); - + +#ifdef USE_OPENSSL + if (srv->ssl_is_init) { + CRYPTO_cleanup_all_ex_data(); + ERR_free_strings(); + ERR_remove_state(0); + EVP_cleanup(); + } +#endif + free(srv); } @@ -276,21 +358,166 @@ static void show_version (void) { #else # define TEXT_SSL #endif - char *b = PACKAGE_NAME "-" PACKAGE_VERSION TEXT_SSL \ + char *b = PACKAGE_DESC TEXT_SSL \ " - a light and fast webserver\n" \ "Build-Date: " __DATE__ " " __TIME__ "\n"; ; -#undef TEXT_SSL +#undef TEXT_SSL write(STDOUT_FILENO, b, strlen(b)); } +static void show_features (void) { + const char features[] = "" +#ifdef USE_SELECT + "\t+ select (generic)\n" +#else + "\t- select (generic)\n" +#endif +#ifdef USE_POLL + "\t+ poll (Unix)\n" +#else + "\t- poll (Unix)\n" +#endif +#ifdef USE_LINUX_SIGIO + "\t+ rt-signals (Linux 2.4+)\n" +#else + "\t- rt-signals (Linux 2.4+)\n" +#endif +#ifdef USE_LINUX_EPOLL + "\t+ epoll (Linux 2.6)\n" +#else + "\t- epoll (Linux 2.6)\n" +#endif +#ifdef USE_SOLARIS_DEVPOLL + "\t+ /dev/poll (Solaris)\n" +#else + "\t- /dev/poll (Solaris)\n" +#endif +#ifdef USE_SOLARIS_PORT + "\t+ eventports (Solaris)\n" +#else + "\t- eventports (Solaris)\n" +#endif +#ifdef USE_FREEBSD_KQUEUE + "\t+ kqueue (FreeBSD)\n" +#else + "\t- kqueue (FreeBSD)\n" +#endif +#ifdef USE_LIBEV + "\t+ libev (generic)\n" +#else + "\t- libev (generic)\n" +#endif + "\nNetwork handler:\n\n" +#if defined USE_LINUX_SENDFILE + "\t+ linux-sendfile\n" +#else + "\t- linux-sendfile\n" +#endif +#if defined USE_FREEBSD_SENDFILE + "\t+ freebsd-sendfile\n" +#else + "\t- freebsd-sendfile\n" +#endif +#if defined USE_SOLARIS_SENDFILEV + "\t+ solaris-sendfilev\n" +#else + "\t- solaris-sendfilev\n" +#endif +#if defined USE_WRITEV + "\t+ writev\n" +#else + "\t- writev\n" +#endif + "\t+ write\n" +#ifdef USE_MMAP + "\t+ mmap support\n" +#else + "\t- mmap support\n" +#endif + "\nFeatures:\n\n" +#ifdef HAVE_IPV6 + "\t+ IPv6 support\n" +#else + "\t- IPv6 support\n" +#endif +#if defined HAVE_ZLIB_H && defined HAVE_LIBZ + "\t+ zlib support\n" +#else + "\t- zlib support\n" +#endif +#if defined HAVE_BZLIB_H && defined HAVE_LIBBZ2 + "\t+ bzip2 support\n" +#else + "\t- bzip2 support\n" +#endif +#ifdef HAVE_LIBCRYPT + "\t+ crypt support\n" +#else + "\t- crypt support\n" +#endif +#ifdef USE_OPENSSL + "\t+ SSL Support\n" +#else + "\t- SSL Support\n" +#endif +#ifdef HAVE_LIBPCRE + "\t+ PCRE support\n" +#else + "\t- PCRE support\n" +#endif +#ifdef HAVE_MYSQL + "\t+ mySQL support\n" +#else + "\t- mySQL support\n" +#endif +#if defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER) + "\t+ LDAP support\n" +#else + "\t- LDAP support\n" +#endif +#ifdef HAVE_MEMCACHE_H + "\t+ memcached support\n" +#else + "\t- memcached support\n" +#endif +#ifdef HAVE_FAM_H + "\t+ FAM support\n" +#else + "\t- FAM support\n" +#endif +#ifdef HAVE_LUA_H + "\t+ LUA support\n" +#else + "\t- LUA support\n" +#endif +#ifdef HAVE_LIBXML_H + "\t+ xml support\n" +#else + "\t- xml support\n" +#endif +#ifdef HAVE_SQLITE3_H + "\t+ SQLite support\n" +#else + "\t- SQLite support\n" +#endif +#ifdef HAVE_GDBM_H + "\t+ GDBM support\n" +#else + "\t- GDBM support\n" +#endif + "\n"; + show_version(); + printf("\nEvent Handlers:\n\n%s", features); +} + static void show_help (void) { #ifdef USE_OPENSSL # define TEXT_SSL " (ssl)" #else # define TEXT_SSL #endif - char *b = PACKAGE_NAME "-" PACKAGE_VERSION TEXT_SSL " ("__DATE__ " " __TIME__ ")" \ + char *b = PACKAGE_DESC TEXT_SSL " ("__DATE__ " " __TIME__ ")" \ " - a light and fast webserver\n" \ "usage:\n" \ " -f <name> filename of the config-file\n" \ @@ -299,10 +526,11 @@ static void show_help (void) { " -t test the config-file, and exit\n" \ " -D don't go to background (default: go to background)\n" \ " -v show version\n" \ +" -V show compile-time features\n" \ " -h show this help\n" \ "\n" ; -#undef TEXT_SSL +#undef TEXT_SSL #undef TEXT_IPV6 write(STDOUT_FILENO, b, strlen(b)); } @@ -322,27 +550,27 @@ int main (int argc, char **argv) { #ifdef HAVE_GETRLIMIT struct rlimit rlim; #endif - + #ifdef USE_ALARM struct itimerval interval; - + interval.it_interval.tv_sec = 1; interval.it_interval.tv_usec = 0; interval.it_value.tv_sec = 1; interval.it_value.tv_usec = 0; #endif - - + + /* for nice %b handling in strfime() */ setlocale(LC_TIME, "C"); - + if (NULL == (srv = server_init())) { fprintf(stderr, "did this really happen?\n"); return -1; } - + /* init structs done */ - + srv->srvconf.port = 0; #ifdef HAVE_GETUID i_am_root = (getuid() == 0); @@ -350,11 +578,18 @@ int main (int argc, char **argv) { i_am_root = 0; #endif srv->srvconf.dont_daemonize = 0; - - while(-1 != (o = getopt(argc, argv, "f:m:hvDpt"))) { + + while(-1 != (o = getopt(argc, argv, "f:m:hvVDpt"))) { switch(o) { - case 'f': - if (config_read(srv, optarg)) { + case 'f': + if (srv->config_storage) { + log_error_write(srv, __FILE__, __LINE__, "s", + "Can only read one config file. Use the include command to use multiple config files."); + + server_free(srv); + return -1; + } + if (config_read(srv, optarg)) { server_free(srv); return -1; } @@ -366,27 +601,28 @@ int main (int argc, char **argv) { case 't': test_config = 1; break; case 'D': srv->srvconf.dont_daemonize = 1; break; case 'v': show_version(); return 0; + case 'V': show_features(); return 0; case 'h': show_help(); return 0; - default: + default: show_help(); server_free(srv); return -1; } } - + if (!srv->config_storage) { log_error_write(srv, __FILE__, __LINE__, "s", "No configuration available. Try using -f option."); - + server_free(srv); return -1; } - + if (print_config) { data_unset *dc = srv->config_context->data[0]; if (dc) { dc->print(dc, 0); - fprintf(stderr, "\n"); + fprintf(stdout, "\n"); } else { /* shouldn't happend */ fprintf(stderr, "global config not found\n"); @@ -401,62 +637,51 @@ int main (int argc, char **argv) { server_free(srv); return 0; } - + /* close stdin and stdout, as they are not needed */ - /* move stdin to /dev/null */ - if (-1 != (fd = open("/dev/null", O_RDONLY))) { - close(STDIN_FILENO); - dup2(fd, STDIN_FILENO); - close(fd); - } - - /* move stdout to /dev/null */ - if (-1 != (fd = open("/dev/null", O_WRONLY))) { - close(STDOUT_FILENO); - dup2(fd, STDOUT_FILENO); - close(fd); - } - + openDevNull(STDIN_FILENO); + openDevNull(STDOUT_FILENO); + if (0 != config_set_defaults(srv)) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "setting default values failed"); server_free(srv); return -1; } - + /* UID handling */ #ifdef HAVE_GETUID - if (!i_am_root && (geteuid() == 0 || getegid() == 0)) { + if (!i_am_root && issetugid()) { /* we are setuid-root */ - - log_error_write(srv, __FILE__, __LINE__, "s", + + log_error_write(srv, __FILE__, __LINE__, "s", "Are you nuts ? Don't apply a SUID bit to this binary"); - + server_free(srv); return -1; } #endif - + /* check document-root */ if (srv->config_storage[0]->document_root->used <= 1) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "document-root is not set\n"); - + server_free(srv); - + return -1; } - + if (plugins_load(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "loading plugins finally failed"); - + plugins_free(srv); server_free(srv); - + return -1; } - + /* open pid file BEFORE chroot */ if (srv->srvconf.pid_file->used) { if (-1 == (pid_fd = open(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { @@ -466,18 +691,18 @@ int main (int argc, char **argv) { "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); return -1; } - + if (0 != stat(srv->srvconf.pid_file->ptr, &st)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "stating existing pid-file failed:", srv->srvconf.pid_file, strerror(errno)); } - + if (!S_ISREG(st.st_mode)) { log_error_write(srv, __FILE__, __LINE__, "sb", "pid-file exists and isn't regular file:", srv->srvconf.pid_file); return -1; } - + if (-1 == (pid_fd = open(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { log_error_write(srv, __FILE__, __LINE__, "sbs", "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); @@ -504,7 +729,7 @@ int main (int argc, char **argv) { #ifdef HAVE_VALGRIND_VALGRIND_H if (RUNNING_ON_VALGRIND) use_rlimit = 0; #endif - + #ifdef HAVE_GETRLIMIT if (0 != getrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, @@ -512,13 +737,13 @@ int main (int argc, char **argv) { strerror(errno)); return -1; } - + if (use_rlimit && srv->srvconf.max_fds) { /* set rlimits */ - + rlim.rlim_cur = srv->srvconf.max_fds; rlim.rlim_max = srv->srvconf.max_fds; - + if (0 != setrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't set 'max filedescriptors'", @@ -527,11 +752,8 @@ int main (int argc, char **argv) { } } - /* #372: solaris need some fds extra for devpoll */ - if (rlim.rlim_cur > 10) rlim.rlim_cur -= 10; - if (srv->event_handler == FDEVENT_HANDLER_SELECT) { - srv->max_fds = rlim.rlim_cur < FD_SETSIZE - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; + srv->max_fds = rlim.rlim_cur < ((int)FD_SETSIZE) - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { srv->max_fds = rlim.rlim_cur; } @@ -544,34 +766,34 @@ int main (int argc, char **argv) { #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* don't raise the limit above FD_SET_SIZE */ - if (srv->max_fds > FD_SETSIZE - 200) { - log_error_write(srv, __FILE__, __LINE__, "sd", + if (srv->max_fds > ((int)FD_SETSIZE) - 200) { + log_error_write(srv, __FILE__, __LINE__, "sd", "can't raise max filedescriptors above", FD_SETSIZE - 200, "if event-handler is 'select'. Use 'poll' or something else or reduce server.max-fds."); return -1; } } - + #ifdef HAVE_PWD_H /* set user and group */ if (srv->srvconf.username->used) { if (NULL == (pwd = getpwnam(srv->srvconf.username->ptr))) { - log_error_write(srv, __FILE__, __LINE__, "sb", + log_error_write(srv, __FILE__, __LINE__, "sb", "can't find username", srv->srvconf.username); return -1; } - + if (pwd->pw_uid == 0) { log_error_write(srv, __FILE__, __LINE__, "s", "I will not set uid to 0\n"); return -1; } } - + if (srv->srvconf.groupname->used) { if (NULL == (grp = getgrnam(srv->srvconf.groupname->ptr))) { - log_error_write(srv, __FILE__, __LINE__, "sb", + log_error_write(srv, __FILE__, __LINE__, "sb", "can't find groupname", srv->srvconf.groupname); return -1; } @@ -581,15 +803,28 @@ int main (int argc, char **argv) { return -1; } } -#endif +#endif /* we need root-perms for port < 1024 */ if (0 != network_init(srv)) { plugins_free(srv); server_free(srv); - + return -1; } -#ifdef HAVE_CHROOT +#ifdef HAVE_PWD_H + /* + * Change group before chroot, when we have access + * to /etc/group + * */ + if (NULL != grp) { + setgid(grp->gr_gid); + setgroups(0, NULL); + if (srv->srvconf.username->used) { + initgroups(srv->srvconf.username->ptr, grp->gr_gid); + } + } +#endif +#ifdef HAVE_CHROOT if (srv->srvconf.changeroot->used) { tzset(); @@ -605,15 +840,14 @@ int main (int argc, char **argv) { #endif #ifdef HAVE_PWD_H /* drop root privs */ - if (srv->srvconf.groupname->used) { - setgid(grp->gr_gid); - setgroups(0, NULL); + if (NULL != pwd) { + setuid(pwd->pw_uid); } - if (srv->srvconf.username->used && srv->srvconf.groupname->used) - initgroups(srv->srvconf.username->ptr, grp->gr_gid); - if (srv->srvconf.username->used) setuid(pwd->pw_uid); #endif -#ifdef HAVE_PRCTL +#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE) + /** + * on IRIX 6.5.30 they have prctl() but no DUMPABLE + */ if (srv->srvconf.enable_cores) { prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); } @@ -628,8 +862,24 @@ int main (int argc, char **argv) { return -1; } + /** + * we are not root can can't increase the fd-limit, but we can reduce it + */ + if (srv->srvconf.max_fds && srv->srvconf.max_fds < rlim.rlim_cur) { + /* set rlimits */ + + rlim.rlim_cur = srv->srvconf.max_fds; + + if (0 != setrlimit(RLIMIT_NOFILE, &rlim)) { + log_error_write(srv, __FILE__, __LINE__, + "ss", "couldn't set 'max filedescriptors'", + strerror(errno)); + return -1; + } + } + if (srv->event_handler == FDEVENT_HANDLER_SELECT) { - srv->max_fds = rlim.rlim_cur < FD_SETSIZE - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; + srv->max_fds = rlim.rlim_cur < ((int)FD_SETSIZE) - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { srv->max_fds = rlim.rlim_cur; } @@ -643,71 +893,83 @@ int main (int argc, char **argv) { #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* don't raise the limit above FD_SET_SIZE */ - if (srv->max_fds > FD_SETSIZE - 200) { - log_error_write(srv, __FILE__, __LINE__, "sd", + if (srv->max_fds > ((int)FD_SETSIZE) - 200) { + log_error_write(srv, __FILE__, __LINE__, "sd", "can't raise max filedescriptors above", FD_SETSIZE - 200, "if event-handler is 'select'. Use 'poll' or something else or reduce server.max-fds."); return -1; } } - + if (0 != network_init(srv)) { plugins_free(srv); server_free(srv); - + return -1; } } /* set max-conns */ - if (srv->srvconf.max_conns > srv->max_fds) { - /* we can't have more connections than max-fds */ - srv->max_conns = srv->max_fds; + if (srv->srvconf.max_conns > srv->max_fds/2) { + /* we can't have more connections than max-fds/2 */ + log_error_write(srv, __FILE__, __LINE__, "sdd", "can't have more connections than fds/2: ", srv->srvconf.max_conns, srv->max_fds); + srv->max_conns = srv->max_fds/2; } else if (srv->srvconf.max_conns) { /* otherwise respect the wishes of the user */ srv->max_conns = srv->srvconf.max_conns; } else { - /* or use the default */ - srv->max_conns = srv->max_fds; + /* or use the default: we really don't want to hit max-fds */ + srv->max_conns = srv->max_fds/3; } - + if (HANDLER_GO_ON != plugins_call_init(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Initialization of plugins failed. Going down."); - + plugins_free(srv); network_close(srv); server_free(srv); - + return -1; } -#ifdef HAVE_FORK +#ifdef HAVE_FORK /* network is up, let's deamonize ourself */ if (srv->srvconf.dont_daemonize == 0) daemonize(); #endif srv->gid = getgid(); srv->uid = getuid(); - + /* write pid file */ if (pid_fd != -1) { buffer_copy_long(srv->tmp_buf, getpid()); - buffer_append_string(srv->tmp_buf, "\n"); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\n")); write(pid_fd, srv->tmp_buf->ptr, srv->tmp_buf->used - 1); close(pid_fd); pid_fd = -1; } - + + /* Close stderr ASAP in the child process to make sure that nothing + * is being written to that fd which may not be valid anymore. */ + if (-1 == log_error_open(srv)) { + log_error_write(srv, __FILE__, __LINE__, "s", "Opening errorlog failed. Going down."); + + plugins_free(srv); + network_close(srv); + server_free(srv); + return -1; + } + if (HANDLER_GO_ON != plugins_call_set_defaults(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration of plugins failed. Going down."); - + plugins_free(srv); network_close(srv); server_free(srv); - + return -1; } - + /* dump unused config-keys */ for (i = 0; i < srv->config_context->used; i++) { array *config = ((data_config *)srv->config_context->data[i])->value; @@ -715,43 +977,42 @@ int main (int argc, char **argv) { for (j = 0; config && j < config->used; j++) { data_unset *du = config->data[j]; - + /* all var.* is known as user defined variable */ if (strncmp(du->key->ptr, "var.", sizeof("var.") - 1) == 0) { continue; } if (NULL == array_get_element(srv->config_touched, du->key->ptr)) { - log_error_write(srv, __FILE__, __LINE__, "sbs", + log_error_write(srv, __FILE__, __LINE__, "sbs", "WARNING: unknown config-key:", du->key, "(ignored)"); } } } - + + if (srv->config_unsupported) { + log_error_write(srv, __FILE__, __LINE__, "s", + "Configuration contains unsupported keys. Going down."); + } + if (srv->config_deprecated) { - log_error_write(srv, __FILE__, __LINE__, "s", + log_error_write(srv, __FILE__, __LINE__, "s", "Configuration contains deprecated keys. Going down."); - - plugins_free(srv); - network_close(srv); - server_free(srv); - - return -1; } - - if (-1 == log_error_open(srv)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "opening errorlog failed, dying"); - + + if (srv->config_unsupported || srv->config_deprecated) { plugins_free(srv); network_close(srv); server_free(srv); + return -1; } - - + + + + #ifdef HAVE_SIGACTION memset(&act, 0, sizeof(act)); act.sa_handler = SIG_IGN; @@ -771,7 +1032,7 @@ int main (int argc, char **argv) { sigaction(SIGHUP, &act, NULL); sigaction(SIGALRM, &act, NULL); sigaction(SIGCHLD, &act, NULL); - + #elif defined(HAVE_SIGNAL) /* ignore the SIGPIPE from sendfile() */ signal(SIGPIPE, SIG_IGN); @@ -782,25 +1043,25 @@ int main (int argc, char **argv) { signal(SIGCHLD, signal_handler); signal(SIGINT, signal_handler); #endif - + #ifdef USE_ALARM signal(SIGALRM, signal_handler); - + /* setup periodic timer (1 second) */ if (setitimer(ITIMER_REAL, &interval, NULL)) { log_error_write(srv, __FILE__, __LINE__, "s", "setting timer failed"); return -1; } - + getitimer(ITIMER_REAL, &interval); #endif -#ifdef HAVE_FORK +#ifdef HAVE_FORK /* start watcher and workers */ num_childs = srv->srvconf.max_worker; if (num_childs > 0) { int child = 0; - while (!child && !srv_shutdown) { + while (!child && !srv_shutdown && !graceful_shutdown) { if (num_childs > 0) { switch (fork()) { case -1: @@ -814,24 +1075,80 @@ int main (int argc, char **argv) { } } else { int status; - wait(&status); - num_childs++; + + if (-1 != wait(&status)) { + /** + * one of our workers went away + */ + num_childs++; + } else { + switch (errno) { + case EINTR: + /** + * if we receive a SIGHUP we have to close our logs ourself as we don't + * have the mainloop who can help us here + */ + if (handle_sig_hup) { + handle_sig_hup = 0; + + log_error_cycle(srv); + + /** + * forward to all procs in the process-group + * + * we also send it ourself + */ + if (!forwarded_sig_hup) { + forwarded_sig_hup = 1; + kill(0, SIGHUP); + } + } + break; + default: + break; + } + } } } - if (srv_shutdown) { - kill(0, SIGTERM); + + /** + * for the parent this is the exit-point + */ + if (!child) { + /** + * kill all children too + */ + if (graceful_shutdown) { + kill(0, SIGINT); + } else if (srv_shutdown) { + kill(0, SIGTERM); + } + + log_error_close(srv); + network_close(srv); + connections_free(srv); + plugins_free(srv); + server_free(srv); + return 0; } - if (!child) return 0; } #endif - if (NULL == (srv->ev = fdevent_init(srv->max_fds + 1, srv->event_handler))) { + if (NULL == (srv->ev = fdevent_init(srv, srv->max_fds + 1, srv->event_handler))) { log_error_write(srv, __FILE__, __LINE__, "s", "fdevent_init failed"); return -1; } - /* - * kqueue() is called here, select resets its internals, + + /* libev backend overwrites our SIGCHLD handler and calls waitpid on SIGCHLD; we want our own SIGCHLD handling. */ +#ifdef HAVE_SIGACTION + sigaction(SIGCHLD, &act, NULL); +#elif defined(HAVE_SIGNAL) + signal(SIGCHLD, signal_handler); +#endif + + /* + * kqueue() is called here, select resets its internals, * all server sockets get their handlers * * */ @@ -839,7 +1156,7 @@ int main (int argc, char **argv) { plugins_free(srv); network_close(srv); server_free(srv); - + return -1; } @@ -854,6 +1171,8 @@ int main (int argc, char **argv) { /* setup FAM */ if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { if (0 != FAMOpen2(srv->stat_cache->fam, "lighttpd")) { + log_error_write(srv, __FILE__, __LINE__, "s", + "could not open a fam connection, dieing."); return -1; } #ifdef HAVE_FAMNOEXISTS @@ -862,7 +1181,7 @@ int main (int argc, char **argv) { srv->stat_cache->fam_fcce_ndx = -1; fdevent_register(srv->ev, FAMCONNECTION_GETFD(srv->stat_cache->fam), stat_cache_handle_fdevent, NULL); - fdevent_event_add(srv->ev, &(srv->stat_cache->fam_fcce_ndx), FAMCONNECTION_GETFD(srv->stat_cache->fam), FDEVENT_IN); + fdevent_event_set(srv->ev, &(srv->stat_cache->fam_fcce_ndx), FAMCONNECTION_GETFD(srv->stat_cache->fam), FDEVENT_IN); } #endif @@ -884,16 +1203,16 @@ int main (int argc, char **argv) { int n; size_t ndx; time_t min_ts; - + if (handle_sig_hup) { handler_t r; - + /* reset notification */ handle_sig_hup = 0; - - + + /* cycle logfiles */ - + switch(r = plugins_call_handle_sighup(srv)) { case HANDLER_GO_ON: break; @@ -901,30 +1220,41 @@ int main (int argc, char **argv) { log_error_write(srv, __FILE__, __LINE__, "sd", "sighup-handler return with an error", r); break; } - + if (-1 == log_error_cycle(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "cycling errorlog failed, dying"); - + return -1; + } else { +#ifdef HAVE_SIGACTION + log_error_write(srv, __FILE__, __LINE__, "sdsd", + "logfiles cycled UID =", + last_sighup_info.si_uid, + "PID =", + last_sighup_info.si_pid); +#else + log_error_write(srv, __FILE__, __LINE__, "s", + "logfiles cycled"); +#endif } } - + if (handle_sig_alarm) { /* a new second */ - + #ifdef USE_ALARM /* reset notification */ handle_sig_alarm = 0; #endif - + /* get current time */ min_ts = time(NULL); - + if (min_ts != srv->cur_ts) { int cs = 0; connections *conns = srv->conns; handler_t r; - + switch(r = plugins_call_handle_trigger(srv)) { case HANDLER_GO_ON: break; @@ -935,21 +1265,21 @@ int main (int argc, char **argv) { log_error_write(srv, __FILE__, __LINE__, "d", r); break; } - + /* trigger waitpid */ srv->cur_ts = min_ts; - - /* cleanup stat-cache */ + + /* cleanup stat-cache */ stat_cache_trigger_cleanup(srv); /** - * check all connections for timeouts - * + * check all connections for timeouts + * */ for (ndx = 0; ndx < conns->used; ndx++) { int changed = 0; connection *con; int t_diff; - + con = conns->ptr[ndx]; if (con->state == CON_STATE_READ || @@ -958,17 +1288,17 @@ int main (int argc, char **argv) { if (srv->cur_ts - con->read_idle_ts > con->conf.max_read_idle) { /* time - out */ #if 0 - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - read-timeout:", con->fd); #endif connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } else { - if (srv->cur_ts - con->read_idle_ts > con->conf.max_keep_alive_idle) { + if (srv->cur_ts - con->read_idle_ts > con->keep_alive_idle) { /* time - out */ #if 0 - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - read-timeout:", con->fd); #endif connection_set_state(srv, con, CON_STATE_ERROR); @@ -976,20 +1306,20 @@ int main (int argc, char **argv) { } } } - + if ((con->state == CON_STATE_WRITE) && - (con->write_request_ts != 0)) { + (con->write_request_ts != 0)) { #if 0 if (srv->cur_ts - con->write_request_ts > 60) { - log_error_write(srv, __FILE__, __LINE__, "sdd", + log_error_write(srv, __FILE__, __LINE__, "sdd", "connection closed - pre-write-request-timeout:", con->fd, srv->cur_ts - con->write_request_ts); } #endif - + if (srv->cur_ts - con->write_request_ts > con->conf.max_write_idle) { /* time - out */ -#if 1 - log_error_write(srv, __FILE__, __LINE__, "sbsosds", + if (con->conf.log_timeouts) { + log_error_write(srv, __FILE__, __LINE__, "sbsosds", "NOTE: a request for", con->request.uri, "timed out after writing", @@ -997,42 +1327,47 @@ int main (int argc, char **argv) { "bytes. We waited", (int)con->conf.max_write_idle, "seconds. If this a problem increase server.max-write-idle"); -#endif + } connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } + + if (con->state == CON_STATE_CLOSE && (srv->cur_ts - con->close_timeout_ts > HTTP_LINGER_TIMEOUT)) { + changed = 1; + } + /* we don't like div by zero */ if (0 == (t_diff = srv->cur_ts - con->connection_start)) t_diff = 1; - - if (con->traffic_limit_reached && - (con->conf.kbytes_per_second == 0 || + + if (con->traffic_limit_reached && + (con->conf.kbytes_per_second == 0 || ((con->bytes_written / t_diff) < con->conf.kbytes_per_second * 1024))) { /* enable connection again */ con->traffic_limit_reached = 0; - + changed = 1; } - + if (changed) { connection_state_machine(srv, con); } con->bytes_written_cur_second = 0; *(con->conf.global_bytes_per_second_cnt_ptr) = 0; - + #if 0 if (cs == 0) { fprintf(stderr, "connection-state: "); cs = 1; } - + fprintf(stderr, "c[%d,%d]: %s ", con->fd, con->fcgi.fd, connection_get_state(con->state)); #endif } - + if (cs == 1) fprintf(stderr, "\n"); } } @@ -1040,25 +1375,25 @@ int main (int argc, char **argv) { if (srv->sockets_disabled) { /* our server sockets are disabled, why ? */ - if ((srv->cur_fds + srv->want_fds < srv->max_fds * 0.8) && /* we have enough unused fds */ - (srv->conns->used < srv->max_conns * 0.9) && + if ((srv->cur_fds + srv->want_fds < srv->max_fds * 8 / 10) && /* we have enough unused fds */ + (srv->conns->used <= srv->max_conns * 9 / 10) && (0 == graceful_shutdown)) { for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; - fdevent_event_add(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN); + fdevent_event_set(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN); } - + log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets enabled again"); - + srv->sockets_disabled = 0; } } else { - if ((srv->cur_fds + srv->want_fds > srv->max_fds * 0.9) || /* out of fds */ - (srv->conns->used > srv->max_conns) || /* out of connections */ - (graceful_shutdown)) { /* graceful_shutdown */ + if ((srv->cur_fds + srv->want_fds > srv->max_fds * 9 / 10) || /* out of fds */ + (srv->conns->used >= srv->max_conns) || /* out of connections */ + (graceful_shutdown)) { /* graceful_shutdown */ /* disable server-fds */ - + for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; fdevent_event_del(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd); @@ -1075,17 +1410,30 @@ int main (int argc, char **argv) { srv_socket->fd = -1; /* network_close() will cleanup after us */ + + if (srv->srvconf.pid_file->used && + srv->srvconf.changeroot->used == 0) { + if (0 != unlink(srv->srvconf.pid_file->ptr)) { + if (errno != EACCES && errno != EPERM) { + log_error_write(srv, __FILE__, __LINE__, "sbds", + "unlink failed for:", + srv->srvconf.pid_file, + errno, + strerror(errno)); + } + } + } } } - + if (graceful_shutdown) { log_error_write(srv, __FILE__, __LINE__, "s", "[note] graceful shutdown started"); - } else if (srv->conns->used > srv->max_conns) { + } else if (srv->conns->used >= srv->max_conns) { log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets disabled, connection limit reached"); } else { log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets disabled, out-of-fds"); } - + srv->sockets_disabled = 1; } } @@ -1095,16 +1443,16 @@ int main (int argc, char **argv) { * we are ready to terminate without harming anyone */ srv_shutdown = 1; } - + /* we still have some fds to share */ - if (srv->want_fds) { + if (srv->want_fds) { /* check the fdwaitqueue for waiting fds */ int free_fds = srv->max_fds - srv->cur_fds - 16; connection *con; - + for (; free_fds > 0 && NULL != (con = fdwaitqueue_unshift(srv, srv->fdwaitqueue)); free_fds--) { connection_state_machine(srv, con); - + srv->want_fds--; } } @@ -1115,27 +1463,29 @@ int main (int argc, char **argv) { int fd_ndx; #if 0 if (n > 0) { - log_error_write(srv, __FILE__, __LINE__, "sd", + log_error_write(srv, __FILE__, __LINE__, "sd", "polls:", n); } -#endif +#endif fd_ndx = -1; do { fdevent_handler handler; void *context; handler_t r; - + fd_ndx = fdevent_event_next_fdndx (srv->ev, fd_ndx); + if (-1 == fd_ndx) break; /* not all fdevent handlers know how many fds got an event */ + revents = fdevent_event_get_revent (srv->ev, fd_ndx); fd = fdevent_event_get_fd (srv->ev, fd_ndx); handler = fdevent_get_handler(srv->ev, fd); context = fdevent_get_context(srv->ev, fd); - + /* connection_handle_fdevent needs a joblist_append */ #if 0 - log_error_write(srv, __FILE__, __LINE__, "sdd", + log_error_write(srv, __FILE__, __LINE__, "sdd", "event for", fd, revents); -#endif +#endif switch (r = (*handler)(srv, context, revents)) { case HANDLER_FINISHED: case HANDLER_GO_ON: @@ -1152,17 +1502,17 @@ int main (int argc, char **argv) { } } while (--n > 0); } else if (n < 0 && errno != EINTR) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "fdevent_poll failed:", + log_error_write(srv, __FILE__, __LINE__, "ss", + "fdevent_poll failed:", strerror(errno)); } - + for (ndx = 0; ndx < srv->joblist->used; ndx++) { connection *con = srv->joblist->ptr[ndx]; handler_t r; - + connection_state_machine(srv, con); - + switch(r = plugins_call_handle_joblist(srv, con)) { case HANDLER_FINISHED: case HANDLER_GO_ON: @@ -1171,32 +1521,44 @@ int main (int argc, char **argv) { log_error_write(srv, __FILE__, __LINE__, "d", r); break; } - + con->in_joblist = 0; } - + srv->joblist->used = 0; } - + if (srv->srvconf.pid_file->used && - srv->srvconf.changeroot->used == 0) { + srv->srvconf.changeroot->used == 0 && + 0 == graceful_shutdown) { if (0 != unlink(srv->srvconf.pid_file->ptr)) { if (errno != EACCES && errno != EPERM) { - log_error_write(srv, __FILE__, __LINE__, "sbds", - "unlink failed for:", + log_error_write(srv, __FILE__, __LINE__, "sbds", + "unlink failed for:", srv->srvconf.pid_file, errno, strerror(errno)); } } } - + +#ifdef HAVE_SIGACTION + log_error_write(srv, __FILE__, __LINE__, "sdsd", + "server stopped by UID =", + last_sigterm_info.si_uid, + "PID =", + last_sigterm_info.si_pid); +#else + log_error_write(srv, __FILE__, __LINE__, "s", + "server stopped"); +#endif + /* clean-up */ log_error_close(srv); network_close(srv); connections_free(srv); plugins_free(srv); server_free(srv); - + return 0; } |