source: MondoRescue/branches/3.3/mindi-busybox/debianutils/start_stop_daemon.c@ 3865

Last change on this file since 3865 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 16.8 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini start-stop-daemon implementation(s) for busybox
4 *
5 * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
6 * Adapted for busybox David Kimdon <dwhedon@gordian.com>
7 *
8 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
9 */
10
11/*
12This is how it is supposed to work:
13
14start-stop-daemon [OPTIONS] [--start|--stop] [[--] arguments...]
15
16One (only) of these must be given:
17 -S,--start Start
18 -K,--stop Stop
19
20Search for matching processes.
21If --stop is given, stop all matching processes (by sending a signal).
22If --start is given, start a new process unless a matching process was found.
23
24Options controlling process matching
25(if multiple conditions are specified, all must match):
26 -u,--user USERNAME|UID Only consider this user's processes
27 -n,--name PROCESS_NAME Look for processes by matching PROCESS_NAME
28 with comm field in /proc/$PID/stat.
29 Only basename is compared:
30 "ntpd" == "./ntpd" == "/path/to/ntpd".
31[TODO: can PROCESS_NAME be a full pathname? Should we require full match then
32with /proc/$PID/exe or argv[0] (comm can't be matched, it never contains path)]
33 -x,--exec EXECUTABLE Look for processes that were started with this
34 command in /proc/$PID/exe and /proc/$PID/cmdline
35 (/proc/$PID/cmdline is a bbox extension)
36 Unlike -n, we match against the full path:
37 "ntpd" != "./ntpd" != "/path/to/ntpd"
38 -p,--pidfile PID_FILE Look for processes with PID from this file
39
40Options which are valid for --start only:
41 -x,--exec EXECUTABLE Program to run (1st arg of execvp). Mandatory.
42 -a,--startas NAME argv[0] (defaults to EXECUTABLE)
43 -b,--background Put process into background
44 -N,--nicelevel N Add N to process' nice level
45 -c,--chuid USER[:[GRP]] Change to specified user [and group]
46 -m,--make-pidfile Write PID to the pidfile
47 (both -m and -p must be given!)
48
49Options which are valid for --stop only:
50 -s,--signal SIG Signal to send (default:TERM)
51 -t,--test Exit with status 0 if process is found
52 (we don't actually start or stop daemons)
53
54Misc options:
55 -o,--oknodo Exit with status 0 if nothing is done
56 -q,--quiet Quiet
57 -v,--verbose Verbose
58*/
59//config:config START_STOP_DAEMON
60//config: bool "start-stop-daemon"
61//config: default y
62//config: help
63//config: start-stop-daemon is used to control the creation and
64//config: termination of system-level processes, usually the ones
65//config: started during the startup of the system.
66//config:
67//config:config FEATURE_START_STOP_DAEMON_FANCY
68//config: bool "Support additional arguments"
69//config: default y
70//config: depends on START_STOP_DAEMON
71//config: help
72//config: Support additional arguments.
73//config: -o|--oknodo ignored since we exit with 0 anyway
74//config: -v|--verbose
75//config: -N|--nicelevel N
76//config:
77//config:config FEATURE_START_STOP_DAEMON_LONG_OPTIONS
78//config: bool "Enable long options"
79//config: default y
80//config: depends on START_STOP_DAEMON && LONG_OPTS
81//config: help
82//config: Support long options for the start-stop-daemon applet.
83
84//applet:IF_START_STOP_DAEMON(APPLET_ODDNAME(start-stop-daemon, start_stop_daemon, BB_DIR_SBIN, BB_SUID_DROP, start_stop_daemon))
85
86//kbuild:lib-$(CONFIG_START_STOP_DAEMON) += start_stop_daemon.o
87
88//usage:#define start_stop_daemon_trivial_usage
89//usage: "[OPTIONS] [-S|-K] ... [-- ARGS...]"
90//usage:#define start_stop_daemon_full_usage "\n\n"
91//usage: "Search for matching processes, and then\n"
92//usage: "-K: stop all matching processes.\n"
93//usage: "-S: start a process unless a matching process is found.\n"
94//usage: IF_FEATURE_START_STOP_DAEMON_LONG_OPTIONS(
95//usage: "\nProcess matching:"
96//usage: "\n -u,--user USERNAME|UID Match only this user's processes"
97//usage: "\n -n,--name NAME Match processes with NAME"
98//usage: "\n in comm field in /proc/PID/stat"
99//usage: "\n -x,--exec EXECUTABLE Match processes with this command"
100//usage: "\n in /proc/PID/{exe,cmdline}"
101//usage: "\n -p,--pidfile FILE Match a process with PID from the file"
102//usage: "\n All specified conditions must match"
103//usage: "\n-S only:"
104//usage: "\n -x,--exec EXECUTABLE Program to run"
105//usage: "\n -a,--startas NAME Zeroth argument"
106//usage: "\n -b,--background Background"
107//usage: IF_FEATURE_START_STOP_DAEMON_FANCY(
108//usage: "\n -N,--nicelevel N Change nice level"
109//usage: )
110//usage: "\n -c,--chuid USER[:[GRP]] Change to user/group"
111//usage: "\n -m,--make-pidfile Write PID to the pidfile specified by -p"
112//usage: "\n-K only:"
113//usage: "\n -s,--signal SIG Signal to send"
114//usage: "\n -t,--test Match only, exit with 0 if a process is found"
115//usage: "\nOther:"
116//usage: IF_FEATURE_START_STOP_DAEMON_FANCY(
117//usage: "\n -o,--oknodo Exit with status 0 if nothing is done"
118//usage: "\n -v,--verbose Verbose"
119//usage: )
120//usage: "\n -q,--quiet Quiet"
121//usage: )
122//usage: IF_NOT_FEATURE_START_STOP_DAEMON_LONG_OPTIONS(
123//usage: "\nProcess matching:"
124//usage: "\n -u USERNAME|UID Match only this user's processes"
125//usage: "\n -n NAME Match processes with NAME"
126//usage: "\n in comm field in /proc/PID/stat"
127//usage: "\n -x EXECUTABLE Match processes with this command"
128//usage: "\n command in /proc/PID/cmdline"
129//usage: "\n -p FILE Match a process with PID from the file"
130//usage: "\n All specified conditions must match"
131//usage: "\n-S only:"
132//usage: "\n -x EXECUTABLE Program to run"
133//usage: "\n -a NAME Zeroth argument"
134//usage: "\n -b Background"
135//usage: IF_FEATURE_START_STOP_DAEMON_FANCY(
136//usage: "\n -N N Change nice level"
137//usage: )
138//usage: "\n -c USER[:[GRP]] Change to user/group"
139//usage: "\n -m Write PID to the pidfile specified by -p"
140//usage: "\n-K only:"
141//usage: "\n -s SIG Signal to send"
142//usage: "\n -t Match only, exit with 0 if a process is found"
143//usage: "\nOther:"
144//usage: IF_FEATURE_START_STOP_DAEMON_FANCY(
145//usage: "\n -o Exit with status 0 if nothing is done"
146//usage: "\n -v Verbose"
147//usage: )
148//usage: "\n -q Quiet"
149//usage: )
150
151#include <sys/resource.h>
152
153/* Override ENABLE_FEATURE_PIDFILE */
154#define WANT_PIDFILE 1
155#include "libbb.h"
156#include "common_bufsiz.h"
157
158struct pid_list {
159 struct pid_list *next;
160 pid_t pid;
161};
162
163enum {
164 CTX_STOP = (1 << 0),
165 CTX_START = (1 << 1),
166 OPT_BACKGROUND = (1 << 2), // -b
167 OPT_QUIET = (1 << 3), // -q
168 OPT_TEST = (1 << 4), // -t
169 OPT_MAKEPID = (1 << 5), // -m
170 OPT_a = (1 << 6), // -a
171 OPT_n = (1 << 7), // -n
172 OPT_s = (1 << 8), // -s
173 OPT_u = (1 << 9), // -u
174 OPT_c = (1 << 10), // -c
175 OPT_x = (1 << 11), // -x
176 OPT_p = (1 << 12), // -p
177 OPT_OKNODO = (1 << 13) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -o
178 OPT_VERBOSE = (1 << 14) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -v
179 OPT_NICELEVEL = (1 << 15) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -N
180};
181#define QUIET (option_mask32 & OPT_QUIET)
182#define TEST (option_mask32 & OPT_TEST)
183
184struct globals {
185 struct pid_list *found_procs;
186 char *userspec;
187 char *cmdname;
188 char *execname;
189 char *pidfile;
190 char *execname_cmpbuf;
191 unsigned execname_sizeof;
192 int user_id;
193 smallint signal_nr;
194} FIX_ALIASING;
195#define G (*(struct globals*)bb_common_bufsiz1)
196#define userspec (G.userspec )
197#define cmdname (G.cmdname )
198#define execname (G.execname )
199#define pidfile (G.pidfile )
200#define user_id (G.user_id )
201#define signal_nr (G.signal_nr )
202#define INIT_G() do { \
203 setup_common_bufsiz(); \
204 user_id = -1; \
205 signal_nr = 15; \
206} while (0)
207
208#ifdef OLDER_VERSION_OF_X
209/* -x,--exec EXECUTABLE
210 * Look for processes with matching /proc/$PID/exe.
211 * Match is performed using device+inode.
212 */
213static int pid_is_exec(pid_t pid)
214{
215 struct stat st;
216 char buf[sizeof("/proc/%u/exe") + sizeof(int)*3];
217
218 sprintf(buf, "/proc/%u/exe", (unsigned)pid);
219 if (stat(buf, &st) < 0)
220 return 0;
221 if (st.st_dev == execstat.st_dev
222 && st.st_ino == execstat.st_ino)
223 return 1;
224 return 0;
225}
226#endif
227
228static int pid_is_exec(pid_t pid)
229{
230 ssize_t bytes;
231 char buf[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
232 char *procname, *exelink;
233 int match;
234
235 procname = buf + sprintf(buf, "/proc/%u/exe", (unsigned)pid) - 3;
236
237 exelink = xmalloc_readlink(buf);
238 match = (exelink && strcmp(execname, exelink) == 0);
239 free(exelink);
240 if (match)
241 return match;
242
243 strcpy(procname, "cmdline");
244 bytes = open_read_close(buf, G.execname_cmpbuf, G.execname_sizeof);
245 if (bytes > 0) {
246 G.execname_cmpbuf[bytes] = '\0';
247 return strcmp(execname, G.execname_cmpbuf) == 0;
248 }
249 return 0;
250}
251
252static int pid_is_name(pid_t pid)
253{
254 /* /proc/PID/stat is "PID (comm_15_bytes_max) ..." */
255 char buf[32]; /* should be enough */
256 char *p, *pe;
257
258 sprintf(buf, "/proc/%u/stat", (unsigned)pid);
259 if (open_read_close(buf, buf, sizeof(buf) - 1) < 0)
260 return 0;
261 buf[sizeof(buf) - 1] = '\0'; /* paranoia */
262 p = strchr(buf, '(');
263 if (!p)
264 return 0;
265 pe = strrchr(++p, ')');
266 if (!pe)
267 return 0;
268 *pe = '\0';
269 /* we require comm to match and to not be truncated */
270 /* in Linux, if comm is 15 chars, it may be a truncated
271 * name, so we don't allow that to match */
272 if (strlen(p) >= COMM_LEN - 1) /* COMM_LEN is 16 */
273 return 0;
274 return strcmp(p, cmdname) == 0;
275}
276
277static int pid_is_user(int pid)
278{
279 struct stat sb;
280 char buf[sizeof("/proc/") + sizeof(int)*3];
281
282 sprintf(buf, "/proc/%u", (unsigned)pid);
283 if (stat(buf, &sb) != 0)
284 return 0;
285 return (sb.st_uid == (uid_t)user_id);
286}
287
288static void check(int pid)
289{
290 struct pid_list *p;
291
292 if (execname && !pid_is_exec(pid)) {
293 return;
294 }
295 if (cmdname && !pid_is_name(pid)) {
296 return;
297 }
298 if (userspec && !pid_is_user(pid)) {
299 return;
300 }
301 p = xmalloc(sizeof(*p));
302 p->next = G.found_procs;
303 p->pid = pid;
304 G.found_procs = p;
305}
306
307static void do_pidfile(void)
308{
309 FILE *f;
310 unsigned pid;
311
312 f = fopen_for_read(pidfile);
313 if (f) {
314 if (fscanf(f, "%u", &pid) == 1)
315 check(pid);
316 fclose(f);
317 } else if (errno != ENOENT)
318 bb_perror_msg_and_die("open pidfile %s", pidfile);
319}
320
321static void do_procinit(void)
322{
323 DIR *procdir;
324 struct dirent *entry;
325 int pid;
326
327 if (pidfile) {
328 do_pidfile();
329 return;
330 }
331
332 procdir = xopendir("/proc");
333
334 pid = 0;
335 while (1) {
336 errno = 0; /* clear any previous error */
337 entry = readdir(procdir);
338// TODO: this check is too generic, it's better
339// to check for exact errno(s) which mean that we got stale entry
340 if (errno) /* Stale entry, process has died after opendir */
341 continue;
342 if (!entry) /* EOF, no more entries */
343 break;
344 pid = bb_strtou(entry->d_name, NULL, 10);
345 if (errno) /* NaN */
346 continue;
347 check(pid);
348 }
349 closedir(procdir);
350 if (!pid)
351 bb_error_msg_and_die("nothing in /proc - not mounted?");
352}
353
354static int do_stop(void)
355{
356 char *what;
357 struct pid_list *p;
358 int killed = 0;
359
360 if (cmdname) {
361 if (ENABLE_FEATURE_CLEAN_UP) what = xstrdup(cmdname);
362 if (!ENABLE_FEATURE_CLEAN_UP) what = cmdname;
363 } else if (execname) {
364 if (ENABLE_FEATURE_CLEAN_UP) what = xstrdup(execname);
365 if (!ENABLE_FEATURE_CLEAN_UP) what = execname;
366 } else if (pidfile) {
367 what = xasprintf("process in pidfile '%s'", pidfile);
368 } else if (userspec) {
369 what = xasprintf("process(es) owned by '%s'", userspec);
370 } else {
371 bb_error_msg_and_die("internal error, please report");
372 }
373
374 if (!G.found_procs) {
375 if (!QUIET)
376 printf("no %s found; none killed\n", what);
377 killed = -1;
378 goto ret;
379 }
380 for (p = G.found_procs; p; p = p->next) {
381 if (kill(p->pid, TEST ? 0 : signal_nr) == 0) {
382 killed++;
383 } else {
384 bb_perror_msg("warning: killing process %u", (unsigned)p->pid);
385 p->pid = 0;
386 if (TEST) {
387 /* Example: -K --test --pidfile PIDFILE detected
388 * that PIDFILE's pid doesn't exist */
389 killed = -1;
390 goto ret;
391 }
392 }
393 }
394 if (!QUIET && killed) {
395 printf("stopped %s (pid", what);
396 for (p = G.found_procs; p; p = p->next)
397 if (p->pid)
398 printf(" %u", (unsigned)p->pid);
399 puts(")");
400 }
401 ret:
402 if (ENABLE_FEATURE_CLEAN_UP)
403 free(what);
404 return killed;
405}
406
407#if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS
408static const char start_stop_daemon_longopts[] ALIGN1 =
409 "stop\0" No_argument "K"
410 "start\0" No_argument "S"
411 "background\0" No_argument "b"
412 "quiet\0" No_argument "q"
413 "test\0" No_argument "t"
414 "make-pidfile\0" No_argument "m"
415#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
416 "oknodo\0" No_argument "o"
417 "verbose\0" No_argument "v"
418 "nicelevel\0" Required_argument "N"
419#endif
420 "startas\0" Required_argument "a"
421 "name\0" Required_argument "n"
422 "signal\0" Required_argument "s"
423 "user\0" Required_argument "u"
424 "chuid\0" Required_argument "c"
425 "exec\0" Required_argument "x"
426 "pidfile\0" Required_argument "p"
427#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
428 "retry\0" Required_argument "R"
429#endif
430 ;
431#endif
432
433int start_stop_daemon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
434int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv)
435{
436 unsigned opt;
437 char *signame;
438 char *startas;
439 char *chuid;
440#ifdef OLDER_VERSION_OF_X
441 struct stat execstat;
442#endif
443#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
444// char *retry_arg = NULL;
445// int retries = -1;
446 char *opt_N;
447#endif
448
449 INIT_G();
450
451#if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS
452 applet_long_options = start_stop_daemon_longopts;
453#endif
454
455 /* -K or -S is required; they are mutually exclusive */
456 /* -p is required if -m is given */
457 /* -xpun (at least one) is required if -K is given */
458 /* -xa (at least one) is required if -S is given */
459 /* -q turns off -v */
460 opt_complementary = "K:S:K--S:S--K:m?p:K?xpun:S?xa"
461 IF_FEATURE_START_STOP_DAEMON_FANCY("q-v");
462 opt = getopt32(argv, "KSbqtma:n:s:u:c:x:p:"
463 IF_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:"),
464 &startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile
465 IF_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N)
466 /* We accept and ignore -R <param> / --retry <param> */
467 IF_FEATURE_START_STOP_DAEMON_FANCY(,NULL)
468 );
469
470 if (opt & OPT_s) {
471 signal_nr = get_signum(signame);
472 if (signal_nr < 0) bb_show_usage();
473 }
474
475 if (!(opt & OPT_a))
476 startas = execname;
477 if (!execname) /* in case -a is given and -x is not */
478 execname = startas;
479 if (execname) {
480 G.execname_sizeof = strlen(execname) + 1;
481 G.execname_cmpbuf = xmalloc(G.execname_sizeof + 1);
482 }
483
484// IF_FEATURE_START_STOP_DAEMON_FANCY(
485// if (retry_arg)
486// retries = xatoi_positive(retry_arg);
487// )
488 //argc -= optind;
489 argv += optind;
490
491 if (userspec) {
492 user_id = bb_strtou(userspec, NULL, 10);
493 if (errno)
494 user_id = xuname2uid(userspec);
495 }
496 /* Both start and stop need to know current processes */
497 do_procinit();
498
499 if (opt & CTX_STOP) {
500 int i = do_stop();
501 return (opt & OPT_OKNODO) ? 0 : (i <= 0);
502 }
503
504 if (G.found_procs) {
505 if (!QUIET)
506 printf("%s is already running\n%u\n", execname, (unsigned)G.found_procs->pid);
507 return !(opt & OPT_OKNODO);
508 }
509
510#ifdef OLDER_VERSION_OF_X
511 if (execname)
512 xstat(execname, &execstat);
513#endif
514
515 *--argv = startas;
516 if (opt & OPT_BACKGROUND) {
517#if BB_MMU
518 bb_daemonize(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS + DAEMON_DOUBLE_FORK);
519 /* DAEMON_DEVNULL_STDIO is superfluous -
520 * it's always done by bb_daemonize() */
521#else
522 pid_t pid = xvfork();
523 if (pid != 0) {
524 /* parent */
525 /* why _exit? the child may have changed the stack,
526 * so "return 0" may do bad things */
527 _exit(EXIT_SUCCESS);
528 }
529 /* Child */
530 setsid(); /* detach from controlling tty */
531 /* Redirect stdio to /dev/null, close extra FDs.
532 * We do not actually daemonize because of DAEMON_ONLY_SANITIZE */
533 bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO
534 + DAEMON_CLOSE_EXTRA_FDS
535 + DAEMON_ONLY_SANITIZE,
536 NULL /* argv, unused */ );
537#endif
538 }
539 if (opt & OPT_MAKEPID) {
540 /* User wants _us_ to make the pidfile */
541 write_pidfile(pidfile);
542 }
543 if (opt & OPT_c) {
544 struct bb_uidgid_t ugid;
545 parse_chown_usergroup_or_die(&ugid, chuid);
546 if (ugid.uid != (uid_t) -1L) {
547 struct passwd *pw = xgetpwuid(ugid.uid);
548 if (ugid.gid != (gid_t) -1L)
549 pw->pw_gid = ugid.gid;
550 /* initgroups, setgid, setuid: */
551 change_identity(pw);
552 } else if (ugid.gid != (gid_t) -1L) {
553 xsetgid(ugid.gid);
554 setgroups(1, &ugid.gid);
555 }
556 }
557#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
558 if (opt & OPT_NICELEVEL) {
559 /* Set process priority */
560 int prio = getpriority(PRIO_PROCESS, 0) + xatoi_range(opt_N, INT_MIN/2, INT_MAX/2);
561 if (setpriority(PRIO_PROCESS, 0, prio) < 0) {
562 bb_perror_msg_and_die("setpriority(%d)", prio);
563 }
564 }
565#endif
566 execvp(startas, argv);
567 bb_perror_msg_and_die("can't execute '%s'", startas);
568}
Note: See TracBrowser for help on using the repository browser.