HEIMDAL: move code from source4/heimdal* to third_party/heimdal*
[Samba.git] / third_party / heimdal / lib / kadm5 / ipropd_common.c
blobbe0adc1b3802e42142f4cad99af4b5206de9471b
1 /*
2 * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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
31 * SUCH DAMAGE.
34 #include "iprop.h"
36 #if defined(HAVE_FORK) && defined(HAVE_WAITPID)
37 #include <sys/types.h>
38 #include <sys/wait.h>
39 #endif
41 sig_atomic_t exit_flag;
43 static RETSIGTYPE
44 sigterm(int sig)
46 exit_flag = sig;
49 void
50 setup_signal(void)
52 #ifdef HAVE_SIGACTION
54 struct sigaction sa;
56 sa.sa_flags = 0;
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);
67 #else
68 signal(SIGINT, sigterm);
69 signal(SIGTERM, sigterm);
70 #ifndef NO_SIGXCPU
71 signal(SIGXCPU, sigterm);
72 #endif
73 #ifndef NO_SIGPIPE
74 signal(SIGPIPE, SIG_IGN);
75 #endif
76 #endif
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
85 * exited.
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
94 * sent to it).
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;
107 pid_t pid = -1;
108 pid_t wpid = -1;
109 int status;
110 int fds[2];
111 int fds2[2];
112 size_t count = 0;
113 fd_set readset;
115 fds[0] = -1;
116 fds[1] = -1;
117 fds2[0] = -1;
118 fds2[1] = -1;
120 signal(SIGCHLD, SIG_DFL);
122 while (!exit_flag) {
123 /* Close the pipe ends we keep open */
124 if (fds[1] != -1)
125 (void) close(fds[1]);
126 if (fds2[0] != -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");
141 fflush(stdout);
142 fflush(stderr);
144 pid = fork();
145 if (pid == -1)
146 krb5_err(context, 1, errno, "Could not fork in service restarter");
147 if (pid == 0) {
148 if (countp != NULL)
149 *countp = count;
150 (void) close(fds[1]);
151 (void) close(fds2[0]);
152 return fds[0];
155 count++;
157 (void) close(fds[0]);
158 (void) close(fds2[1]);
160 do {
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 */
165 if (wpid == -1) {
166 if (errno != ECHILD) {
167 warn("waitpid() failed; killing restarter's child process");
168 kill(pid, SIGTERM);
170 krb5_err(context, 1, errno, "restarter failed waiting for child");
173 assert(wpid == pid);
174 wpid = -1;
175 pid = -1;
176 if (WIFEXITED(status)) {
177 switch (WEXITSTATUS(status)) {
178 case IPROPD_DONE:
179 exit(0);
180 case IPROPD_RESTART_SLOW:
181 if (exit_flag)
182 exit(1);
183 krb5_warnx(context, "Waiting 2 minutes to restart");
184 sleep(120);
185 continue;
186 case IPROPD_FATAL:
187 krb5_errx(context, WEXITSTATUS(status),
188 "Sockets and pipes not supported for "
189 "iprop log files");
190 case IPROPD_RESTART:
191 default:
192 if (exit_flag)
193 exit(1);
194 /* Add exponential backoff (with max backoff)? */
195 krb5_warnx(context, "Waiting 30 seconds to restart");
196 sleep(30);
197 continue;
200 /* else */
201 krb5_warnx(context, "Child was killed; waiting 30 seconds to restart");
202 sleep(30);
205 if (pid == -1)
206 exit(0); /* No dead child to reap; done */
208 assert(pid > 0);
209 if (wpid != pid) {
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 */
217 tmout.tv_sec = 1;
218 tmout.tv_usec = 0;
219 FD_ZERO(&readset);
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.
228 kill(pid, SIGKILL);
229 do {
230 wpid = waitpid(pid, &status, 0);
231 } while (wpid != pid && errno == EINTR);
232 if (wpid == -1)
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)) {
241 case SIGTERM:
242 case SIGXCPU:
243 case SIGINT:
244 exit(0);
245 default:
247 * Attempt to set the same exit status for the parent as for
248 * the child.
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.
257 exit(1);
258 #else
259 if (countp != NULL)
260 *countp = 0;
261 errno = ENOTSUP;
262 return -1;
263 #endif