mirror of
https://github.com/samba-team/samba.git
synced 2025-01-11 05:18:09 +03:00
51569b3152
NOTE: THIS COMMIT WON'T COMPILE/WORK ON ITS OWN! BUG: https://bugzilla.samba.org/show_bug.cgi?id=14995 Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> Reviewed-by: Stefan Metzmacher <metze@samba.org> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
267 lines
8.0 KiB
C
267 lines
8.0 KiB
C
/*
|
|
* Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
|
|
* (Royal Institute of Technology, Stockholm, Sweden).
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the Institute nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "iprop.h"
|
|
|
|
#if defined(HAVE_FORK) && defined(HAVE_WAITPID)
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#endif
|
|
|
|
sig_atomic_t exit_flag;
|
|
|
|
static RETSIGTYPE
|
|
sigterm(int sig)
|
|
{
|
|
exit_flag = sig;
|
|
}
|
|
|
|
void
|
|
setup_signal(void)
|
|
{
|
|
#ifdef HAVE_SIGACTION
|
|
{
|
|
struct sigaction sa;
|
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
sa.sa_flags = 0;
|
|
sa.sa_handler = sigterm;
|
|
sigemptyset(&sa.sa_mask);
|
|
|
|
sigaction(SIGINT, &sa, NULL);
|
|
sigaction(SIGTERM, &sa, NULL);
|
|
sigaction(SIGXCPU, &sa, NULL);
|
|
|
|
sa.sa_handler = SIG_IGN;
|
|
sigaction(SIGPIPE, &sa, NULL);
|
|
}
|
|
#else
|
|
signal(SIGINT, sigterm);
|
|
signal(SIGTERM, sigterm);
|
|
#ifndef NO_SIGXCPU
|
|
signal(SIGXCPU, sigterm);
|
|
#endif
|
|
#ifndef NO_SIGPIPE
|
|
signal(SIGPIPE, SIG_IGN);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Fork a child to run the service, and restart it if it dies.
|
|
*
|
|
* Returns -1 if not supported, else a file descriptor that the service
|
|
* should select() for. Any events on that file descriptor should cause
|
|
* the caller to exit immediately, as that means that the restarter
|
|
* exited.
|
|
*
|
|
* The service's normal exit status values should be should be taken
|
|
* from enum ipropd_exit_code. IPROPD_FATAL causes the restarter to
|
|
* stop restarting the service and to exit.
|
|
*
|
|
* A count of restarts is output via the `countp' argument, if it is
|
|
* non-NULL. This is useful for testing this function (e.g., kill the
|
|
* restarter after N restarts and check that the child gets the signal
|
|
* sent to it).
|
|
*
|
|
* This requires fork() and waitpid() (otherwise returns -1). Ignoring
|
|
* SIGCHLD, of course, would be bad.
|
|
*
|
|
* We could support this on Windows by spawning a child with mostly the
|
|
* same arguments as the restarter process.
|
|
*/
|
|
int
|
|
restarter(krb5_context context, size_t *countp)
|
|
{
|
|
#if defined(HAVE_FORK) && defined(HAVE_WAITPID)
|
|
struct timeval tmout;
|
|
pid_t pid = -1;
|
|
pid_t wpid = -1;
|
|
int status;
|
|
int fds[2];
|
|
int fds2[2];
|
|
size_t count = 0;
|
|
fd_set readset;
|
|
|
|
fds[0] = -1;
|
|
fds[1] = -1;
|
|
fds2[0] = -1;
|
|
fds2[1] = -1;
|
|
|
|
signal(SIGCHLD, SIG_DFL);
|
|
|
|
while (!exit_flag) {
|
|
/* Close the pipe ends we keep open */
|
|
if (fds[1] != -1)
|
|
(void) close(fds[1]);
|
|
if (fds2[0] != -1)
|
|
(void) close(fds2[1]);
|
|
|
|
/* A pipe so the child can detect the parent's death */
|
|
if (pipe(fds) == -1) {
|
|
krb5_err(context, 1, errno,
|
|
"Could not setup pipes in service restarter");
|
|
}
|
|
|
|
/* A pipe so the parent can detect the child's death */
|
|
if (pipe(fds2) == -1) {
|
|
krb5_err(context, 1, errno,
|
|
"Could not setup pipes in service restarter");
|
|
}
|
|
|
|
fflush(stdout);
|
|
fflush(stderr);
|
|
|
|
pid = fork();
|
|
if (pid == -1)
|
|
krb5_err(context, 1, errno, "Could not fork in service restarter");
|
|
if (pid == 0) {
|
|
if (countp != NULL)
|
|
*countp = count;
|
|
(void) close(fds[1]);
|
|
(void) close(fds2[0]);
|
|
return fds[0];
|
|
}
|
|
|
|
count++;
|
|
|
|
(void) close(fds[0]);
|
|
(void) close(fds2[1]);
|
|
|
|
do {
|
|
wpid = waitpid(pid, &status, 0);
|
|
} while (wpid == -1 && errno == EINTR && !exit_flag);
|
|
if (wpid == -1 && errno == EINTR)
|
|
break; /* We were signaled; gotta kill the child and exit */
|
|
if (wpid == -1) {
|
|
if (errno != ECHILD) {
|
|
warn("waitpid() failed; killing restarter's child process");
|
|
kill(pid, SIGTERM);
|
|
}
|
|
krb5_err(context, 1, errno, "restarter failed waiting for child");
|
|
}
|
|
|
|
assert(wpid == pid);
|
|
wpid = -1;
|
|
pid = -1;
|
|
if (WIFEXITED(status)) {
|
|
switch (WEXITSTATUS(status)) {
|
|
case IPROPD_DONE:
|
|
exit(0);
|
|
case IPROPD_RESTART_SLOW:
|
|
if (exit_flag)
|
|
exit(1);
|
|
krb5_warnx(context, "Waiting 2 minutes to restart");
|
|
sleep(120);
|
|
continue;
|
|
case IPROPD_FATAL:
|
|
krb5_errx(context, WEXITSTATUS(status),
|
|
"Sockets and pipes not supported for "
|
|
"iprop log files");
|
|
case IPROPD_RESTART:
|
|
default:
|
|
if (exit_flag)
|
|
exit(1);
|
|
/* Add exponential backoff (with max backoff)? */
|
|
krb5_warnx(context, "Waiting 30 seconds to restart");
|
|
sleep(30);
|
|
continue;
|
|
}
|
|
}
|
|
/* else */
|
|
krb5_warnx(context, "Child was killed; waiting 30 seconds to restart");
|
|
sleep(30);
|
|
}
|
|
|
|
if (pid == -1)
|
|
exit(0); /* No dead child to reap; done */
|
|
|
|
assert(pid > 0);
|
|
if (wpid != pid) {
|
|
warnx("Interrupted; killing child (pid %ld) with %d",
|
|
(long)pid, exit_flag);
|
|
krb5_warnx(context, "Interrupted; killing child (pid %ld) with %d",
|
|
(long)pid, exit_flag);
|
|
kill(pid, exit_flag);
|
|
|
|
/* Wait up to one second for the child */
|
|
tmout.tv_sec = 1;
|
|
tmout.tv_usec = 0;
|
|
FD_ZERO(&readset);
|
|
FD_SET(fds2[0], &readset);
|
|
/* We don't care why select() returns */
|
|
(void) select(fds2[0] + 1, &readset, NULL, NULL, &tmout);
|
|
/*
|
|
* We haven't reaped the child yet; if it's a zombie, then
|
|
* SIGKILLing it won't hurt. If it's not a zombie yet, well,
|
|
* we're out of patience.
|
|
*/
|
|
kill(pid, SIGKILL);
|
|
do {
|
|
wpid = waitpid(pid, &status, 0);
|
|
} while (wpid != pid && errno == EINTR);
|
|
if (wpid == -1)
|
|
krb5_err(context, 1, errno, "restarter failed waiting for child");
|
|
}
|
|
|
|
/* Finally, the child is dead and reaped */
|
|
if (WIFEXITED(status))
|
|
exit(WEXITSTATUS(status));
|
|
if (WIFSIGNALED(status)) {
|
|
switch (WTERMSIG(status)) {
|
|
case SIGTERM:
|
|
case SIGXCPU:
|
|
case SIGINT:
|
|
exit(0);
|
|
default:
|
|
/*
|
|
* Attempt to set the same exit status for the parent as for
|
|
* the child.
|
|
*/
|
|
kill(getpid(), WTERMSIG(status));
|
|
/*
|
|
* We can get past the self-kill if we inherited a SIG_IGN
|
|
* disposition that the child reset to SIG_DFL.
|
|
*/
|
|
}
|
|
}
|
|
exit(1);
|
|
#else
|
|
if (countp != NULL)
|
|
*countp = 0;
|
|
errno = ENOTSUP;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|