diff options
author | Eric Wong <normalperson@yhbt.net> | 2012-01-11 21:46:04 +0000 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2012-01-11 21:46:04 +0000 |
commit | 301b41b6f1350806a750794d615e3468735757a6 (patch) | |
tree | 54deb2b4cb0060a54746e3635746d0f338c294f5 /iostat_process.c | |
download | cmogstored-301b41b6f1350806a750794d615e3468735757a6.tar.gz |
Nuked old history since it was missing copyright/GPLv3 notices.
Diffstat (limited to 'iostat_process.c')
-rw-r--r-- | iostat_process.c | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/iostat_process.c b/iostat_process.c new file mode 100644 index 0000000..cb63ef7 --- /dev/null +++ b/iostat_process.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2012, Eric Wong <normalperson@yhbt.net> + * License: GPLv3 or later (see COPYING for details) + */ +/* + * process management for iostat(1) + * Since iostat(1) watches the entire system, we only spawn it once + * regardless of the number of mog_svc objects we have. + */ +#include "cmogstored.h" +#include <sys/wait.h> + +static pid_t iostat_pid; +static time_t iostat_last_fail; +static struct mog_iostat *iostat; +static time_t iostat_fail_timeout = 10; + +static void iostat_atexit(void) +{ + if (iostat_pid > 0) + kill(iostat_pid, SIGTERM); +} + +static void iostat_wait(void) +{ + int status; + pid_t tmp; + + if (iostat_pid <= 0) return; + + tmp = waitpid(iostat_pid, &status, WNOHANG); + if (tmp < 0) { + syslog(LOG_WARNING, "waitpid(pid=%d) failed: %m", + (int)iostat_pid); + return; + } + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + /* syslog(LOG_DEBUG, "iostat done, restarting"); */ + } else { + iostat_last_fail = time(NULL); + syslog(LOG_WARNING, + "iostat done (pid=%d, status=%d), will retry in %ds", + (int)iostat_pid, status, (int)iostat_fail_timeout); + } + iostat_pid = 0; +} + +static int iostat_pipe_init(int *fds) +{ + if (pipe2(fds, O_CLOEXEC) < 0) { + PRESERVE_ERRNO( syslog(LOG_ERR, "pipe2() failed: %m") ); + return -1; + } + + CHECK(int, 0, set_nonblocking_flag(fds[0], true)); + /* fds[1] (write end) stays _blocking_ */ + + return 0; +} + +/* only called in the child process */ +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; + + if (delay <= 0) + return xasprintf("exec %s", cmd); + + syslog(LOG_DEBUG, + "delaying exec of `%s' for %ds due to previous failure", + cmd, (int)delay); + return xasprintf("sleep %d; exec %s", (int)delay, cmd); +} + +static void dup2_or_die(int oldfd, int newfd, const char *errdesc) +{ + int rc; + + do + rc = dup2(oldfd, newfd); + while (rc < 0 && (errno == EINTR || errno == EBUSY)); + + if (rc < 0) { + syslog(LOG_CRIT, "dup2(%s) failed: %m", errdesc); + abort(); + } +} + +static void preexec_redirect(int out_fd) +{ + 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 */ +} + +static pid_t iostat_fork_exec(int out_fd) +{ + iostat_pid = fork(); + if (iostat_pid < 0) { + syslog(LOG_ERR, "fork() for iostat failed: %m"); + } else if (iostat_pid > 0) { + mog_close(out_fd); + } else { + /* rely on /bin/sh to parse iostat command-line args */ + const char *cmd = getenv("MOG_IOSTAT_CMD"); + if (!cmd) cmd = "iostat -dx 1 30"; + + preexec_redirect(out_fd); + if (! mog_cloexec_atomic) + mog_cloexec_from(STDERR_FILENO + 1); + + cmd = exec_cmd(cmd); + mog_intr_enable(); + execl("/bin/sh", "sh", "-c", cmd, (char *)NULL); + syslog(LOG_CRIT, "execl(%s) failed: %m", cmd); + abort(); + } + return iostat_pid; +} + +struct mog_iostat *mog_iostat_spawn(struct mog_queue *queue) +{ + int fds[2]; + struct mog_fd *mfd; + + iostat_wait(); + if (iostat_pipe_init(fds) < 0) + return NULL; /* EMFILE || ENFILE */ + if (iostat_fork_exec(fds[1]) < 0) + return NULL; /* fork() failure */ + + assert(fds[0] >= 0 && "invalid FD"); + + mfd = mog_fd_get(fds[0]); + + if (iostat == NULL) atexit(iostat_atexit); + iostat = &mfd->as.iostat; + mfd->fd = fds[0]; + iostat->queue = queue; + mog_iostat_init(iostat); + + mfd->fd_type = MOG_FD_TYPE_IOSTAT; + assert((mfd->in_queue = 0) == 0 && "in_queue check"); + mfd->queue_state = MOG_QUEUE_STATE_NEW; + mog_idleq_push(queue, mfd, MOG_QEV_RD); + + return iostat; +} |