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