diff options
-rw-r--r-- | cloexec_from.c | 4 | ||||
-rw-r--r-- | iostat_process.c | 100 | ||||
-rw-r--r-- | svc.c | 6 | ||||
-rw-r--r-- | util.h | 2 |
4 files changed, 67 insertions, 45 deletions
diff --git a/cloexec_from.c b/cloexec_from.c index 444e665..8aa6a92 100644 --- a/cloexec_from.c +++ b/cloexec_from.c @@ -5,11 +5,11 @@ #include "cmogstored.h" /* - * this function is only called in a forked child (for iostat) + * this function is only called in a vforked child (for iostat) * if O_CLOEXEC/SOCK_CLOEXEC is unsupported, or if mog_cloexec_detect() * detects those flags are broken. */ -void mog_cloexec_from(int lowfd) +void mog_cloexec_from(int lowfd) /* vfork-safe */ { int fd; int last_good = lowfd; diff --git a/iostat_process.c b/iostat_process.c index d8766e0..9f98077 100644 --- a/iostat_process.c +++ b/iostat_process.c @@ -14,6 +14,12 @@ static time_t iostat_last_fail; static struct mog_iostat *iostat; static time_t iostat_fail_timeout = 10; +enum mog_exerr { + MOG_EXERR_DUP2 = 3, + MOG_EXERR_SIGPROCMASK = 4, + MOG_EXERR_EXECVE = 5 +}; + static void iostat_atexit(void) { if (iostat_pid > 0) @@ -40,8 +46,7 @@ static int iostat_pipe_init(int *fds) return 0; } -/* only called in the child process */ -static const char * exec_cmd(const char *cmd) +static const char *exec_cmd(const char *cmd) { time_t last_fail = time(NULL) - iostat_last_fail; time_t delay = iostat_fail_timeout - last_fail; @@ -55,7 +60,7 @@ static const char * exec_cmd(const char *cmd) return xasprintf("sleep %d; exec %s", (int)delay, cmd); } -static void dup2_or_die(int oldfd, int newfd, const char *errdesc) +static int dup2_retry(int oldfd, int newfd) /* vfork-safe */ { int rc; @@ -63,57 +68,62 @@ static void dup2_or_die(int oldfd, int newfd, const char *errdesc) rc = dup2(oldfd, newfd); while (rc < 0 && (errno == EINTR || errno == EBUSY)); - if (rc < 0) { - syslog(LOG_CRIT, "dup2(%s) failed: %m", errdesc); - _exit(1); - } + return rc; } -static void preexec_redirect(int out_fd) +static void execve_iostat(int out_fd, const char *cmd) /* vfork-safe */ { - int null_fd; - - dup2_or_die(out_fd, STDOUT_FILENO, "iostat_pipe[1],STDOUT"); - mog_close(out_fd); - - null_fd = open("/dev/null", O_RDONLY); - if (null_fd < 0) { - syslog(LOG_CRIT, "open(/dev/null) failed: %m"); - abort(); - } - dup2_or_die(null_fd, STDIN_FILENO, "/dev/null,STDIN"); - mog_close(null_fd); - - /* don't touch stderr */ + int i; + union { + char *argv[4]; + char const *in[4]; + } u; + + u.in[0] = "/bin/sh"; + u.in[1] = "-c"; + u.in[2] = cmd; + u.in[3] = 0; + + if (dup2_retry(out_fd, STDOUT_FILENO) < 0) + _exit(MOG_EXERR_DUP2); + if (!mog_cloexec_atomic) + mog_cloexec_from(STDERR_FILENO + 1); + + /* ignore errors, not much we can do about missing signals */ + for (i = 1; i < NSIG; i++) + (void)signal(i, SIG_DFL); + + if (sigprocmask(SIG_SETMASK, &mog_emptyset, NULL) != 0) + _exit(MOG_EXERR_SIGPROCMASK); + + execve("/bin/sh", u.argv, environ); + _exit(MOG_EXERR_EXECVE); } static pid_t iostat_fork_exec(int out_fd) { /* rely on /bin/sh to parse iostat command-line args */ const char *cmd = getenv("MOG_IOSTAT_CMD"); + int cs; + if (!cmd) cmd = "iostat -dx 1 30"; cmd = exec_cmd(cmd); - + CHECK(int, 0, pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs)); iostat_pid = mog_fork_for_exec(); - if (iostat_pid < 0) { + if (iostat_pid < 0) syslog(LOG_ERR, "fork() for iostat failed: %m"); - } else if (iostat_pid > 0) { + else if (iostat_pid == 0) /* child */ + execve_iostat(out_fd, cmd); + + /* parent */ + CHECK(int, 0, pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0)); + if (iostat_pid > 0) mog_process_register(iostat_pid, MOG_PROC_IOSTAT); - mog_close(out_fd); - } else { - /* child */ - preexec_redirect(out_fd); - if (! mog_cloexec_atomic) - mog_cloexec_from(STDERR_FILENO + 1); - - mog_intr_enable(); - execl("/bin/sh", "sh", "-c", cmd, (char *)NULL); - syslog(LOG_CRIT, "execl(%s) failed: %m", cmd); - _exit(1); - } + mog_close(out_fd); mog_free(cmd); + return iostat_pid; } @@ -122,8 +132,20 @@ bool mog_iostat_respawn(int oldstatus) int fds[2]; struct mog_fd *mfd; - if (WIFEXITED(oldstatus) && WEXITSTATUS(oldstatus) == 0) { - /* syslog(LOG_DEBUG, "iostat done, restarting"); */ + if (WIFEXITED(oldstatus)) { + int ex = WEXITSTATUS(oldstatus); + const char *fn = 0; + + switch (ex) { + case 0: break; + case MOG_EXERR_DUP2: fn = "dup2"; break; + case MOG_EXERR_SIGPROCMASK: fn = "sigprocmask"; break; + case MOG_EXERR_EXECVE: fn = "execve"; break; + default: fn = "(unknown)"; + } + if (fn) + syslog(LOG_ERR, "iostat exited due to %s failure", fn); + /* else syslog(LOG_DEBUG, "iostat done, restarting"); */ } else { iostat_last_fail = time(NULL); syslog(LOG_WARNING, @@ -166,14 +166,14 @@ size_t mog_svc_each(Hash_processor processor, void *data) return rv; } -static bool cloexec_disable(struct mog_fd *mfd) +static bool cloexec_disable(struct mog_fd *mfd) /* vfork-safe */ { if (mfd) CHECK(int, 0, mog_set_cloexec(mfd->fd, false)); return true; } -static bool svc_cloexec_off_i(void *svcptr, void *unused) +static bool svc_cloexec_off_i(void *svcptr, void *unused) /* vfork-safe */ { struct mog_svc *svc = svcptr; @@ -186,7 +186,7 @@ static bool svc_cloexec_off_i(void *svcptr, void *unused) * Only call this from a freshly forked upgrade child process. * This holds no locks to avoid potential deadlocks in post-fork mutexes */ -void mog_svc_upgrade_prepare(void) +void mog_svc_upgrade_prepare(void) /* vfork-safe */ { (void)hash_do_for_each(by_docroot, svc_cloexec_off_i, NULL); } @@ -63,7 +63,7 @@ static inline int mog_set_nonblocking(int fd, const bool value) * ever get defined, we wouldn't be using them in the first place without * updating this code... (no way they'd be on by default). */ -static inline int mog_set_cloexec(int fd, const bool set) +static inline int mog_set_cloexec(int fd, const bool set) /* vfork-safe */ { return fcntl(fd, F_SETFD, set ? FD_CLOEXEC : 0); } |