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. |