1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156#define WANT_PIDFILE 1
157#include "libbb.h"
158#include "common_bufsiz.h"
159
160struct pid_list {
161 struct pid_list *next;
162 pid_t pid;
163};
164
165enum {
166 CTX_STOP = (1 << 0),
167 CTX_START = (1 << 1),
168 OPT_BACKGROUND = (1 << 2),
169 OPT_QUIET = (1 << 3),
170 OPT_TEST = (1 << 4),
171 OPT_MAKEPID = (1 << 5),
172 OPT_a = (1 << 6),
173 OPT_n = (1 << 7),
174 OPT_s = (1 << 8),
175 OPT_u = (1 << 9),
176 OPT_c = (1 << 10),
177 OPT_d = (1 << 11),
178 OPT_x = (1 << 12),
179 OPT_p = (1 << 13),
180 OPT_OUTPUT = (1 << 14),
181 OPT_OKNODO = (1 << 15) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY,
182 OPT_VERBOSE = (1 << 16) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY,
183 OPT_NICELEVEL = (1 << 17) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY,
184};
185#define QUIET (option_mask32 & OPT_QUIET)
186#define TEST (option_mask32 & OPT_TEST)
187
188struct globals {
189 struct pid_list *found_procs;
190 const char *userspec;
191 const char *cmdname;
192 const char *execname;
193 const char *pidfile;
194 char *execname_cmpbuf;
195 unsigned execname_sizeof;
196 int user_id;
197 smallint signal_nr;
198#ifdef OLDER_VERSION_OF_X
199 struct stat execstat;
200#endif
201} FIX_ALIASING;
202#define G (*(struct globals*)bb_common_bufsiz1)
203#define userspec (G.userspec )
204#define cmdname (G.cmdname )
205#define execname (G.execname )
206#define pidfile (G.pidfile )
207#define user_id (G.user_id )
208#define signal_nr (G.signal_nr )
209#define INIT_G() do { \
210 setup_common_bufsiz(); \
211 user_id = -1; \
212 signal_nr = 15; \
213} while (0)
214
215#ifdef OLDER_VERSION_OF_X
216
217
218
219
220static int pid_is_exec(pid_t pid)
221{
222 struct stat st;
223 char buf[sizeof("/proc/%u/exe") + sizeof(int)*3];
224
225 sprintf(buf, "/proc/%u/exe", (unsigned)pid);
226 if (stat(buf, &st) < 0)
227 return 0;
228 if (st.st_dev == G.execstat.st_dev
229 && st.st_ino == G.execstat.st_ino)
230 return 1;
231 return 0;
232}
233#else
234static int pid_is_exec(pid_t pid)
235{
236 ssize_t bytes;
237 char buf[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
238 char *procname, *exelink;
239 int match;
240
241 procname = buf + sprintf(buf, "/proc/%u/exe", (unsigned)pid) - 3;
242
243 exelink = xmalloc_readlink(buf);
244 match = (exelink && strcmp(execname, exelink) == 0);
245 free(exelink);
246 if (match)
247 return match;
248
249 strcpy(procname, "cmdline");
250 bytes = open_read_close(buf, G.execname_cmpbuf, G.execname_sizeof);
251 if (bytes > 0) {
252 G.execname_cmpbuf[bytes] = '\0';
253 return strcmp(execname, G.execname_cmpbuf) == 0;
254 }
255 return 0;
256}
257#endif
258
259static int pid_is_name(pid_t pid)
260{
261
262 char buf[32];
263 char *p, *pe;
264
265 sprintf(buf, "/proc/%u/stat", (unsigned)pid);
266 if (open_read_close(buf, buf, sizeof(buf) - 1) < 0)
267 return 0;
268 buf[sizeof(buf) - 1] = '\0';
269 p = strchr(buf, '(');
270 if (!p)
271 return 0;
272 pe = strrchr(++p, ')');
273 if (!pe)
274 return 0;
275 *pe = '\0';
276
277
278
279 if (strlen(p) >= COMM_LEN - 1)
280 return 0;
281 return strcmp(p, cmdname) == 0;
282}
283
284static int pid_is_user(int pid)
285{
286 struct stat sb;
287 char buf[sizeof("/proc/") + sizeof(int)*3];
288
289 sprintf(buf, "/proc/%u", (unsigned)pid);
290 if (stat(buf, &sb) != 0)
291 return 0;
292 return (sb.st_uid == (uid_t)user_id);
293}
294
295static void check(int pid)
296{
297 struct pid_list *p;
298
299 if (execname && !pid_is_exec(pid)) {
300 return;
301 }
302 if (cmdname && !pid_is_name(pid)) {
303 return;
304 }
305 if (userspec && !pid_is_user(pid)) {
306 return;
307 }
308 p = xmalloc(sizeof(*p));
309 p->next = G.found_procs;
310 p->pid = pid;
311 G.found_procs = p;
312}
313
314static void do_pidfile(void)
315{
316 FILE *f;
317 unsigned pid;
318
319 f = fopen_for_read(pidfile);
320 if (f) {
321 if (fscanf(f, "%u", &pid) == 1)
322 check(pid);
323 fclose(f);
324 } else if (errno != ENOENT)
325 bb_perror_msg_and_die("open pidfile %s", pidfile);
326}
327
328static void do_procinit(void)
329{
330 DIR *procdir;
331 struct dirent *entry;
332 int pid;
333
334 if (pidfile) {
335 do_pidfile();
336 return;
337 }
338
339 procdir = xopendir("/proc");
340
341 pid = 0;
342 while (1) {
343 errno = 0;
344 entry = readdir(procdir);
345
346
347 if (errno)
348 continue;
349 if (!entry)
350 break;
351 pid = bb_strtou(entry->d_name, NULL, 10);
352 if (errno)
353 continue;
354 check(pid);
355 }
356 closedir(procdir);
357 if (!pid)
358 bb_simple_error_msg_and_die("nothing in /proc - not mounted?");
359}
360
361static int do_stop(void)
362{
363 const char *what;
364 struct pid_list *p;
365 int killed = 0;
366
367 if (cmdname) {
368 if (ENABLE_FEATURE_CLEAN_UP) what = xstrdup(cmdname);
369 if (!ENABLE_FEATURE_CLEAN_UP) what = cmdname;
370 } else if (execname) {
371 if (ENABLE_FEATURE_CLEAN_UP) what = xstrdup(execname);
372 if (!ENABLE_FEATURE_CLEAN_UP) what = execname;
373 } else if (pidfile) {
374 what = xasprintf("process in pidfile '%s'", pidfile);
375 } else if (userspec) {
376 what = xasprintf("process(es) owned by '%s'", userspec);
377 } else {
378 bb_simple_error_msg_and_die("internal error, please report");
379 }
380
381 if (!G.found_procs) {
382 if (!QUIET)
383 printf("no %s found; none killed\n", what);
384 killed = -1;
385 goto ret;
386 }
387 for (p = G.found_procs; p; p = p->next) {
388 if (kill(p->pid, TEST ? 0 : signal_nr) == 0) {
389 killed++;
390 } else {
391 bb_perror_msg("warning: killing process %u", (unsigned)p->pid);
392 p->pid = 0;
393 if (TEST) {
394
395
396 killed = -1;
397 goto ret;
398 }
399 }
400 }
401 if (!QUIET && killed) {
402 printf("stopped %s (pid", what);
403 for (p = G.found_procs; p; p = p->next)
404 if (p->pid)
405 printf(" %u", (unsigned)p->pid);
406 puts(")");
407 }
408 ret:
409 if (ENABLE_FEATURE_CLEAN_UP)
410 free((char *)what);
411 return killed;
412}
413
414#if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS
415static const char start_stop_daemon_longopts[] ALIGN1 =
416 "stop\0" No_argument "K"
417 "start\0" No_argument "S"
418 "background\0" No_argument "b"
419 "quiet\0" No_argument "q"
420 "test\0" No_argument "t"
421 "make-pidfile\0" No_argument "m"
422 "output\0" Required_argument "O"
423# if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
424 "oknodo\0" No_argument "o"
425 "verbose\0" No_argument "v"
426 "nicelevel\0" Required_argument "N"
427# endif
428 "startas\0" Required_argument "a"
429 "name\0" Required_argument "n"
430 "signal\0" Required_argument "s"
431 "user\0" Required_argument "u"
432 "chuid\0" Required_argument "c"
433 "chdir\0" Required_argument "d"
434 "exec\0" Required_argument "x"
435 "pidfile\0" Required_argument "p"
436# if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
437 "retry\0" Required_argument "R"
438# endif
439 ;
440# define GETOPT32 getopt32long
441# define LONGOPTS start_stop_daemon_longopts,
442#else
443# define GETOPT32 getopt32
444# define LONGOPTS
445#endif
446
447int start_stop_daemon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
448int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv)
449{
450 unsigned opt;
451 const char *signame;
452 const char *startas = NULL;
453 char *chuid;
454 const char *chdir;
455 const char *output = NULL;
456#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
457
458
459 const char *opt_N;
460#endif
461
462 INIT_G();
463
464 opt = GETOPT32(argv, "^"
465 "KSbqtma:n:s:u:c:d:x:p:O:"
466 IF_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:")
467 "\0"
468 "K:S:K--S:S--K"
469
470 ":m?p"
471 ":K?xpun"
472
473
474
475
476
477
478 IF_FEATURE_START_STOP_DAEMON_FANCY(":q-v")
479 ,
480 LONGOPTS
481 &startas, &cmdname, &signame, &userspec, &chuid, &chdir, &execname, &pidfile, &output
482 IF_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N)
483
484 IF_FEATURE_START_STOP_DAEMON_FANCY(,NULL)
485 );
486
487
488
489
490
491
492
493
494 if (opt & OPT_s) {
495 signal_nr = get_signum(signame);
496 if (signal_nr < 0) bb_show_usage();
497 }
498
499
500 argv += optind;
501
502
503
504
505
506
507
508 if (opt & CTX_START) {
509 if (!execname) {
510 execname = startas;
511 if (!execname) {
512 execname = argv[0];
513 if (!execname)
514 bb_show_usage();
515 argv++;
516 }
517 }
518 if (!startas)
519 startas = execname;
520 *--argv = (char *)startas;
521 }
522 if (execname) {
523 G.execname_sizeof = strlen(execname) + 1;
524 G.execname_cmpbuf = xmalloc(G.execname_sizeof + 1);
525 }
526
527
528
529
530 if (userspec) {
531 user_id = bb_strtou(userspec, NULL, 10);
532 if (errno)
533 user_id = xuname2uid(userspec);
534 }
535
536
537 do_procinit();
538
539 if (opt & CTX_STOP) {
540 int i = do_stop();
541 return (opt & OPT_OKNODO) ? 0 : (i <= 0);
542 }
543
544
545
546 if (G.found_procs) {
547 if (!QUIET)
548 printf("%s is already running\n", execname);
549 return !(opt & OPT_OKNODO);
550 }
551
552#ifdef OLDER_VERSION_OF_X
553 if (execname)
554 xstat(execname, &G.execstat);
555#endif
556
557 if (opt & OPT_BACKGROUND) {
558
559
560
561
562
563
564
565
566
567
568
569 pid_t pid = xvfork();
570 if (pid != 0) {
571
572
573
574
575 _exit_SUCCESS();
576 }
577
578 setsid();
579
580
581 bb_daemon_helper(DAEMON_DEVNULL_STDIN + DAEMON_CLOSE_EXTRA_FDS);
582 if (!output)
583 output = bb_dev_null;
584
585
586
587
588 pid = xvfork();
589 if (pid != 0)
590 _exit_SUCCESS();
591 }
592 if (opt & OPT_MAKEPID) {
593
594 write_pidfile(pidfile);
595 }
596#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
597 if (opt & OPT_NICELEVEL) {
598
599 int prio = getpriority(PRIO_PROCESS, 0) + xatoi_range(opt_N, INT_MIN/2, INT_MAX/2);
600 if (setpriority(PRIO_PROCESS, 0, prio) < 0) {
601 bb_perror_msg_and_die("setpriority(%d)", prio);
602 }
603 }
604#endif
605 if (opt & OPT_c) {
606 struct bb_uidgid_t ugid;
607 parse_chown_usergroup_or_die(&ugid, chuid);
608 if (ugid.uid != (uid_t) -1L) {
609 struct passwd *pw = xgetpwuid(ugid.uid);
610 if (ugid.gid != (gid_t) -1L)
611 pw->pw_gid = ugid.gid;
612
613 change_identity(pw);
614 } else if (ugid.gid != (gid_t) -1L) {
615 xsetgid(ugid.gid);
616 setgroups(1, &ugid.gid);
617 }
618 }
619 if (opt & OPT_d) {
620 xchdir(chdir);
621 }
622 if (output) {
623 int outfd = xopen(output, O_WRONLY | O_CREAT | O_APPEND);
624 xmove_fd(outfd, STDOUT_FILENO);
625 xdup2(STDOUT_FILENO, STDERR_FILENO);
626
627 }
628
629
630
631
632 execvp(execname, argv);
633 bb_perror_msg_and_die("can't execute '%s'", startas);
634}
635