static int connection_handle_write_prepare(server *srv, connection *con) { if (con->mode == DIRECT) { /* static files */ switch(con->request.http_method) { case HTTP_METHOD_GET: case HTTP_METHOD_POST: case HTTP_METHOD_HEAD: break; case HTTP_METHOD_OPTIONS: /* * 400 is coming from the request-parser BEFORE uri.path is set * 403 is from the response handler when noone else catched it * * */ if ((!con->http_status || con->http_status == 200) && !buffer_string_is_empty(con->uri.path) && con->uri.path->ptr[0] != '*') { response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; con->parsed_response &= ~HTTP_CONTENT_LENGTH; con->http_status = 200; con->file_finished = 1; chunkqueue_reset(con->write_queue); } break; default: if (0 == con->http_status) { con->http_status = 501; } break; } } if (con->http_status == 0) { con->http_status = 403; } switch(con->http_status) { case 204: /* class: header only */ case 205: case 304: /* disable chunked encoding again as we have no body */ con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; con->parsed_response &= ~HTTP_CONTENT_LENGTH; chunkqueue_reset(con->write_queue); con->file_finished = 1; break; default: /* class: header + body */ if (con->mode != DIRECT) break; /* only custom body for 4xx and 5xx */ if (con->http_status < 400 || con->http_status >= 600) break; con->file_finished = 0; connection_handle_errdoc_init(srv, con); /* try to send static errorfile */ if (!buffer_string_is_empty(con->conf.errorfile_prefix)) { stat_cache_entry *sce = NULL; buffer_copy_buffer(con->physical.path, con->conf.errorfile_prefix); buffer_append_int(con->physical.path, con->http_status); buffer_append_string_len(con->physical.path, CONST_STR_LEN(".html")); if (0 == http_chunk_append_file(srv, con, con->physical.path)) { con->file_finished = 1; if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); } } } if (!con->file_finished) { buffer *b; buffer_reset(con->physical.path); con->file_finished = 1; b = buffer_init(); /* build default error-page */ buffer_copy_string_len(b, CONST_STR_LEN( "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" " <head>\n" " <title>")); buffer_append_int(b, con->http_status); buffer_append_string_len(b, CONST_STR_LEN(" - ")); buffer_append_string(b, get_http_status_name(con->http_status)); buffer_append_string_len(b, CONST_STR_LEN( "</title>\n" " </head>\n" " <body>\n" " <h1>")); buffer_append_int(b, con->http_status); buffer_append_string_len(b, CONST_STR_LEN(" - ")); buffer_append_string(b, get_http_status_name(con->http_status)); buffer_append_string_len(b, CONST_STR_LEN("</h1>\n" " </body>\n" "</html>\n" )); (void)http_chunk_append_buffer(srv, con, b); buffer_free(b); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); } break; } /* Allow filter plugins to change response headers before they are written. */ switch(plugins_call_handle_response_start(srv, con)) { case HANDLER_GO_ON: case HANDLER_FINISHED: break; default: log_error_write(srv, __FILE__, __LINE__, "s", "response_start plugin failed"); return -1; } if (con->file_finished) { /* we have all the content and chunked encoding is not used, set a content-length */ if ((!(con->parsed_response & HTTP_CONTENT_LENGTH)) && (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0) { off_t qlen = chunkqueue_length(con->write_queue); /** * The Content-Length header only can be sent if we have content: * - HEAD doesn't have a content-body (but have a content-length) * - 1xx, 204 and 304 don't have a content-body (RFC 2616 Section 4.3) * * Otherwise generate a Content-Length header as chunked encoding is not * available */ if ((con->http_status >= 100 && con->http_status < 200) || con->http_status == 204 || con->http_status == 304) { data_string *ds; /* no Content-Body, no Content-Length */ if (NULL != (ds = (data_string*) array_get_element(con->response.headers, "Content-Length"))) { buffer_reset(ds->value); /* Headers with empty values are ignored for output */ } } else if (qlen > 0 || con->request.http_method != HTTP_METHOD_HEAD) { /* qlen = 0 is important for Redirects (301, ...) as they MAY have * a content. Browsers are waiting for a Content otherwise */ buffer_copy_int(srv->tmp_buf, qlen); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf)); } } } else { /** * the file isn't finished yet, but we have all headers * * to get keep-alive we either need: * - Content-Length: ... (HTTP/1.0 and HTTP/1.0) or * - Transfer-Encoding: chunked (HTTP/1.1) */ if (((con->parsed_response & HTTP_CONTENT_LENGTH) == 0) && ((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0)) { if (con->request.http_version == HTTP_VERSION_1_1) { off_t qlen = chunkqueue_length(con->write_queue); con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; if (qlen) { /* create initial Transfer-Encoding: chunked segment */ buffer *b = srv->tmp_chunk_len; buffer_string_set_length(b, 0); buffer_append_uint_hex(b, (uintmax_t)qlen); buffer_append_string_len(b, CONST_STR_LEN("\r\n")); chunkqueue_prepend_buffer(con->write_queue, b); chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("\r\n")); } } else { con->keep_alive = 0; } } /** * if the backend sent a Connection: close, follow the wish * * NOTE: if the backend sent Connection: Keep-Alive, but no Content-Length, we * will close the connection. That's fine. We can always decide the close * the connection * * FIXME: to be nice we should remove the Connection: ... */ if (con->parsed_response & HTTP_CONNECTION) { /* a subrequest disable keep-alive although the client wanted it */ if (con->keep_alive && !con->response.keep_alive) { con->keep_alive = 0; } } } if (con->request.http_method == HTTP_METHOD_HEAD) { /** * a HEAD request has the same as a GET * without the content */ con->file_finished = 1; chunkqueue_reset(con->write_queue); con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; } http_response_write_header(srv, con); return 0; }
int main (int argc, char **argv) { server *srv = NULL; int print_config = 0; int test_config = 0; int i_am_root; int o; int num_childs = 0; int pid_fd = -1, fd; size_t i; #ifdef HAVE_SIGACTION struct sigaction act; #endif #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); #else i_am_root = 0; #endif srv->srvconf.dont_daemonize = 0; #ifdef APP_IPKG if(!access("/etc/server.pem",F_OK)){ } else{ start_ssl(); } #endif while(-1 != (o = getopt(argc, argv, "f:m:hvVDpt"))) { switch(o) { 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; } break; case 'm': buffer_copy_string(srv->srvconf.modules_dir, optarg); break; case 'p': print_config = 1; break; 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: 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(stdout, "\n"); } else { /* shouldn't happend */ fprintf(stderr, "global config not found\n"); } } if (test_config) { printf("Syntax OK\n"); } if (test_config || print_config) { server_free(srv); return 0; } /* close stdin and stdout, as they are not needed */ openDevNull(STDIN_FILENO); openDevNull(STDOUT_FILENO); if (0 != config_set_defaults(srv)) { 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 && issetugid()) { /* we are setuid-root */ 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 (buffer_string_is_empty(srv->config_storage[0]->document_root)) { 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 (!buffer_string_is_empty(srv->srvconf.pid_file)) { 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))) { struct stat st; if (errno != EEXIST) { log_error_write(srv, __FILE__, __LINE__, "sbs", "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)); return -1; } } } if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* select limits itself * * as it is a hard limit and will lead to a segfault we add some safety * */ srv->max_fds = FD_SETSIZE - 200; } else { srv->max_fds = 4096; } if (i_am_root) { struct group *grp = NULL; struct passwd *pwd = NULL; int use_rlimit = 1; #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__, "ss", "couldn't get 'max filedescriptors'", 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'", strerror(errno)); return -1; } } if (srv->event_handler == FDEVENT_HANDLER_SELECT) { srv->max_fds = rlim.rlim_cur < ((int)FD_SETSIZE) - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { srv->max_fds = rlim.rlim_cur; } /* set core file rlimit, if enable_cores is set */ if (use_rlimit && srv->srvconf.enable_cores && getrlimit(RLIMIT_CORE, &rlim) == 0) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_CORE, &rlim); } #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* don't raise the limit above FD_SET_SIZE */ 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 (!buffer_string_is_empty(srv->srvconf.username)) { if (NULL == (pwd = getpwnam(srv->srvconf.username->ptr))) { 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 (!buffer_string_is_empty(srv->srvconf.groupname)) { if (NULL == (grp = getgrnam(srv->srvconf.groupname->ptr))) { log_error_write(srv, __FILE__, __LINE__, "sb", "can't find groupname", srv->srvconf.groupname); return -1; } if (grp->gr_gid == 0) { log_error_write(srv, __FILE__, __LINE__, "s", "I will not set gid to 0\n"); return -1; } } #endif /* we need root-perms for port < 1024 */ if (0 != network_init(srv)) { plugins_free(srv); server_free(srv); return -1; } #ifdef HAVE_PWD_H /* * Change group before chroot, when we have access * to /etc/group * */ if (NULL != grp) { if (-1 == setgid(grp->gr_gid)) { log_error_write(srv, __FILE__, __LINE__, "ss", "setgid failed: ", strerror(errno)); return -1; } if (-1 == setgroups(0, NULL)) { log_error_write(srv, __FILE__, __LINE__, "ss", "setgroups failed: ", strerror(errno)); return -1; } if (!buffer_string_is_empty(srv->srvconf.username)) { initgroups(srv->srvconf.username->ptr, grp->gr_gid); } } #endif #ifdef HAVE_CHROOT if (!buffer_string_is_empty(srv->srvconf.changeroot)) { tzset(); if (-1 == chroot(srv->srvconf.changeroot->ptr)) { log_error_write(srv, __FILE__, __LINE__, "ss", "chroot failed: ", strerror(errno)); return -1; } if (-1 == chdir("/")) { log_error_write(srv, __FILE__, __LINE__, "ss", "chdir failed: ", strerror(errno)); return -1; } } #endif #ifdef HAVE_PWD_H /* drop root privs */ if (NULL != pwd) { if (-1 == setuid(pwd->pw_uid)) { log_error_write(srv, __FILE__, __LINE__, "ss", "setuid failed: ", strerror(errno)); return -1; } } #endif #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); } #endif } else { #ifdef HAVE_GETRLIMIT if (0 != getrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't get 'max filedescriptors'", strerror(errno)); 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 < ((int)FD_SETSIZE) - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { srv->max_fds = rlim.rlim_cur; } /* set core file rlimit, if enable_cores is set */ if (srv->srvconf.enable_cores && getrlimit(RLIMIT_CORE, &rlim) == 0) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_CORE, &rlim); } #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* don't raise the limit above FD_SET_SIZE */ 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/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: 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 /* network is up, let's deamonize ourself */ if (srv->srvconf.dont_daemonize == 0) daemonize(); #endif #ifdef HAVE_SIGACTION memset(&act, 0, sizeof(act)); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, NULL); sigaction(SIGUSR1, &act, NULL); # if defined(SA_SIGINFO) act.sa_sigaction = sigaction_handler; sigemptyset(&act.sa_mask); act.sa_flags = SA_SIGINFO; # else act.sa_handler = signal_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; # endif sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); 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); signal(SIGUSR1, SIG_IGN); signal(SIGALRM, signal_handler); signal(SIGTERM, signal_handler); signal(SIGHUP, signal_handler); signal(SIGCHLD, signal_handler); signal(SIGINT, signal_handler); #endif #if EMBEDDED_EANBLE sigset_t sigs_to_catch; /* set the signal handler */ sigemptyset(&sigs_to_catch); sigaddset(&sigs_to_catch, SIGTERM); sigaddset(&sigs_to_catch, SIGUSR2); sigprocmask(SIG_UNBLOCK, &sigs_to_catch, NULL); signal(SIGTERM, sigaction_handler); //- 20121108 Sungmin add signal(SIGUSR2, sigaction_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 srv->gid = getgid(); srv->uid = getuid(); /* write pid file */ if (pid_fd != -1) { buffer_copy_int(srv->tmp_buf, getpid()); buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\n")); if (-1 == write_all(pid_fd, CONST_BUF_LEN(srv->tmp_buf))) { log_error_write(srv, __FILE__, __LINE__, "ss", "Couldn't write pid file:", strerror(errno)); close(pid_fd); return -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; } //- Sungmin add if (-1 == log_sys_open(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Opening syslog failed. Going down."); plugins_free(srv); network_close(srv); server_free(srv); return -1; } #if EMBEDDED_EANBLE #ifndef APP_IPKG buffer_copy_string( srv->last_login_info, nvram_get_webdav_last_login_info() ); buffer_copy_string( srv->cur_login_info, nvram_get_webdav_last_login_info() ); #else char *last_login_info = nvram_get_webdav_last_login_info(); fprintf(stderr,"last_login_info=%s\n",last_login_info); if(last_login_info == NULL || *last_login_info == '(') { fprintf(stderr,"111\n"); } else { buffer_copy_string( srv->last_login_info, last_login_info); buffer_copy_string( srv->cur_login_info, last_login_info); free(last_login_info); } #endif #else buffer_copy_string( srv->last_login_info, "admin>2012/08/08 18:28:28>100.100.100.100" ); buffer_copy_string( srv->cur_login_info, "admin>2012/08/08 18:28:28>100.100.100.100" ); #endif ////////////////////////////////////////////////////////////////////////////////////////// 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; size_t j; 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", "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", "Configuration contains deprecated keys. Going down."); } if (srv->config_unsupported || srv->config_deprecated) { plugins_free(srv); network_close(srv); server_free(srv); return -1; } #ifdef HAVE_FORK /* start watcher and workers */ num_childs = srv->srvconf.max_worker; if (num_childs > 0) { int child = 0; while (!child && !srv_shutdown && !graceful_shutdown) { if (num_childs > 0) { switch (fork()) { case -1: return -1; case 0: child = 1; break; default: num_childs--; break; } } else { int status; 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; } } } } /** * 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; } } #endif 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; } /* 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 * * */ if (0 != network_register_fdevents(srv)) { plugins_free(srv); network_close(srv); server_free(srv); return -1; } /* might fail if user is using fam (not gamin) and famd isn't running */ if (NULL == (srv->stat_cache = stat_cache_init())) { log_error_write(srv, __FILE__, __LINE__, "s", "stat-cache could not be setup, dieing."); return -1; } #ifdef HAVE_FAM_H /* 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 FAMNoExists(&srv->stat_cache->fam); #endif fdevent_register(srv->ev, FAMCONNECTION_GETFD(&srv->stat_cache->fam), stat_cache_handle_fdevent, NULL); fdevent_event_set(srv->ev, &(srv->stat_cache->fam_fcce_ndx), FAMCONNECTION_GETFD(&srv->stat_cache->fam), FDEVENT_IN); } #endif /* get the current number of FDs */ srv->cur_fds = open("/dev/null", O_RDONLY); close(srv->cur_fds); for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; if (-1 == fdevent_fcntl_set(srv->ev, srv_socket->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed:", strerror(errno)); return -1; } } /* main-loop */ while (!srv_shutdown) { 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; default: 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) { #ifdef DEBUG_CONNECTION_STATES int cs = 0; #endif connections *conns = srv->conns; handler_t r; switch(r = plugins_call_handle_trigger(srv)) { case HANDLER_GO_ON: break; case HANDLER_ERROR: log_error_write(srv, __FILE__, __LINE__, "s", "one of the triggers failed"); break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } /* trigger waitpid */ srv->cur_ts = min_ts; /* cleanup stat-cache */ stat_cache_trigger_cleanup(srv); /** * 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 || con->state == CON_STATE_READ_POST) { if (con->request_count == 1 || con->state == CON_STATE_READ_POST) { if (srv->cur_ts - con->read_idle_ts > con->conf.max_read_idle) { /* time - out */ if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - read timeout:", con->fd); } connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } else { if (srv->cur_ts - con->read_idle_ts > con->keep_alive_idle) { /* time - out */ if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - keep-alive timeout:", con->fd); } connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } } if ((con->state == CON_STATE_WRITE) && (con->write_request_ts != 0)) { #if 0 if (srv->cur_ts - con->write_request_ts > 60) { 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 (con->conf.log_timeouts) { log_error_write(srv, __FILE__, __LINE__, "sbsosds", "NOTE: a request for", con->request.uri, "timed out after writing", con->bytes_written, "bytes. We waited", (int)con->conf.max_write_idle, "seconds. If this a problem increase server.max-write-idle"); } 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 || ((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 DEBUG_CONNECTION_STATES 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 } #ifdef DEBUG_CONNECTION_STATES if (cs == 1) fprintf(stderr, "\n"); #endif } } if (srv->sockets_disabled) { /* our server sockets are disabled, why ? */ 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_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 * 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); if (graceful_shutdown) { /* we don't want this socket anymore, * * closing it right away will make it possible for * the next lighttpd to take over (graceful restart) * */ fdevent_unregister(srv->ev, srv_socket->fd); close(srv_socket->fd); srv_socket->fd = -1; /* network_close() will cleanup after us */ if (!buffer_string_is_empty(srv->srvconf.pid_file) && buffer_string_is_empty(srv->srvconf.changeroot)) { 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) { 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; } } if (graceful_shutdown && srv->conns->used == 0) { /* we are in graceful shutdown phase and all connections are closed * we are ready to terminate without harming anyone */ srv_shutdown = 1; } /* we still have some fds to share */ 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--; } } if ((n = fdevent_poll(srv->ev, 1000)) > 0) { /* n is the number of events */ int revents; int fd_ndx; #if 0 if (n > 0) { log_error_write(srv, __FILE__, __LINE__, "sd", "polls:", n); } #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", "event for", fd, revents); #endif switch (r = (*handler)(srv, context, revents)) { case HANDLER_FINISHED: case HANDLER_GO_ON: case HANDLER_WAIT_FOR_EVENT: case HANDLER_WAIT_FOR_FD: break; case HANDLER_ERROR: /* should never happen */ SEGFAULT(); break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } } while (--n > 0); } else if (n < 0 && errno != EINTR) { 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: break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } con->in_joblist = 0; } srv->joblist->used = 0; } if (!buffer_string_is_empty(srv->srvconf.pid_file) && buffer_string_is_empty(srv->srvconf.changeroot) && 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:", 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; }