add daemon supervisor
The supervise-daemon process is meant to be a lightweight supervisor which can monitor and restart a daemon if it crashes.
This commit is contained in:
parent
fd80b6fc67
commit
62410eaf4b
@ -6,7 +6,7 @@ MAN3= einfo.3 \
|
|||||||
rc_config.3 rc_deptree.3 rc_find_pids.3 rc_plugin_hook.3 \
|
rc_config.3 rc_deptree.3 rc_find_pids.3 rc_plugin_hook.3 \
|
||||||
rc_runlevel.3 rc_service.3 rc_stringlist.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 \
|
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)
|
ifeq (${OS},Linux)
|
||||||
MAN8 += rc-sstat.8
|
MAN8 += rc-sstat.8
|
||||||
|
@ -95,10 +95,17 @@ String describing the service.
|
|||||||
.It Ar description_$command
|
.It Ar description_$command
|
||||||
String describing the extra command.
|
String describing the extra command.
|
||||||
.It Ar supervisor
|
.It Ar supervisor
|
||||||
Supervisor to use to monitor this daemon. If this is unset,
|
Supervisor to use to monitor this daemon. If this is unset or invalid,
|
||||||
start-stop-daemon will be used. The only alternate supervisor we support
|
start-stop-daemon will be used.
|
||||||
in this release is S6 from Skarnet software. To use this, set
|
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.
|
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
|
.It Ar s6_service_path
|
||||||
The path to the s6 service directory if you are monitoring this service
|
The path to the s6 service directory if you are monitoring this service
|
||||||
with S6. The default is /var/svc.d/${RC_SVCNAME}.
|
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
|
.It Ar command
|
||||||
Daemon to start or stop via
|
Daemon to start or stop via
|
||||||
.Nm start-stop-daemon
|
.Nm start-stop-daemon
|
||||||
|
or
|
||||||
|
.Nm supervise-daemon
|
||||||
if no start or stop function is defined by the service.
|
if no start or stop function is defined by the service.
|
||||||
.It Ar command_args
|
.It Ar command_args
|
||||||
List of arguments to pass to the daemon when starting via
|
List of arguments to pass to the daemon when starting via
|
||||||
.Nm start-stop-daemon .
|
.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
|
.It Ar command_background
|
||||||
Set this to "true", "yes" or "1" (case-insensitive) to force the daemon into
|
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
|
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.
|
so the pidfile variable must be set.
|
||||||
.It Ar chroot
|
.It Ar chroot
|
||||||
.Xr start-stop-daemon 8
|
.Xr start-stop-daemon 8
|
||||||
|
and
|
||||||
|
.Xr supervise-daemon 8
|
||||||
will chroot into this path before writing the pid file or starting the daemon.
|
will chroot into this path before writing the pid file or starting the daemon.
|
||||||
.It Ar pidfile
|
.It Ar pidfile
|
||||||
Pidfile to use for the above defined command.
|
Pidfile to use for the above defined command.
|
||||||
|
142
man/supervise-daemon.8
Normal file
142
man/supervise-daemon.8
Normal file
@ -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>
|
@ -1,7 +1,8 @@
|
|||||||
DIR= ${LIBEXECDIR}/sh
|
DIR= ${LIBEXECDIR}/sh
|
||||||
SRCS= init.sh.in functions.sh.in gendepends.sh.in \
|
SRCS= init.sh.in functions.sh.in gendepends.sh.in \
|
||||||
openrc-run.sh.in rc-functions.sh.in tmpfiles.sh.in ${SRCS-${OS}}
|
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}}
|
BIN= gendepends.sh init.sh openrc-run.sh tmpfiles.sh ${BIN-${OS}}
|
||||||
|
|
||||||
INSTALLAFTER= _installafter
|
INSTALLAFTER= _installafter
|
||||||
|
@ -154,6 +154,7 @@ start()
|
|||||||
local func=ssd_start
|
local func=ssd_start
|
||||||
case "$supervisor" in
|
case "$supervisor" in
|
||||||
s6) func=s6_start ;;
|
s6) func=s6_start ;;
|
||||||
|
supervise-daemon) func=supervise_start ;;
|
||||||
?*)
|
?*)
|
||||||
ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon"
|
ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon"
|
||||||
;;
|
;;
|
||||||
@ -166,6 +167,7 @@ stop()
|
|||||||
local func=ssd_stop
|
local func=ssd_stop
|
||||||
case "$supervisor" in
|
case "$supervisor" in
|
||||||
s6) func=s6_stop ;;
|
s6) func=s6_stop ;;
|
||||||
|
supervise-daemon) func=supervise_stop ;;
|
||||||
?*)
|
?*)
|
||||||
ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon"
|
ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon"
|
||||||
;;
|
;;
|
||||||
@ -178,6 +180,7 @@ status()
|
|||||||
local func=ssd_status
|
local func=ssd_status
|
||||||
case "$supervisor" in
|
case "$supervisor" in
|
||||||
s6) func=s6_status ;;
|
s6) func=s6_status ;;
|
||||||
|
supervise-daemon) func=supervise_status ;;
|
||||||
?*)
|
?*)
|
||||||
ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon"
|
ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon"
|
||||||
;;
|
;;
|
||||||
@ -215,6 +218,7 @@ fi
|
|||||||
# load service supervisor functions
|
# load service supervisor functions
|
||||||
sourcex "@LIBEXECDIR@/sh/s6.sh"
|
sourcex "@LIBEXECDIR@/sh/s6.sh"
|
||||||
sourcex "@LIBEXECDIR@/sh/start-stop-daemon.sh"
|
sourcex "@LIBEXECDIR@/sh/start-stop-daemon.sh"
|
||||||
|
sourcex "@LIBEXECDIR@/sh/supervise-daemon.sh"
|
||||||
|
|
||||||
# Set verbose mode
|
# Set verbose mode
|
||||||
if yesno "${rc_verbose:-$RC_VERBOSE}"; then
|
if yesno "${rc_verbose:-$RC_VERBOSE}"; then
|
||||||
|
49
sh/supervise-daemon.sh
Normal file
49
sh/supervise-daemon.sh
Normal file
@ -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
|
||||||
|
}
|
1
src/rc/.gitignore
vendored
1
src/rc/.gitignore
vendored
@ -5,6 +5,7 @@ rc-update
|
|||||||
runscript
|
runscript
|
||||||
service
|
service
|
||||||
start-stop-daemon
|
start-stop-daemon
|
||||||
|
supervise-daemon
|
||||||
einfon
|
einfon
|
||||||
einfo
|
einfo
|
||||||
ewarnn
|
ewarnn
|
||||||
|
@ -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 \
|
mountinfo.c openrc-run.c rc-abort.c rc.c \
|
||||||
rc-depend.c rc-logger.c rc-misc.c rc-plugin.c \
|
rc-depend.c rc-logger.c rc-misc.c rc-plugin.c \
|
||||||
rc-service.c rc-status.c rc-update.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)
|
ifeq (${MKSELINUX},yes)
|
||||||
SRCS+= rc-selinux.c
|
SRCS+= rc-selinux.c
|
||||||
@ -16,7 +16,8 @@ SBINDIR= ${PREFIX}/sbin
|
|||||||
LINKDIR= ${LIBEXECDIR}
|
LINKDIR= ${LIBEXECDIR}
|
||||||
|
|
||||||
BINPROGS= rc-status
|
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 \
|
RC_BINPROGS= einfon einfo ewarnn ewarn eerrorn eerror ebegin eend ewend \
|
||||||
eindent eoutdent esyslog eval_ecolors ewaitfile \
|
eindent eoutdent esyslog eval_ecolors ewaitfile \
|
||||||
veinfo vewarn vebegin veend vewend veindent veoutdent \
|
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
|
start-stop-daemon: start-stop-daemon.o _usage.o rc-misc.o
|
||||||
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
|
${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
|
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}
|
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
|
||||||
|
|
||||||
|
722
src/rc/supervise-daemon.c
Normal file
722
src/rc/supervise-daemon.c
Normal file
@ -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);
|
||||||
|
}
|
45
supervise-daemon-guide.md
Normal file
45
supervise-daemon-guide.md
Normal file
@ -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.
|
Loading…
Reference in New Issue
Block a user