2 * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #if defined(HAVE_FORK) && defined(HAVE_WAITPID)
37 #include <sys/types.h>
41 sig_atomic_t exit_flag;
57 sa.sa_handler = sigterm;
58 sigemptyset(&sa.sa_mask);
60 sigaction(SIGINT, &sa, NULL);
61 sigaction(SIGTERM, &sa, NULL);
62 sigaction(SIGXCPU, &sa, NULL);
64 sa.sa_handler = SIG_IGN;
65 sigaction(SIGPIPE, &sa, NULL);
68 signal(SIGINT, sigterm);
69 signal(SIGTERM, sigterm);
71 signal(SIGXCPU, sigterm);
74 signal(SIGPIPE, SIG_IGN);
80 * Fork a child to run the service, and restart it if it dies.
82 * Returns -1 if not supported, else a file descriptor that the service
83 * should select() for. Any events on that file descriptor should cause
84 * the caller to exit immediately, as that means that the restarter
87 * The service's normal exit status values should be should be taken
88 * from enum ipropd_exit_code. IPROPD_FATAL causes the restarter to
89 * stop restarting the service and to exit.
91 * A count of restarts is output via the `countp' argument, if it is
92 * non-NULL. This is useful for testing this function (e.g., kill the
93 * restarter after N restarts and check that the child gets the signal
96 * This requires fork() and waitpid() (otherwise returns -1). Ignoring
97 * SIGCHLD, of course, would be bad.
99 * We could support this on Windows by spawning a child with mostly the
100 * same arguments as the restarter process.
103 restarter(krb5_context context, size_t *countp)
105 #if defined(HAVE_FORK) && defined(HAVE_WAITPID)
106 struct timeval tmout;
120 signal(SIGCHLD, SIG_DFL);
123 /* Close the pipe ends we keep open */
125 (void) close(fds[1]);
127 (void) close(fds2[1]);
129 /* A pipe so the child can detect the parent's death */
130 if (pipe(fds) == -1) {
131 krb5_err(context, 1, errno,
132 "Could not setup pipes in service restarter");
135 /* A pipe so the parent can detect the child's death */
136 if (pipe(fds2) == -1) {
137 krb5_err(context, 1, errno,
138 "Could not setup pipes in service restarter");
146 krb5_err(context, 1, errno, "Could not fork in service restarter");
150 (void) close(fds[1]);
151 (void) close(fds2[0]);
157 (void) close(fds[0]);
158 (void) close(fds2[1]);
161 wpid = waitpid(pid, &status, 0);
162 } while (wpid == -1 && errno == EINTR && !exit_flag);
163 if (wpid == -1 && errno == EINTR)
164 break; /* We were signaled; gotta kill the child and exit */
166 if (errno != ECHILD) {
167 warn("waitpid() failed; killing restarter's child process");
170 krb5_err(context, 1, errno, "restarter failed waiting for child");
176 if (WIFEXITED(status)) {
177 switch (WEXITSTATUS(status)) {
180 case IPROPD_RESTART_SLOW:
183 krb5_warnx(context, "Waiting 2 minutes to restart");
187 krb5_errx(context, WEXITSTATUS(status),
188 "Sockets and pipes not supported for "
194 /* Add exponential backoff (with max backoff)? */
195 krb5_warnx(context, "Waiting 30 seconds to restart");
201 krb5_warnx(context, "Child was killed; waiting 30 seconds to restart");
206 exit(0); /* No dead child to reap; done */
210 warnx("Interrupted; killing child (pid %ld) with %d",
211 (long)pid, exit_flag);
212 krb5_warnx(context, "Interrupted; killing child (pid %ld) with %d",
213 (long)pid, exit_flag);
214 kill(pid, exit_flag);
216 /* Wait up to one second for the child */
220 FD_SET(fds2[0], &readset);
221 /* We don't care why select() returns */
222 (void) select(fds2[0] + 1, &readset, NULL, NULL, &tmout);
224 * We haven't reaped the child yet; if it's a zombie, then
225 * SIGKILLing it won't hurt. If it's not a zombie yet, well,
226 * we're out of patience.
230 wpid = waitpid(pid, &status, 0);
231 } while (wpid != pid && errno == EINTR);
233 krb5_err(context, 1, errno, "restarter failed waiting for child");
236 /* Finally, the child is dead and reaped */
237 if (WIFEXITED(status))
238 exit(WEXITSTATUS(status));
239 if (WIFSIGNALED(status)) {
240 switch (WTERMSIG(status)) {
247 * Attempt to set the same exit status for the parent as for
250 kill(getpid(), WTERMSIG(status));
252 * We can get past the self-kill if we inherited a SIG_IGN
253 * disposition that the child reset to SIG_DFL.