cmogstored 1.8.1 - use default system stack size
[cmogstored.git] / iostat_process.c
blobda3b95c76fd3388617af4d2dad217e288726600b
1 /*
2 * Copyright (C) 2012-2020 all contributors <cmogstored-public@yhbt.net>
3 * License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4 */
5 /*
6 * process management for iostat(1)
7 * Since iostat(1) watches the entire system, we only spawn it once
8 * regardless of the number of mog_svc objects we have.
9 */
10 #include "cmogstored.h"
12 static pid_t iostat_pid;
13 static time_t iostat_last_fail;
14 static struct mog_iostat *iostat;
15 static time_t iostat_fail_timeout = 10;
17 enum mog_exerr {
18 MOG_EXERR_DUP2 = 3,
19 MOG_EXERR_SIGPROCMASK = 4,
20 MOG_EXERR_EXECVE = 5
23 static void iostat_atexit(void)
25 if (iostat_pid > 0)
26 kill(iostat_pid, SIGTERM);
29 static int iostat_pipe_init(int *fds)
31 if (pipe2(fds, O_CLOEXEC) < 0) {
32 PRESERVE_ERRNO( syslog(LOG_ERR, "pipe2() failed: %m") );
35 * don't retry here, MFS can deal with not getting iostat
36 * data for a while
38 if (errno == ENFILE || errno == EMFILE)
39 PRESERVE_ERRNO( (void)mog_fdmap_expire(5) );
40 return -1;
43 CHECK(int, 0, mog_set_nonblocking(fds[0], true));
44 /* fds[1] (write end) stays _blocking_ */
46 return 0;
49 static const char *exec_cmd(const char *cmd)
51 time_t last_fail = time(NULL) - iostat_last_fail;
52 time_t delay = iostat_fail_timeout - last_fail;
54 if (delay <= 0)
55 return xasprintf("exec %s", cmd);
57 syslog(LOG_DEBUG,
58 "delaying exec of `%s' for %ds due to previous failure",
59 cmd, (int)delay);
60 return xasprintf("sleep %d; exec %s", (int)delay, cmd);
63 static int dup2_retry(int oldfd, int newfd) /* vfork-safe */
65 int rc;
68 rc = dup2(oldfd, newfd);
69 while (rc < 0 && (errno == EINTR || errno == EBUSY));
71 return rc;
74 static void execve_iostat(int out_fd, const char *cmd) /* vfork-safe */
76 int i;
77 union {
78 char *argv[4];
79 char const *in[4];
80 } u;
82 u.in[0] = "/bin/sh";
83 u.in[1] = "-c";
84 u.in[2] = cmd;
85 u.in[3] = 0;
87 if (dup2_retry(out_fd, STDOUT_FILENO) < 0)
88 _exit(MOG_EXERR_DUP2);
89 if (!mog_cloexec_atomic)
90 mog_cloexec_from(STDERR_FILENO + 1);
92 /* ignore errors, not much we can do about missing signals */
93 for (i = 1; i < NSIG; i++)
94 (void)signal(i, SIG_DFL);
96 if (sigprocmask(SIG_SETMASK, &mog_emptyset, NULL) != 0)
97 _exit(MOG_EXERR_SIGPROCMASK);
99 execve("/bin/sh", u.argv, environ);
100 _exit(MOG_EXERR_EXECVE);
103 static pid_t iostat_fork_exec(int out_fd)
105 /* rely on /bin/sh to parse iostat command-line args */
106 const char *cmd = getenv("MOG_IOSTAT_CMD");
107 int cs;
109 if (!cmd)
110 cmd = "iostat -dx 1 30";
112 cmd = exec_cmd(cmd);
113 CHECK(int, 0, pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs));
114 iostat_pid = mog_fork_for_exec();
115 if (iostat_pid < 0)
116 syslog(LOG_ERR, "fork() for iostat failed: %m");
117 else if (iostat_pid == 0) /* child */
118 execve_iostat(out_fd, cmd);
120 /* parent */
121 CHECK(int, 0, pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0));
122 if (iostat_pid > 0)
123 mog_process_register(iostat_pid, MOG_PROC_IOSTAT);
124 mog_close(out_fd);
125 mog_free(cmd);
127 return iostat_pid;
130 bool mog_iostat_respawn(int oldstatus)
132 int fds[2];
133 struct mog_fd *mfd;
135 if (WIFEXITED(oldstatus)) {
136 int ex = WEXITSTATUS(oldstatus);
137 const char *fn = 0;
139 switch (ex) {
140 case 0: break;
141 case MOG_EXERR_DUP2: fn = "dup2"; break;
142 case MOG_EXERR_SIGPROCMASK: fn = "sigprocmask"; break;
143 case MOG_EXERR_EXECVE: fn = "execve"; break;
144 default: fn = "(unknown)";
146 if (fn)
147 syslog(LOG_ERR, "iostat exited due to %s failure", fn);
148 /* else syslog(LOG_DEBUG, "iostat done, restarting"); */
149 } else {
150 iostat_last_fail = time(NULL);
151 syslog(LOG_WARNING,
152 "iostat done (pid=%d, status=%d), will retry in %ds",
153 (int)iostat_pid, oldstatus, (int)iostat_fail_timeout);
155 iostat_pid = 0;
157 if (iostat_pipe_init(fds) < 0)
158 return false; /* EMFILE || ENFILE */
159 if (iostat_fork_exec(fds[1]) < 0)
160 return false; /* fork() failure */
162 assert(fds[0] >= 0 && "invalid FD");
164 mfd = mog_fd_init(fds[0], MOG_FD_TYPE_IOSTAT);
166 if (iostat == NULL)
167 atexit(iostat_atexit);
168 iostat = &mfd->as.iostat;
169 iostat->queue = mog_notify_queue;
170 mog_iostat_init(iostat);
171 mog_idleq_add(iostat->queue, mfd, MOG_QEV_RD);
173 return true;