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>
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.
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;
19 MOG_EXERR_SIGPROCMASK
= 4,
23 static void iostat_atexit(void)
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
38 if (errno
== ENFILE
|| errno
== EMFILE
)
39 PRESERVE_ERRNO( (void)mog_fdmap_expire(5) );
43 CHECK(int, 0, mog_set_nonblocking(fds
[0], true));
44 /* fds[1] (write end) stays _blocking_ */
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
;
55 return xasprintf("exec %s", cmd
);
58 "delaying exec of `%s' for %ds due to previous failure",
60 return xasprintf("sleep %d; exec %s", (int)delay
, cmd
);
63 static int dup2_retry(int oldfd
, int newfd
) /* vfork-safe */
68 rc
= dup2(oldfd
, newfd
);
69 while (rc
< 0 && (errno
== EINTR
|| errno
== EBUSY
));
74 static void execve_iostat(int out_fd
, const char *cmd
) /* vfork-safe */
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");
110 cmd
= "iostat -dx 1 30";
113 CHECK(int, 0, pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, &cs
));
114 iostat_pid
= mog_fork_for_exec();
116 syslog(LOG_ERR
, "fork() for iostat failed: %m");
117 else if (iostat_pid
== 0) /* child */
118 execve_iostat(out_fd
, cmd
);
121 CHECK(int, 0, pthread_setcancelstate(PTHREAD_CANCEL_ENABLE
, 0));
123 mog_process_register(iostat_pid
, MOG_PROC_IOSTAT
);
130 bool mog_iostat_respawn(int oldstatus
)
135 if (WIFEXITED(oldstatus
)) {
136 int ex
= WEXITSTATUS(oldstatus
);
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)";
147 syslog(LOG_ERR
, "iostat exited due to %s failure", fn
);
148 /* else syslog(LOG_DEBUG, "iostat done, restarting"); */
150 iostat_last_fail
= time(NULL
);
152 "iostat done (pid=%d, status=%d), will retry in %ds",
153 (int)iostat_pid
, oldstatus
, (int)iostat_fail_timeout
);
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
);
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
);