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