Commit | Line | Data |
---|---|---|
487cc3be VM |
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. Based conceptually on start-stop-daemon.pl, by Ian | |
7 | * Jackson <ijackson@gnu.ai.mit.edu>. May be used and distributed | |
8 | * freely for any purpose. Changes by Christian Schwarz | |
9 | * <schwarz@monet.m.isar.de>, to make output conform to the Debian | |
10 | * Console Message Standard, also placed in public domain. Minor | |
11 | * changes by Klee Dienes <klee@debian.org>, also placed in the Public | |
12 | * Domain. | |
13 | * | |
14 | * Changes by Ben Collins <bcollins@debian.org>, added --chuid, --background | |
15 | * and --make-pidfile options, placed in public domain as well. | |
16 | * | |
17 | * Port to OpenBSD by Sontri Tomo Huynh <huynh.29@osu.edu> | |
18 | * and Andreas Schuldei <andreas@schuldei.org> | |
19 | * | |
20 | * Changes by Ian Jackson: added --retry (and associated rearrangements). | |
21 | */ | |
22 | ||
23 | #include <config.h> | |
24 | #include <compat.h> | |
25 | ||
26 | #include <dpkg/macros.h> | |
27 | ||
61a41689 VM |
28 | #if defined(__linux__) |
29 | # define OS_Linux | |
487cc3be | 30 | #elif defined(__GNU__) |
61a41689 | 31 | # define OS_Hurd |
487cc3be | 32 | #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) |
61a41689 | 33 | # define OS_FreeBSD |
487cc3be | 34 | #elif defined(__NetBSD__) |
61a41689 VM |
35 | # define OS_NetBSD |
36 | #elif defined(__OpenBSD__) | |
37 | # define OS_OpenBSD | |
487cc3be | 38 | #elif defined(__DragonFly__) |
61a41689 VM |
39 | # define OS_DragonFlyBSD |
40 | #elif defined(__APPLE__) && defined(__MACH__) | |
41 | # define OS_Darwin | |
42 | #elif defined(__sun) | |
43 | # define OS_Solaris | |
44 | #elif defined(_AIX) | |
45 | # define OS_AIX | |
46 | #elif defined(__hpux) | |
47 | # define OS_HPUX | |
487cc3be VM |
48 | #else |
49 | # error Unknown architecture - cannot build start-stop-daemon | |
50 | #endif | |
51 | ||
61a41689 VM |
52 | /* NetBSD needs this to expose struct proc. */ |
53 | #define _KMEMUSER 1 | |
54 | ||
487cc3be VM |
55 | #ifdef HAVE_SYS_PARAM_H |
56 | #include <sys/param.h> | |
57 | #endif | |
58 | #ifdef HAVE_SYS_SYSCALL_H | |
59 | #include <sys/syscall.h> | |
60 | #endif | |
61 | #ifdef HAVE_SYS_SYSCTL_H | |
62 | #include <sys/sysctl.h> | |
63 | #endif | |
61a41689 VM |
64 | #ifdef HAVE_SYS_PROCFS_H |
65 | #include <sys/procfs.h> | |
66 | #endif | |
487cc3be VM |
67 | #ifdef HAVE_SYS_PROC_H |
68 | #include <sys/proc.h> | |
69 | #endif | |
70 | #ifdef HAVE_SYS_USER_H | |
71 | #include <sys/user.h> | |
72 | #endif | |
73 | #ifdef HAVE_SYS_PSTAT_H | |
74 | #include <sys/pstat.h> | |
75 | #endif | |
76 | #include <sys/types.h> | |
77 | #include <sys/time.h> | |
78 | #include <sys/stat.h> | |
79 | #include <sys/wait.h> | |
80 | #include <sys/select.h> | |
81 | #include <sys/ioctl.h> | |
82 | ||
83 | #include <assert.h> | |
84 | #include <errno.h> | |
85 | #include <limits.h> | |
86 | #include <time.h> | |
87 | #include <fcntl.h> | |
88 | #include <dirent.h> | |
89 | #include <ctype.h> | |
90 | #include <string.h> | |
91 | #include <pwd.h> | |
92 | #include <grp.h> | |
93 | #include <signal.h> | |
94 | #include <termios.h> | |
95 | #include <unistd.h> | |
96 | #ifdef HAVE_STDDEF_H | |
97 | #include <stddef.h> | |
98 | #endif | |
99 | #include <stdbool.h> | |
100 | #include <stdarg.h> | |
101 | #include <stdlib.h> | |
102 | #include <stdio.h> | |
103 | #include <getopt.h> | |
104 | #ifdef HAVE_ERROR_H | |
105 | #include <error.h> | |
106 | #endif | |
107 | #ifdef HAVE_ERR_H | |
108 | #include <err.h> | |
109 | #endif | |
110 | ||
61a41689 | 111 | #if defined(OS_Hurd) |
487cc3be VM |
112 | #include <hurd.h> |
113 | #include <ps.h> | |
114 | #endif | |
115 | ||
61a41689 VM |
116 | #if defined(OS_Darwin) |
117 | #include <libproc.h> | |
118 | #endif | |
119 | ||
487cc3be VM |
120 | #ifdef HAVE_KVM_H |
121 | #include <kvm.h> | |
61a41689 | 122 | #if defined(OS_FreeBSD) |
487cc3be VM |
123 | #define KVM_MEMFILE "/dev/null" |
124 | #else | |
125 | #define KVM_MEMFILE NULL | |
126 | #endif | |
127 | #endif | |
128 | ||
61a41689 | 129 | #if defined(_POSIX_PRIORITY_SCHEDULING) && _POSIX_PRIORITY_SCHEDULING > 0 |
487cc3be VM |
130 | #include <sched.h> |
131 | #else | |
132 | #define SCHED_OTHER -1 | |
133 | #define SCHED_FIFO -1 | |
134 | #define SCHED_RR -1 | |
135 | #endif | |
136 | ||
61a41689 | 137 | #if defined(OS_Linux) |
487cc3be VM |
138 | /* This comes from TASK_COMM_LEN defined in Linux' include/linux/sched.h. */ |
139 | #define PROCESS_NAME_SIZE 15 | |
61a41689 | 140 | #elif defined(OS_Solaris) |
487cc3be | 141 | #define PROCESS_NAME_SIZE 15 |
61a41689 | 142 | #elif defined(OS_Darwin) |
487cc3be | 143 | #define PROCESS_NAME_SIZE 16 |
61a41689 VM |
144 | #elif defined(OS_AIX) |
145 | /* This comes from PRFNSZ defined in AIX's <sys/procfs.h>. */ | |
487cc3be | 146 | #define PROCESS_NAME_SIZE 16 |
61a41689 | 147 | #elif defined(OS_NetBSD) |
487cc3be | 148 | #define PROCESS_NAME_SIZE 16 |
61a41689 VM |
149 | #elif defined(OS_OpenBSD) |
150 | #define PROCESS_NAME_SIZE 16 | |
151 | #elif defined(OS_FreeBSD) | |
487cc3be | 152 | #define PROCESS_NAME_SIZE 19 |
61a41689 | 153 | #elif defined(OS_DragonFlyBSD) |
487cc3be VM |
154 | /* On DragonFlyBSD MAXCOMLEN expands to 16. */ |
155 | #define PROCESS_NAME_SIZE MAXCOMLEN | |
156 | #endif | |
157 | ||
158 | #if defined(SYS_ioprio_set) && defined(linux) | |
159 | #define HAVE_IOPRIO_SET | |
160 | #endif | |
161 | ||
162 | #define IOPRIO_CLASS_SHIFT 13 | |
163 | #define IOPRIO_PRIO_VALUE(class, prio) (((class) << IOPRIO_CLASS_SHIFT) | (prio)) | |
164 | #define IO_SCHED_PRIO_MIN 0 | |
165 | #define IO_SCHED_PRIO_MAX 7 | |
166 | ||
167 | enum { | |
168 | IOPRIO_WHO_PROCESS = 1, | |
169 | IOPRIO_WHO_PGRP, | |
170 | IOPRIO_WHO_USER, | |
171 | }; | |
172 | ||
173 | enum { | |
174 | IOPRIO_CLASS_NONE, | |
175 | IOPRIO_CLASS_RT, | |
176 | IOPRIO_CLASS_BE, | |
177 | IOPRIO_CLASS_IDLE, | |
178 | }; | |
179 | ||
180 | enum action_code { | |
181 | ACTION_NONE, | |
182 | ACTION_START, | |
183 | ACTION_STOP, | |
184 | ACTION_STATUS, | |
185 | }; | |
186 | ||
187 | /* Time conversion constants. */ | |
188 | enum { | |
189 | NANOSEC_IN_SEC = 1000000000L, | |
190 | NANOSEC_IN_MILLISEC = 1000000L, | |
191 | NANOSEC_IN_MICROSEC = 1000L, | |
192 | }; | |
193 | ||
194 | /* The minimum polling interval, 20ms. */ | |
195 | static const long MIN_POLL_INTERVAL = 20 * NANOSEC_IN_MILLISEC; | |
196 | ||
197 | static enum action_code action; | |
198 | static bool testmode = false; | |
199 | static int quietmode = 0; | |
200 | static int exitnodo = 1; | |
201 | static bool background = false; | |
202 | static bool close_io = true; | |
203 | static bool mpidfile = false; | |
204 | static bool rpidfile = false; | |
205 | static int signal_nr = SIGTERM; | |
206 | static int user_id = -1; | |
207 | static int runas_uid = -1; | |
208 | static int runas_gid = -1; | |
209 | static const char *userspec = NULL; | |
210 | static char *changeuser = NULL; | |
211 | static const char *changegroup = NULL; | |
212 | static char *changeroot = NULL; | |
213 | static const char *changedir = "/"; | |
214 | static const char *cmdname = NULL; | |
215 | static char *execname = NULL; | |
216 | static char *startas = NULL; | |
217 | static pid_t match_pid = -1; | |
218 | static pid_t match_ppid = -1; | |
219 | static const char *pidfile = NULL; | |
61a41689 | 220 | static char *what_stop = NULL; |
487cc3be VM |
221 | static const char *progname = ""; |
222 | static int nicelevel = 0; | |
223 | static int umask_value = -1; | |
224 | ||
225 | static struct stat exec_stat; | |
61a41689 | 226 | #if defined(OS_Hurd) |
487cc3be VM |
227 | static struct proc_stat_list *procset = NULL; |
228 | #endif | |
229 | ||
230 | /* LSB Init Script process status exit codes. */ | |
231 | enum status_code { | |
232 | STATUS_OK = 0, | |
233 | STATUS_DEAD_PIDFILE = 1, | |
234 | STATUS_DEAD_LOCKFILE = 2, | |
235 | STATUS_DEAD = 3, | |
236 | STATUS_UNKNOWN = 4, | |
237 | }; | |
238 | ||
239 | struct pid_list { | |
240 | struct pid_list *next; | |
241 | pid_t pid; | |
242 | }; | |
243 | ||
244 | static struct pid_list *found = NULL; | |
245 | static struct pid_list *killed = NULL; | |
246 | ||
247 | /* Resource scheduling policy. */ | |
248 | struct res_schedule { | |
249 | const char *policy_name; | |
250 | int policy; | |
251 | int priority; | |
252 | }; | |
253 | ||
254 | struct schedule_item { | |
255 | enum { | |
256 | sched_timeout, | |
257 | sched_signal, | |
258 | sched_goto, | |
259 | /* Only seen within parse_schedule and callees. */ | |
260 | sched_forever, | |
261 | } type; | |
262 | /* Seconds, signal no., or index into array. */ | |
263 | int value; | |
264 | }; | |
265 | ||
266 | static struct res_schedule *proc_sched = NULL; | |
267 | static struct res_schedule *io_sched = NULL; | |
268 | ||
269 | static int schedule_length; | |
270 | static struct schedule_item *schedule = NULL; | |
271 | ||
272 | ||
273 | static void DPKG_ATTR_PRINTF(1) | |
274 | warning(const char *format, ...) | |
275 | { | |
276 | va_list arglist; | |
277 | ||
278 | fprintf(stderr, "%s: warning: ", progname); | |
279 | va_start(arglist, format); | |
280 | vfprintf(stderr, format, arglist); | |
281 | va_end(arglist); | |
282 | } | |
283 | ||
284 | static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(1) | |
285 | fatal(const char *format, ...) | |
286 | { | |
287 | va_list arglist; | |
288 | int errno_fatal = errno; | |
289 | ||
290 | fprintf(stderr, "%s: ", progname); | |
291 | va_start(arglist, format); | |
292 | vfprintf(stderr, format, arglist); | |
293 | va_end(arglist); | |
294 | if (errno_fatal) | |
295 | fprintf(stderr, " (%s)\n", strerror(errno_fatal)); | |
296 | else | |
297 | fprintf(stderr, "\n"); | |
298 | ||
299 | if (action == ACTION_STATUS) | |
300 | exit(STATUS_UNKNOWN); | |
301 | else | |
302 | exit(2); | |
303 | } | |
304 | ||
305 | static void * | |
306 | xmalloc(int size) | |
307 | { | |
308 | void *ptr; | |
309 | ||
310 | ptr = malloc(size); | |
311 | if (ptr) | |
312 | return ptr; | |
313 | fatal("malloc(%d) failed", size); | |
314 | } | |
315 | ||
316 | static char * | |
317 | xstrndup(const char *str, size_t n) | |
318 | { | |
319 | char *new_str; | |
320 | ||
321 | new_str = strndup(str, n); | |
322 | if (new_str) | |
323 | return new_str; | |
324 | fatal("strndup(%s, %zu) failed", str, n); | |
325 | } | |
326 | ||
327 | static void | |
328 | timespec_gettime(struct timespec *ts) | |
329 | { | |
330 | #if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && \ | |
331 | defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK > 0 | |
332 | if (clock_gettime(CLOCK_MONOTONIC, ts) < 0) | |
333 | fatal("clock_gettime failed"); | |
334 | #else | |
335 | struct timeval tv; | |
336 | ||
337 | if (gettimeofday(&tv, NULL) != 0) | |
338 | fatal("gettimeofday failed"); | |
339 | ||
340 | ts->tv_sec = tv.tv_sec; | |
341 | ts->tv_nsec = tv.tv_usec * NANOSEC_IN_MICROSEC; | |
342 | #endif | |
343 | } | |
344 | ||
345 | #define timespec_cmp(a, b, OP) \ | |
346 | (((a)->tv_sec == (b)->tv_sec) ? \ | |
347 | ((a)->tv_nsec OP (b)->tv_nsec) : \ | |
348 | ((a)->tv_sec OP (b)->tv_sec)) | |
349 | ||
350 | static void | |
351 | timespec_sub(struct timespec *a, struct timespec *b, struct timespec *res) | |
352 | { | |
353 | res->tv_sec = a->tv_sec - b->tv_sec; | |
354 | res->tv_nsec = a->tv_nsec - b->tv_nsec; | |
355 | if (res->tv_nsec < 0) { | |
356 | res->tv_sec--; | |
357 | res->tv_nsec += NANOSEC_IN_SEC; | |
358 | } | |
359 | } | |
360 | ||
361 | static void | |
362 | timespec_mul(struct timespec *a, int b) | |
363 | { | |
364 | long nsec = a->tv_nsec * b; | |
365 | ||
366 | a->tv_sec *= b; | |
367 | a->tv_sec += nsec / NANOSEC_IN_SEC; | |
368 | a->tv_nsec = nsec % NANOSEC_IN_SEC; | |
369 | } | |
370 | ||
371 | static char * | |
372 | newpath(const char *dirname, const char *filename) | |
373 | { | |
374 | char *path; | |
375 | size_t path_len; | |
376 | ||
377 | path_len = strlen(dirname) + 1 + strlen(filename) + 1; | |
378 | path = xmalloc(path_len); | |
379 | snprintf(path, path_len, "%s/%s", dirname, filename); | |
380 | ||
381 | return path; | |
382 | } | |
383 | ||
384 | static long | |
385 | get_open_fd_max(void) | |
386 | { | |
387 | #ifdef HAVE_GETDTABLESIZE | |
388 | return getdtablesize(); | |
389 | #else | |
390 | return sysconf(_SC_OPEN_MAX); | |
391 | #endif | |
392 | } | |
393 | ||
394 | #ifndef HAVE_SETSID | |
395 | static void | |
396 | detach_controlling_tty(void) | |
397 | { | |
398 | #ifdef HAVE_TIOCNOTTY | |
399 | int tty_fd; | |
400 | ||
401 | tty_fd = open("/dev/tty", O_RDWR); | |
402 | ||
403 | /* The current process does not have a controlling tty. */ | |
404 | if (tty_fd < 0) | |
405 | return; | |
406 | ||
407 | if (ioctl(tty_fd, TIOCNOTTY, 0) != 0) | |
408 | fatal("unable to detach controlling tty"); | |
409 | ||
410 | close(tty_fd); | |
411 | #endif | |
412 | } | |
413 | ||
414 | static pid_t | |
415 | setsid(void) | |
416 | { | |
417 | if (setpgid(0, 0) < 0) | |
418 | return -1: | |
419 | ||
420 | detach_controlling_tty(); | |
421 | ||
422 | return 0; | |
423 | } | |
424 | #endif | |
425 | ||
426 | static void | |
427 | wait_for_child(pid_t pid) | |
428 | { | |
429 | pid_t child; | |
430 | int status; | |
431 | ||
432 | do { | |
433 | child = waitpid(pid, &status, 0); | |
434 | } while (child == -1 && errno == EINTR); | |
435 | ||
436 | if (child != pid) | |
437 | fatal("error waiting for child"); | |
438 | ||
439 | if (WIFEXITED(status)) { | |
61a41689 | 440 | int ret = WEXITSTATUS(status); |
487cc3be | 441 | |
61a41689 VM |
442 | if (ret != 0) |
443 | fatal("child returned error exit status %d", ret); | |
487cc3be VM |
444 | } else if (WIFSIGNALED(status)) { |
445 | int signo = WTERMSIG(status); | |
446 | ||
447 | fatal("child was killed by signal %d", signo); | |
448 | } else { | |
449 | fatal("unexpected status %d waiting for child", status); | |
450 | } | |
451 | } | |
452 | ||
453 | static void | |
454 | write_pidfile(const char *filename, pid_t pid) | |
455 | { | |
456 | FILE *fp; | |
457 | int fd; | |
458 | ||
459 | fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW, 0666); | |
460 | if (fd < 0) | |
461 | fp = NULL; | |
462 | else | |
463 | fp = fdopen(fd, "w"); | |
464 | ||
465 | if (fp == NULL) | |
466 | fatal("unable to open pidfile '%s' for writing", filename); | |
467 | ||
468 | fprintf(fp, "%d\n", pid); | |
469 | ||
470 | if (fclose(fp)) | |
471 | fatal("unable to close pidfile '%s'", filename); | |
472 | } | |
473 | ||
474 | static void | |
475 | remove_pidfile(const char *filename) | |
476 | { | |
477 | if (unlink(filename) < 0 && errno != ENOENT) | |
478 | fatal("cannot remove pidfile '%s'", filename); | |
479 | } | |
480 | ||
481 | static void | |
482 | daemonize(void) | |
483 | { | |
484 | pid_t pid; | |
485 | sigset_t mask; | |
486 | sigset_t oldmask; | |
487 | ||
488 | if (quietmode < 0) | |
489 | printf("Detaching to start %s...", startas); | |
490 | ||
491 | /* Block SIGCHLD to allow waiting for the child process while it is | |
492 | * performing actions, such as creating a pidfile. */ | |
493 | sigemptyset(&mask); | |
494 | sigaddset(&mask, SIGCHLD); | |
495 | if (sigprocmask(SIG_BLOCK, &mask, &oldmask) == -1) | |
496 | fatal("cannot block SIGCHLD"); | |
497 | ||
498 | pid = fork(); | |
499 | if (pid < 0) | |
500 | fatal("unable to do first fork"); | |
501 | else if (pid) { /* First Parent. */ | |
502 | /* Wait for the second parent to exit, so that if we need to | |
503 | * perform any actions there, like creating a pidfile, we do | |
504 | * not suffer from race conditions on return. */ | |
505 | wait_for_child(pid); | |
506 | ||
507 | _exit(0); | |
508 | } | |
509 | ||
510 | /* Create a new session. */ | |
511 | if (setsid() < 0) | |
512 | fatal("cannot set session ID"); | |
513 | ||
514 | pid = fork(); | |
515 | if (pid < 0) | |
516 | fatal("unable to do second fork"); | |
517 | else if (pid) { /* Second parent. */ | |
518 | /* Set a default umask for dumb programs, which might get | |
519 | * overridden by the --umask option later on, so that we get | |
520 | * a defined umask when creating the pidfille. */ | |
521 | umask(022); | |
522 | ||
523 | if (mpidfile && pidfile != NULL) | |
524 | /* User wants _us_ to make the pidfile. */ | |
525 | write_pidfile(pidfile, pid); | |
526 | ||
527 | _exit(0); | |
528 | } | |
529 | ||
530 | if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1) | |
531 | fatal("cannot restore signal mask"); | |
532 | ||
533 | if (quietmode < 0) | |
534 | printf("done.\n"); | |
535 | } | |
536 | ||
537 | static void | |
538 | pid_list_push(struct pid_list **list, pid_t pid) | |
539 | { | |
540 | struct pid_list *p; | |
541 | ||
542 | p = xmalloc(sizeof(*p)); | |
543 | p->next = *list; | |
544 | p->pid = pid; | |
545 | *list = p; | |
546 | } | |
547 | ||
548 | static void | |
549 | pid_list_free(struct pid_list **list) | |
550 | { | |
551 | struct pid_list *here, *next; | |
552 | ||
553 | for (here = *list; here != NULL; here = next) { | |
554 | next = here->next; | |
555 | free(here); | |
556 | } | |
557 | ||
558 | *list = NULL; | |
559 | } | |
560 | ||
561 | static void | |
562 | usage(void) | |
563 | { | |
564 | printf( | |
61a41689 VM |
565 | "Usage: start-stop-daemon [<option>...] <command>\n" |
566 | "\n"); | |
567 | ||
568 | printf( | |
487cc3be | 569 | "Commands:\n" |
61a41689 VM |
570 | " -S, --start -- <argument>... start a program and pass <arguments> to it\n" |
571 | " -K, --stop stop a program\n" | |
572 | " -T, --status get the program status\n" | |
573 | " -H, --help print help information\n" | |
574 | " -V, --version print version\n" | |
575 | "\n"); | |
576 | ||
577 | printf( | |
487cc3be | 578 | "Matching options (at least one is required):\n" |
61a41689 VM |
579 | " --pid <pid> pid to check\n" |
580 | " --ppid <ppid> parent pid to check\n" | |
581 | " -p, --pidfile <pid-file> pid file to check\n" | |
582 | " -x, --exec <executable> program to start/check if it is running\n" | |
583 | " -n, --name <process-name> process name to check\n" | |
584 | " -u, --user <username|uid> process owner to check\n" | |
585 | "\n"); | |
586 | ||
587 | printf( | |
487cc3be | 588 | "Options:\n" |
61a41689 VM |
589 | " -g, --group <group|gid> run process as this group\n" |
590 | " -c, --chuid <name|uid[:group|gid]>\n" | |
487cc3be VM |
591 | " change to this user/group before starting\n" |
592 | " process\n" | |
61a41689 VM |
593 | " -s, --signal <signal> signal to send (default TERM)\n" |
594 | " -a, --startas <pathname> program to start (default is <executable>)\n" | |
595 | " -r, --chroot <directory> chroot to <directory> before starting\n" | |
596 | " -d, --chdir <directory> change to <directory> (default is /)\n" | |
597 | " -N, --nicelevel <incr> add incr to the process' nice level\n" | |
598 | " -P, --procsched <policy[:prio]>\n" | |
487cc3be VM |
599 | " use <policy> with <prio> for the kernel\n" |
600 | " process scheduler (default prio is 0)\n" | |
61a41689 | 601 | " -I, --iosched <class[:prio]> use <class> with <prio> to set the IO\n" |
487cc3be | 602 | " scheduler (default prio is 4)\n" |
61a41689 VM |
603 | " -k, --umask <mask> change the umask to <mask> before starting\n" |
604 | " -b, --background force the process to detach\n" | |
605 | " -C, --no-close do not close any file descriptor\n" | |
606 | " -m, --make-pidfile create the pidfile before starting\n" | |
607 | " --remove-pidfile delete the pidfile after stopping\n" | |
608 | " -R, --retry <schedule> check whether processes die, and retry\n" | |
609 | " -t, --test test mode, don't do anything\n" | |
610 | " -o, --oknodo exit status 0 (not 1) if nothing done\n" | |
611 | " -q, --quiet be more quiet\n" | |
612 | " -v, --verbose be more verbose\n" | |
613 | "\n"); | |
614 | ||
615 | printf( | |
487cc3be VM |
616 | "Retry <schedule> is <item>|/<item>/... where <item> is one of\n" |
617 | " -<signal-num>|[-]<signal-name> send that signal\n" | |
618 | " <timeout> wait that many seconds\n" | |
619 | " forever repeat remainder forever\n" | |
620 | "or <schedule> may be just <timeout>, meaning <signal>/<timeout>/KILL/<timeout>\n" | |
61a41689 VM |
621 | "\n"); |
622 | ||
623 | printf( | |
487cc3be VM |
624 | "The process scheduler <policy> can be one of:\n" |
625 | " other, fifo or rr\n" | |
61a41689 VM |
626 | "\n"); |
627 | ||
628 | printf( | |
487cc3be VM |
629 | "The IO scheduler <class> can be one of:\n" |
630 | " real-time, best-effort or idle\n" | |
61a41689 VM |
631 | "\n"); |
632 | ||
633 | printf( | |
487cc3be VM |
634 | "Exit status:\n" |
635 | " 0 = done\n" | |
636 | " 1 = nothing done (=> 0 if --oknodo)\n" | |
637 | " 2 = with --retry, processes would not die\n" | |
638 | " 3 = trouble\n" | |
639 | "Exit status with --status:\n" | |
640 | " 0 = program is running\n" | |
641 | " 1 = program is not running and the pid file exists\n" | |
642 | " 3 = program is not running\n" | |
643 | " 4 = unable to determine status\n"); | |
644 | } | |
645 | ||
646 | static void | |
647 | do_version(void) | |
648 | { | |
649 | printf("start-stop-daemon %s for Debian\n\n", VERSION); | |
650 | ||
651 | printf("Written by Marek Michalkiewicz, public domain.\n"); | |
652 | } | |
653 | ||
654 | static void DPKG_ATTR_NORET | |
655 | badusage(const char *msg) | |
656 | { | |
657 | if (msg) | |
658 | fprintf(stderr, "%s: %s\n", progname, msg); | |
659 | fprintf(stderr, "Try '%s --help' for more information.\n", progname); | |
660 | ||
661 | if (action == ACTION_STATUS) | |
662 | exit(STATUS_UNKNOWN); | |
663 | else | |
664 | exit(3); | |
665 | } | |
666 | ||
667 | struct sigpair { | |
668 | const char *name; | |
669 | int signal; | |
670 | }; | |
671 | ||
672 | static const struct sigpair siglist[] = { | |
673 | { "ABRT", SIGABRT }, | |
674 | { "ALRM", SIGALRM }, | |
675 | { "FPE", SIGFPE }, | |
676 | { "HUP", SIGHUP }, | |
677 | { "ILL", SIGILL }, | |
678 | { "INT", SIGINT }, | |
679 | { "KILL", SIGKILL }, | |
680 | { "PIPE", SIGPIPE }, | |
681 | { "QUIT", SIGQUIT }, | |
682 | { "SEGV", SIGSEGV }, | |
683 | { "TERM", SIGTERM }, | |
684 | { "USR1", SIGUSR1 }, | |
685 | { "USR2", SIGUSR2 }, | |
686 | { "CHLD", SIGCHLD }, | |
687 | { "CONT", SIGCONT }, | |
688 | { "STOP", SIGSTOP }, | |
689 | { "TSTP", SIGTSTP }, | |
690 | { "TTIN", SIGTTIN }, | |
691 | { "TTOU", SIGTTOU } | |
692 | }; | |
693 | ||
694 | static int | |
695 | parse_unsigned(const char *string, int base, int *value_r) | |
696 | { | |
697 | long value; | |
698 | char *endptr; | |
699 | ||
700 | if (!string[0]) | |
701 | return -1; | |
702 | ||
703 | errno = 0; | |
704 | value = strtol(string, &endptr, base); | |
705 | if (string == endptr || *endptr != '\0' || errno != 0) | |
706 | return -1; | |
707 | if (value < 0 || value > INT_MAX) | |
708 | return -1; | |
709 | ||
710 | *value_r = value; | |
711 | return 0; | |
712 | } | |
713 | ||
714 | static int | |
715 | parse_pid(const char *pid_str, int *pid_num) | |
716 | { | |
717 | if (parse_unsigned(pid_str, 10, pid_num) != 0) | |
718 | return -1; | |
719 | if (*pid_num == 0) | |
720 | return -1; | |
721 | ||
722 | return 0; | |
723 | } | |
724 | ||
725 | static int | |
726 | parse_signal(const char *sig_str, int *sig_num) | |
727 | { | |
728 | unsigned int i; | |
729 | ||
730 | if (parse_unsigned(sig_str, 10, sig_num) == 0) | |
731 | return 0; | |
732 | ||
733 | for (i = 0; i < array_count(siglist); i++) { | |
734 | if (strcmp(sig_str, siglist[i].name) == 0) { | |
735 | *sig_num = siglist[i].signal; | |
736 | return 0; | |
737 | } | |
738 | } | |
739 | return -1; | |
740 | } | |
741 | ||
742 | static int | |
743 | parse_umask(const char *string, int *value_r) | |
744 | { | |
745 | return parse_unsigned(string, 0, value_r); | |
746 | } | |
747 | ||
748 | static void | |
749 | validate_proc_schedule(void) | |
750 | { | |
61a41689 | 751 | #if defined(_POSIX_PRIORITY_SCHEDULING) && _POSIX_PRIORITY_SCHEDULING > 0 |
487cc3be VM |
752 | int prio_min, prio_max; |
753 | ||
754 | prio_min = sched_get_priority_min(proc_sched->policy); | |
755 | prio_max = sched_get_priority_max(proc_sched->policy); | |
756 | ||
757 | if (proc_sched->priority < prio_min) | |
758 | badusage("process scheduler priority less than min"); | |
759 | if (proc_sched->priority > prio_max) | |
760 | badusage("process scheduler priority greater than max"); | |
761 | #endif | |
762 | } | |
763 | ||
764 | static void | |
765 | parse_proc_schedule(const char *string) | |
766 | { | |
767 | char *policy_str; | |
768 | size_t policy_len; | |
769 | int prio = 0; | |
770 | ||
771 | policy_len = strcspn(string, ":"); | |
772 | policy_str = xstrndup(string, policy_len); | |
773 | ||
774 | if (string[policy_len] == ':' && | |
775 | parse_unsigned(string + policy_len + 1, 10, &prio) != 0) | |
776 | fatal("invalid process scheduler priority"); | |
777 | ||
778 | proc_sched = xmalloc(sizeof(*proc_sched)); | |
779 | proc_sched->policy_name = policy_str; | |
780 | ||
781 | if (strcmp(policy_str, "other") == 0) { | |
782 | proc_sched->policy = SCHED_OTHER; | |
783 | proc_sched->priority = 0; | |
784 | } else if (strcmp(policy_str, "fifo") == 0) { | |
785 | proc_sched->policy = SCHED_FIFO; | |
786 | proc_sched->priority = prio; | |
787 | } else if (strcmp(policy_str, "rr") == 0) { | |
788 | proc_sched->policy = SCHED_RR; | |
789 | proc_sched->priority = prio; | |
790 | } else | |
791 | badusage("invalid process scheduler policy"); | |
792 | ||
793 | validate_proc_schedule(); | |
794 | } | |
795 | ||
796 | static void | |
797 | parse_io_schedule(const char *string) | |
798 | { | |
799 | char *class_str; | |
800 | size_t class_len; | |
801 | int prio = 4; | |
802 | ||
803 | class_len = strcspn(string, ":"); | |
804 | class_str = xstrndup(string, class_len); | |
805 | ||
806 | if (string[class_len] == ':' && | |
807 | parse_unsigned(string + class_len + 1, 10, &prio) != 0) | |
808 | fatal("invalid IO scheduler priority"); | |
809 | ||
810 | io_sched = xmalloc(sizeof(*io_sched)); | |
811 | io_sched->policy_name = class_str; | |
812 | ||
813 | if (strcmp(class_str, "real-time") == 0) { | |
814 | io_sched->policy = IOPRIO_CLASS_RT; | |
815 | io_sched->priority = prio; | |
816 | } else if (strcmp(class_str, "best-effort") == 0) { | |
817 | io_sched->policy = IOPRIO_CLASS_BE; | |
818 | io_sched->priority = prio; | |
819 | } else if (strcmp(class_str, "idle") == 0) { | |
820 | io_sched->policy = IOPRIO_CLASS_IDLE; | |
821 | io_sched->priority = 7; | |
822 | } else | |
823 | badusage("invalid IO scheduler policy"); | |
824 | ||
825 | if (io_sched->priority < IO_SCHED_PRIO_MIN) | |
826 | badusage("IO scheduler priority less than min"); | |
827 | if (io_sched->priority > IO_SCHED_PRIO_MAX) | |
828 | badusage("IO scheduler priority greater than max"); | |
829 | } | |
830 | ||
831 | static void | |
832 | set_proc_schedule(struct res_schedule *sched) | |
833 | { | |
61a41689 | 834 | #if defined(_POSIX_PRIORITY_SCHEDULING) && _POSIX_PRIORITY_SCHEDULING > 0 |
487cc3be VM |
835 | struct sched_param param; |
836 | ||
837 | param.sched_priority = sched->priority; | |
838 | ||
839 | if (sched_setscheduler(getpid(), sched->policy, ¶m) == -1) | |
840 | fatal("unable to set process scheduler"); | |
841 | #endif | |
842 | } | |
843 | ||
844 | #ifdef HAVE_IOPRIO_SET | |
845 | static inline int | |
846 | ioprio_set(int which, int who, int ioprio) | |
847 | { | |
848 | return syscall(SYS_ioprio_set, which, who, ioprio); | |
849 | } | |
850 | #endif | |
851 | ||
852 | static void | |
853 | set_io_schedule(struct res_schedule *sched) | |
854 | { | |
855 | #ifdef HAVE_IOPRIO_SET | |
856 | int io_sched_mask; | |
857 | ||
858 | io_sched_mask = IOPRIO_PRIO_VALUE(sched->policy, sched->priority); | |
859 | if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), io_sched_mask) == -1) | |
860 | warning("unable to alter IO priority to mask %i (%s)\n", | |
861 | io_sched_mask, strerror(errno)); | |
862 | #endif | |
863 | } | |
864 | ||
865 | static void | |
866 | parse_schedule_item(const char *string, struct schedule_item *item) | |
867 | { | |
868 | const char *after_hyph; | |
869 | ||
870 | if (strcmp(string, "forever") == 0) { | |
871 | item->type = sched_forever; | |
872 | } else if (isdigit(string[0])) { | |
873 | item->type = sched_timeout; | |
874 | if (parse_unsigned(string, 10, &item->value) != 0) | |
875 | badusage("invalid timeout value in schedule"); | |
876 | } else if ((after_hyph = string + (string[0] == '-')) && | |
877 | parse_signal(after_hyph, &item->value) == 0) { | |
878 | item->type = sched_signal; | |
879 | } else { | |
880 | badusage("invalid schedule item (must be [-]<signal-name>, " | |
881 | "-<signal-number>, <timeout> or 'forever'"); | |
882 | } | |
883 | } | |
884 | ||
885 | static void | |
886 | parse_schedule(const char *schedule_str) | |
887 | { | |
888 | char item_buf[20]; | |
889 | const char *slash; | |
890 | int count, repeatat; | |
891 | size_t str_len; | |
892 | ||
893 | count = 0; | |
894 | for (slash = schedule_str; *slash; slash++) | |
895 | if (*slash == '/') | |
896 | count++; | |
897 | ||
898 | schedule_length = (count == 0) ? 4 : count + 1; | |
899 | schedule = xmalloc(sizeof(*schedule) * schedule_length); | |
900 | ||
901 | if (count == 0) { | |
902 | schedule[0].type = sched_signal; | |
903 | schedule[0].value = signal_nr; | |
904 | parse_schedule_item(schedule_str, &schedule[1]); | |
905 | if (schedule[1].type != sched_timeout) { | |
906 | badusage("--retry takes timeout, or schedule list" | |
907 | " of at least two items"); | |
908 | } | |
909 | schedule[2].type = sched_signal; | |
910 | schedule[2].value = SIGKILL; | |
911 | schedule[3] = schedule[1]; | |
912 | } else { | |
913 | count = 0; | |
914 | repeatat = -1; | |
915 | while (schedule_str != NULL) { | |
916 | slash = strchr(schedule_str, '/'); | |
917 | str_len = slash ? (size_t)(slash - schedule_str) : strlen(schedule_str); | |
918 | if (str_len >= sizeof(item_buf)) | |
919 | badusage("invalid schedule item: far too long" | |
920 | " (you must delimit items with slashes)"); | |
921 | memcpy(item_buf, schedule_str, str_len); | |
922 | item_buf[str_len] = '\0'; | |
923 | schedule_str = slash ? slash + 1 : NULL; | |
924 | ||
925 | parse_schedule_item(item_buf, &schedule[count]); | |
926 | if (schedule[count].type == sched_forever) { | |
927 | if (repeatat >= 0) | |
928 | badusage("invalid schedule: 'forever'" | |
929 | " appears more than once"); | |
930 | repeatat = count; | |
931 | continue; | |
932 | } | |
933 | count++; | |
934 | } | |
935 | if (repeatat == count) | |
936 | badusage("invalid schedule: 'forever' appears last, " | |
937 | "nothing to repeat"); | |
938 | if (repeatat >= 0) { | |
939 | schedule[count].type = sched_goto; | |
940 | schedule[count].value = repeatat; | |
941 | count++; | |
942 | } | |
943 | assert(count == schedule_length); | |
944 | } | |
945 | } | |
946 | ||
947 | static void | |
948 | set_action(enum action_code new_action) | |
949 | { | |
950 | if (action == new_action) | |
951 | return; | |
952 | ||
953 | if (action != ACTION_NONE) | |
954 | badusage("only one command can be specified"); | |
955 | ||
956 | action = new_action; | |
957 | } | |
958 | ||
959 | #define OPT_PID 500 | |
960 | #define OPT_PPID 501 | |
961 | #define OPT_RM_PIDFILE 502 | |
962 | ||
963 | static void | |
964 | parse_options(int argc, char * const *argv) | |
965 | { | |
966 | static struct option longopts[] = { | |
967 | { "help", 0, NULL, 'H'}, | |
968 | { "stop", 0, NULL, 'K'}, | |
969 | { "start", 0, NULL, 'S'}, | |
970 | { "status", 0, NULL, 'T'}, | |
971 | { "version", 0, NULL, 'V'}, | |
972 | { "startas", 1, NULL, 'a'}, | |
973 | { "name", 1, NULL, 'n'}, | |
974 | { "oknodo", 0, NULL, 'o'}, | |
975 | { "pid", 1, NULL, OPT_PID}, | |
976 | { "ppid", 1, NULL, OPT_PPID}, | |
977 | { "pidfile", 1, NULL, 'p'}, | |
978 | { "quiet", 0, NULL, 'q'}, | |
979 | { "signal", 1, NULL, 's'}, | |
980 | { "test", 0, NULL, 't'}, | |
981 | { "user", 1, NULL, 'u'}, | |
982 | { "group", 1, NULL, 'g'}, | |
983 | { "chroot", 1, NULL, 'r'}, | |
984 | { "verbose", 0, NULL, 'v'}, | |
985 | { "exec", 1, NULL, 'x'}, | |
986 | { "chuid", 1, NULL, 'c'}, | |
987 | { "nicelevel", 1, NULL, 'N'}, | |
988 | { "procsched", 1, NULL, 'P'}, | |
989 | { "iosched", 1, NULL, 'I'}, | |
990 | { "umask", 1, NULL, 'k'}, | |
991 | { "background", 0, NULL, 'b'}, | |
992 | { "no-close", 0, NULL, 'C'}, | |
993 | { "make-pidfile", 0, NULL, 'm'}, | |
994 | { "remove-pidfile", 0, NULL, OPT_RM_PIDFILE}, | |
995 | { "retry", 1, NULL, 'R'}, | |
996 | { "chdir", 1, NULL, 'd'}, | |
997 | { NULL, 0, NULL, 0 } | |
998 | }; | |
999 | const char *pid_str = NULL; | |
1000 | const char *ppid_str = NULL; | |
1001 | const char *umask_str = NULL; | |
1002 | const char *signal_str = NULL; | |
1003 | const char *schedule_str = NULL; | |
1004 | const char *proc_schedule_str = NULL; | |
1005 | const char *io_schedule_str = NULL; | |
1006 | size_t changeuser_len; | |
1007 | int c; | |
1008 | ||
1009 | for (;;) { | |
1010 | c = getopt_long(argc, argv, | |
1011 | "HKSVTa:n:op:qr:s:tu:vx:c:N:P:I:k:bCmR:g:d:", | |
1012 | longopts, NULL); | |
1013 | if (c == -1) | |
1014 | break; | |
1015 | switch (c) { | |
1016 | case 'H': /* --help */ | |
1017 | usage(); | |
1018 | exit(0); | |
1019 | case 'K': /* --stop */ | |
1020 | set_action(ACTION_STOP); | |
1021 | break; | |
1022 | case 'S': /* --start */ | |
1023 | set_action(ACTION_START); | |
1024 | break; | |
1025 | case 'T': /* --status */ | |
1026 | set_action(ACTION_STATUS); | |
1027 | break; | |
1028 | case 'V': /* --version */ | |
1029 | do_version(); | |
1030 | exit(0); | |
1031 | case 'a': /* --startas <pathname> */ | |
1032 | startas = optarg; | |
1033 | break; | |
1034 | case 'n': /* --name <process-name> */ | |
1035 | cmdname = optarg; | |
1036 | break; | |
1037 | case 'o': /* --oknodo */ | |
1038 | exitnodo = 0; | |
1039 | break; | |
1040 | case OPT_PID: /* --pid <pid> */ | |
1041 | pid_str = optarg; | |
1042 | break; | |
1043 | case OPT_PPID: /* --ppid <ppid> */ | |
1044 | ppid_str = optarg; | |
1045 | break; | |
1046 | case 'p': /* --pidfile <pid-file> */ | |
1047 | pidfile = optarg; | |
1048 | break; | |
1049 | case 'q': /* --quiet */ | |
1050 | quietmode = true; | |
1051 | break; | |
1052 | case 's': /* --signal <signal> */ | |
1053 | signal_str = optarg; | |
1054 | break; | |
1055 | case 't': /* --test */ | |
1056 | testmode = true; | |
1057 | break; | |
1058 | case 'u': /* --user <username>|<uid> */ | |
1059 | userspec = optarg; | |
1060 | break; | |
1061 | case 'v': /* --verbose */ | |
1062 | quietmode = -1; | |
1063 | break; | |
1064 | case 'x': /* --exec <executable> */ | |
1065 | execname = optarg; | |
1066 | break; | |
1067 | case 'c': /* --chuid <username>|<uid> */ | |
1068 | /* We copy the string just in case we need the | |
1069 | * argument later. */ | |
1070 | changeuser_len = strcspn(optarg, ":"); | |
1071 | changeuser = xstrndup(optarg, changeuser_len); | |
1072 | if (optarg[changeuser_len] == ':') { | |
1073 | if (optarg[changeuser_len + 1] == '\0') | |
1074 | fatal("missing group name"); | |
1075 | changegroup = optarg + changeuser_len + 1; | |
1076 | } | |
1077 | break; | |
1078 | case 'g': /* --group <group>|<gid> */ | |
1079 | changegroup = optarg; | |
1080 | break; | |
1081 | case 'r': /* --chroot /new/root */ | |
1082 | changeroot = optarg; | |
1083 | break; | |
1084 | case 'N': /* --nice */ | |
1085 | nicelevel = atoi(optarg); | |
1086 | break; | |
1087 | case 'P': /* --procsched */ | |
1088 | proc_schedule_str = optarg; | |
1089 | break; | |
1090 | case 'I': /* --iosched */ | |
1091 | io_schedule_str = optarg; | |
1092 | break; | |
1093 | case 'k': /* --umask <mask> */ | |
1094 | umask_str = optarg; | |
1095 | break; | |
1096 | case 'b': /* --background */ | |
1097 | background = true; | |
1098 | break; | |
1099 | case 'C': /* --no-close */ | |
1100 | close_io = false; | |
1101 | break; | |
1102 | case 'm': /* --make-pidfile */ | |
1103 | mpidfile = true; | |
1104 | break; | |
1105 | case OPT_RM_PIDFILE: /* --remove-pidfile */ | |
1106 | rpidfile = true; | |
1107 | break; | |
1108 | case 'R': /* --retry <schedule>|<timeout> */ | |
1109 | schedule_str = optarg; | |
1110 | break; | |
1111 | case 'd': /* --chdir /new/dir */ | |
1112 | changedir = optarg; | |
1113 | break; | |
1114 | default: | |
1115 | /* Message printed by getopt. */ | |
1116 | badusage(NULL); | |
1117 | } | |
1118 | } | |
1119 | ||
1120 | if (pid_str != NULL) { | |
1121 | if (parse_pid(pid_str, &match_pid) != 0) | |
1122 | badusage("pid value must be a number greater than 0"); | |
1123 | } | |
1124 | ||
1125 | if (ppid_str != NULL) { | |
1126 | if (parse_pid(ppid_str, &match_ppid) != 0) | |
1127 | badusage("ppid value must be a number greater than 0"); | |
1128 | } | |
1129 | ||
1130 | if (signal_str != NULL) { | |
1131 | if (parse_signal(signal_str, &signal_nr) != 0) | |
1132 | badusage("signal value must be numeric or name" | |
1133 | " of signal (KILL, INT, ...)"); | |
1134 | } | |
1135 | ||
1136 | if (schedule_str != NULL) { | |
1137 | parse_schedule(schedule_str); | |
1138 | } | |
1139 | ||
1140 | if (proc_schedule_str != NULL) | |
1141 | parse_proc_schedule(proc_schedule_str); | |
1142 | ||
1143 | if (io_schedule_str != NULL) | |
1144 | parse_io_schedule(io_schedule_str); | |
1145 | ||
1146 | if (umask_str != NULL) { | |
1147 | if (parse_umask(umask_str, &umask_value) != 0) | |
1148 | badusage("umask value must be a positive number"); | |
1149 | } | |
1150 | ||
1151 | if (action == ACTION_NONE) | |
1152 | badusage("need one of --start or --stop or --status"); | |
1153 | ||
1154 | if (!execname && !pid_str && !ppid_str && !pidfile && !userspec && | |
1155 | !cmdname) | |
1156 | badusage("need at least one of --exec, --pid, --ppid, --pidfile, --user or --name"); | |
1157 | ||
1158 | #ifdef PROCESS_NAME_SIZE | |
1159 | if (cmdname && strlen(cmdname) > PROCESS_NAME_SIZE) | |
1160 | warning("this system is not able to track process names\n" | |
1161 | "longer than %d characters, please use --exec " | |
1162 | "instead of --name.\n", PROCESS_NAME_SIZE); | |
1163 | #endif | |
1164 | ||
1165 | if (!startas) | |
1166 | startas = execname; | |
1167 | ||
1168 | if (action == ACTION_START && !startas) | |
1169 | badusage("--start needs --exec or --startas"); | |
1170 | ||
1171 | if (mpidfile && pidfile == NULL) | |
1172 | badusage("--make-pidfile requires --pidfile"); | |
1173 | if (rpidfile && pidfile == NULL) | |
1174 | badusage("--remove-pidfile requires --pidfile"); | |
1175 | ||
1176 | if (pid_str && pidfile) | |
1177 | badusage("need either --pid of --pidfile, not both"); | |
1178 | ||
1179 | if (background && action != ACTION_START) | |
1180 | badusage("--background is only relevant with --start"); | |
1181 | ||
1182 | if (!close_io && !background) | |
1183 | badusage("--no-close is only relevant with --background"); | |
1184 | } | |
1185 | ||
1186 | static void | |
1187 | setup_options(void) | |
1188 | { | |
1189 | if (execname) { | |
1190 | char *fullexecname; | |
1191 | ||
1192 | /* If it's a relative path, normalize it. */ | |
1193 | if (execname[0] != '/') | |
1194 | execname = newpath(changedir, execname); | |
1195 | ||
1196 | if (changeroot) | |
1197 | fullexecname = newpath(changeroot, execname); | |
1198 | else | |
1199 | fullexecname = execname; | |
1200 | ||
1201 | if (stat(fullexecname, &exec_stat)) | |
1202 | fatal("unable to stat %s", fullexecname); | |
1203 | ||
1204 | if (fullexecname != execname) | |
1205 | free(fullexecname); | |
1206 | } | |
1207 | ||
61a41689 | 1208 | if (userspec && parse_unsigned(userspec, 10, &user_id) < 0) { |
487cc3be VM |
1209 | struct passwd *pw; |
1210 | ||
1211 | pw = getpwnam(userspec); | |
1212 | if (!pw) | |
1213 | fatal("user '%s' not found", userspec); | |
1214 | ||
1215 | user_id = pw->pw_uid; | |
1216 | } | |
1217 | ||
61a41689 | 1218 | if (changegroup && parse_unsigned(changegroup, 10, &runas_gid) < 0) { |
487cc3be VM |
1219 | struct group *gr; |
1220 | ||
1221 | gr = getgrnam(changegroup); | |
1222 | if (!gr) | |
1223 | fatal("group '%s' not found", changegroup); | |
1224 | changegroup = gr->gr_name; | |
1225 | runas_gid = gr->gr_gid; | |
1226 | } | |
1227 | if (changeuser) { | |
1228 | struct passwd *pw; | |
1229 | struct stat st; | |
1230 | ||
61a41689 | 1231 | if (parse_unsigned(changeuser, 10, &runas_uid) == 0) |
487cc3be VM |
1232 | pw = getpwuid(runas_uid); |
1233 | else | |
1234 | pw = getpwnam(changeuser); | |
1235 | if (!pw) | |
1236 | fatal("user '%s' not found", changeuser); | |
1237 | changeuser = pw->pw_name; | |
1238 | runas_uid = pw->pw_uid; | |
1239 | if (changegroup == NULL) { | |
1240 | /* Pass the default group of this user. */ | |
1241 | changegroup = ""; /* Just empty. */ | |
1242 | runas_gid = pw->pw_gid; | |
1243 | } | |
1244 | if (stat(pw->pw_dir, &st) == 0) | |
1245 | setenv("HOME", pw->pw_dir, 1); | |
1246 | } | |
1247 | } | |
1248 | ||
61a41689 | 1249 | #if defined(OS_Linux) |
487cc3be VM |
1250 | static const char * |
1251 | proc_status_field(pid_t pid, const char *field) | |
1252 | { | |
1253 | static char *line = NULL; | |
1254 | static size_t line_size = 0; | |
1255 | ||
1256 | FILE *fp; | |
1257 | char filename[32]; | |
1258 | char *value = NULL; | |
1259 | ssize_t line_len; | |
1260 | size_t field_len = strlen(field); | |
1261 | ||
1262 | sprintf(filename, "/proc/%d/status", pid); | |
1263 | fp = fopen(filename, "r"); | |
1264 | if (!fp) | |
1265 | return NULL; | |
1266 | while ((line_len = getline(&line, &line_size, fp)) >= 0) { | |
1267 | if (strncasecmp(line, field, field_len) == 0) { | |
1268 | line[line_len - 1] = '\0'; | |
1269 | ||
1270 | value = line + field_len; | |
1271 | while (isspace(*value)) | |
1272 | value++; | |
1273 | ||
1274 | break; | |
1275 | } | |
1276 | } | |
1277 | fclose(fp); | |
1278 | ||
1279 | return value; | |
1280 | } | |
61a41689 VM |
1281 | #elif defined(OS_AIX) |
1282 | static bool | |
1283 | proc_get_psinfo(pid_t pid, struct psinfo *psinfo) | |
1284 | { | |
1285 | char filename[64]; | |
1286 | FILE *fp; | |
1287 | ||
1288 | sprintf(filename, "/proc/%d/psinfo", pid); | |
1289 | fp = fopen(filename, "r"); | |
1290 | if (!fp) | |
1291 | return false; | |
1292 | if (fread(psinfo, sizeof(*psinfo), 1, fp) == 0) | |
1293 | return false; | |
1294 | if (ferror(fp)) | |
1295 | return false; | |
487cc3be | 1296 | |
61a41689 VM |
1297 | return true; |
1298 | } | |
1299 | #elif defined(OS_Hurd) | |
487cc3be VM |
1300 | static void |
1301 | init_procset(void) | |
1302 | { | |
1303 | struct ps_context *context; | |
1304 | error_t err; | |
1305 | ||
1306 | err = ps_context_create(getproc(), &context); | |
1307 | if (err) | |
1308 | error(1, err, "ps_context_create"); | |
1309 | ||
1310 | err = proc_stat_list_create(context, &procset); | |
1311 | if (err) | |
1312 | error(1, err, "proc_stat_list_create"); | |
1313 | ||
1314 | err = proc_stat_list_add_all(procset, 0, 0); | |
1315 | if (err) | |
1316 | error(1, err, "proc_stat_list_add_all"); | |
1317 | } | |
1318 | ||
1319 | static struct proc_stat * | |
1320 | get_proc_stat(pid_t pid, ps_flags_t flags) | |
1321 | { | |
1322 | struct proc_stat *ps; | |
1323 | ps_flags_t wanted_flags = PSTAT_PID | flags; | |
1324 | ||
1325 | if (!procset) | |
1326 | init_procset(); | |
1327 | ||
1328 | ps = proc_stat_list_pid_proc_stat(procset, pid); | |
1329 | if (!ps) | |
1330 | return NULL; | |
1331 | if (proc_stat_set_flags(ps, wanted_flags)) | |
1332 | return NULL; | |
1333 | if ((proc_stat_flags(ps) & wanted_flags) != wanted_flags) | |
1334 | return NULL; | |
1335 | ||
1336 | return ps; | |
1337 | } | |
1338 | #elif defined(HAVE_KVM_H) | |
1339 | static kvm_t * | |
1340 | ssd_kvm_open(void) | |
1341 | { | |
1342 | kvm_t *kd; | |
1343 | char errbuf[_POSIX2_LINE_MAX]; | |
1344 | ||
1345 | kd = kvm_openfiles(NULL, KVM_MEMFILE, NULL, O_RDONLY, errbuf); | |
1346 | if (kd == NULL) | |
1347 | errx(1, "%s", errbuf); | |
1348 | ||
1349 | return kd; | |
1350 | } | |
1351 | ||
1352 | static struct kinfo_proc * | |
1353 | ssd_kvm_get_procs(kvm_t *kd, int op, int arg, int *count) | |
1354 | { | |
1355 | struct kinfo_proc *kp; | |
1356 | int lcount; | |
1357 | ||
1358 | if (count == NULL) | |
1359 | count = &lcount; | |
1360 | *count = 0; | |
1361 | ||
61a41689 VM |
1362 | #if defined(OS_OpenBSD) |
1363 | kp = kvm_getprocs(kd, op, arg, sizeof(*kp), count); | |
1364 | #else | |
487cc3be | 1365 | kp = kvm_getprocs(kd, op, arg, count); |
61a41689 | 1366 | #endif |
487cc3be VM |
1367 | if (kp == NULL && errno != ESRCH) |
1368 | errx(1, "%s", kvm_geterr(kd)); | |
1369 | ||
1370 | return kp; | |
1371 | } | |
1372 | #endif | |
1373 | ||
61a41689 | 1374 | #if defined(OS_Linux) |
487cc3be VM |
1375 | static bool |
1376 | pid_is_exec(pid_t pid, const struct stat *esb) | |
1377 | { | |
1378 | char lname[32]; | |
1379 | char lcontents[_POSIX_PATH_MAX + 1]; | |
1380 | char *filename; | |
1381 | const char deleted[] = " (deleted)"; | |
1382 | int nread; | |
1383 | struct stat sb; | |
1384 | ||
1385 | sprintf(lname, "/proc/%d/exe", pid); | |
1386 | nread = readlink(lname, lcontents, sizeof(lcontents) - 1); | |
1387 | if (nread == -1) | |
1388 | return false; | |
1389 | ||
1390 | filename = lcontents; | |
1391 | filename[nread] = '\0'; | |
1392 | ||
1393 | /* OpenVZ kernels contain a bogus patch that instead of appending, | |
1394 | * prepends the deleted marker. Workaround those. Otherwise handle | |
1395 | * the normal appended marker. */ | |
1396 | if (strncmp(filename, deleted, strlen(deleted)) == 0) | |
1397 | filename += strlen(deleted); | |
1398 | else if (strcmp(filename + nread - strlen(deleted), deleted) == 0) | |
1399 | filename[nread - strlen(deleted)] = '\0'; | |
1400 | ||
1401 | if (stat(filename, &sb) != 0) | |
1402 | return false; | |
1403 | ||
1404 | return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino); | |
1405 | } | |
61a41689 VM |
1406 | #elif defined(OS_AIX) |
1407 | static bool | |
1408 | pid_is_exec(pid_t pid, const struct stat *esb) | |
1409 | { | |
1410 | struct stat sb; | |
1411 | char filename[64]; | |
1412 | ||
1413 | sprintf(filename, "/proc/%d/object/a.out", pid); | |
1414 | ||
1415 | if (stat(filename, &sb) != 0) | |
1416 | return false; | |
1417 | ||
1418 | return sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino; | |
1419 | } | |
1420 | #elif defined(OS_Hurd) | |
487cc3be VM |
1421 | static bool |
1422 | pid_is_exec(pid_t pid, const struct stat *esb) | |
1423 | { | |
1424 | struct proc_stat *ps; | |
1425 | struct stat sb; | |
1426 | const char *filename; | |
1427 | ||
1428 | ps = get_proc_stat(pid, PSTAT_ARGS); | |
1429 | if (ps == NULL) | |
1430 | return false; | |
1431 | ||
61a41689 VM |
1432 | /* On old Hurd systems we have to use the argv[0] value, because |
1433 | * there is nothing better. */ | |
487cc3be | 1434 | filename = proc_stat_args(ps); |
61a41689 VM |
1435 | #ifdef PSTAT_EXE |
1436 | /* On new Hurd systems we can use the correct value, as long | |
1437 | * as it's not NULL nor empty, as it was the case on the first | |
1438 | * implementation. */ | |
1439 | if (proc_stat_set_flags(ps, PSTAT_EXE) == 0 && | |
1440 | proc_stat_flags(ps) & PSTAT_EXE && | |
1441 | proc_stat_exe(ps) != NULL && | |
1442 | proc_stat_exe(ps)[0] != '\0') | |
1443 | filename = proc_stat_exe(ps); | |
1444 | #endif | |
487cc3be VM |
1445 | |
1446 | if (stat(filename, &sb) != 0) | |
1447 | return false; | |
1448 | ||
1449 | return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino); | |
1450 | } | |
61a41689 VM |
1451 | #elif defined(OS_Darwin) |
1452 | static bool | |
1453 | pid_is_exec(pid_t pid, const struct stat *esb) | |
1454 | { | |
1455 | struct stat sb; | |
1456 | char pathname[_POSIX_PATH_MAX]; | |
1457 | ||
1458 | if (proc_pidpath(pid, pathname, sizeof(pathname)) < 0) | |
1459 | return false; | |
1460 | ||
1461 | if (stat(pathname, &sb) != 0) | |
1462 | return false; | |
1463 | ||
1464 | return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino); | |
1465 | } | |
1466 | #elif defined(OS_HPUX) | |
487cc3be VM |
1467 | static bool |
1468 | pid_is_exec(pid_t pid, const struct stat *esb) | |
1469 | { | |
1470 | struct pst_status pst; | |
1471 | ||
1472 | if (pstat_getproc(&pst, sizeof(pst), (size_t)0, (int)pid) < 0) | |
1473 | return false; | |
1474 | return ((dev_t)pst.pst_text.psf_fsid.psfs_id == esb->st_dev && | |
1475 | (ino_t)pst.pst_text.psf_fileid == esb->st_ino); | |
1476 | } | |
61a41689 | 1477 | #elif defined(OS_FreeBSD) |
487cc3be VM |
1478 | static bool |
1479 | pid_is_exec(pid_t pid, const struct stat *esb) | |
1480 | { | |
1481 | struct stat sb; | |
61a41689 | 1482 | int error, mib[4]; |
487cc3be VM |
1483 | size_t len; |
1484 | char pathname[PATH_MAX]; | |
1485 | ||
61a41689 VM |
1486 | mib[0] = CTL_KERN; |
1487 | mib[1] = KERN_PROC; | |
1488 | mib[2] = KERN_PROC_PATHNAME; | |
1489 | mib[3] = pid; | |
487cc3be VM |
1490 | len = sizeof(pathname); |
1491 | ||
61a41689 | 1492 | error = sysctl(mib, 4, pathname, &len, NULL, 0); |
487cc3be VM |
1493 | if (error != 0 && errno != ESRCH) |
1494 | return false; | |
1495 | if (len == 0) | |
1496 | pathname[0] = '\0'; | |
1497 | ||
1498 | if (stat(pathname, &sb) != 0) | |
1499 | return false; | |
1500 | ||
1501 | return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino); | |
1502 | } | |
1503 | #elif defined(HAVE_KVM_H) | |
1504 | static bool | |
1505 | pid_is_exec(pid_t pid, const struct stat *esb) | |
1506 | { | |
1507 | kvm_t *kd; | |
1508 | int argv_len = 0; | |
1509 | struct kinfo_proc *kp; | |
1510 | struct stat sb; | |
1511 | char buf[_POSIX2_LINE_MAX]; | |
1512 | char **pid_argv_p; | |
1513 | char *start_argv_0_p, *end_argv_0_p; | |
1514 | bool res = false; | |
1515 | ||
1516 | kd = ssd_kvm_open(); | |
1517 | kp = ssd_kvm_get_procs(kd, KERN_PROC_PID, pid, NULL); | |
1518 | if (kp == NULL) | |
1519 | goto cleanup; | |
1520 | ||
1521 | pid_argv_p = kvm_getargv(kd, kp, argv_len); | |
1522 | if (pid_argv_p == NULL) | |
1523 | errx(1, "%s", kvm_geterr(kd)); | |
1524 | ||
1525 | /* Find and compare string. */ | |
1526 | start_argv_0_p = *pid_argv_p; | |
1527 | ||
1528 | /* Find end of argv[0] then copy and cut of str there. */ | |
1529 | end_argv_0_p = strchr(*pid_argv_p, ' '); | |
1530 | if (end_argv_0_p == NULL) | |
1531 | /* There seems to be no space, so we have the command | |
1532 | * already in its desired form. */ | |
1533 | start_argv_0_p = *pid_argv_p; | |
1534 | else { | |
1535 | /* Tests indicate that this never happens, since | |
1536 | * kvm_getargv itself cuts of tailing stuff. This is | |
1537 | * not what the manpage says, however. */ | |
1538 | strncpy(buf, *pid_argv_p, (end_argv_0_p - start_argv_0_p)); | |
1539 | buf[(end_argv_0_p - start_argv_0_p) + 1] = '\0'; | |
1540 | start_argv_0_p = buf; | |
1541 | } | |
1542 | ||
1543 | if (stat(start_argv_0_p, &sb) != 0) | |
1544 | goto cleanup; | |
1545 | ||
1546 | res = (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino); | |
1547 | ||
1548 | cleanup: | |
1549 | kvm_close(kd); | |
1550 | ||
1551 | return res; | |
1552 | } | |
1553 | #endif | |
1554 | ||
61a41689 | 1555 | #if defined(OS_Linux) |
487cc3be VM |
1556 | static bool |
1557 | pid_is_child(pid_t pid, pid_t ppid) | |
1558 | { | |
1559 | const char *ppid_str; | |
1560 | pid_t proc_ppid; | |
1561 | int rc; | |
1562 | ||
1563 | ppid_str = proc_status_field(pid, "PPid:"); | |
1564 | if (ppid_str == NULL) | |
1565 | return false; | |
1566 | ||
1567 | rc = parse_pid(ppid_str, &proc_ppid); | |
1568 | if (rc < 0) | |
1569 | return false; | |
1570 | ||
1571 | return proc_ppid == ppid; | |
1572 | } | |
61a41689 | 1573 | #elif defined(OS_Hurd) |
487cc3be VM |
1574 | static bool |
1575 | pid_is_child(pid_t pid, pid_t ppid) | |
1576 | { | |
1577 | struct proc_stat *ps; | |
1578 | struct procinfo *pi; | |
1579 | ||
1580 | ps = get_proc_stat(pid, PSTAT_PROC_INFO); | |
1581 | if (ps == NULL) | |
1582 | return false; | |
1583 | ||
1584 | pi = proc_stat_proc_info(ps); | |
1585 | ||
1586 | return pi->ppid == ppid; | |
1587 | } | |
61a41689 VM |
1588 | #elif defined(OS_Darwin) |
1589 | static bool | |
1590 | pid_is_child(pid_t pid, pid_t ppid) | |
1591 | { | |
1592 | struct proc_bsdinfo info; | |
1593 | ||
1594 | if (proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &info, sizeof(info)) < 0) | |
1595 | return false; | |
1596 | ||
1597 | return (pid_t)info.pbi_ppid == ppid; | |
1598 | } | |
1599 | #elif defined(OS_AIX) | |
1600 | static bool | |
1601 | pid_is_child(pid_t pid, pid_t ppid) | |
1602 | { | |
1603 | struct psinfo psi; | |
1604 | ||
1605 | if (!proc_get_psinfo(pid, &psi)) | |
1606 | return false; | |
1607 | ||
1608 | return (pid_t)psi.pr_ppid == ppid; | |
1609 | } | |
1610 | #elif defined(OS_HPUX) | |
487cc3be VM |
1611 | static bool |
1612 | pid_is_child(pid_t pid, pid_t ppid) | |
1613 | { | |
1614 | struct pst_status pst; | |
1615 | ||
1616 | if (pstat_getproc(&pst, sizeof(pst), (size_t)0, (int)pid) < 0) | |
1617 | return false; | |
1618 | ||
1619 | return pst.pst_ppid == ppid; | |
1620 | } | |
61a41689 VM |
1621 | #elif defined(OS_FreeBSD) |
1622 | static bool | |
1623 | pid_is_child(pid_t pid, pid_t ppid) | |
1624 | { | |
1625 | struct kinfo_proc kp; | |
1626 | int rc, mib[4]; | |
1627 | size_t len; | |
1628 | ||
1629 | mib[0] = CTL_KERN; | |
1630 | mib[1] = KERN_PROC; | |
1631 | mib[2] = KERN_PROC_PID; | |
1632 | mib[3] = pid; | |
1633 | len = sizeof(kp); | |
1634 | ||
1635 | rc = sysctl(mib, 4, &kp, &len, NULL, 0); | |
1636 | if (rc != 0 && errno != ESRCH) | |
1637 | return false; | |
1638 | if (len == 0 || len != sizeof(kp)) | |
1639 | return false; | |
1640 | ||
1641 | return kp.ki_ppid == ppid; | |
1642 | } | |
487cc3be VM |
1643 | #elif defined(HAVE_KVM_H) |
1644 | static bool | |
1645 | pid_is_child(pid_t pid, pid_t ppid) | |
1646 | { | |
1647 | kvm_t *kd; | |
1648 | struct kinfo_proc *kp; | |
1649 | pid_t proc_ppid; | |
1650 | bool res = false; | |
1651 | ||
1652 | kd = ssd_kvm_open(); | |
1653 | kp = ssd_kvm_get_procs(kd, KERN_PROC_PID, pid, NULL); | |
1654 | if (kp == NULL) | |
1655 | goto cleanup; | |
1656 | ||
61a41689 | 1657 | #if defined(OS_FreeBSD) |
487cc3be | 1658 | proc_ppid = kp->ki_ppid; |
61a41689 | 1659 | #elif defined(OS_OpenBSD) |
487cc3be | 1660 | proc_ppid = kp->p_ppid; |
61a41689 | 1661 | #elif defined(OS_DragonFlyBSD) |
487cc3be VM |
1662 | proc_ppid = kp->kp_ppid; |
1663 | #else | |
1664 | proc_ppid = kp->kp_proc.p_ppid; | |
1665 | #endif | |
1666 | ||
1667 | res = (proc_ppid == ppid); | |
1668 | ||
1669 | cleanup: | |
1670 | kvm_close(kd); | |
1671 | ||
1672 | return res; | |
1673 | } | |
1674 | #endif | |
1675 | ||
61a41689 | 1676 | #if defined(OS_Linux) |
487cc3be VM |
1677 | static bool |
1678 | pid_is_user(pid_t pid, uid_t uid) | |
1679 | { | |
1680 | struct stat sb; | |
1681 | char buf[32]; | |
1682 | ||
1683 | sprintf(buf, "/proc/%d", pid); | |
1684 | if (stat(buf, &sb) != 0) | |
1685 | return false; | |
1686 | return (sb.st_uid == uid); | |
1687 | } | |
61a41689 | 1688 | #elif defined(OS_Hurd) |
487cc3be VM |
1689 | static bool |
1690 | pid_is_user(pid_t pid, uid_t uid) | |
1691 | { | |
1692 | struct proc_stat *ps; | |
1693 | ||
1694 | ps = get_proc_stat(pid, PSTAT_OWNER_UID); | |
1695 | return ps && (uid_t)proc_stat_owner_uid(ps) == uid; | |
1696 | } | |
61a41689 VM |
1697 | #elif defined(OS_Darwin) |
1698 | static bool | |
1699 | pid_is_user(pid_t pid, uid_t uid) | |
1700 | { | |
1701 | struct proc_bsdinfo info; | |
1702 | ||
1703 | if (proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &info, sizeof(info)) < 0) | |
1704 | return false; | |
1705 | ||
1706 | return info.pbi_ruid == uid; | |
1707 | } | |
1708 | #elif defined(OS_AIX) | |
1709 | static bool | |
1710 | pid_is_user(pid_t pid, uid_t uid) | |
1711 | { | |
1712 | struct psinfo psi; | |
1713 | ||
1714 | if (!proc_get_psinfo(pid, &psi)) | |
1715 | return false; | |
1716 | ||
1717 | return psi.pr_uid == uid; | |
1718 | } | |
1719 | #elif defined(OS_HPUX) | |
487cc3be VM |
1720 | static bool |
1721 | pid_is_user(pid_t pid, uid_t uid) | |
1722 | { | |
1723 | struct pst_status pst; | |
1724 | ||
1725 | if (pstat_getproc(&pst, sizeof(pst), (size_t)0, (int)pid) < 0) | |
1726 | return false; | |
1727 | return ((uid_t)pst.pst_uid == uid); | |
1728 | } | |
61a41689 VM |
1729 | #elif defined(OS_FreeBSD) |
1730 | static bool | |
1731 | pid_is_user(pid_t pid, uid_t uid) | |
1732 | { | |
1733 | struct kinfo_proc kp; | |
1734 | int rc, mib[4]; | |
1735 | size_t len; | |
1736 | ||
1737 | mib[0] = CTL_KERN; | |
1738 | mib[1] = KERN_PROC; | |
1739 | mib[2] = KERN_PROC_PID; | |
1740 | mib[3] = pid; | |
1741 | len = sizeof(kp); | |
1742 | ||
1743 | rc = sysctl(mib, 4, &kp, &len, NULL, 0); | |
1744 | if (rc != 0 && errno != ESRCH) | |
1745 | return false; | |
1746 | if (len == 0 || len != sizeof(kp)) | |
1747 | return false; | |
1748 | ||
1749 | return kp.ki_ruid == uid; | |
1750 | } | |
487cc3be VM |
1751 | #elif defined(HAVE_KVM_H) |
1752 | static bool | |
1753 | pid_is_user(pid_t pid, uid_t uid) | |
1754 | { | |
1755 | kvm_t *kd; | |
1756 | uid_t proc_uid; | |
1757 | struct kinfo_proc *kp; | |
1758 | bool res = false; | |
1759 | ||
1760 | kd = ssd_kvm_open(); | |
1761 | kp = ssd_kvm_get_procs(kd, KERN_PROC_PID, pid, NULL); | |
1762 | if (kp == NULL) | |
1763 | goto cleanup; | |
1764 | ||
61a41689 | 1765 | #if defined(OS_FreeBSD) |
487cc3be | 1766 | proc_uid = kp->ki_ruid; |
61a41689 | 1767 | #elif defined(OS_OpenBSD) |
487cc3be | 1768 | proc_uid = kp->p_ruid; |
61a41689 | 1769 | #elif defined(OS_DragonFlyBSD) |
487cc3be | 1770 | proc_uid = kp->kp_ruid; |
61a41689 VM |
1771 | #elif defined(OS_NetBSD) |
1772 | proc_uid = kp->kp_eproc.e_pcred.p_ruid; | |
487cc3be VM |
1773 | #else |
1774 | if (kp->kp_proc.p_cred) | |
1775 | kvm_read(kd, (u_long)&(kp->kp_proc.p_cred->p_ruid), | |
1776 | &proc_uid, sizeof(uid_t)); | |
1777 | else | |
1778 | goto cleanup; | |
1779 | #endif | |
1780 | ||
1781 | res = (proc_uid == (uid_t)uid); | |
1782 | ||
1783 | cleanup: | |
1784 | kvm_close(kd); | |
1785 | ||
1786 | return res; | |
1787 | } | |
1788 | #endif | |
1789 | ||
61a41689 | 1790 | #if defined(OS_Linux) |
487cc3be VM |
1791 | static bool |
1792 | pid_is_cmd(pid_t pid, const char *name) | |
1793 | { | |
1794 | const char *comm; | |
1795 | ||
1796 | comm = proc_status_field(pid, "Name:"); | |
1797 | if (comm == NULL) | |
1798 | return false; | |
1799 | ||
1800 | return strcmp(comm, name) == 0; | |
1801 | } | |
61a41689 | 1802 | #elif defined(OS_Hurd) |
487cc3be VM |
1803 | static bool |
1804 | pid_is_cmd(pid_t pid, const char *name) | |
1805 | { | |
1806 | struct proc_stat *ps; | |
1807 | size_t argv0_len; | |
1808 | const char *argv0; | |
1809 | const char *binary_name; | |
1810 | ||
1811 | ps = get_proc_stat(pid, PSTAT_ARGS); | |
1812 | if (ps == NULL) | |
1813 | return false; | |
1814 | ||
1815 | argv0 = proc_stat_args(ps); | |
1816 | argv0_len = strlen(argv0) + 1; | |
1817 | ||
1818 | binary_name = basename(argv0); | |
1819 | if (strcmp(binary_name, name) == 0) | |
1820 | return true; | |
1821 | ||
1822 | /* XXX: This is all kinds of ugly, but on the Hurd there's no way to | |
1823 | * know the command name of a process, so we have to try to match | |
1824 | * also on argv[1] for the case of an interpreted script. */ | |
1825 | if (proc_stat_args_len(ps) > argv0_len) { | |
1826 | const char *script_name = basename(argv0 + argv0_len); | |
1827 | ||
1828 | return strcmp(script_name, name) == 0; | |
1829 | } | |
1830 | ||
1831 | return false; | |
1832 | } | |
61a41689 VM |
1833 | #elif defined(OS_AIX) |
1834 | static bool | |
1835 | pid_is_cmd(pid_t pid, const char *name) | |
1836 | { | |
1837 | struct psinfo psi; | |
1838 | ||
1839 | if (!proc_get_psinfo(pid, &psi)) | |
1840 | return false; | |
1841 | ||
1842 | return strcmp(psi.pr_fname, name) == 0; | |
1843 | } | |
1844 | #elif defined(OS_HPUX) | |
487cc3be VM |
1845 | static bool |
1846 | pid_is_cmd(pid_t pid, const char *name) | |
1847 | { | |
1848 | struct pst_status pst; | |
1849 | ||
1850 | if (pstat_getproc(&pst, sizeof(pst), (size_t)0, (int)pid) < 0) | |
1851 | return false; | |
1852 | return (strcmp(pst.pst_ucomm, name) == 0); | |
1853 | } | |
61a41689 VM |
1854 | #elif defined(OS_Darwin) |
1855 | static bool | |
1856 | pid_is_cmd(pid_t pid, const char *name) | |
1857 | { | |
1858 | char pathname[_POSIX_PATH_MAX]; | |
1859 | ||
1860 | if (proc_pidpath(pid, pathname, sizeof(pathname)) < 0) | |
1861 | return false; | |
1862 | ||
1863 | return strcmp(pathname, name) == 0; | |
1864 | } | |
1865 | #elif defined(OS_FreeBSD) | |
1866 | static bool | |
1867 | pid_is_cmd(pid_t pid, const char *name) | |
1868 | { | |
1869 | struct kinfo_proc kp; | |
1870 | int rc, mib[4]; | |
1871 | size_t len; | |
1872 | ||
1873 | mib[0] = CTL_KERN; | |
1874 | mib[1] = KERN_PROC; | |
1875 | mib[2] = KERN_PROC_PID; | |
1876 | mib[3] = pid; | |
1877 | len = sizeof(kp); | |
1878 | ||
1879 | rc = sysctl(mib, 4, &kp, &len, NULL, 0); | |
1880 | if (rc != 0 && errno != ESRCH) | |
1881 | return false; | |
1882 | if (len == 0 || len != sizeof(kp)) | |
1883 | return false; | |
1884 | ||
1885 | return strcmp(kp.ki_comm, name) == 0; | |
1886 | } | |
487cc3be VM |
1887 | #elif defined(HAVE_KVM_H) |
1888 | static bool | |
1889 | pid_is_cmd(pid_t pid, const char *name) | |
1890 | { | |
1891 | kvm_t *kd; | |
1892 | struct kinfo_proc *kp; | |
1893 | char *process_name; | |
1894 | bool res = false; | |
1895 | ||
1896 | kd = ssd_kvm_open(); | |
1897 | kp = ssd_kvm_get_procs(kd, KERN_PROC_PID, pid, NULL); | |
1898 | if (kp == NULL) | |
1899 | goto cleanup; | |
1900 | ||
61a41689 | 1901 | #if defined(OS_FreeBSD) |
487cc3be | 1902 | process_name = kp->ki_comm; |
61a41689 | 1903 | #elif defined(OS_OpenBSD) |
487cc3be | 1904 | process_name = kp->p_comm; |
61a41689 | 1905 | #elif defined(OS_DragonFlyBSD) |
487cc3be VM |
1906 | process_name = kp->kp_comm; |
1907 | #else | |
1908 | process_name = kp->kp_proc.p_comm; | |
1909 | #endif | |
1910 | ||
1911 | res = (strcmp(name, process_name) == 0); | |
1912 | ||
1913 | cleanup: | |
1914 | kvm_close(kd); | |
1915 | ||
1916 | return res; | |
1917 | } | |
1918 | #endif | |
1919 | ||
61a41689 | 1920 | #if defined(OS_Hurd) |
487cc3be VM |
1921 | static bool |
1922 | pid_is_running(pid_t pid) | |
1923 | { | |
1924 | return get_proc_stat(pid, 0) != NULL; | |
1925 | } | |
61a41689 | 1926 | #else /* !OS_Hurd */ |
487cc3be VM |
1927 | static bool |
1928 | pid_is_running(pid_t pid) | |
1929 | { | |
1930 | if (kill(pid, 0) == 0 || errno == EPERM) | |
1931 | return true; | |
1932 | else if (errno == ESRCH) | |
1933 | return false; | |
1934 | else | |
1935 | fatal("error checking pid %u status", pid); | |
1936 | } | |
1937 | #endif | |
1938 | ||
1939 | static enum status_code | |
1940 | pid_check(pid_t pid) | |
1941 | { | |
1942 | if (execname && !pid_is_exec(pid, &exec_stat)) | |
1943 | return STATUS_DEAD; | |
1944 | if (match_ppid > 0 && !pid_is_child(pid, match_ppid)) | |
1945 | return STATUS_DEAD; | |
1946 | if (userspec && !pid_is_user(pid, user_id)) | |
1947 | return STATUS_DEAD; | |
1948 | if (cmdname && !pid_is_cmd(pid, cmdname)) | |
1949 | return STATUS_DEAD; | |
1950 | if (action != ACTION_STOP && !pid_is_running(pid)) | |
1951 | return STATUS_DEAD; | |
1952 | ||
1953 | pid_list_push(&found, pid); | |
1954 | ||
1955 | return STATUS_OK; | |
1956 | } | |
1957 | ||
1958 | static enum status_code | |
1959 | do_pidfile(const char *name) | |
1960 | { | |
1961 | FILE *f; | |
1962 | static pid_t pid = 0; | |
1963 | ||
1964 | if (pid) | |
1965 | return pid_check(pid); | |
1966 | ||
1967 | f = fopen(name, "r"); | |
1968 | if (f) { | |
1969 | enum status_code pid_status; | |
1970 | ||
1971 | if (fscanf(f, "%d", &pid) == 1) | |
1972 | pid_status = pid_check(pid); | |
1973 | else | |
1974 | pid_status = STATUS_UNKNOWN; | |
1975 | fclose(f); | |
1976 | ||
1977 | if (pid_status == STATUS_DEAD) | |
1978 | return STATUS_DEAD_PIDFILE; | |
1979 | else | |
1980 | return pid_status; | |
1981 | } else if (errno == ENOENT) | |
1982 | return STATUS_DEAD; | |
1983 | else | |
1984 | fatal("unable to open pidfile %s", name); | |
1985 | } | |
1986 | ||
61a41689 | 1987 | #if defined(OS_Linux) || defined(OS_Solaris) || defined(OS_AIX) |
487cc3be VM |
1988 | static enum status_code |
1989 | do_procinit(void) | |
1990 | { | |
1991 | DIR *procdir; | |
1992 | struct dirent *entry; | |
1993 | int foundany; | |
1994 | pid_t pid; | |
1995 | enum status_code prog_status = STATUS_DEAD; | |
1996 | ||
1997 | procdir = opendir("/proc"); | |
1998 | if (!procdir) | |
1999 | fatal("unable to opendir /proc"); | |
2000 | ||
2001 | foundany = 0; | |
2002 | while ((entry = readdir(procdir)) != NULL) { | |
2003 | enum status_code pid_status; | |
2004 | ||
2005 | if (sscanf(entry->d_name, "%d", &pid) != 1) | |
2006 | continue; | |
2007 | foundany++; | |
2008 | ||
2009 | pid_status = pid_check(pid); | |
2010 | if (pid_status < prog_status) | |
2011 | prog_status = pid_status; | |
2012 | } | |
2013 | closedir(procdir); | |
2014 | if (!foundany) | |
2015 | fatal("nothing in /proc - not mounted?"); | |
2016 | ||
2017 | return prog_status; | |
2018 | } | |
61a41689 | 2019 | #elif defined(OS_Hurd) |
487cc3be VM |
2020 | static int |
2021 | check_proc_stat(struct proc_stat *ps) | |
2022 | { | |
61a41689 | 2023 | pid_check(proc_stat_pid(ps)); |
487cc3be VM |
2024 | return 0; |
2025 | } | |
2026 | ||
2027 | static enum status_code | |
2028 | do_procinit(void) | |
2029 | { | |
2030 | if (!procset) | |
2031 | init_procset(); | |
2032 | ||
2033 | proc_stat_list_for_each(procset, check_proc_stat); | |
2034 | ||
2035 | if (found) | |
2036 | return STATUS_OK; | |
2037 | else | |
2038 | return STATUS_DEAD; | |
2039 | } | |
61a41689 VM |
2040 | #elif defined(OS_Darwin) |
2041 | static enum status_code | |
2042 | do_procinit(void) | |
2043 | { | |
2044 | pid_t *pid_buf; | |
2045 | int i, npids, pid_bufsize; | |
2046 | enum status_code prog_status = STATUS_DEAD; | |
2047 | ||
2048 | npids = proc_listallpids(NULL, 0); | |
2049 | if (npids == 0) | |
2050 | return STATUS_UNKNOWN; | |
2051 | ||
2052 | /* Try to avoid sudden changes in number of PIDs. */ | |
2053 | npids += 4096; | |
2054 | pid_bufsize = sizeof(pid_t) * npids; | |
2055 | pid_buf = xmalloc(pid_bufsize); | |
2056 | ||
2057 | npids = proc_listallpids(pid_buf, pid_bufsize); | |
2058 | if (npids == 0) | |
2059 | return STATUS_UNKNOWN; | |
2060 | ||
2061 | for (i = 0; i < npids; i++) { | |
2062 | enum status_code pid_status; | |
2063 | ||
2064 | pid_status = pid_check(pid_buf[i]); | |
2065 | if (pid_status < prog_status) | |
2066 | prog_status = pid_status; | |
2067 | } | |
2068 | ||
2069 | free(pid_buf); | |
2070 | ||
2071 | return prog_status; | |
2072 | } | |
2073 | #elif defined(OS_HPUX) | |
487cc3be VM |
2074 | static enum status_code |
2075 | do_procinit(void) | |
2076 | { | |
2077 | struct pst_status pst[10]; | |
2078 | int i, count; | |
2079 | int idx = 0; | |
2080 | enum status_code prog_status = STATUS_DEAD; | |
2081 | ||
2082 | while ((count = pstat_getproc(pst, sizeof(pst[0]), 10, idx)) > 0) { | |
2083 | enum status_code pid_status; | |
2084 | ||
2085 | for (i = 0; i < count; i++) { | |
2086 | pid_status = pid_check(pst[i].pst_pid); | |
2087 | if (pid_status < prog_status) | |
2088 | prog_status = pid_status; | |
2089 | } | |
2090 | idx = pst[count - 1].pst_idx + 1; | |
2091 | } | |
2092 | ||
2093 | return prog_status; | |
2094 | } | |
61a41689 VM |
2095 | #elif defined(OS_FreeBSD) |
2096 | static enum status_code | |
2097 | do_procinit(void) | |
2098 | { | |
2099 | struct kinfo_proc *kp; | |
2100 | int rc, mib[3]; | |
2101 | size_t len = 0; | |
2102 | int nentries, i; | |
2103 | enum status_code prog_status = STATUS_DEAD; | |
2104 | ||
2105 | mib[0] = CTL_KERN; | |
2106 | mib[1] = KERN_PROC; | |
2107 | mib[2] = KERN_PROC_PROC; | |
2108 | ||
2109 | rc = sysctl(mib, 3, NULL, &len, NULL, 0); | |
2110 | if (rc != 0 && errno != ESRCH) | |
2111 | return STATUS_UNKNOWN; | |
2112 | if (len == 0) | |
2113 | return STATUS_UNKNOWN; | |
2114 | ||
2115 | kp = xmalloc(len); | |
2116 | rc = sysctl(mib, 3, kp, &len, NULL, 0); | |
2117 | if (rc != 0 && errno != ESRCH) | |
2118 | return STATUS_UNKNOWN; | |
2119 | if (len == 0) | |
2120 | return STATUS_UNKNOWN; | |
2121 | nentries = len / sizeof(*kp); | |
2122 | ||
2123 | for (i = 0; i < nentries; i++) { | |
2124 | enum status_code pid_status; | |
2125 | ||
2126 | pid_status = pid_check(kp[i].ki_pid); | |
2127 | if (pid_status < prog_status) | |
2128 | prog_status = pid_status; | |
2129 | } | |
2130 | ||
2131 | free(kp); | |
2132 | ||
2133 | return prog_status; | |
2134 | } | |
487cc3be VM |
2135 | #elif defined(HAVE_KVM_H) |
2136 | static enum status_code | |
2137 | do_procinit(void) | |
2138 | { | |
2139 | kvm_t *kd; | |
2140 | int nentries, i; | |
2141 | struct kinfo_proc *kp; | |
2142 | enum status_code prog_status = STATUS_DEAD; | |
2143 | ||
2144 | kd = ssd_kvm_open(); | |
2145 | kp = ssd_kvm_get_procs(kd, KERN_PROC_ALL, 0, &nentries); | |
2146 | ||
2147 | for (i = 0; i < nentries; i++) { | |
2148 | enum status_code pid_status; | |
2149 | pid_t pid; | |
2150 | ||
61a41689 | 2151 | #if defined(OS_FreeBSD) |
487cc3be | 2152 | pid = kp[i].ki_pid; |
61a41689 | 2153 | #elif defined(OS_OpenBSD) |
487cc3be | 2154 | pid = kp[i].p_pid; |
61a41689 | 2155 | #elif defined(OS_DragonFlyBSD) |
487cc3be VM |
2156 | pid = kp[i].kp_pid; |
2157 | #else | |
2158 | pid = kp[i].kp_proc.p_pid; | |
2159 | #endif | |
2160 | ||
2161 | pid_status = pid_check(pid); | |
2162 | if (pid_status < prog_status) | |
2163 | prog_status = pid_status; | |
2164 | } | |
2165 | ||
2166 | kvm_close(kd); | |
2167 | ||
2168 | return prog_status; | |
2169 | } | |
2170 | #endif | |
2171 | ||
2172 | static enum status_code | |
2173 | do_findprocs(void) | |
2174 | { | |
2175 | pid_list_free(&found); | |
2176 | ||
2177 | if (match_pid > 0) | |
2178 | return pid_check(match_pid); | |
2179 | else if (pidfile) | |
2180 | return do_pidfile(pidfile); | |
2181 | else | |
2182 | return do_procinit(); | |
2183 | } | |
2184 | ||
61a41689 | 2185 | static int |
487cc3be VM |
2186 | do_start(int argc, char **argv) |
2187 | { | |
2188 | int devnull_fd = -1; | |
2189 | gid_t rgid; | |
2190 | uid_t ruid; | |
2191 | ||
2192 | do_findprocs(); | |
2193 | ||
2194 | if (found) { | |
2195 | if (quietmode <= 0) | |
2196 | printf("%s already running.\n", execname ? execname : "process"); | |
61a41689 | 2197 | return exitnodo; |
487cc3be VM |
2198 | } |
2199 | if (testmode && quietmode <= 0) { | |
2200 | printf("Would start %s ", startas); | |
2201 | while (argc-- > 0) | |
2202 | printf("%s ", *argv++); | |
2203 | if (changeuser != NULL) { | |
2204 | printf(" (as user %s[%d]", changeuser, runas_uid); | |
2205 | if (changegroup != NULL) | |
2206 | printf(", and group %s[%d])", changegroup, runas_gid); | |
2207 | else | |
2208 | printf(")"); | |
2209 | } | |
2210 | if (changeroot != NULL) | |
2211 | printf(" in directory %s", changeroot); | |
2212 | if (nicelevel) | |
2213 | printf(", and add %i to the priority", nicelevel); | |
2214 | if (proc_sched) | |
2215 | printf(", with scheduling policy %s with priority %i", | |
2216 | proc_sched->policy_name, proc_sched->priority); | |
2217 | if (io_sched) | |
2218 | printf(", with IO scheduling class %s with priority %i", | |
2219 | io_sched->policy_name, io_sched->priority); | |
2220 | printf(".\n"); | |
2221 | } | |
2222 | if (testmode) | |
61a41689 | 2223 | return 0; |
487cc3be VM |
2224 | if (quietmode < 0) |
2225 | printf("Starting %s...\n", startas); | |
2226 | *--argv = startas; | |
2227 | if (background) | |
2228 | /* Ok, we need to detach this process. */ | |
2229 | daemonize(); | |
2230 | else if (mpidfile && pidfile != NULL) | |
2231 | /* User wants _us_ to make the pidfile, but detach themself! */ | |
2232 | write_pidfile(pidfile, getpid()); | |
2233 | if (background && close_io) { | |
2234 | devnull_fd = open("/dev/null", O_RDWR); | |
2235 | if (devnull_fd < 0) | |
2236 | fatal("unable to open '%s'", "/dev/null"); | |
2237 | } | |
2238 | if (nicelevel) { | |
2239 | errno = 0; | |
2240 | if ((nice(nicelevel) == -1) && (errno != 0)) | |
2241 | fatal("unable to alter nice level by %i", nicelevel); | |
2242 | } | |
2243 | if (proc_sched) | |
2244 | set_proc_schedule(proc_sched); | |
2245 | if (io_sched) | |
2246 | set_io_schedule(io_sched); | |
2247 | if (umask_value >= 0) | |
2248 | umask(umask_value); | |
2249 | if (changeroot != NULL) { | |
2250 | if (chdir(changeroot) < 0) | |
2251 | fatal("unable to chdir() to %s", changeroot); | |
2252 | if (chroot(changeroot) < 0) | |
2253 | fatal("unable to chroot() to %s", changeroot); | |
2254 | } | |
2255 | if (chdir(changedir) < 0) | |
2256 | fatal("unable to chdir() to %s", changedir); | |
2257 | ||
2258 | rgid = getgid(); | |
2259 | ruid = getuid(); | |
2260 | if (changegroup != NULL) { | |
2261 | if (rgid != (gid_t)runas_gid) | |
2262 | if (setgid(runas_gid)) | |
2263 | fatal("unable to set gid to %d", runas_gid); | |
2264 | } | |
2265 | if (changeuser != NULL) { | |
2266 | /* We assume that if our real user and group are the same as | |
2267 | * the ones we should switch to, the supplementary groups | |
2268 | * will be already in place. */ | |
2269 | if (rgid != (gid_t)runas_gid || ruid != (uid_t)runas_uid) | |
2270 | if (initgroups(changeuser, runas_gid)) | |
2271 | fatal("unable to set initgroups() with gid %d", | |
2272 | runas_gid); | |
2273 | ||
2274 | if (ruid != (uid_t)runas_uid) | |
2275 | if (setuid(runas_uid)) | |
2276 | fatal("unable to set uid to %s", changeuser); | |
2277 | } | |
2278 | ||
2279 | if (background && close_io) { | |
2280 | int i; | |
2281 | ||
2282 | dup2(devnull_fd, 0); /* stdin */ | |
2283 | dup2(devnull_fd, 1); /* stdout */ | |
2284 | dup2(devnull_fd, 2); /* stderr */ | |
2285 | ||
2286 | /* Now close all extra fds. */ | |
2287 | for (i = get_open_fd_max() - 1; i >= 3; --i) | |
2288 | close(i); | |
2289 | } | |
2290 | execv(startas, argv); | |
2291 | fatal("unable to start %s", startas); | |
2292 | } | |
2293 | ||
2294 | static void | |
2295 | do_stop(int sig_num, int *n_killed, int *n_notkilled) | |
2296 | { | |
2297 | struct pid_list *p; | |
2298 | ||
2299 | do_findprocs(); | |
2300 | ||
2301 | *n_killed = 0; | |
2302 | *n_notkilled = 0; | |
2303 | ||
2304 | if (!found) | |
2305 | return; | |
2306 | ||
2307 | pid_list_free(&killed); | |
2308 | ||
2309 | for (p = found; p; p = p->next) { | |
2310 | if (testmode) { | |
2311 | if (quietmode <= 0) | |
2312 | printf("Would send signal %d to %d.\n", | |
2313 | sig_num, p->pid); | |
2314 | (*n_killed)++; | |
2315 | } else if (kill(p->pid, sig_num) == 0) { | |
2316 | pid_list_push(&killed, p->pid); | |
2317 | (*n_killed)++; | |
2318 | } else { | |
2319 | if (sig_num) | |
2320 | warning("failed to kill %d: %s\n", | |
2321 | p->pid, strerror(errno)); | |
2322 | (*n_notkilled)++; | |
2323 | } | |
2324 | } | |
2325 | } | |
2326 | ||
2327 | static void | |
2328 | do_stop_summary(int retry_nr) | |
2329 | { | |
2330 | struct pid_list *p; | |
2331 | ||
2332 | if (quietmode >= 0 || !killed) | |
2333 | return; | |
2334 | ||
2335 | printf("Stopped %s (pid", what_stop); | |
2336 | for (p = killed; p; p = p->next) | |
2337 | printf(" %d", p->pid); | |
2338 | putchar(')'); | |
2339 | if (retry_nr > 0) | |
2340 | printf(", retry #%d", retry_nr); | |
2341 | printf(".\n"); | |
2342 | } | |
2343 | ||
61a41689 VM |
2344 | static void DPKG_ATTR_PRINTF(1) |
2345 | set_what_stop(const char *format, ...) | |
487cc3be | 2346 | { |
61a41689 VM |
2347 | va_list arglist; |
2348 | int rc; | |
2349 | ||
2350 | va_start(arglist, format); | |
2351 | rc = vasprintf(&what_stop, format, arglist); | |
2352 | va_end(arglist); | |
2353 | ||
2354 | if (rc < 0) | |
2355 | fatal("cannot allocate formatted string"); | |
487cc3be VM |
2356 | } |
2357 | ||
2358 | /* | |
2359 | * We want to keep polling for the processes, to see if they've exited, or | |
2360 | * until the timeout expires. | |
2361 | * | |
2362 | * This is a somewhat complicated algorithm to try to ensure that we notice | |
2363 | * reasonably quickly when all the processes have exited, but don't spend | |
2364 | * too much CPU time polling. In particular, on a fast machine with | |
2365 | * quick-exiting daemons we don't want to delay system shutdown too much, | |
2366 | * whereas on a slow one, or where processes are taking some time to exit, | |
2367 | * we want to increase the polling interval. | |
2368 | * | |
2369 | * The algorithm is as follows: we measure the elapsed time it takes to do | |
2370 | * one poll(), and wait a multiple of this time for the next poll. However, | |
2371 | * if that would put us past the end of the timeout period we wait only as | |
2372 | * long as the timeout period, but in any case we always wait at least | |
2373 | * MIN_POLL_INTERVAL (20ms). The multiple (‘ratio’) starts out as 2, and | |
2374 | * increases by 1 for each poll to a maximum of 10; so we use up to between | |
2375 | * 30% and 10% of the machine's resources (assuming a few reasonable things | |
2376 | * about system performance). | |
2377 | */ | |
2378 | static bool | |
2379 | do_stop_timeout(int timeout, int *n_killed, int *n_notkilled) | |
2380 | { | |
2381 | struct timespec stopat, before, after, interval, maxinterval; | |
2382 | int rc, ratio; | |
2383 | ||
2384 | timespec_gettime(&stopat); | |
2385 | stopat.tv_sec += timeout; | |
2386 | ratio = 1; | |
2387 | for (;;) { | |
2388 | timespec_gettime(&before); | |
2389 | if (timespec_cmp(&before, &stopat, >)) | |
2390 | return false; | |
2391 | ||
2392 | do_stop(0, n_killed, n_notkilled); | |
2393 | if (!*n_killed) | |
2394 | return true; | |
2395 | ||
2396 | timespec_gettime(&after); | |
2397 | ||
2398 | if (!timespec_cmp(&after, &stopat, <)) | |
2399 | return false; | |
2400 | ||
2401 | if (ratio < 10) | |
2402 | ratio++; | |
2403 | ||
2404 | timespec_sub(&stopat, &after, &maxinterval); | |
2405 | timespec_sub(&after, &before, &interval); | |
2406 | timespec_mul(&interval, ratio); | |
2407 | ||
2408 | if (interval.tv_sec < 0 || interval.tv_nsec < 0) | |
2409 | interval.tv_sec = interval.tv_nsec = 0; | |
2410 | ||
2411 | if (timespec_cmp(&interval, &maxinterval, >)) | |
2412 | interval = maxinterval; | |
2413 | ||
2414 | if (interval.tv_sec == 0 && | |
2415 | interval.tv_nsec <= MIN_POLL_INTERVAL) | |
2416 | interval.tv_nsec = MIN_POLL_INTERVAL; | |
2417 | ||
2418 | rc = pselect(0, NULL, NULL, NULL, &interval, NULL); | |
2419 | if (rc < 0 && errno != EINTR) | |
2420 | fatal("select() failed for pause"); | |
2421 | } | |
2422 | } | |
2423 | ||
2424 | static int | |
2425 | finish_stop_schedule(bool anykilled) | |
2426 | { | |
2427 | if (rpidfile && pidfile && !testmode) | |
2428 | remove_pidfile(pidfile); | |
2429 | ||
2430 | if (anykilled) | |
2431 | return 0; | |
2432 | ||
2433 | if (quietmode <= 0) | |
2434 | printf("No %s found running; none killed.\n", what_stop); | |
2435 | ||
2436 | return exitnodo; | |
2437 | } | |
2438 | ||
2439 | static int | |
2440 | run_stop_schedule(void) | |
2441 | { | |
2442 | int position, n_killed, n_notkilled, value, retry_nr; | |
2443 | bool anykilled; | |
2444 | ||
2445 | if (testmode) { | |
2446 | if (schedule != NULL) { | |
2447 | if (quietmode <= 0) | |
2448 | printf("Ignoring --retry in test mode\n"); | |
2449 | schedule = NULL; | |
2450 | } | |
2451 | } | |
2452 | ||
2453 | if (cmdname) | |
61a41689 | 2454 | set_what_stop("%s", cmdname); |
487cc3be | 2455 | else if (execname) |
61a41689 | 2456 | set_what_stop("%s", execname); |
487cc3be | 2457 | else if (pidfile) |
61a41689 | 2458 | set_what_stop("process in pidfile '%s'", pidfile); |
487cc3be | 2459 | else if (match_pid > 0) |
61a41689 | 2460 | set_what_stop("process with pid %d", match_pid); |
487cc3be | 2461 | else if (match_ppid > 0) |
61a41689 | 2462 | set_what_stop("process(es) with parent pid %d", match_ppid); |
487cc3be | 2463 | else if (userspec) |
61a41689 | 2464 | set_what_stop("process(es) owned by '%s'", userspec); |
487cc3be VM |
2465 | else |
2466 | fatal("internal error, no match option, please report"); | |
2467 | ||
2468 | anykilled = false; | |
2469 | retry_nr = 0; | |
2470 | ||
2471 | if (schedule == NULL) { | |
2472 | do_stop(signal_nr, &n_killed, &n_notkilled); | |
2473 | do_stop_summary(0); | |
2474 | if (n_notkilled > 0 && quietmode <= 0) | |
2475 | printf("%d pids were not killed\n", n_notkilled); | |
2476 | if (n_killed) | |
2477 | anykilled = true; | |
2478 | return finish_stop_schedule(anykilled); | |
2479 | } | |
2480 | ||
2481 | for (position = 0; position < schedule_length; position++) { | |
2482 | reposition: | |
2483 | value = schedule[position].value; | |
2484 | n_notkilled = 0; | |
2485 | ||
2486 | switch (schedule[position].type) { | |
2487 | case sched_goto: | |
2488 | position = value; | |
2489 | goto reposition; | |
2490 | case sched_signal: | |
2491 | do_stop(value, &n_killed, &n_notkilled); | |
2492 | do_stop_summary(retry_nr++); | |
2493 | if (!n_killed) | |
2494 | return finish_stop_schedule(anykilled); | |
2495 | else | |
2496 | anykilled = true; | |
2497 | continue; | |
2498 | case sched_timeout: | |
2499 | if (do_stop_timeout(value, &n_killed, &n_notkilled)) | |
2500 | return finish_stop_schedule(anykilled); | |
2501 | else | |
2502 | continue; | |
2503 | default: | |
2504 | assert(!"schedule[].type value must be valid"); | |
2505 | } | |
2506 | } | |
2507 | ||
2508 | if (quietmode <= 0) | |
2509 | printf("Program %s, %d process(es), refused to die.\n", | |
2510 | what_stop, n_killed); | |
2511 | ||
2512 | return 2; | |
2513 | } | |
2514 | ||
2515 | int | |
2516 | main(int argc, char **argv) | |
2517 | { | |
2518 | progname = argv[0]; | |
2519 | ||
2520 | parse_options(argc, argv); | |
2521 | setup_options(); | |
2522 | ||
2523 | argc -= optind; | |
2524 | argv += optind; | |
2525 | ||
2526 | if (action == ACTION_START) | |
61a41689 VM |
2527 | return do_start(argc, argv); |
2528 | else if (action == ACTION_STOP) | |
2529 | return run_stop_schedule(); | |
2530 | else if (action == ACTION_STATUS) | |
2531 | return do_findprocs(); | |
487cc3be VM |
2532 | |
2533 | return 0; | |
2534 | } |