move everything to new NOMMU helpers, except udhcp
[platform/upstream/busybox.git] / debianutils / start_stop_daemon.c
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 tarball for details.
9  */
10
11 #include "busybox.h"
12 #include <getopt.h>
13 #include <sys/resource.h>
14
15 static int signal_nr = 15;
16 static int user_id = -1;
17 static char *userspec;
18 static char *cmdname;
19 static char *execname;
20 static char *pidfile;
21 static smallint quiet;
22
23 struct pid_list {
24         struct pid_list *next;
25         pid_t pid;
26 };
27
28 static struct pid_list *found;
29
30 static inline void push(pid_t pid)
31 {
32         struct pid_list *p;
33
34         p = xmalloc(sizeof(*p));
35         p->next = found;
36         p->pid = pid;
37         found = p;
38 }
39
40 static int pid_is_exec(pid_t pid, const char *name)
41 {
42         char buf[sizeof("/proc//exe") + sizeof(int)*3];
43         char *execbuf;
44         int sz;
45         int equal;
46
47         sprintf(buf, "/proc/%d/exe", pid);
48         sz = strlen(name) + 1;
49         execbuf = xzalloc(sz);
50         readlink(buf, execbuf, sz);
51
52         /* if readlink fails, execbuf still contains "" */
53         equal = !strcmp(execbuf, name);
54         if (ENABLE_FEATURE_CLEAN_UP)
55                 free(execbuf);
56         return equal;
57 }
58
59 static int pid_is_user(int pid, int uid)
60 {
61         struct stat sb;
62         char buf[sizeof("/proc/") + sizeof(int)*3];
63
64         sprintf(buf, "/proc/%u", pid);
65         if (stat(buf, &sb) != 0)
66                 return 0;
67         return (sb.st_uid == uid);
68 }
69
70 static int pid_is_cmd(pid_t pid, const char *name)
71 {
72         char fname[sizeof("/proc//stat") + sizeof(int)*3];
73         char *buf;
74         int r = 0;
75
76         sprintf(fname, "/proc/%u/stat", pid);
77         buf = xmalloc_open_read_close(fname, NULL);
78         if (buf) {
79                 char *p = strchr(buf, '(');
80                 if (p) {
81                         char *pe = strrchr(++p, ')');
82                         if (pe) {
83                                 *pe = '\0';
84                                 r = !strcmp(p, name);
85                         }
86                 }
87                 free(buf);
88         }
89         return r;
90 }
91
92
93 static void check(int pid)
94 {
95         if (execname && !pid_is_exec(pid, execname)) {
96                 return;
97         }
98         if (userspec && !pid_is_user(pid, user_id)) {
99                 return;
100         }
101         if (cmdname && !pid_is_cmd(pid, cmdname)) {
102                 return;
103         }
104         push(pid);
105 }
106
107
108 static void do_pidfile(void)
109 {
110         FILE *f;
111         pid_t pid;
112
113         f = fopen(pidfile, "r");
114         if (f) {
115                 if (fscanf(f, "%u", &pid) == 1)
116                         check(pid);
117                 fclose(f);
118         } else if (errno != ENOENT)
119                 bb_perror_msg_and_die("open pidfile %s", pidfile);
120 }
121
122 static void do_procinit(void)
123 {
124         DIR *procdir;
125         struct dirent *entry;
126         int foundany, pid;
127
128         if (pidfile) {
129                 do_pidfile();
130                 return;
131         }
132
133         procdir = xopendir("/proc");
134
135         foundany = 0;
136         while ((entry = readdir(procdir)) != NULL) {
137                 pid = bb_strtou(entry->d_name, NULL, 10);
138                 if (errno)
139                         continue;
140                 foundany++;
141                 check(pid);
142         }
143         closedir(procdir);
144         if (!foundany)
145                 bb_error_msg_and_die ("nothing in /proc - not mounted?");
146 }
147
148
149 static int do_stop(void)
150 {
151         char *what;
152         struct pid_list *p;
153         int killed = 0;
154
155         do_procinit();
156
157         if (cmdname)
158                 what = xstrdup(cmdname);
159         else if (execname)
160                 what = xstrdup(execname);
161         else if (pidfile)
162                 what = xasprintf("process in pidfile '%s'", pidfile);
163         else if (userspec)
164                 what = xasprintf("process(es) owned by '%s'", userspec);
165         else
166                 bb_error_msg_and_die("internal error, please report");
167
168         if (!found) {
169                 if (!quiet)
170                         printf("no %s found; none killed\n", what);
171                 if (ENABLE_FEATURE_CLEAN_UP)
172                         free(what);
173                 return -1;
174         }
175         for (p = found; p; p = p->next) {
176                 if (kill(p->pid, signal_nr) == 0) {
177                         p->pid = -p->pid;
178                         killed++;
179                 } else {
180                         bb_perror_msg("warning: failed to kill %d", p->pid);
181                 }
182         }
183         if (!quiet && killed) {
184                 printf("stopped %s (pid", what);
185                 for (p = found; p; p = p->next)
186                         if(p->pid < 0)
187                                 printf(" %d", -p->pid);
188                 puts(")");
189         }
190         if (ENABLE_FEATURE_CLEAN_UP)
191                 free(what);
192         return killed;
193 }
194
195 #if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS
196 static const struct option long_options[] = {
197         { "stop",               0,      NULL,   'K' },
198         { "start",              0,      NULL,   'S' },
199         { "background",         0,      NULL,   'b' },
200         { "quiet",              0,      NULL,   'q' },
201         { "make-pidfile",       0,      NULL,   'm' },
202 #if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
203         { "oknodo",             0,      NULL,   'o' },
204         { "verbose",            0,      NULL,   'v' },
205         { "nicelevel",          1,      NULL,   'N' },
206 #endif
207         { "startas",            1,      NULL,   'a' },
208         { "name",               1,      NULL,   'n' },
209         { "signal",             1,      NULL,   's' },
210         { "user",               1,      NULL,   'u' },
211         { "chuid",              1,      NULL,   'c' },
212         { "exec",               1,      NULL,   'x' },
213         { "pidfile",            1,      NULL,   'p' },
214 #if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
215         { "retry",              1,      NULL,   'R' },
216 #endif
217         { 0,                    0,      0,      0 }
218 };
219 #endif
220
221 enum {
222         CTX_STOP       = 0x1,
223         CTX_START      = 0x2,
224         OPT_BACKGROUND = 0x4, // -b
225         OPT_QUIET      = 0x8, // -q
226         OPT_MAKEPID    = 0x10, // -m
227         OPT_a          = 0x20, // -a
228         OPT_n          = 0x40, // -n
229         OPT_s          = 0x80, // -s
230         OPT_u          = 0x100, // -u
231         OPT_c          = 0x200, // -c
232         OPT_x          = 0x400, // -x
233         OPT_p          = 0x800, // -p
234         OPT_OKNODO     = 0x1000 * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -o
235         OPT_VERBOSE    = 0x2000 * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -v
236         OPT_NICELEVEL  = 0x4000 * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -N
237 };
238
239 int start_stop_daemon_main(int argc, char **argv);
240 int start_stop_daemon_main(int argc, char **argv)
241 {
242         unsigned opt;
243         char *signame;
244         char *startas;
245         char *chuid;
246 #if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
247 //      char *retry_arg = NULL;
248 //      int retries = -1;
249         char *opt_N;
250 #endif
251 #if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS
252         applet_long_options = long_options;
253 #endif
254
255         /* Check required one context option was given */
256         opt_complementary = "K:S:?:K--S:S--K:m?p:K?xpun:S?xa";
257         opt = getopt32(argc, argv, "KSbqma:n:s:u:c:x:p:"
258                 USE_FEATURE_START_STOP_DAEMON_FANCY("ovN:"),
259 //              USE_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:"),
260                 &startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile
261                 USE_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N)
262 //              USE_FEATURE_START_STOP_DAEMON_FANCY(,&retry_arg)
263         );
264
265         quiet = (opt & OPT_QUIET) && !(opt & OPT_VERBOSE);
266
267         if (opt & OPT_s) {
268                 signal_nr = get_signum(signame);
269                 if (signal_nr < 0) bb_show_usage();
270         }
271
272         if (!(opt & OPT_a))
273                 startas = execname;
274
275 //      USE_FEATURE_START_STOP_DAEMON_FANCY(
276 //              if (retry_arg)
277 //                      retries = xatoi_u(retry_arg);
278 //      )
279         argc -= optind;
280         argv += optind;
281
282         if (userspec) {
283                 user_id = bb_strtou(userspec, NULL, 10);
284                 if (errno)
285                         user_id = xuname2uid(userspec);
286         }
287
288         if (opt & CTX_STOP) {
289                 int i = do_stop();
290                 return (opt & OPT_OKNODO) ? 0 : (i<=0);
291         }
292
293         do_procinit();
294
295         if (found) {
296                 if (!quiet)
297                         printf("%s already running\n%d\n", execname, found->pid);
298                 return !(opt & OPT_OKNODO);
299         }
300         *--argv = startas;
301         if (opt & OPT_BACKGROUND) {
302                 bb_daemonize(0);
303         }
304         if (opt & OPT_MAKEPID) {
305                 /* user wants _us_ to make the pidfile */
306                 FILE *pidf = xfopen(pidfile, "w");
307
308                 pid_t pidt = getpid();
309                 fprintf(pidf, "%d\n", pidt);
310                 fclose(pidf);
311         }
312         if (opt & OPT_c) {
313                 struct bb_uidgid_t ugid;
314                 parse_chown_usergroup_or_die(&ugid, chuid);
315                 if (ugid.gid != (gid_t) -1) xsetgid(ugid.gid);
316                 if (ugid.uid != (uid_t) -1) xsetuid(ugid.uid);
317         }
318 #if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
319         if (opt & OPT_NICELEVEL) {
320                 /* Set process priority */
321                 int prio = getpriority(PRIO_PROCESS, 0) + xatoi_range(opt_N, INT_MIN/2, INT_MAX/2);
322                 if (setpriority(PRIO_PROCESS, 0, prio) < 0) {
323                         bb_perror_msg_and_die("setpriority(%d)", prio);
324                 }
325         }
326 #endif
327         execv(startas, argv);
328         bb_perror_msg_and_die("cannot start %s", startas);
329 }