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