]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * A rewrite of the original Debian's start-stop-daemon Perl script | |
3 | * in C (faster - it is executed many times during system startup). | |
4 | * | |
5 | * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>, | |
6 | * public domain. | |
7 | */ | |
8 | ||
9 | #include <stdio.h> | |
10 | #include <stdlib.h> | |
11 | #include <string.h> | |
12 | #include <stdarg.h> | |
13 | #include <signal.h> | |
14 | #include <errno.h> | |
15 | #include <sys/stat.h> | |
16 | #include <dirent.h> | |
17 | #include <unistd.h> | |
18 | #include <getopt.h> | |
19 | #include <pwd.h> | |
20 | ||
21 | #define VERSION "version 0.3, 1996-06-05" | |
22 | ||
23 | static int testmode = 0; | |
24 | static int quietmode = 0; | |
25 | static int exitnodo = 1; | |
26 | static int start = 0; | |
27 | static int stop = 0; | |
28 | static int signal_nr = 15; | |
29 | static int user_id = -1; | |
30 | static const char *userspec = NULL; | |
31 | static const char *cmdname = NULL; | |
32 | static char *execname = NULL; | |
33 | static char *startas = NULL; | |
34 | static const char *pidfile = NULL; | |
35 | static const char *progname = ""; | |
36 | ||
37 | static struct stat exec_stat; | |
38 | ||
39 | struct pid_list { | |
40 | struct pid_list *next; | |
41 | int pid; | |
42 | }; | |
43 | ||
44 | static struct pid_list *found = NULL; | |
45 | static struct pid_list *killed = NULL; | |
46 | ||
47 | static void *xmalloc(int size); | |
48 | static void push(struct pid_list **list, int pid); | |
49 | static void do_help(void); | |
50 | static void parse_options(int argc, char * const *argv); | |
51 | static int pid_is_exec(int pid, const struct stat *esb); | |
52 | static int pid_is_user(int pid, int uid); | |
53 | static int pid_is_cmd(int pid, const char *name); | |
54 | static void check(int pid); | |
55 | static void do_pidfile(const char *name); | |
56 | static void do_procfs(void); | |
57 | static void do_stop(void); | |
58 | ||
59 | #ifdef __GNUC__ | |
60 | static void fatal(const char *format, ...) | |
61 | __attribute__((noreturn, format(printf, 1, 2))); | |
62 | static void badusage(const char *msg) | |
63 | __attribute__((noreturn)); | |
64 | #else | |
65 | static void fatal(const char *format, ...); | |
66 | static void badusage(const char *msg); | |
67 | #endif | |
68 | ||
69 | static void | |
70 | fatal(const char *format, ...) | |
71 | { | |
72 | va_list arglist; | |
73 | ||
74 | fprintf(stderr, "%s: ", progname); | |
75 | va_start(arglist, format); | |
76 | vfprintf(stderr, format, arglist); | |
77 | va_end(arglist); | |
78 | putc('\n', stderr); | |
79 | exit(2); | |
80 | } | |
81 | ||
82 | ||
83 | static void * | |
84 | xmalloc(int size) | |
85 | { | |
86 | void *ptr; | |
87 | ||
88 | ptr = malloc(size); | |
89 | if (ptr) | |
90 | return ptr; | |
91 | fatal("malloc(%d) failed", size); | |
92 | } | |
93 | ||
94 | ||
95 | static void | |
96 | push(struct pid_list **list, int pid) | |
97 | { | |
98 | struct pid_list *p; | |
99 | ||
100 | p = xmalloc(sizeof(*p)); | |
101 | p->next = *list; | |
102 | p->pid = pid; | |
103 | *list = p; | |
104 | } | |
105 | ||
106 | ||
107 | static void | |
108 | do_help(void) | |
109 | { | |
110 | printf("\ | |
111 | start-stop-daemon for Debian Linux - small and fast C version written by\n\ | |
112 | Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>, public domain.\n" | |
113 | VERSION "\n\ | |
114 | \n\ | |
115 | Usage: | |
116 | start-stop-daemon -S|--start options ... -- arguments ...\n\ | |
117 | start-stop-daemon -K|--stop options ...\n\ | |
118 | start-stop-daemon -H|--help\n\ | |
119 | start-stop-daemon -V|--version\n\ | |
120 | \n\ | |
121 | Options (at least one of --exec|--pidfile|--user is required): | |
122 | -x|--exec <executable> program to start/check if it is running\n\ | |
123 | -p|--pidfile <pid-file> pid file to check\n\ | |
124 | -u|--user <username>|<uid> stop this user's processes\n\ | |
125 | -n|--name <process-name> stop processes with this name\n\ | |
126 | -s|--signal <signal> signal to send (default 15)\n\ | |
127 | -a|--startas <pathname> program to start (default <executable>)\n\ | |
128 | -t|--test test mode, don't do anything\n\ | |
129 | -o|--oknodo exit status 0 (not 1) if nothing done\n\ | |
130 | -q|--quiet | -v, --verbose\n\ | |
131 | \n\ | |
132 | Exit status: 0 = done 1 = nothing done (=> 0 if --oknodo) 2 = trouble\n"); | |
133 | } | |
134 | ||
135 | ||
136 | static void | |
137 | badusage(const char *msg) | |
138 | { | |
139 | if (msg && *msg) | |
140 | fprintf(stderr, "%s: %s\n", progname, msg); | |
141 | fprintf(stderr, "Try `%s --help' for more information.\n", progname); | |
142 | exit(2); | |
143 | } | |
144 | ||
145 | ||
146 | static void | |
147 | parse_options(int argc, char * const *argv) | |
148 | { | |
149 | static struct option longopts[] = { | |
150 | { "help", 0, NULL, 'H'}, | |
151 | { "stop", 0, NULL, 'K'}, | |
152 | { "start", 0, NULL, 'S'}, | |
153 | { "version", 0, NULL, 'V'}, | |
154 | { "startas", 1, NULL, 'a'}, | |
155 | { "name", 1, NULL, 'n'}, | |
156 | { "oknodo", 0, NULL, 'o'}, | |
157 | { "pidfile", 1, NULL, 'p'}, | |
158 | { "quiet", 0, NULL, 'q'}, | |
159 | { "signal", 1, NULL, 's'}, | |
160 | { "test", 0, NULL, 't'}, | |
161 | { "user", 1, NULL, 'u'}, | |
162 | { "verbose", 0, NULL, 'v'}, | |
163 | { "exec", 1, NULL, 'x'}, | |
164 | { NULL, 0, NULL, 0} | |
165 | }; | |
166 | int c; | |
167 | ||
168 | for (;;) { | |
169 | c = getopt_long(argc, argv, "HKSVa:n:op:qs:tu:vx:", | |
170 | longopts, (int *) 0); | |
171 | if (c == -1) | |
172 | break; | |
173 | switch (c) { | |
174 | case 'H': /* --help */ | |
175 | do_help(); | |
176 | exit(0); | |
177 | case 'K': /* --stop */ | |
178 | stop = 1; | |
179 | break; | |
180 | case 'S': /* --start */ | |
181 | start = 1; | |
182 | break; | |
183 | case 'V': /* --version */ | |
184 | printf("start-stop-daemon " VERSION "\n"); | |
185 | exit(0); | |
186 | case 'a': /* --startas <pathname> */ | |
187 | startas = optarg; | |
188 | break; | |
189 | case 'n': /* --name <process-name> */ | |
190 | cmdname = optarg; | |
191 | break; | |
192 | case 'o': /* --oknodo */ | |
193 | exitnodo = 0; | |
194 | break; | |
195 | case 'p': /* --pidfile <pid-file> */ | |
196 | pidfile = optarg; | |
197 | break; | |
198 | case 'q': /* --quiet */ | |
199 | quietmode = 1; | |
200 | break; | |
201 | case 's': /* --signal <signal> */ | |
202 | if (sscanf(optarg, "%d", &signal_nr) != 1) | |
203 | badusage("--signal takes a numeric argument"); | |
204 | break; | |
205 | case 't': /* --test */ | |
206 | testmode = 1; | |
207 | break; | |
208 | case 'u': /* --user <username>|<uid> */ | |
209 | userspec = optarg; | |
210 | break; | |
211 | case 'v': /* --verbose */ | |
212 | quietmode = -1; | |
213 | break; | |
214 | case 'x': /* --exec <executable> */ | |
215 | execname = optarg; | |
216 | break; | |
217 | default: | |
218 | badusage(""); /* message printed by getopt */ | |
219 | } | |
220 | } | |
221 | ||
222 | if (start == stop) | |
223 | badusage("need one of --start or --stop"); | |
224 | ||
225 | if (!execname && !pidfile && !userspec) | |
226 | badusage("need at least one of --exec, --pidfile or --user"); | |
227 | ||
228 | if (!startas) | |
229 | startas = execname; | |
230 | ||
231 | if (start && !startas) | |
232 | badusage("--start needs --exec or --startas"); | |
233 | } | |
234 | ||
235 | ||
236 | static int | |
237 | pid_is_exec(int pid, const struct stat *esb) | |
238 | { | |
239 | struct stat sb; | |
240 | char buf[32]; | |
241 | ||
242 | sprintf(buf, "/proc/%d/exe", pid); | |
243 | if (stat(buf, &sb) != 0) | |
244 | return 0; | |
245 | return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino); | |
246 | } | |
247 | ||
248 | ||
249 | static int | |
250 | pid_is_user(int pid, int uid) | |
251 | { | |
252 | struct stat sb; | |
253 | char buf[32]; | |
254 | ||
255 | sprintf(buf, "/proc/%d", pid); | |
256 | if (stat(buf, &sb) != 0) | |
257 | return 0; | |
258 | return (sb.st_uid == uid); | |
259 | } | |
260 | ||
261 | ||
262 | static int | |
263 | pid_is_cmd(int pid, const char *name) | |
264 | { | |
265 | char buf[32]; | |
266 | FILE *f; | |
267 | int c; | |
268 | ||
269 | sprintf(buf, "/proc/%d/stat", pid); | |
270 | f = fopen(buf, "r"); | |
271 | if (!f) | |
272 | return 0; | |
273 | while ((c = getc(f)) != EOF && c != '(') | |
274 | ; | |
275 | if (c != '(') { | |
276 | fclose(f); | |
277 | return 0; | |
278 | } | |
279 | /* this hopefully handles command names containing ')' */ | |
280 | while ((c = getc(f)) != EOF && c == *name) | |
281 | name++; | |
282 | fclose(f); | |
283 | return (c == ')' && *name == '\0'); | |
284 | } | |
285 | ||
286 | ||
287 | static void | |
288 | check(int pid) | |
289 | { | |
290 | if (execname && !pid_is_exec(pid, &exec_stat)) | |
291 | return; | |
292 | if (userspec && !pid_is_user(pid, user_id)) | |
293 | return; | |
294 | if (cmdname && !pid_is_cmd(pid, cmdname)) | |
295 | return; | |
296 | push(&found, pid); | |
297 | } | |
298 | ||
299 | ||
300 | static void | |
301 | do_pidfile(const char *name) | |
302 | { | |
303 | FILE *f; | |
304 | int pid; | |
305 | ||
306 | f = fopen(name, "r"); | |
307 | if (f) { | |
308 | if (fscanf(f, "%d", &pid) == 1) | |
309 | check(pid); | |
310 | fclose(f); | |
311 | } | |
312 | } | |
313 | ||
314 | ||
315 | static void | |
316 | do_procfs(void) | |
317 | { | |
318 | DIR *procdir; | |
319 | struct dirent *entry; | |
320 | int foundany, pid; | |
321 | ||
322 | procdir = opendir("/proc"); | |
323 | if (!procdir) | |
324 | fatal("opendir /proc: %s", strerror(errno)); | |
325 | ||
326 | foundany = 0; | |
327 | while ((entry = readdir(procdir)) != NULL) { | |
328 | if (sscanf(entry->d_name, "%d", &pid) != 1) | |
329 | continue; | |
330 | foundany++; | |
331 | check(pid); | |
332 | } | |
333 | closedir(procdir); | |
334 | if (!foundany) | |
335 | fatal("nothing in /proc - not mounted?"); | |
336 | } | |
337 | ||
338 | ||
339 | static void | |
340 | do_stop(void) | |
341 | { | |
342 | char what[1024]; | |
343 | struct pid_list *p; | |
344 | ||
345 | if (cmdname) | |
346 | strcpy(what, cmdname); | |
347 | else if (execname) | |
348 | strcpy(what, execname); | |
349 | else if (pidfile) | |
350 | sprintf(what, "process in pidfile `%s'", pidfile); | |
351 | else if (userspec) | |
352 | sprintf(what, "process(es) owned by `%s'", userspec); | |
353 | else | |
354 | fatal("internal error, please report"); | |
355 | ||
356 | if (!found) { | |
357 | if (quietmode <= 0) | |
358 | printf("no %s found; none killed.\n", what); | |
359 | exit(exitnodo); | |
360 | } | |
361 | for (p = found; p; p = p->next) { | |
362 | if (testmode) | |
363 | printf("would send signal %d to %d.\n", | |
364 | signal_nr, p->pid); | |
365 | else if (kill(p->pid, signal_nr) == 0) | |
366 | push(&killed, p->pid); | |
367 | else | |
368 | printf("%s: warning: failed to kill %d: %s\n", | |
369 | progname, p->pid, strerror(errno)); | |
370 | } | |
371 | if (quietmode < 0 && killed) { | |
372 | printf("stopped %s (pid", what); | |
373 | for (p = killed; p; p = p->next) | |
374 | printf(" %d", p->pid); | |
375 | printf(").\n"); | |
376 | } | |
377 | } | |
378 | ||
379 | ||
380 | int | |
381 | main(int argc, char **argv) | |
382 | { | |
383 | progname = argv[0]; | |
384 | ||
385 | parse_options(argc, argv); | |
386 | argc -= optind; | |
387 | argv += optind; | |
388 | ||
389 | if (execname && stat(execname, &exec_stat)) | |
390 | fatal("stat %s: %s", execname, strerror(errno)); | |
391 | ||
392 | if (userspec && sscanf(userspec, "%d", &user_id) != 1) { | |
393 | struct passwd *pw; | |
394 | ||
395 | pw = getpwnam(userspec); | |
396 | if (!pw) | |
397 | fatal("user `%s' not found\n", userspec); | |
398 | ||
399 | user_id = pw->pw_uid; | |
400 | } | |
401 | ||
402 | if (pidfile) | |
403 | do_pidfile(pidfile); | |
404 | else | |
405 | do_procfs(); | |
406 | ||
407 | if (stop) { | |
408 | do_stop(); | |
409 | exit(0); | |
410 | } | |
411 | ||
412 | if (found) { | |
413 | if (quietmode <= 0) | |
414 | printf("%s already running.\n", execname); | |
415 | exit(exitnodo); | |
416 | } | |
417 | if (testmode) { | |
418 | printf("would start %s ", startas); | |
419 | while (argc-- > 0) | |
420 | printf("%s ", *argv++); | |
421 | printf(".\n"); | |
422 | exit(0); | |
423 | } | |
424 | if (quietmode < 0) | |
425 | printf("starting %s ...\n", startas); | |
426 | *--argv = startas; | |
427 | execv(startas, argv); | |
428 | fatal("unable to start %s: %s", startas, strerror(errno)); | |
429 | } | |
430 |