Refactoring: Changed all check parameters starting with a 'p' to the new rulespec...
[check_mk.git] / agents / waitmax.c
blob5988edfa3675ba11b54cd184fa34c11f4ab0e30e
1 // +------------------------------------------------------------------+
2 // | ____ _ _ __ __ _ __ |
3 // | / ___| |__ ___ ___| | __ | \/ | |/ / |
4 // | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
5 // | | |___| | | | __/ (__| < | | | | . \ |
6 // | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
7 // | |
8 // | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
9 // +------------------------------------------------------------------+
11 // This file is part of Check_MK.
12 // The official homepage is at http://mathias-kettner.de/check_mk.
14 // check_mk is free software; you can redistribute it and/or modify it
15 // under the terms of the GNU General Public License as published by
16 // the Free Software Foundation in version 2. check_mk is distributed
17 // in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
18 // out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
19 // PARTICULAR PURPOSE. See the GNU General Public License for more de-
20 // ails. You should have received a copy of the GNU General Public
21 // License along with GNU Make; see the file COPYING. If not, write
22 // to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
23 // Boston, MA 02110-1301 USA.
25 #include <errno.h>
26 #include <getopt.h>
27 #include <signal.h>
28 #include <stddef.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/wait.h>
32 #include <unistd.h>
34 static pid_t g_pid = 0;
35 static int g_timeout = 0;
36 static int g_signum = SIGTERM;
38 static void out(const char *buf) {
39 size_t bytes_to_write = strlen(buf);
40 while (bytes_to_write > 0) {
41 ssize_t written = write(STDERR_FILENO, buf, bytes_to_write);
42 if (written == -1) {
43 if (errno == EINTR) continue;
44 return;
46 buf += written;
47 bytes_to_write -= written;
51 static void exit_with(const char *message, int err, int status) {
52 out(message);
53 if (err != 0) {
54 out(": ");
55 out(strerror(errno));
57 out("\n");
58 exit(status);
61 static void version() {
62 exit_with(
63 "waitmax version 1.1\n"
64 "Copyright Mathias Kettner 2008\n"
65 "This is free software; see the source for copying conditions. "
66 "There is NO\n"
67 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR "
68 "PURPOSE.",
69 0, 0);
72 static void usage(int status) {
73 exit_with(
74 "Usage: waitmax [-s SIGNUM] MAXTIME PROGRAM [ARGS...]\n"
75 "\n"
76 "Execute PROGRAM as a subprocess. If PROGRAM does not exit before "
77 "MAXTIME\n"
78 "seconds, it will be killed with SIGTERM or an alternative signal.\n"
79 "\n"
80 " -s, --signal SIGNUM kill with SIGNUM on timeout\n"
81 " -h, --help this help\n"
82 " -V, --version show version an exit\n",
83 0, status);
86 static void kill_group(pid_t pid, int signum) {
87 /* The child might have become a process group leader itself, so send the
88 signal directly to it. */
89 kill(pid, signum);
91 /* Guard against harakiri... */
92 signal(signum, SIG_IGN);
94 /* Send the signal to all processes in our fresh process group. */
95 kill(0, signum);
98 static void signalhandler(int signum) {
99 if (signum == SIGALRM) {
100 /* The child took too long, so remember that we timed out and send the
101 configured signal instead of SIGALRM. */
102 g_timeout = 1;
103 signum = g_signum;
106 /* Are we the child process or has the child not been execvp'd yet? */
107 if (g_pid == 0) exit(signum + 128);
109 /* Send the configure signal to our process group. */
110 kill_group(g_pid, signum);
112 /* Make sure the children actually react on the signal. */
113 if (signum != SIGKILL && signum != SIGCONT) kill_group(g_pid, SIGCONT);
116 static void setup_signal_handlers() {
117 struct sigaction sa;
118 sigemptyset(&sa.sa_mask);
119 sa.sa_handler = signalhandler;
120 sa.sa_flags = SA_RESTART; /* just to be sure... */
121 sigaction(g_signum, &sa, NULL);
122 sigaction(SIGALRM, &sa, NULL);
123 sigaction(SIGHUP, &sa, NULL);
124 sigaction(SIGINT, &sa, NULL);
125 sigaction(SIGQUIT, &sa, NULL);
126 sigaction(SIGTERM, &sa, NULL);
128 /* Guard against a background child doing I/O on the tty. */
129 sa.sa_handler = SIG_IGN;
130 sigaction(SIGTTIN, &sa, NULL);
131 sigaction(SIGTTOU, &sa, NULL);
133 /* Make sure that waitpid won't fail. */
134 sa.sa_handler = SIG_DFL;
135 sigaction(SIGCHLD, &sa, NULL);
138 static void unblock_signal(int signum) {
139 sigset_t signals_to_unblock;
140 sigemptyset(&signals_to_unblock);
141 sigaddset(&signals_to_unblock, signum);
142 if (sigprocmask(SIG_UNBLOCK, &signals_to_unblock, NULL) == -1)
143 exit_with("sigprocmask failed", errno, 1);
146 static struct option long_options[] = {{"version", no_argument, 0, 'V'},
147 {"help", no_argument, 0, 'h'},
148 {"signal", required_argument, 0, 's'},
149 {0, 0, 0, 0}};
151 int main(int argc, char **argv) {
152 /* Note: setenv calls malloc, and 'diet' warns about that. */
153 if (getenv("POSIXLY_CORRECT") == NULL) putenv("POSIXLY_CORRECT=true");
154 int ret;
155 while ((ret = getopt_long(argc, argv, "Vhs:", long_options, NULL)) != -1) {
156 switch (ret) {
157 case 'V':
158 version();
159 break;
161 case 'h':
162 usage(0);
163 break;
165 case 's':
166 g_signum = atoi(optarg);
167 if (g_signum < 1 || g_signum > 32)
168 exit_with("Signalnumber must be between 1 and 32.", 0, 1);
169 break;
171 default:
172 usage(1);
173 break;
177 if (optind + 1 >= argc) usage(1);
179 int maxtime = atoi(argv[optind]);
180 if (maxtime <= 0) usage(1);
182 /* Create a new process group with ourselves as the process group
183 leader. This way we can send a signal to all subprocesses later (unless
184 some non-direct descendant creates its own process group). Doing this in
185 the parent process already simplifies things, because we don't have to
186 worry about foreground/background groups then. */
187 setpgid(0, 0);
189 /* Setting up signal handlers before forking avoids race conditions with the
190 child. */
191 setup_signal_handlers();
193 g_pid = fork();
194 if (g_pid == -1) exit_with("fork() failed", errno, 1);
196 if (g_pid == 0) {
197 /* Restore tty behavior in the child. */
198 struct sigaction sa;
199 sigemptyset(&sa.sa_mask);
200 sa.sa_flags = SA_RESTART; /* just to be sure... */
201 sa.sa_handler = SIG_DFL;
202 sigaction(SIGTTIN, &sa, NULL);
203 sigaction(SIGTTOU, &sa, NULL);
205 execvp(argv[optind + 1], argv + optind + 1);
206 exit_with(argv[optind + 1], errno, 253);
209 /* Make sure SIGALRM is not blocked (e.g. by parent). */
210 unblock_signal(SIGALRM);
211 alarm(maxtime);
213 int status;
214 while (waitpid(g_pid, &status, 0) == -1) {
215 if (errno != EINTR) exit_with("waitpid() failed", errno, 1);
218 if (WIFEXITED(status)) return WEXITSTATUS(status);
219 if (WIFSIGNALED(status)) return g_timeout ? 255 : 128 + WTERMSIG(status);
220 exit_with("Program did neither exit nor was signalled.", 0, 254);
221 return 0; /* Make GCC happy. */