Gentoo Archives: gentoo-commits

From: William Hubbs <williamh@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/openrc:master commit in: src/rc/, /, man/, sh/
Date: Wed, 27 Apr 2016 16:21:55
Message-Id: 1461773630.62410eaf4ba92516a58a550717d7f3faf63bb79f.williamh@OpenRC
1 commit: 62410eaf4ba92516a58a550717d7f3faf63bb79f
2 Author: William Hubbs <w.d.hubbs <AT> gmail <DOT> com>
3 AuthorDate: Mon Feb 1 18:42:58 2016 +0000
4 Commit: William Hubbs <williamh <AT> gentoo <DOT> org>
5 CommitDate: Wed Apr 27 16:13:50 2016 +0000
6 URL: https://gitweb.gentoo.org/proj/openrc.git/commit/?id=62410eaf
7
8 add daemon supervisor
9
10 The supervise-daemon process is meant to be a lightweight supervisor
11 which can monitor and restart a daemon if it crashes.
12
13 man/Makefile | 2 +-
14 man/openrc-run.8 | 21 +-
15 man/supervise-daemon.8 | 142 +++++++++
16 sh/Makefile | 3 +-
17 sh/openrc-run.sh.in | 4 +
18 sh/supervise-daemon.sh | 49 ++++
19 src/rc/.gitignore | 1 +
20 src/rc/Makefile | 8 +-
21 src/rc/supervise-daemon.c | 722 ++++++++++++++++++++++++++++++++++++++++++++++
22 supervise-daemon-guide.md | 45 +++
23 10 files changed, 990 insertions(+), 7 deletions(-)
24
25 diff --git a/man/Makefile b/man/Makefile
26 index 73db2a8..48c5842 100644
27 --- a/man/Makefile
28 +++ b/man/Makefile
29 @@ -6,7 +6,7 @@ MAN3= einfo.3 \
30 rc_config.3 rc_deptree.3 rc_find_pids.3 rc_plugin_hook.3 \
31 rc_runlevel.3 rc_service.3 rc_stringlist.3
32 MAN8= rc-service.8 rc-status.8 rc-update.8 openrc.8 openrc-run.8 \
33 - service.8 start-stop-daemon.8
34 + service.8 start-stop-daemon.8 supervise-daemon.8
35
36 ifeq (${OS},Linux)
37 MAN8 += rc-sstat.8
38
39 diff --git a/man/openrc-run.8 b/man/openrc-run.8
40 index b23c5fe..be15d59 100644
41 --- a/man/openrc-run.8
42 +++ b/man/openrc-run.8
43 @@ -95,10 +95,17 @@ String describing the service.
44 .It Ar description_$command
45 String describing the extra command.
46 .It Ar supervisor
47 -Supervisor to use to monitor this daemon. If this is unset,
48 -start-stop-daemon will be used. The only alternate supervisor we support
49 -in this release is S6 from Skarnet software. To use this, set
50 +Supervisor to use to monitor this daemon. If this is unset or invalid,
51 +start-stop-daemon will be used.
52 +Currently, we support s6 from scarnet software, and supervise-daemon
53 +which is a light-weight supervisor internal to OpenRC.
54 +To use s6, set
55 supervisor=s6.
56 +or set
57 +supervisor=supervise-daemon
58 +to use supervise-daemon.
59 +Note that supervise-daemon is still in early development, so it is
60 +considered experimental.
61 .It Ar s6_service_path
62 The path to the s6 service directory if you are monitoring this service
63 with S6. The default is /var/svc.d/${RC_SVCNAME}.
64 @@ -112,10 +119,16 @@ List of arguments passed to start-stop-daemon when starting the daemon.
65 .It Ar command
66 Daemon to start or stop via
67 .Nm start-stop-daemon
68 +or
69 +.Nm supervise-daemon
70 if no start or stop function is defined by the service.
71 .It Ar command_args
72 List of arguments to pass to the daemon when starting via
73 .Nm start-stop-daemon .
74 +.It Ar command_args_foreground
75 +List of arguments to pass to the daemon when starting via
76 +.Nm supervise-daemon .
77 +to force the daemon to stay in the foreground
78 .It Ar command_background
79 Set this to "true", "yes" or "1" (case-insensitive) to force the daemon into
80 the background. This implies the "--make-pidfile" and "--pidfile" option of
81 @@ -123,6 +136,8 @@ the background. This implies the "--make-pidfile" and "--pidfile" option of
82 so the pidfile variable must be set.
83 .It Ar chroot
84 .Xr start-stop-daemon 8
85 +and
86 +.Xr supervise-daemon 8
87 will chroot into this path before writing the pid file or starting the daemon.
88 .It Ar pidfile
89 Pidfile to use for the above defined command.
90
91 diff --git a/man/supervise-daemon.8 b/man/supervise-daemon.8
92 new file mode 100644
93 index 0000000..0608767
94 --- /dev/null
95 +++ b/man/supervise-daemon.8
96 @@ -0,0 +1,142 @@
97 +.\" Copyright (c) 2007-2015 The OpenRC Authors.
98 +.\" See the Authors file at the top-level directory of this distribution and
99 +.\" https://github.com/OpenRC/openrc/blob/master/AUTHORS
100 +.\"
101 +.\" This file is part of OpenRC. It is subject to the license terms in
102 +.\" the LICENSE file found in the top-level directory of this
103 +.\" distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
104 +.\" This file may not be copied, modified, propagated, or distributed
105 +.\" except according to the terms contained in the LICENSE file.
106 +.\"
107 +.Dd April 27, 2016
108 +.Dt supervise-DAEMON 8 SMM
109 +.Os OpenRC
110 +.Sh NAME
111 +.Nm supervise-daemon
112 +.Nd starts a daemon and restarts it if it crashes
113 +.Sh SYNOPSIS
114 +.Nm
115 +.Fl d , -chdir
116 +.Ar path
117 +.Fl e , -env
118 +.Ar var=value
119 +.Fl g , -group
120 +.Ar group
121 +.Fl I , -ionice
122 +.Ar arg
123 +.Fl k , -umask
124 +.Ar value
125 +.Fl N , -nicelevel
126 +.Ar level
127 +.Fl p , -pidfile
128 +.Ar pidfile
129 +.Fl u , -user
130 +.Ar user
131 +.Fl r , -chroot
132 +.Ar chrootpath
133 +.Fl 1 , -stdout
134 +.Ar logfile
135 +.Fl 2 , -stderr
136 +.Ar logfile
137 +.Fl S , -start
138 +.Ar daemon
139 +.Op Fl -
140 +.Op Ar arguments
141 +.Nm
142 +.Fl K , -stop
143 +.Ar daemon
144 +.Fl p , -pidfile
145 +.Ar pidfile
146 +.Fl r , -chroot
147 +.Ar chrootpath
148 +.Sh DESCRIPTION
149 +.Nm
150 +provides a consistent method of starting, stopping and restarting
151 +daemons. If
152 +.Fl K , -stop
153 +is not provided, then we assume we are starting the daemon.
154 +.Nm
155 +only works with daemons which do not fork. Also, it uses its own pid
156 +file, so the daemon should not write a pid file, or the pid file passed
157 +to
158 +.Nm
159 +should not be the one the daemon writes.
160 +.Pp
161 +Here are the options to specify the daemon and how it should start or stop:
162 +.Bl -tag -width indent
163 +.It Fl p , -pidfile Ar pidfile
164 +When starting, we write a
165 +.Ar pidfile
166 +so we know which supervisor to stop. When stopping we only stop the pid(s)
167 +listed in the
168 +.Ar pidfile .
169 +.It Fl u , -user Ar user Ns Op : Ns Ar group
170 +Start the daemon as the
171 +.Ar user
172 +and update $HOME accordingly or stop daemons
173 +owned by the user. You can optionally append a
174 +.Ar group
175 +name here also.
176 +.It Fl v , -verbose
177 +Print the action(s) that are taken just before doing them.
178 +.Pp
179 +The options are as follows:
180 +.Bl -tag -width indent
181 +.It Fl d , -chdir Ar path
182 +chdir to this directory before starting the daemon.
183 +.It Fl e , -env Ar VAR=VALUE
184 +Set the environment variable VAR to VALUE.
185 +.It Fl g , -group Ar group
186 +Start the daemon as in the group.
187 +.It Fl I , -ionice Ar class Ns Op : Ns Ar data
188 +Modifies the IO scheduling priority of the daemon.
189 +Class can be 0 for none, 1 for real time, 2 for best effort and 3 for idle.
190 +Data can be from 0 to 7 inclusive.
191 +.It Fl k , -umask Ar mode
192 +Set the umask of the daemon.
193 +.It Fl N , -nicelevel Ar level
194 +Modifies the scheduling priority of the daemon.
195 +.It Fl r , -chroot Ar path
196 +chroot to this directory before starting the daemon. All other paths, such
197 +as the path to the daemon, chdir and pidfile, should be relative to the chroot.
198 +.It Fl u , -user Ar user
199 +Start the daemon as the specified user.
200 +.It Fl 1 , -stdout Ar logfile
201 +Redirect the standard output of the process to logfile.
202 +Must be an absolute pathname, but relative to the path optionally given with
203 +.Fl r , -chroot .
204 +The logfile can also be a named pipe.
205 +.It Fl 2 , -stderr Ar logfile
206 +The same thing as
207 +.Fl 1 , -stdout
208 +but with the standard error output.
209 +.El
210 +.Sh ENVIRONMENT
211 +.Va SSD_NICELEVEL
212 +can also set the scheduling priority of the daemon, but the command line
213 +option takes precedence.
214 +.Sh NOTE
215 +.Nm
216 +uses
217 +.Xr getopt 3
218 +to parse its options, which allows it to accept the `--' option which will
219 +cause it to stop processing options at that point. Any subsequent arguments
220 +are passed as arguments to the daemon to start and used when finding a daemon
221 +to stop or signal.
222 +.Sh SEE ALSO
223 +.Xr chdir 2 ,
224 +.Xr chroot 2 ,
225 +.Xr getopt 3 ,
226 +.Xr nice 2 ,
227 +.Xr rc_find_pids 3
228 +.Sh BUGS
229 +.Nm
230 +cannot stop an interpreted daemon that no longer exists without a pidfile.
231 +.Sh HISTORY
232 +.Nm
233 +first appeared in Debian.
234 +.Pp
235 +This is a complete re-implementation with the process finding code in the
236 +OpenRC library (librc, -lrc) so other programs can make use of it.
237 +.Sh AUTHORS
238 +.An William Hubbs <w.d.hubbs@×××××.com>
239
240 diff --git a/sh/Makefile b/sh/Makefile
241 index b9b9fb3..24c2315 100644
242 --- a/sh/Makefile
243 +++ b/sh/Makefile
244 @@ -1,7 +1,8 @@
245 DIR= ${LIBEXECDIR}/sh
246 SRCS= init.sh.in functions.sh.in gendepends.sh.in \
247 openrc-run.sh.in rc-functions.sh.in tmpfiles.sh.in ${SRCS-${OS}}
248 -INC= rc-mount.sh functions.sh rc-functions.sh s6.sh start-stop-daemon.sh
249 +INC= functions.sh rc-mount.sh rc-functions.sh s6.sh start-stop-daemon.sh \
250 + supervise-daemon.sh
251 BIN= gendepends.sh init.sh openrc-run.sh tmpfiles.sh ${BIN-${OS}}
252
253 INSTALLAFTER= _installafter
254
255 diff --git a/sh/openrc-run.sh.in b/sh/openrc-run.sh.in
256 index fb6f95b..36bc366 100644
257 --- a/sh/openrc-run.sh.in
258 +++ b/sh/openrc-run.sh.in
259 @@ -154,6 +154,7 @@ start()
260 local func=ssd_start
261 case "$supervisor" in
262 s6) func=s6_start ;;
263 + supervise-daemon) func=supervise_start ;;
264 ?*)
265 ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon"
266 ;;
267 @@ -166,6 +167,7 @@ stop()
268 local func=ssd_stop
269 case "$supervisor" in
270 s6) func=s6_stop ;;
271 + supervise-daemon) func=supervise_stop ;;
272 ?*)
273 ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon"
274 ;;
275 @@ -178,6 +180,7 @@ status()
276 local func=ssd_status
277 case "$supervisor" in
278 s6) func=s6_status ;;
279 + supervise-daemon) func=supervise_status ;;
280 ?*)
281 ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon"
282 ;;
283 @@ -215,6 +218,7 @@ fi
284 # load service supervisor functions
285 sourcex "@LIBEXECDIR@/sh/s6.sh"
286 sourcex "@LIBEXECDIR@/sh/start-stop-daemon.sh"
287 +sourcex "@LIBEXECDIR@/sh/supervise-daemon.sh"
288
289 # Set verbose mode
290 if yesno "${rc_verbose:-$RC_VERBOSE}"; then
291
292 diff --git a/sh/supervise-daemon.sh b/sh/supervise-daemon.sh
293 new file mode 100644
294 index 0000000..34e3ef7
295 --- /dev/null
296 +++ b/sh/supervise-daemon.sh
297 @@ -0,0 +1,49 @@
298 +# start / stop / status functions for supervise-daemon
299 +
300 +# Copyright (c) 2016 The OpenRC Authors.
301 +# See the Authors file at the top-level directory of this distribution and
302 +# https://github.com/OpenRC/openrc/blob/master/AUTHORS
303 +#
304 +# This file is part of OpenRC. It is subject to the license terms in
305 +# the LICENSE file found in the top-level directory of this
306 +# distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
307 +# This file may not be copied, modified, propagated, or distributed
308 +# except according to the terms contained in the LICENSE file.
309 +
310 +supervise_start()
311 +{
312 + if [ -z "$command" ]; then
313 + ewarn "The command variable is undefined."
314 + ewarn "There is nothing for ${name:-$RC_SVCNAME} to start."
315 + return 1
316 + fi
317 +
318 + ebegin "Starting ${name:-$RC_SVCNAME}"
319 + eval supervise-daemon --start \
320 + ${pidfile:+--pidfile} $pidfile \
321 + ${command_user+--user} $command_user \
322 + $supervise_daemon_args \
323 + $command \
324 + -- $command_args $command_args_foreground
325 + rc=$?
326 + [ -n "${pidfile}" ] && service_set_value "pidfile" "${pidfile}"
327 + eend $rc "failed to start $RC_SVCNAME"
328 +}
329 +
330 +supervise_stop()
331 +{
332 + local startpidfile="$(service_get_value "pidfile")"
333 + pidfile="${startpidfile:-$pidfile}"
334 + [ -n "$pidfile" ] || return 0
335 + ebegin "Stopping ${name:-$RC_SVCNAME}"
336 + supervise-daemon --stop \
337 + ${pidfile:+--pidfile} $pidfile \
338 + ${stopsig:+--signal} $stopsig
339 +
340 + eend $? "Failed to stop $RC_SVCNAME"
341 +}
342 +
343 +supervise_status()
344 +{
345 + _status
346 +}
347
348 diff --git a/src/rc/.gitignore b/src/rc/.gitignore
349 index bbfede6..c977919 100644
350 --- a/src/rc/.gitignore
351 +++ b/src/rc/.gitignore
352 @@ -5,6 +5,7 @@ rc-update
353 runscript
354 service
355 start-stop-daemon
356 +supervise-daemon
357 einfon
358 einfo
359 ewarnn
360
361 diff --git a/src/rc/Makefile b/src/rc/Makefile
362 index 71ae503..d4759e7 100644
363 --- a/src/rc/Makefile
364 +++ b/src/rc/Makefile
365 @@ -3,7 +3,7 @@ SRCS= checkpath.c do_e.c do_mark_service.c do_service.c \
366 mountinfo.c openrc-run.c rc-abort.c rc.c \
367 rc-depend.c rc-logger.c rc-misc.c rc-plugin.c \
368 rc-service.c rc-status.c rc-update.c \
369 - shell_var.c start-stop-daemon.c swclock.c _usage.c
370 + shell_var.c start-stop-daemon.c supervise-daemon.c swclock.c _usage.c
371
372 ifeq (${MKSELINUX},yes)
373 SRCS+= rc-selinux.c
374 @@ -16,7 +16,8 @@ SBINDIR= ${PREFIX}/sbin
375 LINKDIR= ${LIBEXECDIR}
376
377 BINPROGS= rc-status
378 -SBINPROGS = openrc openrc-run rc rc-service rc-update runscript service start-stop-daemon
379 +SBINPROGS = openrc openrc-run rc rc-service rc-update runscript service \
380 + start-stop-daemon supervise-daemon
381 RC_BINPROGS= einfon einfo ewarnn ewarn eerrorn eerror ebegin eend ewend \
382 eindent eoutdent esyslog eval_ecolors ewaitfile \
383 veinfo vewarn vebegin veend vewend veindent veoutdent \
384 @@ -136,6 +137,9 @@ rc-update: rc-update.o _usage.o rc-misc.o
385 start-stop-daemon: start-stop-daemon.o _usage.o rc-misc.o
386 ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
387
388 +supervise-daemon: supervise-daemon.o _usage.o rc-misc.o
389 + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
390 +
391 service_get_value service_set_value get_options save_options: do_value.o rc-misc.o
392 ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
393
394
395 diff --git a/src/rc/supervise-daemon.c b/src/rc/supervise-daemon.c
396 new file mode 100644
397 index 0000000..6bb75f3
398 --- /dev/null
399 +++ b/src/rc/supervise-daemon.c
400 @@ -0,0 +1,722 @@
401 +/*
402 + * supervise-daemon
403 + * This is an experimental supervisor for daemons.
404 + * It will start a deamon and make sure it restarts if it crashes.
405 + */
406 +
407 +/*
408 + * Copyright (c) 2016 The OpenRC Authors.
409 + * See the Authors file at the top-level directory of this distribution and
410 + * https://github.com/OpenRC/openrc/blob/master/AUTHORS
411 + *
412 + * This file is part of OpenRC. It is subject to the license terms in
413 + * the LICENSE file found in the top-level directory of this
414 + * distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
415 + * This file may not be copied, modified, propagated, or distributed
416 + * except according to the terms contained in the LICENSE file.
417 + */
418 +
419 +/* nano seconds */
420 +#define POLL_INTERVAL 20000000
421 +#define WAIT_PIDFILE 500000000
422 +#define ONE_SECOND 1000000000
423 +#define ONE_MS 1000000
424 +
425 +#include <sys/types.h>
426 +#include <sys/ioctl.h>
427 +#include <sys/resource.h>
428 +#include <sys/stat.h>
429 +#include <termios.h>
430 +#include <sys/time.h>
431 +#include <sys/wait.h>
432 +
433 +#ifdef __linux__
434 +#include <sys/syscall.h> /* For io priority */
435 +#endif
436 +
437 +#include <ctype.h>
438 +#include <errno.h>
439 +#include <fcntl.h>
440 +#include <getopt.h>
441 +#include <limits.h>
442 +#include <grp.h>
443 +#include <pwd.h>
444 +#include <signal.h>
445 +#include <stddef.h>
446 +#include <stdio.h>
447 +#include <stdlib.h>
448 +#include <string.h>
449 +#include <syslog.h>
450 +#include <time.h>
451 +#include <unistd.h>
452 +
453 +#ifdef HAVE_PAM
454 +#include <security/pam_appl.h>
455 +
456 +/* We are not supporting authentication conversations */
457 +static struct pam_conv conv = { NULL, NULL};
458 +#endif
459 +
460 +#include "einfo.h"
461 +#include "queue.h"
462 +#include "rc.h"
463 +#include "rc-misc.h"
464 +#include "_usage.h"
465 +
466 +const char *applet = NULL;
467 +const char *extraopts = NULL;
468 +const char *getoptstring = "d:e:g:I:Kk:N:p:r:Su:1:2:" \
469 + getoptstring_COMMON;
470 +const struct option longopts[] = {
471 + { "chdir", 1, NULL, 'd'},
472 + { "env", 1, NULL, 'e'},
473 + { "group", 1, NULL, 'g'},
474 + { "ionice", 1, NULL, 'I'},
475 + { "stop", 0, NULL, 'K'},
476 + { "umask", 1, NULL, 'k'},
477 + { "nicelevel", 1, NULL, 'N'},
478 + { "pidfile", 1, NULL, 'p'},
479 + { "user", 1, NULL, 'u'},
480 + { "chroot", 1, NULL, 'r'},
481 + { "start", 0, NULL, 'S'},
482 + { "stdout", 1, NULL, '1'},
483 + { "stderr", 1, NULL, '2'},
484 + longopts_COMMON
485 +};
486 +const char * const longopts_help[] = {
487 + "Change the PWD",
488 + "Set an environment string",
489 + "Change the process group",
490 + "Set an ionice class:data when starting",
491 + "Stop daemon",
492 + "Set the umask for the daemon",
493 + "Set a nicelevel when starting",
494 + "Match pid found in this file",
495 + "Change the process user",
496 + "Chroot to this directory",
497 + "Start daemon",
498 + "Redirect stdout to file",
499 + "Redirect stderr to file",
500 + longopts_help_COMMON
501 +};
502 +const char *usagestring = NULL;
503 +
504 +static int nicelevel = 0;
505 +static int ionicec = -1;
506 +static int ioniced = 0;
507 +static char *changeuser, *ch_root, *ch_dir;
508 +static uid_t uid = 0;
509 +static gid_t gid = 0;
510 +static int devnull_fd = -1;
511 +static int stdin_fd;
512 +static int stdout_fd;
513 +static int stderr_fd;
514 +static char *redirect_stderr = NULL;
515 +static char *redirect_stdout = NULL;
516 +static bool exiting = false;
517 +#ifdef TIOCNOTTY
518 +static int tty_fd = -1;
519 +#endif
520 +
521 +extern char **environ;
522 +
523 +#if !defined(SYS_ioprio_set) && defined(__NR_ioprio_set)
524 +# define SYS_ioprio_set __NR_ioprio_set
525 +#endif
526 +#if !defined(__DragonFly__)
527 +static inline int ioprio_set(int which, int who, int ioprio)
528 +{
529 +#ifdef SYS_ioprio_set
530 + return syscall(SYS_ioprio_set, which, who, ioprio);
531 +#else
532 + return 0;
533 +#endif
534 +}
535 +#endif
536 +
537 +static void cleanup(void)
538 +{
539 + free(changeuser);
540 +}
541 +
542 +static pid_t get_pid(const char *pidfile)
543 +{
544 + FILE *fp;
545 + pid_t pid;
546 +
547 + if (! pidfile)
548 + return -1;
549 +
550 + if ((fp = fopen(pidfile, "r")) == NULL) {
551 + ewarnv("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
552 + return -1;
553 + }
554 +
555 + if (fscanf(fp, "%d", &pid) != 1) {
556 + ewarnv("%s: no pid found in `%s'", applet, pidfile);
557 + fclose(fp);
558 + return -1;
559 + }
560 +
561 + fclose(fp);
562 +
563 + return pid;
564 +}
565 +
566 +static void child_process(char *exec, char **argv)
567 +{
568 + RC_STRINGLIST *env_list;
569 + RC_STRING *env;
570 + int i;
571 + char *p;
572 + char *token;
573 + size_t len;
574 + char *newpath;
575 + char *np;
576 + char **c;
577 + char cmdline[PATH_MAX];
578 +
579 +#ifdef HAVE_PAM
580 + pam_handle_t *pamh = NULL;
581 + int pamr;
582 + const char *const *pamenv = NULL;
583 +#endif
584 +
585 + setsid();
586 +
587 + if (nicelevel) {
588 + if (setpriority(PRIO_PROCESS, getpid(), nicelevel) == -1)
589 + eerrorx("%s: setpriority %d: %s", applet, nicelevel,
590 + strerror(errno));
591 + }
592 +
593 + if (ionicec != -1 && ioprio_set(1, getpid(), ionicec | ioniced) == -1)
594 + eerrorx("%s: ioprio_set %d %d: %s", applet, ionicec, ioniced,
595 + strerror(errno));
596 +
597 + if (ch_root && chroot(ch_root) < 0)
598 + eerrorx("%s: chroot `%s': %s", applet, ch_root, strerror(errno));
599 +
600 + if (ch_dir && chdir(ch_dir) < 0)
601 + eerrorx("%s: chdir `%s': %s", applet, ch_dir, strerror(errno));
602 +
603 +#ifdef HAVE_PAM
604 + if (changeuser != NULL) {
605 + pamr = pam_start("start-stop-daemon",
606 + changeuser, &conv, &pamh);
607 +
608 + if (pamr == PAM_SUCCESS)
609 + pamr = pam_acct_mgmt(pamh, PAM_SILENT);
610 + if (pamr == PAM_SUCCESS)
611 + pamr = pam_open_session(pamh, PAM_SILENT);
612 + if (pamr != PAM_SUCCESS)
613 + eerrorx("%s: pam error: %s", applet, pam_strerror(pamh, pamr));
614 + }
615 +#endif
616 +
617 + if (gid && setgid(gid))
618 + eerrorx("%s: unable to set groupid to %d", applet, gid);
619 + if (changeuser && initgroups(changeuser, gid))
620 + eerrorx("%s: initgroups (%s, %d)", applet, changeuser, gid);
621 + if (uid && setuid(uid))
622 + eerrorx ("%s: unable to set userid to %d", applet, uid);
623 +
624 + /* Close any fd's to the passwd database */
625 + endpwent();
626 +
627 +#ifdef TIOCNOTTY
628 + ioctl(tty_fd, TIOCNOTTY, 0);
629 + close(tty_fd);
630 +#endif
631 +
632 + /* Clean the environment of any RC_ variables */
633 + env_list = rc_stringlist_new();
634 + i = 0;
635 + while (environ[i])
636 + rc_stringlist_add(env_list, environ[i++]);
637 +
638 +#ifdef HAVE_PAM
639 + if (changeuser != NULL) {
640 + pamenv = (const char *const *)pam_getenvlist(pamh);
641 + if (pamenv) {
642 + while (*pamenv) {
643 + /* Don't add strings unless they set a var */
644 + if (strchr(*pamenv, '='))
645 + putenv(xstrdup(*pamenv));
646 + else
647 + unsetenv(*pamenv);
648 + pamenv++;
649 + }
650 + }
651 + }
652 +#endif
653 +
654 + TAILQ_FOREACH(env, env_list, entries) {
655 + if ((strncmp(env->value, "RC_", 3) == 0 &&
656 + strncmp(env->value, "RC_SERVICE=", 10) != 0 &&
657 + strncmp(env->value, "RC_SVCNAME=", 10) != 0) ||
658 + strncmp(env->value, "SSD_NICELEVEL=", 14) == 0)
659 + {
660 + p = strchr(env->value, '=');
661 + *p = '\0';
662 + unsetenv(env->value);
663 + continue;
664 + }
665 + }
666 + rc_stringlist_free(env_list);
667 +
668 + /* For the path, remove the rcscript bin dir from it */
669 + if ((token = getenv("PATH"))) {
670 + len = strlen(token);
671 + newpath = np = xmalloc(len + 1);
672 + while (token && *token) {
673 + p = strchr(token, ':');
674 + if (p) {
675 + *p++ = '\0';
676 + while (*p == ':')
677 + p++;
678 + }
679 + if (strcmp(token, RC_LIBEXECDIR "/bin") != 0 &&
680 + strcmp(token, RC_LIBEXECDIR "/sbin") != 0)
681 + {
682 + len = strlen(token);
683 + if (np != newpath)
684 + *np++ = ':';
685 + memcpy(np, token, len);
686 + np += len;
687 + }
688 + token = p;
689 + }
690 + *np = '\0';
691 + unsetenv("PATH");
692 + setenv("PATH", newpath, 1);
693 + }
694 +
695 + stdin_fd = devnull_fd;
696 + stdout_fd = devnull_fd;
697 + stderr_fd = devnull_fd;
698 + if (redirect_stdout) {
699 + if ((stdout_fd = open(redirect_stdout,
700 + O_WRONLY | O_CREAT | O_APPEND,
701 + S_IRUSR | S_IWUSR)) == -1)
702 + eerrorx("%s: unable to open the logfile"
703 + " for stdout `%s': %s",
704 + applet, redirect_stdout, strerror(errno));
705 + }
706 + if (redirect_stderr) {
707 + if ((stderr_fd = open(redirect_stderr,
708 + O_WRONLY | O_CREAT | O_APPEND,
709 + S_IRUSR | S_IWUSR)) == -1)
710 + eerrorx("%s: unable to open the logfile"
711 + " for stderr `%s': %s",
712 + applet, redirect_stderr, strerror(errno));
713 + }
714 +
715 + dup2(stdin_fd, STDIN_FILENO);
716 + if (redirect_stdout || rc_yesno(getenv("EINFO_QUIET")))
717 + dup2(stdout_fd, STDOUT_FILENO);
718 + if (redirect_stderr || rc_yesno(getenv("EINFO_QUIET")))
719 + dup2(stderr_fd, STDERR_FILENO);
720 +
721 + for (i = getdtablesize() - 1; i >= 3; --i)
722 + close(i);
723 +
724 + *cmdline = '\0';
725 + c = argv;
726 + while (*c) {
727 + strcat(cmdline, *c);
728 + strcat(cmdline, " ");
729 + c++;
730 + }
731 + syslog(LOG_INFO, "Running command line: %s", cmdline);
732 + execvp(exec, argv);
733 +
734 +#ifdef HAVE_PAM
735 + if (changeuser != NULL && pamr == PAM_SUCCESS)
736 + pam_close_session(pamh, PAM_SILENT);
737 +#endif
738 + eerrorx("%s: failed to exec `%s': %s", applet, exec,strerror(errno));
739 +}
740 +
741 +static void handle_signal(int sig)
742 +{
743 + int serrno = errno;
744 + char signame[10] = { '\0' };
745 +
746 + switch (sig) {
747 + case SIGINT:
748 + snprintf(signame, sizeof(signame), "SIGINT");
749 + break;
750 + case SIGTERM:
751 + snprintf(signame, sizeof(signame), "SIGTERM");
752 + break;
753 + case SIGQUIT:
754 + snprintf(signame, sizeof(signame), "SIGQUIT");
755 + break;
756 + }
757 +
758 + if (*signame != 0) {
759 + syslog(LOG_INFO, "%s: caught signal %s, exiting", applet, signame);
760 + exiting = true;
761 + } else
762 + syslog(LOG_INFO, "%s: caught unknown signal %d", applet, sig);
763 +
764 + /* Restore errno */
765 + errno = serrno;
766 +}
767 +
768 +static char * expand_home(const char *home, const char *path)
769 +{
770 + char *opath, *ppath, *p, *nh;
771 + size_t len;
772 + struct passwd *pw;
773 +
774 + if (!path || *path != '~')
775 + return xstrdup(path);
776 +
777 + opath = ppath = xstrdup(path);
778 + if (ppath[1] != '/' && ppath[1] != '\0') {
779 + p = strchr(ppath + 1, '/');
780 + if (p)
781 + *p = '\0';
782 + pw = getpwnam(ppath + 1);
783 + if (pw) {
784 + home = pw->pw_dir;
785 + ppath = p;
786 + if (ppath)
787 + *ppath = '/';
788 + } else
789 + home = NULL;
790 + } else
791 + ppath++;
792 +
793 + if (!home) {
794 + free(opath);
795 + return xstrdup(path);
796 + }
797 + if (!ppath) {
798 + free(opath);
799 + return xstrdup(home);
800 + }
801 +
802 + len = strlen(ppath) + strlen(home) + 1;
803 + nh = xmalloc(len);
804 + snprintf(nh, len, "%s%s", home, ppath);
805 + free(opath);
806 + return nh;
807 +}
808 +
809 +int main(int argc, char **argv)
810 +{
811 + int opt;
812 + bool start = false;
813 + bool stop = false;
814 + char *exec = NULL;
815 + char *pidfile = NULL;
816 + char *home = NULL;
817 + int tid = 0;
818 + pid_t child_pid, pid;
819 + char *svcname = getenv("RC_SVCNAME");
820 + char *tmp;
821 + char *p;
822 + char *token;
823 + int i;
824 + char exec_file[PATH_MAX];
825 + struct passwd *pw;
826 + struct group *gr;
827 + FILE *fp;
828 + mode_t numask = 022;
829 +
830 + applet = basename_c(argv[0]);
831 + atexit(cleanup);
832 +
833 + signal_setup(SIGINT, handle_signal);
834 + signal_setup(SIGQUIT, handle_signal);
835 + signal_setup(SIGTERM, handle_signal);
836 + openlog(applet, LOG_PID, LOG_DAEMON);
837 +
838 + if ((tmp = getenv("SSD_NICELEVEL")))
839 + if (sscanf(tmp, "%d", &nicelevel) != 1)
840 + eerror("%s: invalid nice level `%s' (SSD_NICELEVEL)",
841 + applet, tmp);
842 +
843 + /* Get our user name and initial dir */
844 + p = getenv("USER");
845 + home = getenv("HOME");
846 + if (home == NULL || p == NULL) {
847 + pw = getpwuid(getuid());
848 + if (pw != NULL) {
849 + if (p == NULL)
850 + setenv("USER", pw->pw_name, 1);
851 + if (home == NULL) {
852 + setenv("HOME", pw->pw_dir, 1);
853 + home = pw->pw_dir;
854 + }
855 + }
856 + }
857 +
858 + while ((opt = getopt_long(argc, argv, getoptstring, longopts,
859 + (int *) 0)) != -1)
860 + switch (opt) {
861 + case 'I': /* --ionice */
862 + if (sscanf(optarg, "%d:%d", &ionicec, &ioniced) == 0)
863 + eerrorx("%s: invalid ionice `%s'",
864 + applet, optarg);
865 + if (ionicec == 0)
866 + ioniced = 0;
867 + else if (ionicec == 3)
868 + ioniced = 7;
869 + ionicec <<= 13; /* class shift */
870 + break;
871 +
872 + case 'K': /* --stop */
873 + stop = true;
874 + break;
875 +
876 + case 'N': /* --nice */
877 + if (sscanf(optarg, "%d", &nicelevel) != 1)
878 + eerrorx("%s: invalid nice level `%s'",
879 + applet, optarg);
880 + break;
881 +
882 + case 'S': /* --start */
883 + start = true;
884 + break;
885 +
886 + case 'd': /* --chdir /new/dir */
887 + ch_dir = optarg;
888 + break;
889 +
890 + case 'e': /* --env */
891 + putenv(optarg);
892 + break;
893 +
894 + case 'g': /* --group <group>|<gid> */
895 + if (sscanf(optarg, "%d", &tid) != 1)
896 + gr = getgrnam(optarg);
897 + else
898 + gr = getgrgid((gid_t)tid);
899 + if (gr == NULL)
900 + eerrorx("%s: group `%s' not found",
901 + applet, optarg);
902 + gid = gr->gr_gid;
903 + break;
904 +
905 + case 'k':
906 + if (parse_mode(&numask, optarg))
907 + eerrorx("%s: invalid mode `%s'",
908 + applet, optarg);
909 + break;
910 +
911 + case 'p': /* --pidfile <pid-file> */
912 + pidfile = optarg;
913 + break;
914 +
915 + case 'r': /* --chroot /new/root */
916 + ch_root = optarg;
917 + break;
918 +
919 + case 'u': /* --user <username>|<uid> */
920 + {
921 + p = optarg;
922 + tmp = strsep(&p, ":");
923 + changeuser = xstrdup(tmp);
924 + if (sscanf(tmp, "%d", &tid) != 1)
925 + pw = getpwnam(tmp);
926 + else
927 + pw = getpwuid((uid_t)tid);
928 +
929 + if (pw == NULL)
930 + eerrorx("%s: user `%s' not found",
931 + applet, tmp);
932 + uid = pw->pw_uid;
933 + home = pw->pw_dir;
934 + unsetenv("HOME");
935 + if (pw->pw_dir)
936 + setenv("HOME", pw->pw_dir, 1);
937 + unsetenv("USER");
938 + if (pw->pw_name)
939 + setenv("USER", pw->pw_name, 1);
940 + if (gid == 0)
941 + gid = pw->pw_gid;
942 +
943 + if (p) {
944 + tmp = strsep (&p, ":");
945 + if (sscanf(tmp, "%d", &tid) != 1)
946 + gr = getgrnam(tmp);
947 + else
948 + gr = getgrgid((gid_t) tid);
949 +
950 + if (gr == NULL)
951 + eerrorx("%s: group `%s'"
952 + " not found",
953 + applet, tmp);
954 + gid = gr->gr_gid;
955 + }
956 + }
957 + break;
958 +
959 + case '1': /* --stdout /path/to/stdout.lgfile */
960 + redirect_stdout = optarg;
961 + break;
962 +
963 + case '2': /* --stderr /path/to/stderr.logfile */
964 + redirect_stderr = optarg;
965 + break;
966 +
967 + case_RC_COMMON_GETOPT
968 + }
969 +
970 + if (!pidfile)
971 + eerrorx("%s: --pidfile must be specified", applet);
972 +
973 + endpwent();
974 + argc -= optind;
975 + argv += optind;
976 + exec = *argv;
977 +
978 + if (start) {
979 + if (!exec)
980 + eerrorx("%s: nothing to start", applet);
981 + }
982 +
983 + /* Expand ~ */
984 + if (ch_dir && *ch_dir == '~')
985 + ch_dir = expand_home(home, ch_dir);
986 + if (ch_root && *ch_root == '~')
987 + ch_root = expand_home(home, ch_root);
988 + if (exec) {
989 + if (*exec == '~')
990 + exec = expand_home(home, exec);
991 +
992 + /* Validate that the binary exists if we are starting */
993 + if (*exec == '/' || *exec == '.') {
994 + /* Full or relative path */
995 + if (ch_root)
996 + snprintf(exec_file, sizeof(exec_file),
997 + "%s/%s", ch_root, exec);
998 + else
999 + snprintf(exec_file, sizeof(exec_file),
1000 + "%s", exec);
1001 + } else {
1002 + /* Something in $PATH */
1003 + p = tmp = xstrdup(getenv("PATH"));
1004 + *exec_file = '\0';
1005 + while ((token = strsep(&p, ":"))) {
1006 + if (ch_root)
1007 + snprintf(exec_file, sizeof(exec_file),
1008 + "%s/%s/%s",
1009 + ch_root, token, exec);
1010 + else
1011 + snprintf(exec_file, sizeof(exec_file),
1012 + "%s/%s", token, exec);
1013 + if (exists(exec_file))
1014 + break;
1015 + *exec_file = '\0';
1016 + }
1017 + free(tmp);
1018 + }
1019 + }
1020 + if (start && !exists(exec_file))
1021 + eerrorx("%s: %s does not exist", applet,
1022 + *exec_file ? exec_file : exec);
1023 +
1024 + if (stop) {
1025 + pid = get_pid(pidfile);
1026 + if (pid == -1)
1027 + i = pid;
1028 + else
1029 + i = kill(pid, SIGTERM);
1030 + if (i != 0)
1031 + /* We failed to stop something */
1032 + exit(EXIT_FAILURE);
1033 +
1034 + /* Even if we have not actually killed anything, we should
1035 + * remove information about it as it may have unexpectedly
1036 + * crashed out. We should also return success as the end
1037 + * result would be the same. */
1038 + if (pidfile && exists(pidfile))
1039 + unlink(pidfile);
1040 + if (svcname)
1041 + rc_service_daemon_set(svcname, exec,
1042 + (const char *const *)argv,
1043 + pidfile, false);
1044 + exit(EXIT_SUCCESS);
1045 + }
1046 +
1047 + pid = get_pid(pidfile);
1048 + if (pid != -1)
1049 + if (kill(pid, 0) == 0)
1050 + eerrorx("%s: %s is already running", applet, exec);
1051 +
1052 + einfov("Detaching to start `%s'", exec);
1053 + eindentv();
1054 +
1055 + /* Remove existing pidfile */
1056 + if (pidfile)
1057 + unlink(pidfile);
1058 +
1059 + /*
1060 + * Make sure we can write a pid file
1061 + */
1062 + fp = fopen(pidfile, "w");
1063 + if (! fp)
1064 + eerrorx("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
1065 + fclose(fp);
1066 +
1067 + child_pid = fork();
1068 + if (child_pid == -1)
1069 + eerrorx("%s: fork: %s", applet, strerror(errno));
1070 +
1071 + /* first parent process, do nothing. */
1072 + if (child_pid != 0)
1073 + exit(EXIT_SUCCESS);
1074 +
1075 + child_pid = fork();
1076 + if (child_pid == -1)
1077 + eerrorx("%s: fork: %s", applet, strerror(errno));
1078 +
1079 + if (child_pid != 0) {
1080 + /* this is the supervisor */
1081 + umask(numask);
1082 +
1083 +#ifdef TIOCNOTTY
1084 + tty_fd = open("/dev/tty", O_RDWR);
1085 +#endif
1086 +
1087 + devnull_fd = open("/dev/null", O_RDWR);
1088 +
1089 + fp = fopen(pidfile, "w");
1090 + if (! fp)
1091 + eerrorx("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
1092 + fprintf(fp, "%d\n", getpid());
1093 + fclose(fp);
1094 +
1095 + /*
1096 + * Supervisor main loop
1097 + */
1098 + i = 0;
1099 + while (!exiting) {
1100 + wait(&i);
1101 + if (exiting) {
1102 + syslog(LOG_INFO, "stopping %s, pid %d", exec, child_pid);
1103 + kill(child_pid, SIGTERM);
1104 + } else {
1105 + syslog(LOG_INFO, "%s, pid %d, terminated unexpectedly",
1106 + exec, child_pid);
1107 + child_pid = fork();
1108 + if (child_pid == -1)
1109 + eerrorx("%s: fork: %s", applet, strerror(errno));
1110 + if (child_pid == 0)
1111 + child_process(exec, argv);
1112 + }
1113 + }
1114 +
1115 + if (svcname)
1116 + rc_service_daemon_set(svcname, exec,
1117 + (const char * const *) argv, pidfile, true);
1118 +
1119 + exit(EXIT_SUCCESS);
1120 + } else if (child_pid == 0)
1121 + child_process(exec, argv);
1122 +}
1123
1124 diff --git a/supervise-daemon-guide.md b/supervise-daemon-guide.md
1125 new file mode 100644
1126 index 0000000..7dae0e6
1127 --- /dev/null
1128 +++ b/supervise-daemon-guide.md
1129 @@ -0,0 +1,45 @@
1130 +# Using supervise-daemon
1131 +
1132 +Beginning with OpenRC-0.21 we have our own daemon supervisor,
1133 +supervise-daemon., which can start a daemon and restart it if it
1134 +terminates unexpectedly.
1135 +
1136 +## Use Default start, stop and status functions
1137 +
1138 +If you write your own start, stop and status functions in your service
1139 +script, none of this will work. You must allow OpenRC to use the default
1140 +functions.
1141 +
1142 +## Daemons must not fork
1143 +
1144 +Any deamon that you would like to have monitored by supervise-daemon
1145 +must not fork. Instead, it must stay in the foreground. If the daemon
1146 +itself forks, the supervisor will be unable to monitor it.
1147 +
1148 +If the daemon has an option to instruct it not to fork, you should add this
1149 +to the command_args_foreground variable listed below.
1150 +
1151 +## Variable Settings
1152 +
1153 +The most important setting is the supervisor variable. At the top of
1154 +your service script, you should set this variable as follows:
1155 +
1156 +supervisor=supervise-daemon
1157 +
1158 +Several other variables affect the way services behave under
1159 +supervise-daemon. They are documented on the openrc-run man page, but I
1160 +will list them here for convenience:
1161 +
1162 +pidfile=/pid/of/supervisor.pid
1163 +
1164 +If you are using start-stop-daemon to monitor your scripts, the pidfile
1165 +is the path to the pidfile the daemon creates. If, on the other hand,
1166 +you are using supervise-daemon, this is the path to the pidfile the
1167 +supervisor creates.
1168 +
1169 +command_args_foreground should be used if the daemon you want to monitor
1170 +forks and goes to the background by default. This should be set to the
1171 +command line option that instructs the daemon to stay in the foreground.
1172 +
1173 +This is very early support, so feel free to file bugs if you have
1174 +issues.