diff dcclib/restart.c @ 0:c7f6b056b673

First import of vendor version
author Peter Gervai <grin@grin.hu>
date Tue, 10 Mar 2009 13:49:58 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/restart.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,231 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * continually restart a daemon
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Rhyolite Software DCC 1.3.103-1.16 $Revision$
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <pwd.h>
+#include "dcc_clnt.h"
+
+
+static pid_t start_pid = -1;
+
+static void
+restart_sigterm(int sig)
+{
+	if (start_pid > 0
+	    && 0 <= kill(start_pid, sig))
+		return;
+	exit(-1);
+}
+
+
+
+#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, void(reconn)(void))
+{
+#if defined(WIFEXITED) && defined(WTERMSIG) && defined(WIFSIGNALED)
+	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);
+			etype = "exit ";
+			if (status != EX_DCC_RESTART) {
+				if (dcc_clnt_debug)
+					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 SIGXCPU
+			    && status != SIGXCPU
+#endif
+#ifdef SIGFPE
+			    && status != SIGFPE
+#endif
+#ifdef SIGSYS
+			    && status != SIGSYS
+#endif
+#ifdef SIGTRAP
+			    && status != SIGTRAP
+#endif
+			    && status != SIGQUIT
+			    && status != SIGABRT) {
+				if (dcc_clnt_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);
+
+		} 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 /* not POSIX */
+}
+
+
+
+/* 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, sizeof(DCC_PATH), "%s/%s.pid",
+		 rundir, dcc_progname);
+	unlink(pidpath);
+	f = fopen(pidpath, "w");
+	if (!f) {
+		dcc_error_msg("fopen(%s): %s", pidpath, 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);
+	}
+}