Fixup 'make clean' to properly clean the _install directory
[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  * public domain.
7  * Adapted for busybox David Kimdon <dwhedon@gordian.com>
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stdarg.h>
14 #include <signal.h>
15 #include <errno.h>
16 #include <sys/stat.h>
17 #include <dirent.h>
18 #include <unistd.h>
19 #include <getopt.h>
20
21 #include "busybox.h"
22 #include "pwd_.h"
23
24 static int start = 0;
25 static int stop = 0;
26 static int fork_before_exec = 0;
27 static int signal_nr = 15;
28 static int user_id = -1;
29 static char *userspec = NULL;
30 static char *cmdname = NULL;
31 static char *execname = NULL;
32 static char *startas = NULL;
33
34 typedef struct pid_list {
35         struct pid_list *next;
36         int pid;
37 } pid_list;
38
39 static pid_list *found = NULL;
40
41 static inline void
42 push(int pid)
43 {
44         pid_list *p;
45
46         p = xmalloc(sizeof(*p));
47         p->next = found;
48         p->pid = pid;
49         found = p;
50 }
51
52 static int
53 pid_is_exec(int pid, const char *exec)
54 {
55         char buf[PATH_MAX];
56         FILE *fp;
57
58         sprintf(buf, "/proc/%d/cmdline", pid);
59         fp = fopen(buf, "r");
60         if (fp && fgets (buf, sizeof (buf), fp) ) {
61                 fclose(fp);
62             if (strncmp (buf, exec, strlen(exec)) == 0)
63                 return 1;
64         }
65         return 0;
66 }
67
68
69 static int
70 pid_is_user(int pid, int uid)
71 {
72         struct stat sb;
73         char buf[32];
74
75         sprintf(buf, "/proc/%d", pid);
76         if (stat(buf, &sb) != 0)
77                 return 0;
78         return (sb.st_uid == uid);
79 }
80
81
82 static int
83 pid_is_cmd(int pid, const char *name)
84 {
85         char buf[32];
86         FILE *f;
87         int c;
88
89         sprintf(buf, "/proc/%d/stat", pid);
90         f = fopen(buf, "r");
91         if (!f)
92                 return 0;
93         while ((c = getc(f)) != EOF && c != '(')
94                 ;
95         if (c != '(') {
96                 fclose(f);
97                 return 0;
98         }
99         /* this hopefully handles command names containing ')' */
100         while ((c = getc(f)) != EOF && c == *name)
101                 name++;
102         fclose(f);
103         return (c == ')' && *name == '\0');
104 }
105
106
107 static void
108 check(int pid)
109 {
110         if (execname && !pid_is_exec(pid, execname)) {
111                 return;
112         }
113         if (userspec && !pid_is_user(pid, user_id)) {
114                 return;
115         }
116         if (cmdname && !pid_is_cmd(pid, cmdname)) {
117                 return;
118         }
119         push(pid);
120 }
121
122
123
124 static void
125 do_procfs(void)
126 {
127         DIR *procdir;
128         struct dirent *entry;
129         int foundany, pid;
130
131         procdir = opendir("/proc");
132         if (!procdir)
133                 bb_perror_msg_and_die ("opendir /proc");
134
135         foundany = 0;
136         while ((entry = readdir(procdir)) != NULL) {
137                 if (sscanf(entry->d_name, "%d", &pid) != 1)
138                         continue;
139                 foundany++;
140                 check(pid);
141         }
142         closedir(procdir);
143         if (!foundany)
144                 bb_error_msg_and_die ("nothing in /proc - not mounted?");
145 }
146
147
148 static void
149 do_stop(void)
150 {
151         char what[1024];
152         pid_list *p;
153         int killed = 0;
154
155         if (cmdname)
156                 strcpy(what, cmdname);
157         else if (execname)
158                 strcpy(what, execname);
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                 printf("no %s found; none killed.\n", what);
166                 return;
167         }
168         for (p = found; p; p = p->next) {
169                 if (kill(p->pid, signal_nr) == 0) {
170                         p->pid = -p->pid;
171                         killed++;
172                 } else {
173                         bb_perror_msg("warning: failed to kill %d:", p->pid);
174                 }
175         }
176         if (killed) {
177                 printf("stopped %s (pid", what);
178                 for (p = found; p; p = p->next)
179                         if(p->pid < 0)
180                                 printf(" %d", -p->pid);
181                 printf(").\n");
182         }
183 }
184
185
186 static const struct option ssd_long_options[] = {
187         { "stop",               0,              NULL,           'K' },
188         { "start",              0,              NULL,           'S' },
189         { "background", 0,              NULL,           'b' },
190         { "startas",    1,              NULL,           'a' },
191         { "name",               1,              NULL,           'n' },
192         { "signal",             1,              NULL,           's' },
193         { "user",               1,              NULL,           'u' },
194         { "exec",               1,              NULL,           'x' },
195         { 0,                    0,              0,                      0 }
196 };
197
198 int
199 start_stop_daemon_main(int argc, char **argv)
200 {
201         int flags;
202         char *signame = NULL;
203         bb_applet_long_options = ssd_long_options;
204
205         flags = bb_getopt_ulflags(argc, argv, "KSba:n:s:u:x:", 
206                         &startas, &cmdname, &signame, &userspec, &execname);
207
208         /* Be sneaky and avoid branching */
209         stop = (flags & 1);
210         start = (flags & 2);
211         fork_before_exec = (flags & 4);
212
213         if (signame) {
214                 signal_nr = bb_xgetlarg(signame, 10, 0, NSIG);
215         }
216
217         if (start == stop)
218                 bb_error_msg_and_die ("need exactly one of -S or -K");
219
220         if (!execname && !userspec)
221                 bb_error_msg_and_die ("need at least one of -x or -u");
222
223         if (!startas)
224                 startas = execname;
225
226         if (start && !startas)
227                 bb_error_msg_and_die ("-S needs -x or -a");
228
229         argc -= optind;
230         argv += optind;
231
232         if (userspec && sscanf(userspec, "%d", &user_id) != 1)
233                 user_id = my_getpwnam(userspec);
234
235         do_procfs();
236
237         if (stop) {
238                 do_stop();
239                 return EXIT_SUCCESS;
240         }
241
242         if (found) {
243                 printf("%s already running.\n%d\n", execname ,found->pid);
244                 return EXIT_SUCCESS;
245         }
246         *--argv = startas;
247         if (fork_before_exec) {
248                 if (daemon(0, 0) == -1)
249                         bb_perror_msg_and_die ("unable to fork");
250         }
251         setsid();
252         execv(startas, argv);
253         bb_perror_msg_and_die ("unable to start %s", startas);
254 }
255