From a9970fec23d2a7bbd7f8e4805133d84fb62cb68e Mon Sep 17 00:00:00 2001 From: Glenn Strauss Date: Mon, 19 Jun 2017 23:00:45 -0400 Subject: [PATCH] [core] consolidate fork()/execve() code (#1393) (refactoring work to address issue #1393) x-ref: "access log pipe writer should restart child process if it exits" https://redmine.lighttpd.net/issues/1393 --- src/fdevent.c | 193 ++++++++++++++++++++++++++++++++++++++++++ src/fdevent.h | 6 ++ src/log.c | 156 ++++++---------------------------- src/log.h | 7 -- src/mod_accesslog.c | 11 ++- src/mod_cgi.c | 101 +++++----------------- src/mod_fastcgi.c | 123 +++++++-------------------- src/mod_rrdtool.c | 103 +++++----------------- src/mod_scgi.c | 114 ++++++------------------- src/mod_ssi.c | 43 +++------- src/server.c | 38 +++++++-- src/test_configfile.c | 24 ++++++ 12 files changed, 405 insertions(+), 514 deletions(-) diff --git a/src/fdevent.c b/src/fdevent.c index 626ae27d..eb658f90 100644 --- a/src/fdevent.c +++ b/src/fdevent.c @@ -373,6 +373,31 @@ int fdevent_open_cloexec(const char *pathname, int flags, mode_t mode) { } +int fdevent_open_devnull(void) { + #if defined(_WIN32) + return fdevent_open_cloexec("nul", O_RDWR, 0); + #else + return fdevent_open_cloexec("/dev/null", O_RDWR, 0); + #endif +} + + +int fdevent_open_dirname(char *path) { + /*(handle special cases of no dirname or dirname is root directory)*/ + char * const c = strrchr(path, '/'); + const char * const dname = (NULL != c ? c == path ? "/" : path : "."); + int dfd; + int flags = O_RDONLY; + #ifdef O_DIRECTORY + flags |= O_DIRECTORY; + #endif + if (NULL != c) *c = '\0'; + dfd = fdevent_open_cloexec(dname, flags, 0); + if (NULL != c) *c = '/'; + return dfd; +} + + int fdevent_accept_listenfd(int listenfd, struct sockaddr *addr, size_t *addrlen) { int fd; socklen_t len = (socklen_t) *addrlen; @@ -401,6 +426,174 @@ int fdevent_event_next_fdndx(fdevents *ev, int ndx) { } +#ifdef FD_CLOEXEC +static int fdevent_dup2_close_clrfd_cloexec(int oldfd, int newfd) { + if (oldfd >= 0) { + if (oldfd != newfd) { + force_assert(oldfd > STDERR_FILENO); + if (newfd != dup2(oldfd, newfd)) return -1; + } + else { + fdevent_clrfd_cloexec(newfd); + } + } + return newfd; +} +#else +static int fdevent_dup2_close_clrfd_cloexec(int oldfd, int newfd, int reuse) { + if (oldfd >= 0) { + if (oldfd != newfd) { + force_assert(oldfd > STDERR_FILENO); + if (newfd != dup2(oldfd, newfd)) return -1; + if (!reuse) close(oldfd); + } + } + return newfd; +} +#endif + + +int fdevent_set_stdin_stdout_stderr(int fdin, int fdout, int fderr) { + #ifdef FD_CLOEXEC + if (STDIN_FILENO != fdevent_dup2_close_clrfd_cloexec(fdin, STDIN_FILENO)) + return -1; + if (STDOUT_FILENO != fdevent_dup2_close_clrfd_cloexec(fdout, STDOUT_FILENO)) + return -1; + if (STDERR_FILENO != fdevent_dup2_close_clrfd_cloexec(fderr, STDERR_FILENO)) + return -1; + #else + if (STDIN_FILENO != fdevent_dup2_close_clrfd_cloexec(fdin, STDIN_FILENO, + fdin == fdout + || fdin == fderr)) + return -1; + if (STDOUT_FILENO != fdevent_dup2_close_clrfd_cloexec(fdout, STDOUT_FILENO, + fdout == fderr)) + return -1; + if (STDERR_FILENO != fdevent_dup2_close_clrfd_cloexec(fderr, STDERR_FILENO, + 0)) + return -1; + #endif + + return 0; +} + + +#include /* perror() */ +#include /* signal() */ + +pid_t fdevent_fork_execve(const char *name, char *argv[], char *envp[], int fdin, int fdout, int fderr, int dfd) { + #ifdef HAVE_FORK + + pid_t pid = fork(); + if (0 != pid) return pid; /* parent (pid > 0) or fork() error (-1 == pid) */ + + /* child (0 == pid) */ + + if (-1 != dfd) { + if (0 != fchdir(dfd)) + _exit(errno); + close(dfd); + } + + if (0 != fdevent_set_stdin_stdout_stderr(fdin, fdout, fderr)) _exit(errno); + #ifdef FD_CLOEXEC + /*(might not be sufficient for open fds, but modern OS have FD_CLOEXEC)*/ + for (int i = 3; i < 256; ++i) close(i); + #endif + + /* reset_signals which may have been ignored (SIG_IGN) */ + #ifdef SIGTTOU + signal(SIGTTOU, SIG_DFL); + #endif + #ifdef SIGTTIN + signal(SIGTTIN, SIG_DFL); + #endif + #ifdef SIGTSTP + signal(SIGTSTP, SIG_DFL); + #endif + signal(SIGPIPE, SIG_DFL); + + execve(name, argv, envp ? envp : environ); + + if (0 == memcmp(argv[0], "/bin/sh", sizeof("/bin/sh")-1) + && argv[1] && 0 == memcmp(argv[1], "-c", sizeof("-c")-1)) + perror(argv[2]); + else + perror(argv[0]); + _exit(errno); + + #else + + UNUSED(name); + UNUSED(argv); + UNUSED(envp); + UNUSED(fdin); + UNUSED(fdout); + UNUSED(fderr); + UNUSED(dfd); + return (pid_t)-1; + + #endif +} + + +static int fdevent_open_logger_pipe(const char *logger) { + /* create write pipe and spawn process */ + char *args[4]; + int to_log_fds[2]; + int devnull = fdevent_open_devnull(); + pid_t pid; + + if (-1 == devnull) { + return -1; + } + + if (pipe(to_log_fds)) { + close(devnull); + return -1; + } + fdevent_setfd_cloexec(to_log_fds[0]); + fdevent_setfd_cloexec(to_log_fds[1]); + + *(const char **)&args[0] = "/bin/sh"; + *(const char **)&args[1] = "-c"; + *(const char **)&args[2] = logger; + args[3] = NULL; + + pid = fdevent_fork_execve(args[0], args, NULL, to_log_fds[0], devnull, devnull, -1); + + if (pid > 0) { + /*(future: save pipe fds, pid, and command line to restart if child exits)*/ + close(devnull); + close(to_log_fds[0]); + return to_log_fds[1]; + } + else { + int errnum = errno; + close(devnull); + close(to_log_fds[0]); + close(to_log_fds[1]); + errno = errnum; + return -1; + } +} + + +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +int fdevent_open_logger(const char *logger) { + if (logger[0] != '|') { + int flags = O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE; + return fdevent_open_cloexec(logger, flags, 0644); + } + else { + return fdevent_open_logger_pipe(logger+1); /*(skip the '|')*/ + } +} + + #include #ifdef HAVE_SYS_FILIO_H #include /* FIONREAD (for illumos (OpenIndiana)) */ diff --git a/src/fdevent.h b/src/fdevent.h index 44c48aa9..7ca88229 100644 --- a/src/fdevent.h +++ b/src/fdevent.h @@ -220,6 +220,12 @@ int fdevent_open_cloexec(const char *pathname, int flags, mode_t mode); struct sockaddr; int fdevent_accept_listenfd(int listenfd, struct sockaddr *addr, size_t *addrlen); +int fdevent_open_devnull(void); +int fdevent_open_dirname(char *path); +int fdevent_set_stdin_stdout_stderr(int fdin, int fdout, int fderr); +pid_t fdevent_fork_execve(const char *name, char *argv[], char *envp[], int fdin, int fdout, int fderr, int dfd); +int fdevent_open_logger(const char *logger); + int fdevent_select_init(fdevents *ev); int fdevent_poll_init(fdevents *ev); int fdevent_linux_sysepoll_init(fdevents *ev); diff --git a/src/log.c b/src/log.c index c187fdc7..f6cd5f8c 100644 --- a/src/log.c +++ b/src/log.c @@ -3,32 +3,18 @@ #include "base.h" #include "fdevent.h" #include "log.h" -#include "array.h" #include - #include -#include #include #include #include -#include - #include -#include #ifdef HAVE_SYSLOG_H # include #endif -#ifdef HAVE_VALGRIND_VALGRIND_H -# include -#endif - -#ifndef O_LARGEFILE -# define O_LARGEFILE 0 -#endif - #ifndef HAVE_CLOCK_GETTIME #ifdef HAVE_SYS_TIME_H # include /* gettimeofday() */ @@ -79,109 +65,6 @@ ssize_t write_all(int fd, const void* buf, size_t count) { return written; } -/* Close fd and _try_ to get a /dev/null for it instead. - * close() alone may trigger some bugs when a - * process opens another file and gets fd = STDOUT_FILENO or STDERR_FILENO - * and later tries to just print on stdout/stderr - * - * Returns 0 on success and -1 on failure (fd gets closed in all cases) - */ -int openDevNull(int fd) { - int tmpfd; - close(fd); -#if defined(__WIN32) - /* Cygwin should work with /dev/null */ - tmpfd = open("nul", O_RDWR); -#else - tmpfd = open("/dev/null", O_RDWR); -#endif - if (tmpfd != -1 && tmpfd != fd) { - dup2(tmpfd, fd); - close(tmpfd); - } - /* coverity[leaked_handle : FALSE] */ - return (tmpfd != -1) ? 0 : -1; -} - -int open_logfile_or_pipe(server *srv, const char* logfile) { - int fd; - - if (logfile[0] == '|') { -#ifdef HAVE_FORK - /* create write pipe and spawn process */ - - int to_log_fds[2]; - - if (pipe(to_log_fds)) { - log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed: ", strerror(errno)); - return -1; - } - - /* fork, execve */ - switch (fork()) { - case 0: - /* child */ - close(STDIN_FILENO); - - /* dup the filehandle to STDIN */ - if (to_log_fds[0] != STDIN_FILENO) { - if (STDIN_FILENO != dup2(to_log_fds[0], STDIN_FILENO)) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "dup2 failed: ", strerror(errno)); - exit(-1); - } - close(to_log_fds[0]); - } - close(to_log_fds[1]); - -#ifndef FD_CLOEXEC - { - int i; - /* we don't need the client socket */ - for (i = 3; i < 256; i++) { - close(i); - } - } -#endif - - /* close old stderr */ - openDevNull(STDERR_FILENO); - - /* exec the log-process (skip the | ) */ - execl("/bin/sh", "sh", "-c", logfile + 1, NULL); - log_error_write(srv, __FILE__, __LINE__, "sss", - "spawning log process failed: ", strerror(errno), - logfile + 1); - - exit(-1); - break; - case -1: - /* error */ - log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed: ", strerror(errno)); - return -1; - default: - close(to_log_fds[0]); - fd = to_log_fds[1]; - break; - } - -#else - return -1; -#endif - } else if (-1 == (fd = open(logfile, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) { - log_error_write(srv, __FILE__, __LINE__, "SSSS", - "opening errorlog '", logfile, - "' failed: ", strerror(errno)); - - return -1; - } - - fd_close_on_exec(fd); - - return fd; -} - - /** * open the errorlog * @@ -196,6 +79,7 @@ int open_logfile_or_pipe(server *srv, const char* logfile) { */ int log_error_open(server *srv) { + int errfd; #ifdef HAVE_SYSLOG_H /* perhaps someone wants to use syslog() */ int facility = -1; @@ -272,7 +156,10 @@ int log_error_open(server *srv) { } else if (!buffer_string_is_empty(srv->srvconf.errorlog_file)) { const char *logfile = srv->srvconf.errorlog_file->ptr; - if (-1 == (srv->errorlog_fd = open_logfile_or_pipe(srv, logfile))) { + if (-1 == (srv->errorlog_fd = fdevent_open_logger(logfile))) { + log_error_write(srv, __FILE__, __LINE__, "SSSS", + "opening errorlog '", logfile, + "' failed: ", strerror(errno)); return -1; } srv->errorlog_mode = (logfile[0] == '|') ? ERRORLOG_PIPE : ERRORLOG_FILE; @@ -286,7 +173,6 @@ int log_error_open(server *srv) { } if (!buffer_string_is_empty(srv->srvconf.breakagelog_file)) { - int breakage_fd; const char *logfile = srv->srvconf.breakagelog_file->ptr; if (srv->errorlog_mode == ERRORLOG_FD) { @@ -294,18 +180,32 @@ int log_error_open(server *srv) { fd_close_on_exec(srv->errorlog_fd); } - if (-1 == (breakage_fd = open_logfile_or_pipe(srv, logfile))) { + if (-1 == (errfd = fdevent_open_logger(logfile))) { + log_error_write(srv, __FILE__, __LINE__, "SSSS", + "opening errorlog '", logfile, + "' failed: ", strerror(errno)); return -1; } - - if (STDERR_FILENO != breakage_fd) { - dup2(breakage_fd, STDERR_FILENO); - close(breakage_fd); - } } else if (!srv->srvconf.dont_daemonize) { - /* move stderr to /dev/null */ - openDevNull(STDERR_FILENO); + /* move STDERR_FILENO to /dev/null */ + if (-1 == (errfd = fdevent_open_devnull())) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "opening /dev/null failed:", strerror(errno)); + return -1; + } + } else { + /*(leave STDERR_FILENO as-is)*/ + errfd = -1; + } + + if (0 != fdevent_set_stdin_stdout_stderr(-1, -1, errfd)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "setting stderr failed:", strerror(errno)); + close(errfd); + return -1; } + + if (-1 != errfd) close(errfd); return 0; } @@ -326,7 +226,7 @@ int log_error_cycle(server *srv) { int new_fd; - if (-1 == (new_fd = open_logfile_or_pipe(srv, logfile))) { + if (-1 == (new_fd = fdevent_open_logger(logfile))) { /* write to old log */ log_error_write(srv, __FILE__, __LINE__, "SSSSS", "cycling errorlog '", logfile, diff --git a/src/log.h b/src/log.h index 32ce8d45..5ef18529 100644 --- a/src/log.h +++ b/src/log.h @@ -9,13 +9,6 @@ int log_clock_gettime_realtime (struct timespec *ts); ssize_t write_all(int fd, const void* buf, size_t count); -/* Close fd and _try_ to get a /dev/null for it instead. - * Returns 0 on success and -1 on failure (fd gets closed in all cases) - */ -int openDevNull(int fd); - -int open_logfile_or_pipe(server *srv, const char* logfile); - int log_error_open(server *srv); int log_error_close(server *srv); int log_error_write(server *srv, const char *filename, unsigned int line, const char *fmt, ...); diff --git a/src/mod_accesslog.c b/src/mod_accesslog.c index fbd3da25..482d1887 100644 --- a/src/mod_accesslog.c +++ b/src/mod_accesslog.c @@ -630,14 +630,21 @@ SETDEFAULTS_FUNC(log_access_open) { if (srv->srvconf.preflight_check) continue; - if (-1 == (s->log_access_fd = open_logfile_or_pipe(srv, s->access_logfile->ptr))) + if (-1 == (s->log_access_fd = fdevent_open_logger(s->access_logfile->ptr))) { + log_error_write(srv, __FILE__, __LINE__, "SBSS", + "opening log '", s->access_logfile, + "' failed: ", strerror(errno)); return HANDLER_ERROR; - + } } return HANDLER_GO_ON; } +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + SIGHUP_FUNC(log_access_cycle) { plugin_data *p = p_d; size_t i; diff --git a/src/mod_cgi.c b/src/mod_cgi.c index a8f6a393..1d2c050f 100644 --- a/src/mod_cgi.c +++ b/src/mod_cgi.c @@ -21,9 +21,7 @@ #include #include #include -#include -#include #include static int pipe_cloexec(int pipefd[2]) { @@ -284,8 +282,6 @@ static void cgi_connection_close(server *srv, handler_ctx *hctx) { plugin_data *p = hctx->plugin_data; connection *con = hctx->remote_conn; -#ifndef __WIN32 - /* the connection to the browser went away, but we still have a connection * to the CGI script * @@ -310,7 +306,7 @@ static void cgi_connection_close(server *srv, handler_ctx *hctx) { cgi_handler_ctx_free(hctx); /* if waitpid hasn't been called by response.c yet, do it here */ - if (pid) { + if (pid > 0) { /* check if the CGI-script is already gone */ switch(waitpid(pid, &status, WNOHANG)) { case 0: @@ -359,7 +355,6 @@ static void cgi_connection_close(server *srv, handler_ctx *hctx) { cgi_pid_add(srv, p, pid); } } -#endif /* finish response (if not already con->file_started, con->file_finished) */ if (con->mode == p->id) { @@ -771,17 +766,16 @@ static int cgi_write_request(server *srv, handler_ctx *hctx, int fd) { } static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ctx *hctx, buffer *cgi_handler) { - pid_t pid; - + char_array env; + char *args[3]; int to_cgi_fds[2]; int from_cgi_fds[2]; - struct stat st; + int dfd = -1; UNUSED(p); -#ifndef __WIN32 - if (!buffer_string_is_empty(cgi_handler)) { /* stat the exec file */ + struct stat st; if (-1 == (stat(cgi_handler->ptr, &st))) { log_error_write(srv, __FILE__, __LINE__, "sbss", "stat for cgi-handler", cgi_handler, @@ -794,42 +788,20 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } - if (pipe_cloexec(from_cgi_fds)) { close(to_cgi_fds[0]); close(to_cgi_fds[1]); log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } + fdevent_setfd_cloexec(to_cgi_fds[1]); + fdevent_setfd_cloexec(from_cgi_fds[0]); - /* fork, execve */ - switch (pid = fork()) { - case 0: { - /* child */ - char **args; - int argc; + { int i = 0; - char_array env; - char *c; const char *s; http_cgi_opts opts = { 0, 0, NULL, NULL }; - /* move stdout to from_cgi_fd[1] */ - dup2(from_cgi_fds[1], STDOUT_FILENO); - #ifndef FD_CLOEXEC - close(from_cgi_fds[1]); - /* not needed */ - close(from_cgi_fds[0]); - #endif - - /* move the stdin to to_cgi_fd[0] */ - dup2(to_cgi_fds[0], STDIN_FILENO); - #ifndef FD_CLOEXEC - close(to_cgi_fds[0]); - /* not needed */ - close(to_cgi_fds[1]); - #endif - /* create environment */ env.ptr = NULL; env.size = 0; @@ -860,9 +832,6 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ env.ptr[env.used] = NULL; /* set up args */ - argc = 3; - args = malloc(sizeof(*args) * argc); - force_assert(args); i = 0; if (!buffer_string_is_empty(cgi_handler)) { @@ -870,51 +839,34 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ } args[i++] = con->physical.path->ptr; args[i ] = NULL; + } - /* search for the last / */ - if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) { - /* handle special case of file in root directory */ - const char* physdir = (c == con->physical.path->ptr) ? "/" : con->physical.path->ptr; - - /* temporarily shorten con->physical.path to directory without terminating '/' */ - *c = '\0'; - /* change to the physical directory */ - if (-1 == chdir(physdir)) { - log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path); - } - *c = '/'; - } + dfd = fdevent_open_dirname(con->physical.path->ptr); + if (-1 == dfd) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "open dirname failed:", strerror(errno), con->physical.path); + } - /* we don't need the client socket */ - for (i = 3; i < 256; i++) { - if (i != srv->errorlog_fd) close(i); - } + hctx->pid = (dfd >= 0) ? fdevent_fork_execve(con->physical.path->ptr, args, env.ptr, to_cgi_fds[0], from_cgi_fds[1], -1, dfd) : -1; - /* exec the cgi */ - execve(args[0], args, env.ptr); + for (size_t i = 0; i < env.used; ++i) free(env.ptr[i]); + free(env.ptr); - /* most log files may have been closed/redirected by this point, - * though stderr might still point to lighttpd.breakage.log */ - perror(args[0]); - _exit(1); - } - case -1: - /* error */ + if (-1 == hctx->pid) { + /* log error with errno prior to calling close() (might change errno) */ log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); + if (-1 != dfd) close(dfd); close(from_cgi_fds[0]); close(from_cgi_fds[1]); close(to_cgi_fds[0]); close(to_cgi_fds[1]); return -1; - default: { - /* parent process */ - + } else { + if (-1 != dfd) close(dfd); close(from_cgi_fds[1]); close(to_cgi_fds[0]); /* register PID and wait for them asynchronously */ - hctx->pid = pid; hctx->fd = from_cgi_fds[0]; hctx->fde_ndx = -1; @@ -948,14 +900,8 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ } fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); - break; - } + return 0; } - - return 0; -#else - return -1; -#endif } static buffer * cgi_get_handler(array *a, buffer *fn) { @@ -1073,7 +1019,6 @@ TRIGGER_FUNC(cgi_trigger) { plugin_data *p = p_d; size_t ndx; /* the trigger handle only cares about lonely PID which we have to wait for */ -#ifndef __WIN32 for (ndx = 0; ndx < p->cgi_pid.used; ndx++) { int status; @@ -1122,7 +1067,7 @@ TRIGGER_FUNC(cgi_trigger) { ndx--; } } -#endif + return HANDLER_GO_ON; } diff --git a/src/mod_fastcgi.c b/src/mod_fastcgi.c index e348a8fa..74c1aa0c 100644 --- a/src/mod_fastcgi.c +++ b/src/mod_fastcgi.c @@ -348,23 +348,6 @@ typedef struct { /* ok, we need a prototype */ static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents); -#ifdef HAVE_FORK -static void reset_signals(void) { -#ifdef SIGTTOU - signal(SIGTTOU, SIG_DFL); -#endif -#ifdef SIGTTIN - signal(SIGTTIN, SIG_DFL); -#endif -#ifdef SIGTSTP - signal(SIGTSTP, SIG_DFL); -#endif - signal(SIGHUP, SIG_DFL); - signal(SIGPIPE, SIG_DFL); - signal(SIGUSR1, SIG_DFL); -} -#endif /* HAVE_FORK */ - static void fastcgi_status_copy_procname(buffer *b, fcgi_extension_host *host, fcgi_proc *proc) { buffer_copy_string_len(b, CONST_STR_LEN("fastcgi.backend.")); buffer_append_string_buffer(b, host->id); @@ -788,7 +771,7 @@ FREE_FUNC(mod_fastcgi_free) { host = ex->hosts[n]; for (proc = host->first; proc; proc = proc->next) { - if (proc->pid != 0) { + if (proc->pid > 0) { kill(proc->pid, host->kill_signal); } @@ -799,7 +782,7 @@ FREE_FUNC(mod_fastcgi_free) { } for (proc = host->unused_procs; proc; proc = proc->next) { - if (proc->pid != 0) { + if (proc->pid > 0) { kill(proc->pid, host->kill_signal); } if (proc->is_local && @@ -826,7 +809,6 @@ FREE_FUNC(mod_fastcgi_free) { return HANDLER_GO_ON; } -#ifdef HAVE_FORK static int env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) { char *dst; size_t i; @@ -841,8 +823,7 @@ static int env_add(char_array *env, const char *key, size_t key_len, const char for (i = 0; i < env->used; i++) { if (0 == strncmp(dst, env->ptr[i], key_len + 1)) { - /* don't care about free as we are in a forked child which is going to exec(...) */ - /* free(env->ptr[i]); */ + free(env->ptr[i]); env->ptr[i] = dst; return 0; } @@ -916,21 +897,6 @@ static int parse_binpath(char_array *env, buffer *b) { return 0; } -#endif /* HAVE_FORK */ - -#if !defined(HAVE_FORK) -static int fcgi_spawn_connection(server *srv, - plugin_data *p, - fcgi_extension_host *host, - fcgi_proc *proc) { - UNUSED(srv); - UNUSED(p); - UNUSED(host); - UNUSED(proc); - return -1; -} - -#else /* -> defined(HAVE_FORK) */ static int fcgi_spawn_connection(server *srv, plugin_data *p, @@ -1071,8 +1037,12 @@ static int fcgi_spawn_connection(server *srv, if (-1 == status) { /* server is not up, spawn it */ - pid_t child; + char_array env; + char_array arg; + buffer *bin_path = NULL; + size_t i; int val; + int dfd = -1; /* reopen socket */ if (-1 == (fcgi_fd = fdevent_socket_cloexec(fcgi_addr->sa_family, SOCK_STREAM, 0))) { @@ -1106,13 +1076,7 @@ static int fcgi_spawn_connection(server *srv, return -1; } - switch ((child = fork())) { - case 0: { - size_t i = 0; - char *c; - char_array env; - char_array arg; - + { /* create environment */ env.ptr = NULL; env.size = 0; @@ -1122,19 +1086,6 @@ static int fcgi_spawn_connection(server *srv, arg.size = 0; arg.used = 0; - if(fcgi_fd != FCGI_LISTENSOCK_FILENO) { - dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO); - close(fcgi_fd); - } - else { - fdevent_clrfd_cloexec(fcgi_fd); - } - - /* we don't need the client socket */ - for (i = 3; i < 256; i++) { - close(i); - } - /* build clean environment */ if (host->bin_env_copy->used) { for (i = 0; i < host->bin_env_copy->used; i++) { @@ -1175,44 +1126,35 @@ static int fcgi_spawn_connection(server *srv, env.ptr[env.used] = NULL; - parse_binpath(&arg, host->bin_path); - - /* chdir into the base of the bin-path, - * search for the last / */ - if (NULL != (c = strrchr(arg.ptr[0], '/'))) { - *c = '\0'; - - /* change to the physical directory */ - if (-1 == chdir(arg.ptr[0])) { - *c = '/'; - log_error_write(srv, __FILE__, __LINE__, "sss", "chdir failed:", strerror(errno), arg.ptr[0]); - } - *c = '/'; - } - - reset_signals(); + bin_path = buffer_init_buffer(host->bin_path); + parse_binpath(&arg, bin_path); + } - /* exec the cgi */ - execve(arg.ptr[0], arg.ptr, env.ptr); + dfd = fdevent_open_dirname(arg.ptr[0]); + if (-1 == dfd) { + log_error_write(srv, __FILE__, __LINE__, "sss", "open dirname failed:", strerror(errno), arg.ptr[0]); + } - /* log_error_write(srv, __FILE__, __LINE__, "sbs", - "execve failed for:", host->bin_path, strerror(errno)); */ + /*(FCGI_LISTENSOCK_FILENO == STDIN_FILENO == 0)*/ + proc->pid = (dfd >= 0) ? fdevent_fork_execve(arg.ptr[0], arg.ptr, env.ptr, fcgi_fd, -1, -1, dfd) : -1; - _exit(errno); + for (i = 0; i < env.used; ++i) free(env.ptr[i]); + free(env.ptr); + /*(arg[] contains string references into bin_path)*/ + /*for (i = 0; i < arg.used; ++i) free(arg.ptr[i]);*/ + free(arg.ptr); + buffer_free(bin_path); + if (-1 != dfd) close(dfd); + close(fcgi_fd); - break; + if (-1 == proc->pid) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "fastcgi-backend failed to start:", host->bin_path); + return -1; } - case -1: - /* error */ - close(fcgi_fd); - break; - default: - /* father */ - close(fcgi_fd); /* register process */ proc->is_local = 1; - proc->pid = child; /* wait */ select(0, NULL, NULL, NULL, &tv); @@ -1226,9 +1168,6 @@ static int fcgi_spawn_connection(server *srv, "If this is PHP, try removing the bytecode caches for now and try again."); return -1; } - - break; - } } else { proc->is_local = 0; proc->pid = 0; @@ -1244,8 +1183,6 @@ static int fcgi_spawn_connection(server *srv, return 0; } -#endif /* HAVE_FORK */ - static fcgi_extension_host * unixsocket_is_dup(plugin_data *p, size_t used, buffer *unixsocket) { size_t i, j, n; for (i = 0; i < used; ++i) { diff --git a/src/mod_rrdtool.c b/src/mod_rrdtool.c index 2f1b8f79..f68587a9 100644 --- a/src/mod_rrdtool.c +++ b/src/mod_rrdtool.c @@ -1,13 +1,12 @@ #include "first.h" -#include "server.h" -#include "connections.h" -#include "response.h" -#include "connections.h" +#include "base.h" +#include "fdevent.h" #include "log.h" #include "plugin.h" #include +#include #include #include @@ -16,10 +15,6 @@ #include #include -#ifdef HAVE_FORK -/* no need for waitpid if we don't have fork */ -#include -#endif typedef struct { buffer *path_rrdtool_bin; buffer *path_rrd; @@ -78,13 +73,11 @@ FREE_FUNC(mod_rrd_free) { free(p->config_storage); - if (p->rrdtool_pid) { + if (p->rrdtool_pid > 0) { close(p->read_fd); close(p->write_fd); -#ifdef HAVE_FORK /* collect status */ while (-1 == waitpid(p->rrdtool_pid, NULL, 0) && errno == EINTR) ; -#endif } free(p); @@ -93,9 +86,7 @@ FREE_FUNC(mod_rrd_free) { } static int mod_rrd_create_pipe(server *srv, plugin_data *p) { -#ifdef HAVE_FORK - pid_t pid; - + char *args[3]; int to_rrdtool_fds[2]; int from_rrdtool_fds[2]; if (pipe(to_rrdtool_fds)) { @@ -103,87 +94,33 @@ static int mod_rrd_create_pipe(server *srv, plugin_data *p) { "pipe failed: ", strerror(errno)); return -1; } - if (pipe(from_rrdtool_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed: ", strerror(errno)); return -1; } + fdevent_setfd_cloexec(to_rrdtool_fds[1]); + fdevent_setfd_cloexec(from_rrdtool_fds[0]); + *(const char **)&args[0] = p->conf.path_rrdtool_bin->ptr; + *(const char **)&args[1] = "-"; + args[2] = NULL; - /* fork, execve */ - switch (pid = fork()) { - case 0: { - /* child */ - char **args; - int argc; - int i = 0; - char *dash = "-"; - - /* move stdout to from_rrdtool_fd[1] */ - close(STDOUT_FILENO); - dup2(from_rrdtool_fds[1], STDOUT_FILENO); - close(from_rrdtool_fds[1]); - /* not needed */ - close(from_rrdtool_fds[0]); - - /* move the stdin to to_rrdtool_fd[0] */ - close(STDIN_FILENO); - dup2(to_rrdtool_fds[0], STDIN_FILENO); - close(to_rrdtool_fds[0]); - /* not needed */ - close(to_rrdtool_fds[1]); - - /* set up args */ - argc = 3; - args = malloc(sizeof(*args) * argc); - i = 0; - - args[i++] = p->conf.path_rrdtool_bin->ptr; - args[i++] = dash; - args[i ] = NULL; - - /* we don't need the client socket */ - for (i = 3; i < 256; i++) { - close(i); - } - - /* exec the cgi */ - execv(args[0], args); - - /* log_error_write(srv, __FILE__, __LINE__, "sss", "spawing rrdtool failed: ", strerror(errno), args[0]); */ - - /* */ - SEGFAULT(); - break; - } - case -1: - /* error */ - log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed: ", strerror(errno)); - break; - default: { - /* father */ + p->rrdtool_pid = fdevent_fork_execve(args[0], args, NULL, to_rrdtool_fds[0], from_rrdtool_fds[1], -1, -1); + if (-1 != p->rrdtool_pid) { close(from_rrdtool_fds[1]); close(to_rrdtool_fds[0]); - - /* register PID and wait for them asyncronously */ p->write_fd = to_rrdtool_fds[1]; p->read_fd = from_rrdtool_fds[0]; - p->rrdtool_pid = pid; - - fd_close_on_exec(p->write_fd); - fd_close_on_exec(p->read_fd); - - break; - } + return 0; + } else { + log_error_write(srv, __FILE__, __LINE__, "SBss", "fork/exec(", p->conf.path_rrdtool_bin, "):", strerror(errno)); + close(to_rrdtool_fds[0]); + close(to_rrdtool_fds[1]); + close(from_rrdtool_fds[0]); + close(from_rrdtool_fds[1]); + return -1; } - - return 0; -#else - UNUSED(srv); - UNUSED(p); - return -1; -#endif } /* read/write wrappers to catch EINTR */ diff --git a/src/mod_scgi.c b/src/mod_scgi.c index 3ca9d58a..e294caef 100644 --- a/src/mod_scgi.c +++ b/src/mod_scgi.c @@ -20,7 +20,6 @@ #include #include #include -#include #include "sys-socket.h" #include "sys-endian.h" @@ -327,23 +326,6 @@ static handler_t scgi_handle_fdevent(server *srv, void *ctx, int revents); int scgi_proclist_sort_down(server *srv, scgi_extension_host *host, scgi_proc *proc); -#ifdef HAVE_FORK -static void reset_signals(void) { -#ifdef SIGTTOU - signal(SIGTTOU, SIG_DFL); -#endif -#ifdef SIGTTIN - signal(SIGTTIN, SIG_DFL); -#endif -#ifdef SIGTSTP - signal(SIGTSTP, SIG_DFL); -#endif - signal(SIGHUP, SIG_DFL); - signal(SIGPIPE, SIG_DFL); - signal(SIGUSR1, SIG_DFL); -} -#endif /* HAVE_FORK */ - static handler_ctx * handler_ctx_init(void) { handler_ctx * hctx; @@ -618,7 +600,7 @@ FREE_FUNC(mod_scgi_free) { host = ex->hosts[n]; for (proc = host->first; proc; proc = proc->next) { - if (proc->pid != 0) kill(proc->pid, SIGTERM); + if (proc->pid > 0) kill(proc->pid, SIGTERM); if (proc->is_local && !buffer_string_is_empty(proc->socket)) { @@ -627,7 +609,7 @@ FREE_FUNC(mod_scgi_free) { } for (proc = host->unused_procs; proc; proc = proc->next) { - if (proc->pid != 0) kill(proc->pid, SIGTERM); + if (proc->pid > 0) kill(proc->pid, SIGTERM); if (proc->is_local && !buffer_string_is_empty(proc->socket)) { @@ -649,7 +631,6 @@ FREE_FUNC(mod_scgi_free) { return HANDLER_GO_ON; } -#ifdef HAVE_FORK static int env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) { char *dst; size_t i; @@ -665,8 +646,7 @@ static int env_add(char_array *env, const char *key, size_t key_len, const char for (i = 0; i < env->used; i++) { if (0 == strncmp(dst, env->ptr[i], key_len + 1)) { - /* don't care about free as we are in a forked child which is going to exec(...) */ - /* free(env->ptr[i]); */ + free(env->ptr[i]); env->ptr[i] = dst; return 0; } @@ -686,21 +666,6 @@ static int env_add(char_array *env, const char *key, size_t key_len, const char return 0; } -#endif /* HAVE_FORK */ - -#if !defined(HAVE_FORK) -static int scgi_spawn_connection(server *srv, - plugin_data *p, - scgi_extension_host *host, - scgi_proc *proc) { - UNUSED(srv); - UNUSED(p); - UNUSED(host); - UNUSED(proc); - return -1; -} - -#else /* -> defined(HAVE_FORK) */ static int scgi_spawn_connection(server *srv, plugin_data *p, @@ -827,7 +792,10 @@ static int scgi_spawn_connection(server *srv, if (-1 == status) { /* server is not up, spawn in */ - pid_t child; + char_array env; + char *args[4]; + buffer *b; + size_t i; int val; /* reopen socket */ @@ -863,32 +831,12 @@ static int scgi_spawn_connection(server *srv, return -1; } - switch ((child = fork())) { - case 0: { - buffer *b; - size_t i = 0; - int fd = 0; - char_array env; - - + { /* create environment */ env.ptr = NULL; env.size = 0; env.used = 0; - if (scgi_fd != 0) { - dup2(scgi_fd, 0); - close(scgi_fd); - } - else { - fdevent_clrfd_cloexec(scgi_fd); - } - - /* we don't need the client socket */ - for (fd = 3; fd < 256; fd++) { - close(fd); - } - /* build clean environment */ if (host->bin_env_copy->used) { for (i = 0; i < host->bin_env_copy->used; i++) { @@ -928,35 +876,34 @@ static int scgi_spawn_connection(server *srv, } env.ptr[env.used] = NULL; + } - b = buffer_init(); - buffer_copy_string_len(b, CONST_STR_LEN("exec ")); - buffer_append_string_buffer(b, host->bin_path); - - reset_signals(); - - /* exec the cgi */ - execle("/bin/sh", "sh", "-c", b->ptr, (char *)NULL, env.ptr); + /*(preserve prior behavior for exec of command)*/ + /*(admin should really prefer to put any complex command into script)*/ + b = buffer_init(); + buffer_copy_string_len(b, CONST_STR_LEN("exec ")); + buffer_append_string_buffer(b, host->bin_path); + *(const char **)&args[0] = "/bin/sh"; + *(const char **)&args[1] = "-c"; + *(const char **)&args[2] = b->ptr; + args[3] = NULL; - log_error_write(srv, __FILE__, __LINE__, "sbs", - "execl failed for:", host->bin_path, strerror(errno)); + proc->pid = fdevent_fork_execve(args[0], args, env.ptr, scgi_fd, -1, -1, -1); - _exit(errno); + for (i = 0; i < env.used; ++i) free(env.ptr[i]); + free(env.ptr); + buffer_free(b); + close(scgi_fd); - break; + if (-1 == proc->pid) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "scgi-backend failed to start:", host->bin_path); + return -1; } - case -1: - /* error */ - close(scgi_fd); - break; - default: - /* father */ - close(scgi_fd); /* register process */ proc->last_used = srv->cur_ts; proc->is_local = 1; - proc->pid = child; /* wait */ select(0, NULL, NULL, NULL, &tv); @@ -966,9 +913,6 @@ static int scgi_spawn_connection(server *srv, "scgi-backend failed to start:", host->bin_path); return -1; } - - break; - } } else { proc->is_local = 0; proc->pid = 0; @@ -984,8 +928,6 @@ static int scgi_spawn_connection(server *srv, return 0; } -#endif /* HAVE_FORK */ - static scgi_extension_host * unixsocket_is_dup(plugin_data *p, size_t used, buffer *unixsocket) { size_t i, j, n; for (i = 0; i < used; ++i) { @@ -2604,7 +2546,7 @@ TRIGGER_FUNC(mod_scgi_handle_trigger) { for (proc = host->first; proc; proc = proc->next) { if (proc->load != 0) break; if (host->num_procs <= host->min_procs) break; - if (proc->pid == 0) continue; + if (proc->pid <= 0) continue; if (srv->cur_ts - proc->last_used > host->idle_timeout) { /* a proc is idling for a long time now, diff --git a/src/mod_ssi.c b/src/mod_ssi.c index d0c6bfab..3df6eb6e 100644 --- a/src/mod_ssi.c +++ b/src/mod_ssi.c @@ -1,6 +1,7 @@ #include "first.h" #include "base.h" +#include "fdevent.h" #include "log.h" #include "buffer.h" @@ -14,6 +15,7 @@ #include #include "sys-strings.h" +#include #include #include @@ -27,10 +29,6 @@ # include #endif -#ifdef HAVE_FORK -# include -#endif - #ifdef HAVE_SYS_FILIO_H # include #endif @@ -763,6 +761,7 @@ static int process_ssi_stmt(server *srv, connection *con, handler_ctx *p, const const char *cmd = NULL; pid_t pid; chunk *c; + char *args[4]; if (!p->conf.ssi_exec) { /* disabled by config */ break; @@ -785,31 +784,21 @@ static int process_ssi_stmt(server *srv, connection *con, handler_ctx *p, const */ if (!cmd) break; -#ifdef HAVE_FORK + /* send cmd output to a temporary file */ if (0 != chunkqueue_append_mem_to_tempfile(srv, con->write_queue, "", 0)) break; c = con->write_queue->last; - /* fork, execve */ - switch (pid = fork()) { - case 0: { - close(STDOUT_FILENO); - dup2(c->file.fd, STDOUT_FILENO); - close(c->file.fd); - - /* close stdin */ - close(STDIN_FILENO); - - execl("/bin/sh", "sh", "-c", cmd, (char *)NULL); + *(const char **)&args[0] = "/bin/sh"; + *(const char **)&args[1] = "-c"; + *(const char **)&args[2] = cmd; + args[3] = NULL; + /*(expects STDIN_FILENO open to /dev/null)*/ + pid = fdevent_fork_execve(args[0], args, NULL, -1, c->file.fd, -1, -1); + if (-1 == pid) { log_error_write(srv, __FILE__, __LINE__, "sss", "spawning exec failed:", strerror(errno), cmd); - _exit(errno); - } - case -1: - /* error */ - log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); - break; - default: { + } else { struct stat stb; int status; @@ -831,15 +820,7 @@ static int process_ssi_stmt(server *srv, connection *con, handler_ctx *p, const if (0 == fstat(c->file.fd, &stb)) { c->file.length = stb.st_size; } - - break; - } } -#else - UNUSED(pid); - UNUSED(c); - return -1; -#endif break; } diff --git a/src/server.c b/src/server.c index d2857030..574d2ccf 100644 --- a/src/server.c +++ b/src/server.c @@ -841,7 +841,6 @@ static int server_main (server * const srv, int argc, char **argv) { #ifdef HAVE_FORK int num_childs = 0; #endif - int fd; size_t i; time_t idle_limit = 0, last_active_ts = time(NULL); #ifdef HAVE_SIGACTION @@ -965,11 +964,32 @@ static int server_main (server * const srv, int argc, char **argv) { } /* close stdin and stdout, as they are not needed */ - openDevNull(STDIN_FILENO); - openDevNull(STDOUT_FILENO); { struct stat st; - if (0 != fstat(STDERR_FILENO, &st)) openDevNull(STDERR_FILENO); + int devnull; + int errfd; + do { + devnull = fdevent_open_devnull(); + } while (-1 != devnull && devnull <= STDERR_FILENO); + if (-1 == devnull) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "opening /dev/null failed:", strerror(errno)); + return -1; + } + errfd = (0 == fstat(STDERR_FILENO, &st)) ? -1 : devnull; + if (0 != fdevent_set_stdin_stdout_stderr(devnull, devnull, errfd)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "setting default fds failed:", strerror(errno)); + #ifdef FD_CLOEXEC + if (-1 != errfd) close(errfd); + if (devnull != errfd) close(devnull); + #endif + return -1; + } + #ifdef FD_CLOEXEC + if (-1 != errfd) close(errfd); + if (devnull != errfd) close(devnull); + #endif } if (0 != config_set_defaults(srv)) { @@ -1495,8 +1515,13 @@ static int server_main (server * const srv, int argc, char **argv) { /* get the current number of FDs */ - srv->cur_fds = open("/dev/null", O_RDONLY); - close(srv->cur_fds); + { + int fd = fdevent_open_devnull(); + if (fd >= 0) { + srv->cur_fds = fd; + close(fd); + } + } if (0 != server_sockets_set_nb_cloexec(srv)) { return -1; @@ -1769,6 +1794,7 @@ static int server_main (server * const srv, int argc, char **argv) { if ((n = fdevent_poll(srv->ev, 1000)) > 0) { /* n is the number of events */ + int fd; int revents; int fd_ndx; last_active_ts = srv->cur_ts; diff --git a/src/test_configfile.c b/src/test_configfile.c index 990a1d24..397c29ed 100644 --- a/src/test_configfile.c +++ b/src/test_configfile.c @@ -89,3 +89,27 @@ int main (void) { */ void fdevent_setfd_cloexec(int fd); void fdevent_setfd_cloexec(int fd) { UNUSED(fd); } +int fdevent_open_cloexec(const char *pathname); +int fdevent_open_cloexec(const char *pathname) { UNUSED(pathname); return 0; } +int fdevent_open_devnull(void); +int fdevent_open_devnull(void) { return -1; } +int fdevent_open_logger(const char *logfile); +int fdevent_open_logger(const char *logfile) { UNUSED(logfile); return -1; } +int fdevent_set_stdin_stdout_stderr(int fdin, int fdout, int fderr); +int fdevent_set_stdin_stdout_stderr(int fdin, int fdout, int fderr) { + UNUSED(fdin); + UNUSED(fdout); + UNUSED(fderr); + return -1; +} +pid_t fdevent_fork_execve(const char *name, char *argv[], char *envp[], int fdin, int fdout, int fderr, int dfd); +pid_t fdevent_fork_execve(const char *name, char *argv[], char *envp[], int fdin, int fdout, int fderr, int dfd) { + UNUSED(name); + UNUSED(argv); + UNUSED(envp); + UNUSED(fdin); + UNUSED(fdout); + UNUSED(fderr); + UNUSED(dfd); + return (pid_t)-1; +}