Major coreutils update.
[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
20 #include "busybox.h"
21 #include "pwd_.h"
22
23 static int start = 0;
24 static int stop = 0;
25 static int fork_before_exec = 0;
26 static int signal_nr = 15;
27 static int user_id = -1;
28 static const char *userspec = NULL;
29 static const char *cmdname = NULL;
30 static char *execname = NULL;
31 static char *startas = NULL;
32
33 typedef struct pid_list {
34         struct pid_list *next;
35         int pid;
36 } pid_list;
37
38 static pid_list *found = NULL;
39
40 static inline void
41 push(int pid)
42 {
43         pid_list *p;
44
45         p = xmalloc(sizeof(*p));
46         p->next = found;
47         p->pid = pid;
48         found = p;
49 }
50
51
52 static void
53 parse_options(int argc, char * const *argv)
54 {
55         
56         int c;
57
58         for (;;) {
59             c = getopt (argc, argv, "a:n:s:u:x:KSb");
60                 if (c == EOF)
61                         break;
62                 switch (c) {
63                 case 'K':
64                         stop = 1;
65                         break;
66                 case 'S':
67                         start = 1;
68                         break;
69                 case 'a':
70                         startas = optarg;
71                         break;
72                 case 'n':
73                         cmdname = optarg;
74                         break;
75                 case 's':
76                         if (sscanf(optarg, "%d", &signal_nr) != 1)
77                                 bb_error_msg_and_die ("-s takes a numeric argument");
78                         break;
79                 case 'u':
80                         userspec = optarg;
81                         break;
82                 case 'x':
83                         execname = optarg;
84                         break;
85                 case 'b':
86                         fork_before_exec = 1;
87                         break;
88                 default:
89                         bb_show_usage();
90                 }
91         }
92
93         if (start == stop)
94                 bb_error_msg_and_die ("need one of -S or -K");
95
96         if (!execname && !userspec)
97                 bb_error_msg_and_die ("need at least one of -x or -u");
98
99         if (!startas)
100                 startas = execname;
101
102         if (start && !startas)
103                 bb_error_msg_and_die ("-S needs -x or -a");
104 }
105
106
107 static int
108 pid_is_exec(int pid, const char *exec)
109 {
110         char buf[PATH_MAX];
111         FILE *fp;
112
113         sprintf(buf, "/proc/%d/cmdline", pid);
114         fp = fopen(buf, "r");
115         if (fp && fgets (buf, sizeof (buf), fp) ) {
116                 fclose(fp);
117             if (strncmp (buf, exec, strlen(exec)) == 0)
118                 return 1;
119         }
120         return 0;
121 }
122
123
124 static int
125 pid_is_user(int pid, int uid)
126 {
127         struct stat sb;
128         char buf[32];
129
130         sprintf(buf, "/proc/%d", pid);
131         if (stat(buf, &sb) != 0)
132                 return 0;
133         return (sb.st_uid == uid);
134 }
135
136
137 static int
138 pid_is_cmd(int pid, const char *name)
139 {
140         char buf[32];
141         FILE *f;
142         int c;
143
144         sprintf(buf, "/proc/%d/stat", pid);
145         f = fopen(buf, "r");
146         if (!f)
147                 return 0;
148         while ((c = getc(f)) != EOF && c != '(')
149                 ;
150         if (c != '(') {
151                 fclose(f);
152                 return 0;
153         }
154         /* this hopefully handles command names containing ')' */
155         while ((c = getc(f)) != EOF && c == *name)
156                 name++;
157         fclose(f);
158         return (c == ')' && *name == '\0');
159 }
160
161
162 static void
163 check(int pid)
164 {
165         if (execname && !pid_is_exec(pid, execname)) {
166                 return;
167         }
168         if (userspec && !pid_is_user(pid, user_id)) {
169                 return;
170         }
171         if (cmdname && !pid_is_cmd(pid, cmdname)) {
172                 return;
173         }
174         push(pid);
175 }
176
177
178
179 static void
180 do_procfs(void)
181 {
182         DIR *procdir;
183         struct dirent *entry;
184         int foundany, pid;
185
186         procdir = opendir("/proc");
187         if (!procdir)
188                 bb_perror_msg_and_die ("opendir /proc");
189
190         foundany = 0;
191         while ((entry = readdir(procdir)) != NULL) {
192                 if (sscanf(entry->d_name, "%d", &pid) != 1)
193                         continue;
194                 foundany++;
195                 check(pid);
196         }
197         closedir(procdir);
198         if (!foundany)
199                 bb_error_msg_and_die ("nothing in /proc - not mounted?");
200 }
201
202
203 static void
204 do_stop(void)
205 {
206         char what[1024];
207         pid_list *p;
208         int killed = 0;
209
210         if (cmdname)
211                 strcpy(what, cmdname);
212         else if (execname)
213                 strcpy(what, execname);
214         else if (userspec)
215                 sprintf(what, "process(es) owned by `%s'", userspec);
216         else
217                 bb_error_msg_and_die ("internal error, please report");
218
219         if (!found) {
220                 printf("no %s found; none killed.\n", what);
221                 return;
222         }
223         for (p = found; p; p = p->next) {
224                 if (kill(p->pid, signal_nr) == 0) {
225                         p->pid = -p->pid;
226                         killed++;
227                 } else {
228                         bb_perror_msg("warning: failed to kill %d:", p->pid);
229                 }
230         }
231         if (killed) {
232                 printf("stopped %s (pid", what);
233                 for (p = found; p; p = p->next)
234                         if(p->pid < 0)
235                                 printf(" %d", -p->pid);
236                 printf(").\n");
237         }
238 }
239
240
241 int
242 start_stop_daemon_main(int argc, char **argv)
243 {
244         parse_options(argc, argv);
245         argc -= optind;
246         argv += optind;
247
248         if (userspec && sscanf(userspec, "%d", &user_id) != 1)
249                 user_id = my_getpwnam(userspec);
250
251         do_procfs();
252
253         if (stop) {
254                 do_stop();
255                 return EXIT_SUCCESS;
256         }
257
258         if (found) {
259                 printf("%s already running.\n%d\n", execname ,found->pid);
260                 return EXIT_SUCCESS;
261         }
262         *--argv = startas;
263         if (fork_before_exec) {
264                 if (daemon(0, 0) == -1)
265                         bb_perror_msg_and_die ("unable to fork");
266         }
267         setsid();
268         execv(startas, argv);
269         bb_perror_msg_and_die ("unable to start %s", startas);
270 }
271