/* Distributed Checksum Clearinghouse * * continually restart a daemon * * --C-LICENSE-- * $Revision: 1.22 $ */ #include "dcc_defs.h" #include #include #include #if defined(WIFEXITED) && defined(WTERMSIG) && defined(WIFSIGNALED) #define HAVE_POSIX_WAIT #else #undef HAVE_POSIX_WAIT #endif #ifdef HAVE_POSIX_WAIT static pid_t start_pid = -1; static void restart_sigterm(int sig) { if (start_pid > 0 && 0 <= kill(start_pid, sig)) return; exit(-1); } #endif /* HAVE_POSIX_WAIT */ #define MIN_RESTART_DELAY 5 #define MAX_RESTART_DELAY (5*60) /* stay alive despite core dumps * Restart immediately, but not more often than every MINRESTART_DELAY * seconds. If the restarts are happening at the maximum rate, * halve the rate. */ void dcc_daemon_restart(const char *rundir DCC_UNUSED, void(reconn)(void) DCC_UNUSED, u_char debug) { #ifdef HAVE_POSIX_WAIT pid_t pid; time_t next_restart, restart_delay; int status; const char *etype; DCC_PATH pidpath; signal(SIGHUP, restart_sigterm); signal(SIGTERM, restart_sigterm); signal(SIGINT, restart_sigterm); restart_delay = MIN_RESTART_DELAY; next_restart = 0; for (;;) { start_pid = fork(); if (!start_pid) { #ifdef HAVE_SETPGID if (0 > setpgid(0, 0)) dcc_error_msg("setpgid(): %s", ERROR_STR()); #endif /* reconnect sockets or whatever except first time */ if (next_restart != 0 && reconn) reconn(); return; } if (start_pid < 0) dcc_logbad(EX_OSERR, "(re)start fork(): %s", ERROR_STR()); next_restart = time(0)+restart_delay; pid = waitpid(start_pid, &status, 0); if (pid < 0) { if (errno != EINTR) dcc_logbad(EX_OSERR, "restart waitpid(): %s", ERROR_STR()); exit(0); } start_pid = -1; if (WIFEXITED(status)) { status = WEXITSTATUS(status); if (status == EX_DCC_RESTART) { etype = "exit "; } else { if (debug) { if (status >= EX_DCC_SIGNAL && status <= EX_DCC_SIGNAL_MAX) dcc_error_msg("do not restart after" " signal %d", status - EX_DCC_SIGNAL); else dcc_error_msg("do not restart after" " exit(%d)", status); } exit(status); } } else if (WIFSIGNALED(status)) { status = WTERMSIG(status); etype = "signal "; if (status != SIGILL && status != SIGSEGV #ifdef SIGBUS && status != SIGBUS #endif #ifdef SIGFPE && status != SIGFPE #endif #ifdef SIGSYS && status != SIGSYS #endif #ifdef SIGTRAP && status != SIGTRAP #endif #ifdef SIGXCPU && status != SIGXCPU #endif #ifdef SIGXFSZ && status != SIGXFSZ #endif && status != SIGQUIT && status != SIGABRT) { if (debug) dcc_error_msg("do not restart after" " signal %d", status); exit(0); } } else { dcc_logbad(EX_OSERR, "unknown exit status %#x", status); } next_restart -= time(0); if (next_restart > 0 && next_restart <= MAX_RESTART_DELAY) { restart_delay *= 2; if (restart_delay > MAX_RESTART_DELAY) restart_delay = MAX_RESTART_DELAY; /* while we wait, become the designated target */ dcc_error_msg("delay %d seconds to restart after %s%d", (int)next_restart, etype, status); dcc_pidfile(&pidpath, rundir); sleep(next_restart); unlink(pidpath.c); } else { restart_delay -= next_restart-1; if (restart_delay < MIN_RESTART_DELAY) restart_delay = MIN_RESTART_DELAY; } dcc_error_msg("restart after %s%d", etype, status); } #endif /* HAVE_POSIX_WAIT */ } /* change to the UID and GID of a daemon */ void dcc_daemon_su(const char *user) { struct passwd *pw; pw = getpwnam(user); if (!pw) { dcc_error_msg("getpwnam(%s): %s", user, ERROR_STR()); return; } if (0 > setgid(pw->pw_gid)) dcc_error_msg("setgid(%d %s): %s", (int)pw->pw_gid, user, ERROR_STR()); if (0 > setuid(pw->pw_uid)) dcc_error_msg("setuid(%d %s): %s", (int)pw->pw_uid, user, ERROR_STR()); } void dcc_pidfile(DCC_PATH *pidpath, const char *rundir) { FILE *f; snprintf(pidpath->c, sizeof(DCC_PATH), "%s/%s.pid", rundir, dcc_progname.c); unlink(pidpath->c); f = fopen(pidpath->c, "w"); if (!f) { dcc_error_msg("fopen(%s): %s", pidpath->c, ERROR_STR()); } else { #ifdef linux /* Linux threads are broken. Signals given the * original process are delivered to only the * thread that happens to have that PID. The * sendmail libmilter thread that needs to hear * SIGINT and other signals is known only to the milter. * Unless you put its PID into the file, it will not hear * the signals. That breaks scripts that need to stop dccm. * However, signaling the process group works. */ fprintf(f, "-%d\n", (u_int)getpgrp()); #else fprintf(f, "%d\n", (u_int)getpid()); #endif fclose(f); } }