public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-commits] proj/openrc:master commit in: src/rc/, /, man/, sh/
@ 2016-04-27 16:21 William Hubbs
  0 siblings, 0 replies; only message in thread
From: William Hubbs @ 2016-04-27 16:21 UTC (permalink / raw
  To: gentoo-commits

commit:     62410eaf4ba92516a58a550717d7f3faf63bb79f
Author:     William Hubbs <w.d.hubbs <AT> gmail <DOT> com>
AuthorDate: Mon Feb  1 18:42:58 2016 +0000
Commit:     William Hubbs <williamh <AT> gentoo <DOT> org>
CommitDate: Wed Apr 27 16:13:50 2016 +0000
URL:        https://gitweb.gentoo.org/proj/openrc.git/commit/?id=62410eaf

add daemon supervisor

The supervise-daemon process is meant to be a lightweight supervisor
which can monitor and restart a daemon if it crashes.

 man/Makefile              |   2 +-
 man/openrc-run.8          |  21 +-
 man/supervise-daemon.8    | 142 +++++++++
 sh/Makefile               |   3 +-
 sh/openrc-run.sh.in       |   4 +
 sh/supervise-daemon.sh    |  49 ++++
 src/rc/.gitignore         |   1 +
 src/rc/Makefile           |   8 +-
 src/rc/supervise-daemon.c | 722 ++++++++++++++++++++++++++++++++++++++++++++++
 supervise-daemon-guide.md |  45 +++
 10 files changed, 990 insertions(+), 7 deletions(-)

diff --git a/man/Makefile b/man/Makefile
index 73db2a8..48c5842 100644
--- a/man/Makefile
+++ b/man/Makefile
@@ -6,7 +6,7 @@ MAN3=		einfo.3 \
 		rc_config.3 rc_deptree.3 rc_find_pids.3 rc_plugin_hook.3 \
 		rc_runlevel.3 rc_service.3 rc_stringlist.3
 MAN8=		rc-service.8 rc-status.8 rc-update.8 openrc.8 openrc-run.8 \
-		service.8 start-stop-daemon.8
+		service.8 start-stop-daemon.8 supervise-daemon.8
 
 ifeq (${OS},Linux)
 MAN8 += rc-sstat.8

diff --git a/man/openrc-run.8 b/man/openrc-run.8
index b23c5fe..be15d59 100644
--- a/man/openrc-run.8
+++ b/man/openrc-run.8
@@ -95,10 +95,17 @@ String describing the service.
 .It Ar description_$command
 String describing the extra command.
 .It Ar supervisor
-Supervisor to use to monitor this daemon. If this is unset,
-start-stop-daemon will be used. The only alternate supervisor we support
-in this release is S6 from Skarnet software. To use this, set
+Supervisor to use to monitor this daemon. If this is unset or invalid,
+start-stop-daemon will be used.
+Currently, we support s6 from scarnet software, and supervise-daemon
+which is a light-weight supervisor internal to OpenRC.
+To use s6, set
 supervisor=s6.
+or set
+supervisor=supervise-daemon
+to use supervise-daemon.
+Note that supervise-daemon is still in early development, so it is
+considered experimental.
 .It Ar s6_service_path
 The path to the s6 service directory if you are monitoring this service
 with S6. The default is /var/svc.d/${RC_SVCNAME}.
@@ -112,10 +119,16 @@ List of arguments passed to start-stop-daemon when starting the daemon.
 .It Ar command
 Daemon to start or stop via
 .Nm start-stop-daemon
+or
+.Nm supervise-daemon
 if no start or stop function is defined by the service.
 .It Ar command_args
 List of arguments to pass to the daemon when starting via
 .Nm start-stop-daemon .
+.It Ar command_args_foreground
+List of arguments to pass to the daemon when starting via
+.Nm supervise-daemon .
+to force the daemon to stay in the foreground
 .It Ar command_background
 Set this to "true", "yes" or "1" (case-insensitive) to force the daemon into
 the background. This implies the "--make-pidfile" and "--pidfile" option of
@@ -123,6 +136,8 @@ the background. This implies the "--make-pidfile" and "--pidfile" option of
 so the pidfile variable must be set.
 .It Ar chroot
 .Xr start-stop-daemon 8
+and
+.Xr supervise-daemon 8
 will chroot into this path before writing the pid file or starting the daemon.
 .It Ar pidfile
 Pidfile to use for the above defined command.

diff --git a/man/supervise-daemon.8 b/man/supervise-daemon.8
new file mode 100644
index 0000000..0608767
--- /dev/null
+++ b/man/supervise-daemon.8
@@ -0,0 +1,142 @@
+.\" Copyright (c) 2007-2015 The OpenRC Authors.
+.\" See the Authors file at the top-level directory of this distribution and
+.\" https://github.com/OpenRC/openrc/blob/master/AUTHORS
+.\"
+.\" This file is part of OpenRC. It is subject to the license terms in
+.\" the LICENSE file found in the top-level directory of this
+.\" distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
+.\" This file may not be copied, modified, propagated, or distributed
+.\"    except according to the terms contained in the LICENSE file.
+.\"
+.Dd April 27, 2016
+.Dt supervise-DAEMON 8 SMM
+.Os OpenRC
+.Sh NAME
+.Nm supervise-daemon
+.Nd starts a daemon and restarts it if it crashes
+.Sh SYNOPSIS
+.Nm
+.Fl d , -chdir
+.Ar path
+.Fl e , -env
+.Ar var=value
+.Fl g , -group
+.Ar group
+.Fl I , -ionice
+.Ar arg
+.Fl k , -umask
+.Ar value
+.Fl N , -nicelevel
+.Ar level
+.Fl p , -pidfile
+.Ar pidfile
+.Fl u , -user
+.Ar user
+.Fl r , -chroot
+.Ar chrootpath
+.Fl 1 , -stdout
+.Ar logfile
+.Fl 2 , -stderr
+.Ar logfile
+.Fl S , -start
+.Ar daemon
+.Op Fl -
+.Op Ar arguments
+.Nm
+.Fl K , -stop
+.Ar daemon
+.Fl p , -pidfile
+.Ar pidfile
+.Fl r , -chroot
+.Ar chrootpath
+.Sh DESCRIPTION
+.Nm
+provides a consistent method of starting, stopping and restarting
+daemons. If
+.Fl K , -stop
+is not provided, then we assume we are starting the daemon.
+.Nm
+only works with daemons which do not fork. Also, it uses its own pid
+file, so the daemon should not write a pid file, or the pid file passed
+to 
+.Nm
+should not be the one the daemon writes.
+.Pp
+Here are the options to specify the daemon and how it should start or stop:
+.Bl -tag -width indent
+.It Fl p , -pidfile Ar pidfile
+When starting, we write a
+.Ar pidfile
+so we know which supervisor to stop.  When stopping we only stop the pid(s)
+listed in the
+.Ar pidfile .
+.It Fl u , -user Ar user Ns Op : Ns Ar group
+Start the daemon as the
+.Ar user
+and update $HOME accordingly or stop daemons
+owned by the user. You can optionally append a
+.Ar group
+name here also.
+.It Fl v , -verbose
+Print the action(s) that are taken just before doing them.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl d , -chdir Ar path
+chdir to this directory before starting the daemon.
+.It Fl e , -env Ar VAR=VALUE
+Set the environment variable VAR to VALUE.
+.It Fl g , -group Ar group
+Start the daemon as in the group.
+.It Fl I , -ionice Ar class Ns Op : Ns Ar data
+Modifies the IO scheduling priority of the daemon.
+Class can be 0 for none, 1 for real time, 2 for best effort and 3 for idle.
+Data can be from 0 to 7 inclusive.
+.It Fl k , -umask Ar mode
+Set the umask of the daemon.
+.It Fl N , -nicelevel Ar level
+Modifies the scheduling priority of the daemon.
+.It Fl r , -chroot Ar path
+chroot to this directory before starting the daemon. All other paths, such
+as the path to the daemon, chdir and pidfile, should be relative to the chroot.
+.It Fl u , -user Ar user
+Start the daemon as the specified user.
+.It Fl 1 , -stdout Ar logfile
+Redirect the standard output of the process to logfile.
+Must be an absolute pathname, but relative to the path optionally given with
+.Fl r , -chroot .
+The logfile can also be a named pipe.
+.It Fl 2 , -stderr Ar logfile
+The same thing as
+.Fl 1 , -stdout
+but with the standard error output.
+.El
+.Sh ENVIRONMENT
+.Va SSD_NICELEVEL
+can also set the scheduling priority of the daemon, but the command line
+option takes precedence.
+.Sh NOTE
+.Nm
+uses
+.Xr getopt 3
+to parse its options, which allows it to accept the `--' option which will
+cause it to stop processing options at that point. Any subsequent arguments
+are passed as arguments to the daemon to start and used when finding a daemon
+to stop or signal.
+.Sh SEE ALSO
+.Xr chdir 2 ,
+.Xr chroot 2 ,
+.Xr getopt 3 ,
+.Xr nice 2 ,
+.Xr rc_find_pids 3
+.Sh BUGS
+.Nm
+cannot stop an interpreted daemon that no longer exists without a pidfile.
+.Sh HISTORY
+.Nm
+first appeared in Debian.
+.Pp
+This is a complete re-implementation with the process finding code in the
+OpenRC library (librc, -lrc) so other programs can make use of it.
+.Sh AUTHORS
+.An William Hubbs <w.d.hubbs@gmail.com>

diff --git a/sh/Makefile b/sh/Makefile
index b9b9fb3..24c2315 100644
--- a/sh/Makefile
+++ b/sh/Makefile
@@ -1,7 +1,8 @@
 DIR=	${LIBEXECDIR}/sh
 SRCS=	init.sh.in functions.sh.in gendepends.sh.in \
 	openrc-run.sh.in rc-functions.sh.in tmpfiles.sh.in ${SRCS-${OS}}
-INC=	rc-mount.sh functions.sh rc-functions.sh s6.sh start-stop-daemon.sh
+INC=	functions.sh rc-mount.sh rc-functions.sh s6.sh start-stop-daemon.sh \
+		supervise-daemon.sh
 BIN=	gendepends.sh init.sh openrc-run.sh tmpfiles.sh ${BIN-${OS}}
 
 INSTALLAFTER=	_installafter

diff --git a/sh/openrc-run.sh.in b/sh/openrc-run.sh.in
index fb6f95b..36bc366 100644
--- a/sh/openrc-run.sh.in
+++ b/sh/openrc-run.sh.in
@@ -154,6 +154,7 @@ start()
 	local func=ssd_start
 	case "$supervisor" in
 		s6) func=s6_start ;;
+		supervise-daemon) func=supervise_start ;;
 		?*)
 			ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon"
 			;;
@@ -166,6 +167,7 @@ stop()
 	local func=ssd_stop
 	case "$supervisor" in
 		s6) func=s6_stop ;;
+		supervise-daemon) func=supervise_stop ;;
 		?*)
 			ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon"
 			;;
@@ -178,6 +180,7 @@ status()
 	local func=ssd_status
 	case "$supervisor" in
 		s6) func=s6_status ;;
+		supervise-daemon) func=supervise_status ;;
 		?*)
 			ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon"
 			;;
@@ -215,6 +218,7 @@ fi
 # load service supervisor functions
 sourcex "@LIBEXECDIR@/sh/s6.sh"
 sourcex "@LIBEXECDIR@/sh/start-stop-daemon.sh"
+sourcex "@LIBEXECDIR@/sh/supervise-daemon.sh"
 
 # Set verbose mode
 if yesno "${rc_verbose:-$RC_VERBOSE}"; then

diff --git a/sh/supervise-daemon.sh b/sh/supervise-daemon.sh
new file mode 100644
index 0000000..34e3ef7
--- /dev/null
+++ b/sh/supervise-daemon.sh
@@ -0,0 +1,49 @@
+# start / stop / status functions for supervise-daemon
+
+# Copyright (c) 2016 The OpenRC Authors.
+# See the Authors file at the top-level directory of this distribution and
+# https://github.com/OpenRC/openrc/blob/master/AUTHORS
+#
+# This file is part of OpenRC. It is subject to the license terms in
+# the LICENSE file found in the top-level directory of this
+# distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
+# This file may not be copied, modified, propagated, or distributed
+#    except according to the terms contained in the LICENSE file.
+
+supervise_start()
+{
+	if [ -z "$command" ]; then
+		ewarn "The command variable is undefined."
+		ewarn "There is nothing for ${name:-$RC_SVCNAME} to start."
+		return 1
+	fi
+
+	ebegin "Starting ${name:-$RC_SVCNAME}"
+	eval supervise-daemon --start \
+		${pidfile:+--pidfile} $pidfile \
+		${command_user+--user} $command_user \
+		$supervise_daemon_args \
+		$command \
+		-- $command_args $command_args_foreground
+	rc=$?
+	[ -n "${pidfile}" ] && service_set_value "pidfile" "${pidfile}"
+	eend $rc "failed to start $RC_SVCNAME"
+}
+
+supervise_stop()
+{
+	local startpidfile="$(service_get_value "pidfile")"
+	pidfile="${startpidfile:-$pidfile}"
+	[ -n "$pidfile" ] || return 0
+	ebegin "Stopping ${name:-$RC_SVCNAME}"
+	supervise-daemon --stop \
+		${pidfile:+--pidfile} $pidfile \
+		${stopsig:+--signal} $stopsig
+
+	eend $? "Failed to stop $RC_SVCNAME"
+}
+
+supervise_status()
+{
+	_status
+}

diff --git a/src/rc/.gitignore b/src/rc/.gitignore
index bbfede6..c977919 100644
--- a/src/rc/.gitignore
+++ b/src/rc/.gitignore
@@ -5,6 +5,7 @@ rc-update
 runscript
 service
 start-stop-daemon
+supervise-daemon
 einfon
 einfo
 ewarnn

diff --git a/src/rc/Makefile b/src/rc/Makefile
index 71ae503..d4759e7 100644
--- a/src/rc/Makefile
+++ b/src/rc/Makefile
@@ -3,7 +3,7 @@ SRCS=	checkpath.c do_e.c do_mark_service.c do_service.c \
 		mountinfo.c openrc-run.c rc-abort.c rc.c \
 		rc-depend.c rc-logger.c rc-misc.c rc-plugin.c \
 		rc-service.c rc-status.c rc-update.c \
-		shell_var.c start-stop-daemon.c swclock.c _usage.c
+		shell_var.c start-stop-daemon.c supervise-daemon.c swclock.c _usage.c
 
 ifeq (${MKSELINUX},yes)
 SRCS+=		rc-selinux.c
@@ -16,7 +16,8 @@ SBINDIR=	${PREFIX}/sbin
 LINKDIR=	${LIBEXECDIR}
 
 BINPROGS=	rc-status
-SBINPROGS = openrc openrc-run rc rc-service rc-update runscript service start-stop-daemon
+SBINPROGS = openrc openrc-run rc rc-service rc-update runscript service \
+			start-stop-daemon supervise-daemon
 RC_BINPROGS=	einfon einfo ewarnn ewarn eerrorn eerror ebegin eend ewend \
 				eindent eoutdent esyslog eval_ecolors ewaitfile \
 				veinfo vewarn vebegin veend vewend veindent veoutdent \
@@ -136,6 +137,9 @@ rc-update: rc-update.o _usage.o rc-misc.o
 start-stop-daemon: start-stop-daemon.o _usage.o rc-misc.o
 	${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
 
+supervise-daemon: supervise-daemon.o _usage.o rc-misc.o
+	${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
+
 service_get_value service_set_value get_options save_options: do_value.o rc-misc.o
 	${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
 

diff --git a/src/rc/supervise-daemon.c b/src/rc/supervise-daemon.c
new file mode 100644
index 0000000..6bb75f3
--- /dev/null
+++ b/src/rc/supervise-daemon.c
@@ -0,0 +1,722 @@
+/*
+ * supervise-daemon
+ * This is an experimental supervisor for daemons.
+ * It will start a deamon and make sure it restarts if it crashes.
+ */
+
+/*
+ * Copyright (c) 2016 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/master/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ *    except according to the terms contained in the LICENSE file.
+ */
+
+/* nano seconds */
+#define POLL_INTERVAL   20000000
+#define WAIT_PIDFILE   500000000
+#define ONE_SECOND    1000000000
+#define ONE_MS           1000000
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#ifdef __linux__
+#include <sys/syscall.h> /* For io priority */
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifdef HAVE_PAM
+#include <security/pam_appl.h>
+
+/* We are not supporting authentication conversations */
+static struct pam_conv conv = { NULL, NULL};
+#endif
+
+#include "einfo.h"
+#include "queue.h"
+#include "rc.h"
+#include "rc-misc.h"
+#include "_usage.h"
+
+const char *applet = NULL;
+const char *extraopts = NULL;
+const char *getoptstring = "d:e:g:I:Kk:N:p:r:Su:1:2:" \
+	getoptstring_COMMON;
+const struct option longopts[] = {
+	{ "chdir",        1, NULL, 'd'},
+	{ "env",          1, NULL, 'e'},
+	{ "group",        1, NULL, 'g'},
+	{ "ionice",       1, NULL, 'I'},
+	{ "stop",         0, NULL, 'K'},
+	{ "umask",        1, NULL, 'k'},
+	{ "nicelevel",    1, NULL, 'N'},
+	{ "pidfile",      1, NULL, 'p'},
+	{ "user",         1, NULL, 'u'},
+	{ "chroot",       1, NULL, 'r'},
+	{ "start",        0, NULL, 'S'},
+	{ "stdout",       1, NULL, '1'},
+	{ "stderr",       1, NULL, '2'},
+	longopts_COMMON
+};
+const char * const longopts_help[] = {
+	"Change the PWD",
+	"Set an environment string",
+	"Change the process group",
+	"Set an ionice class:data when starting",
+	"Stop daemon",
+	"Set the umask for the daemon",
+	"Set a nicelevel when starting",
+	"Match pid found in this file",
+	"Change the process user",
+	"Chroot to this directory",
+	"Start daemon",
+	"Redirect stdout to file",
+	"Redirect stderr to file",
+	longopts_help_COMMON
+};
+const char *usagestring = NULL;
+
+static int nicelevel = 0;
+static int ionicec = -1;
+static int ioniced = 0;
+static char *changeuser, *ch_root, *ch_dir;
+static uid_t uid = 0;
+static gid_t gid = 0;
+static int devnull_fd = -1;
+static int stdin_fd;
+static int stdout_fd;
+static int stderr_fd;
+static char *redirect_stderr = NULL;
+static char *redirect_stdout = NULL;
+static bool exiting = false;
+#ifdef TIOCNOTTY
+static int tty_fd = -1;
+#endif
+
+extern char **environ;
+
+#if !defined(SYS_ioprio_set) && defined(__NR_ioprio_set)
+# define SYS_ioprio_set __NR_ioprio_set
+#endif
+#if !defined(__DragonFly__)
+static inline int ioprio_set(int which, int who, int ioprio)
+{
+#ifdef SYS_ioprio_set
+	return syscall(SYS_ioprio_set, which, who, ioprio);
+#else
+	return 0;
+#endif
+}
+#endif
+
+static void cleanup(void)
+{
+	free(changeuser);
+}
+
+static pid_t get_pid(const char *pidfile)
+{
+	FILE *fp;
+	pid_t pid;
+
+	if (! pidfile)
+		return -1;
+
+	if ((fp = fopen(pidfile, "r")) == NULL) {
+		ewarnv("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
+		return -1;
+	}
+
+	if (fscanf(fp, "%d", &pid) != 1) {
+		ewarnv("%s: no pid found in `%s'", applet, pidfile);
+		fclose(fp);
+		return -1;
+	}
+
+	fclose(fp);
+
+	return pid;
+}
+
+static void child_process(char *exec, char **argv)
+{
+	RC_STRINGLIST *env_list;
+	RC_STRING *env;
+	int i;
+	char *p;
+	char *token;
+	size_t len;
+	char *newpath;
+	char *np;
+	char **c;
+	char cmdline[PATH_MAX];
+
+#ifdef HAVE_PAM
+	pam_handle_t *pamh = NULL;
+	int pamr;
+	const char *const *pamenv = NULL;
+#endif
+
+	setsid();
+
+	if (nicelevel) {
+		if (setpriority(PRIO_PROCESS, getpid(), nicelevel) == -1)
+			eerrorx("%s: setpriority %d: %s", applet, nicelevel,
+					strerror(errno));
+	}
+
+	if (ionicec != -1 && ioprio_set(1, getpid(), ionicec | ioniced) == -1)
+		eerrorx("%s: ioprio_set %d %d: %s", applet, ionicec, ioniced,
+				strerror(errno));
+
+	if (ch_root && chroot(ch_root) < 0)
+		eerrorx("%s: chroot `%s': %s", applet, ch_root, strerror(errno));
+
+	if (ch_dir && chdir(ch_dir) < 0)
+		eerrorx("%s: chdir `%s': %s", applet, ch_dir, strerror(errno));
+
+#ifdef HAVE_PAM
+	if (changeuser != NULL) {
+		pamr = pam_start("start-stop-daemon",
+		    changeuser, &conv, &pamh);
+
+		if (pamr == PAM_SUCCESS)
+			pamr = pam_acct_mgmt(pamh, PAM_SILENT);
+		if (pamr == PAM_SUCCESS)
+			pamr = pam_open_session(pamh, PAM_SILENT);
+		if (pamr != PAM_SUCCESS)
+			eerrorx("%s: pam error: %s", applet, pam_strerror(pamh, pamr));
+	}
+#endif
+
+	if (gid && setgid(gid))
+		eerrorx("%s: unable to set groupid to %d", applet, gid);
+	if (changeuser && initgroups(changeuser, gid))
+		eerrorx("%s: initgroups (%s, %d)", applet, changeuser, gid);
+	if (uid && setuid(uid))
+		eerrorx ("%s: unable to set userid to %d", applet, uid);
+
+	/* Close any fd's to the passwd database */
+	endpwent();
+
+#ifdef TIOCNOTTY
+	ioctl(tty_fd, TIOCNOTTY, 0);
+	close(tty_fd);
+#endif
+
+	/* Clean the environment of any RC_ variables */
+	env_list = rc_stringlist_new();
+	i = 0;
+	while (environ[i])
+		rc_stringlist_add(env_list, environ[i++]);
+
+#ifdef HAVE_PAM
+	if (changeuser != NULL) {
+		pamenv = (const char *const *)pam_getenvlist(pamh);
+		if (pamenv) {
+			while (*pamenv) {
+				/* Don't add strings unless they set a var */
+				if (strchr(*pamenv, '='))
+					putenv(xstrdup(*pamenv));
+				else
+					unsetenv(*pamenv);
+				pamenv++;
+			}
+		}
+	}
+#endif
+
+	TAILQ_FOREACH(env, env_list, entries) {
+		if ((strncmp(env->value, "RC_", 3) == 0 &&
+			strncmp(env->value, "RC_SERVICE=", 10) != 0 &&
+			strncmp(env->value, "RC_SVCNAME=", 10) != 0) ||
+		    strncmp(env->value, "SSD_NICELEVEL=", 14) == 0)
+		{
+			p = strchr(env->value, '=');
+			*p = '\0';
+			unsetenv(env->value);
+			continue;
+		}
+	}
+	rc_stringlist_free(env_list);
+
+	/* For the path, remove the rcscript bin dir from it */
+	if ((token = getenv("PATH"))) {
+		len = strlen(token);
+		newpath = np = xmalloc(len + 1);
+		while (token && *token) {
+			p = strchr(token, ':');
+			if (p) {
+				*p++ = '\0';
+				while (*p == ':')
+					p++;
+			}
+			if (strcmp(token, RC_LIBEXECDIR "/bin") != 0 &&
+			    strcmp(token, RC_LIBEXECDIR "/sbin") != 0)
+			{
+				len = strlen(token);
+				if (np != newpath)
+					*np++ = ':';
+				memcpy(np, token, len);
+				np += len;
+				}
+			token = p;
+		}
+		*np = '\0';
+		unsetenv("PATH");
+		setenv("PATH", newpath, 1);
+	}
+
+	stdin_fd = devnull_fd;
+	stdout_fd = devnull_fd;
+	stderr_fd = devnull_fd;
+	if (redirect_stdout) {
+		if ((stdout_fd = open(redirect_stdout,
+			    O_WRONLY | O_CREAT | O_APPEND,
+			    S_IRUSR | S_IWUSR)) == -1)
+			eerrorx("%s: unable to open the logfile"
+				    " for stdout `%s': %s",
+				    applet, redirect_stdout, strerror(errno));
+	}
+	if (redirect_stderr) {
+		if ((stderr_fd = open(redirect_stderr,
+			    O_WRONLY | O_CREAT | O_APPEND,
+			    S_IRUSR | S_IWUSR)) == -1)
+			eerrorx("%s: unable to open the logfile"
+			    " for stderr `%s': %s",
+			    applet, redirect_stderr, strerror(errno));
+	}
+
+	dup2(stdin_fd, STDIN_FILENO);
+	if (redirect_stdout || rc_yesno(getenv("EINFO_QUIET")))
+		dup2(stdout_fd, STDOUT_FILENO);
+	if (redirect_stderr || rc_yesno(getenv("EINFO_QUIET")))
+		dup2(stderr_fd, STDERR_FILENO);
+
+	for (i = getdtablesize() - 1; i >= 3; --i)
+		close(i);
+
+	*cmdline = '\0';
+	c = argv;
+	while (*c) {
+		strcat(cmdline, *c);
+		strcat(cmdline, " ");
+		c++;
+	}
+	syslog(LOG_INFO, "Running command line: %s", cmdline);
+	execvp(exec, argv);
+
+#ifdef HAVE_PAM
+	if (changeuser != NULL && pamr == PAM_SUCCESS)
+		pam_close_session(pamh, PAM_SILENT);
+#endif
+	eerrorx("%s: failed to exec `%s': %s", applet, exec,strerror(errno));
+}
+
+static void handle_signal(int sig)
+{
+	int serrno = errno;
+	char signame[10] = { '\0' };
+
+	switch (sig) {
+	case SIGINT:
+		snprintf(signame, sizeof(signame), "SIGINT");
+		break;
+	case SIGTERM:
+		snprintf(signame, sizeof(signame), "SIGTERM");
+		break;
+	case SIGQUIT:
+		snprintf(signame, sizeof(signame), "SIGQUIT");
+		break;
+	}
+
+	if (*signame != 0) {
+		syslog(LOG_INFO, "%s: caught signal %s, exiting", applet, signame);
+		exiting = true;
+	} else
+		syslog(LOG_INFO, "%s: caught unknown signal %d", applet, sig);
+
+	/* Restore errno */
+	errno = serrno;
+}
+
+static char * expand_home(const char *home, const char *path)
+{
+	char *opath, *ppath, *p, *nh;
+	size_t len;
+	struct passwd *pw;
+
+	if (!path || *path != '~')
+		return xstrdup(path);
+
+	opath = ppath = xstrdup(path);
+	if (ppath[1] != '/' && ppath[1] != '\0') {
+		p = strchr(ppath + 1, '/');
+		if (p)
+			*p = '\0';
+		pw = getpwnam(ppath + 1);
+		if (pw) {
+			home = pw->pw_dir;
+			ppath = p;
+			if (ppath)
+				*ppath = '/';
+		} else
+			home = NULL;
+	} else
+		ppath++;
+
+	if (!home) {
+	free(opath);
+		return xstrdup(path);
+	}
+	if (!ppath) {
+		free(opath);
+		return xstrdup(home);
+	}
+
+	len = strlen(ppath) + strlen(home) + 1;
+	nh = xmalloc(len);
+	snprintf(nh, len, "%s%s", home, ppath);
+	free(opath);
+	return nh;
+}
+
+int main(int argc, char **argv)
+{
+	int opt;
+	bool start = false;
+	bool stop = false;
+	char *exec = NULL;
+	char *pidfile = NULL;
+	char *home = NULL;
+	int tid = 0;
+	pid_t child_pid, pid;
+	char *svcname = getenv("RC_SVCNAME");
+	char *tmp;
+	char *p;
+	char *token;
+	int i;
+	char exec_file[PATH_MAX];
+	struct passwd *pw;
+	struct group *gr;
+	FILE *fp;
+	mode_t numask = 022;
+
+	applet = basename_c(argv[0]);
+	atexit(cleanup);
+
+	signal_setup(SIGINT, handle_signal);
+	signal_setup(SIGQUIT, handle_signal);
+	signal_setup(SIGTERM, handle_signal);
+	openlog(applet, LOG_PID, LOG_DAEMON);
+
+	if ((tmp = getenv("SSD_NICELEVEL")))
+		if (sscanf(tmp, "%d", &nicelevel) != 1)
+			eerror("%s: invalid nice level `%s' (SSD_NICELEVEL)",
+			    applet, tmp);
+
+	/* Get our user name and initial dir */
+	p = getenv("USER");
+	home = getenv("HOME");
+	if (home == NULL || p == NULL) {
+		pw = getpwuid(getuid());
+		if (pw != NULL) {
+			if (p == NULL)
+				setenv("USER", pw->pw_name, 1);
+			if (home == NULL) {
+				setenv("HOME", pw->pw_dir, 1);
+				home = pw->pw_dir;
+			}
+		}
+	}
+
+	while ((opt = getopt_long(argc, argv, getoptstring, longopts,
+		    (int *) 0)) != -1)
+		switch (opt) {
+		case 'I': /* --ionice */
+			if (sscanf(optarg, "%d:%d", &ionicec, &ioniced) == 0)
+				eerrorx("%s: invalid ionice `%s'",
+				    applet, optarg);
+			if (ionicec == 0)
+				ioniced = 0;
+			else if (ionicec == 3)
+				ioniced = 7;
+			ionicec <<= 13; /* class shift */
+			break;
+
+		case 'K':  /* --stop */
+			stop = true;
+			break;
+
+		case 'N':  /* --nice */
+			if (sscanf(optarg, "%d", &nicelevel) != 1)
+				eerrorx("%s: invalid nice level `%s'",
+				    applet, optarg);
+			break;
+
+		case 'S':  /* --start */
+			start = true;
+			break;
+
+		case 'd':  /* --chdir /new/dir */
+			ch_dir = optarg;
+			break;
+
+		case 'e': /* --env */
+			putenv(optarg);
+			break;
+
+		case 'g':  /* --group <group>|<gid> */
+			if (sscanf(optarg, "%d", &tid) != 1)
+				gr = getgrnam(optarg);
+			else
+				gr = getgrgid((gid_t)tid);
+			if (gr == NULL)
+				eerrorx("%s: group `%s' not found",
+				    applet, optarg);
+			gid = gr->gr_gid;
+			break;
+
+		case 'k':
+			if (parse_mode(&numask, optarg))
+				eerrorx("%s: invalid mode `%s'",
+				    applet, optarg);
+			break;
+
+		case 'p':  /* --pidfile <pid-file> */
+			pidfile = optarg;
+			break;
+
+		case 'r':  /* --chroot /new/root */
+			ch_root = optarg;
+			break;
+
+		case 'u':  /* --user <username>|<uid> */
+		{
+			p = optarg;
+			tmp = strsep(&p, ":");
+			changeuser = xstrdup(tmp);
+			if (sscanf(tmp, "%d", &tid) != 1)
+				pw = getpwnam(tmp);
+			else
+				pw = getpwuid((uid_t)tid);
+
+			if (pw == NULL)
+				eerrorx("%s: user `%s' not found",
+				    applet, tmp);
+			uid = pw->pw_uid;
+			home = pw->pw_dir;
+			unsetenv("HOME");
+			if (pw->pw_dir)
+				setenv("HOME", pw->pw_dir, 1);
+			unsetenv("USER");
+			if (pw->pw_name)
+				setenv("USER", pw->pw_name, 1);
+			if (gid == 0)
+				gid = pw->pw_gid;
+
+			if (p) {
+				tmp = strsep (&p, ":");
+				if (sscanf(tmp, "%d", &tid) != 1)
+					gr = getgrnam(tmp);
+				else
+					gr = getgrgid((gid_t) tid);
+
+				if (gr == NULL)
+					eerrorx("%s: group `%s'"
+					    " not found",
+					    applet, tmp);
+				gid = gr->gr_gid;
+			}
+		}
+		break;
+
+		case '1':   /* --stdout /path/to/stdout.lgfile */
+			redirect_stdout = optarg;
+			break;
+
+		case '2':  /* --stderr /path/to/stderr.logfile */
+			redirect_stderr = optarg;
+			break;
+
+		case_RC_COMMON_GETOPT
+		}
+
+	if (!pidfile)
+		eerrorx("%s: --pidfile must be specified", applet);
+
+	endpwent();
+	argc -= optind;
+	argv += optind;
+	exec = *argv;
+
+	if (start) {
+		if (!exec)
+			eerrorx("%s: nothing to start", applet);
+	}
+
+	/* Expand ~ */
+	if (ch_dir && *ch_dir == '~')
+		ch_dir = expand_home(home, ch_dir);
+	if (ch_root && *ch_root == '~')
+		ch_root = expand_home(home, ch_root);
+	if (exec) {
+		if (*exec == '~')
+			exec = expand_home(home, exec);
+
+		/* Validate that the binary exists if we are starting */
+		if (*exec == '/' || *exec == '.') {
+			/* Full or relative path */
+			if (ch_root)
+				snprintf(exec_file, sizeof(exec_file),
+				    "%s/%s", ch_root, exec);
+			else
+				snprintf(exec_file, sizeof(exec_file),
+				    "%s", exec);
+		} else {
+			/* Something in $PATH */
+			p = tmp = xstrdup(getenv("PATH"));
+			*exec_file = '\0';
+			while ((token = strsep(&p, ":"))) {
+				if (ch_root)
+					snprintf(exec_file, sizeof(exec_file),
+					    "%s/%s/%s",
+					    ch_root, token, exec);
+				else
+					snprintf(exec_file, sizeof(exec_file),
+					    "%s/%s", token, exec);
+				if (exists(exec_file))
+					break;
+				*exec_file = '\0';
+			}
+			free(tmp);
+		}
+	}
+	if (start && !exists(exec_file))
+		eerrorx("%s: %s does not exist", applet,
+		    *exec_file ? exec_file : exec);
+
+	if (stop) {
+		pid = get_pid(pidfile);
+		if (pid == -1)
+			i = pid;
+		else
+			i = kill(pid, SIGTERM);
+		if (i != 0)
+			/* We failed to stop something */
+			exit(EXIT_FAILURE);
+
+		/* Even if we have not actually killed anything, we should
+		 * remove information about it as it may have unexpectedly
+		 * crashed out. We should also return success as the end
+		 * result would be the same. */
+		if (pidfile && exists(pidfile))
+			unlink(pidfile);
+		if (svcname)
+			rc_service_daemon_set(svcname, exec,
+			    (const char *const *)argv,
+			    pidfile, false);
+		exit(EXIT_SUCCESS);
+	}
+
+	pid = get_pid(pidfile);
+	if (pid != -1)
+		if (kill(pid, 0) == 0)
+			eerrorx("%s: %s is already running", applet, exec);
+
+	einfov("Detaching to start `%s'", exec);
+	eindentv();
+
+	/* Remove existing pidfile */
+	if (pidfile)
+		unlink(pidfile);
+
+	/*
+	 * Make sure we can write a pid file
+	 */
+	fp = fopen(pidfile, "w");
+	if (! fp)
+		eerrorx("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
+		fclose(fp);
+
+	child_pid = fork();
+	if (child_pid == -1)
+		eerrorx("%s: fork: %s", applet, strerror(errno));
+
+	/* first parent process, do nothing. */
+	if (child_pid != 0)
+		exit(EXIT_SUCCESS);
+
+	child_pid = fork();
+	if (child_pid == -1)
+		eerrorx("%s: fork: %s", applet, strerror(errno));
+
+	if (child_pid != 0) {
+		/* this is the supervisor */
+		umask(numask);
+
+#ifdef TIOCNOTTY
+		tty_fd = open("/dev/tty", O_RDWR);
+#endif
+
+		devnull_fd = open("/dev/null", O_RDWR);
+
+		fp = fopen(pidfile, "w");
+		if (! fp)
+			eerrorx("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
+		fprintf(fp, "%d\n", getpid());
+		fclose(fp);
+
+		/*
+		 * Supervisor main loop
+		 */
+		i = 0;
+		while (!exiting) {
+			wait(&i);
+			if (exiting) {
+				syslog(LOG_INFO, "stopping %s, pid %d", exec, child_pid);
+				kill(child_pid, SIGTERM);
+			} else {
+				syslog(LOG_INFO, "%s, pid %d, terminated unexpectedly",
+						exec, child_pid);
+				child_pid = fork();
+				if (child_pid == -1)
+					eerrorx("%s: fork: %s", applet, strerror(errno));
+				if (child_pid == 0)
+					child_process(exec, argv);
+			}
+		}
+
+		if (svcname)
+			rc_service_daemon_set(svcname, exec,
+									(const char * const *) argv, pidfile, true);
+
+		exit(EXIT_SUCCESS);
+	} else if (child_pid == 0)
+		child_process(exec, argv);
+}

diff --git a/supervise-daemon-guide.md b/supervise-daemon-guide.md
new file mode 100644
index 0000000..7dae0e6
--- /dev/null
+++ b/supervise-daemon-guide.md
@@ -0,0 +1,45 @@
+# Using supervise-daemon
+
+Beginning with OpenRC-0.21 we have our own daemon supervisor,
+supervise-daemon., which can start a daemon and restart it if it
+terminates unexpectedly.
+
+## Use Default start, stop and status functions
+
+If you write your own start, stop and status functions in your service
+script, none of this will work. You must allow OpenRC to use the default
+functions.
+
+## Daemons must not fork
+
+Any deamon that you would like to have monitored by supervise-daemon
+must not fork. Instead, it must stay in the foreground. If the daemon
+itself forks, the supervisor will be unable to monitor it.
+
+If the daemon has an option to instruct it not to fork, you should add this
+to the command_args_foreground variable listed below.
+
+## Variable Settings
+
+The most important setting is the supervisor variable. At the top of
+your service script, you should set this variable as follows:
+
+supervisor=supervise-daemon
+
+Several other variables affect the way services behave under
+supervise-daemon. They are documented on the  openrc-run man page, but I
+will list them here for convenience:
+
+pidfile=/pid/of/supervisor.pid
+
+If you are using start-stop-daemon to monitor your scripts, the pidfile
+is the path to the pidfile the daemon creates. If, on the other hand,
+you are using supervise-daemon, this is the path to the pidfile the
+supervisor creates.
+
+command_args_foreground should be used if the daemon you want to monitor
+forks and goes to the background by default. This should be set to the
+command line option that instructs the daemon to stay in the foreground.
+
+This is very early support, so feel free to file bugs if you have
+issues.


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2016-04-27 16:21 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-04-27 16:21 [gentoo-commits] proj/openrc:master commit in: src/rc/, /, man/, sh/ William Hubbs

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox