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.