Contents of /trunk/initscripts/sysvinit/src/start-stop-daemon.c
Parent Directory | Revision Log
Revision 111 -
(show annotations)
(download)
Sat Jul 2 15:46:28 2005 UTC (19 years, 2 months ago) by niro
Original Path: trunk/magellan-initscripts/src/start-stop-daemon.c
File MIME type: text/plain
File size: 32243 byte(s)
Sat Jul 2 15:46:28 2005 UTC (19 years, 2 months ago) by niro
Original Path: trunk/magellan-initscripts/src/start-stop-daemon.c
File MIME type: text/plain
File size: 32243 byte(s)
new
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 aswell. |
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 | * Modified for Gentoo rc-scripts by Donny Davies <woodchip@gentoo.org>: |
23 | * I removed the BSD/Hurd/OtherOS stuff, added #include <stddef.h> |
24 | * and stuck in a #define VERSION "1.9.18". Now it compiles without |
25 | * the whole automake/config.h dance. |
26 | * |
27 | * Updated by Aron Griffis <agriffis@gentoo.org>: |
28 | * Fetched updates from Debian's dpkg-1.10.20, including fix for |
29 | * Gentoo bug 22686 (start-stop-daemon in baselayout doesn't allow |
30 | * altered nicelevel). |
31 | */ |
32 | |
33 | #define VERSION "1.10.20" |
34 | #include <stddef.h> |
35 | |
36 | #define NONRETURNPRINTFFORMAT(x, y) \ |
37 | __attribute__((noreturn, format(printf, x, y))) |
38 | #define NONRETURNING \ |
39 | __attribute__((noreturn)) |
40 | |
41 | #if defined(linux) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) |
42 | # define OSLinux |
43 | #elif defined(__GNU__) |
44 | # define OSHURD |
45 | #elif defined(__sparc__) |
46 | # define OSsunos |
47 | #elif defined(OPENBSD) || defined(__OpenBSD__) |
48 | # define OSOpenBSD |
49 | #elif defined(hpux) |
50 | # define OShpux |
51 | #elif defined(__FreeBSD__) |
52 | # define OSFreeBSD |
53 | #elif defined(__NetBSD__) |
54 | # define OSNetBSD |
55 | #else |
56 | # error Unknown architecture - cannot build start-stop-daemon |
57 | #endif |
58 | |
59 | #define MIN_POLL_INTERVAL 20000 /*us*/ |
60 | |
61 | #if defined(OSHURD) |
62 | # include <hurd.h> |
63 | # include <ps.h> |
64 | #endif |
65 | |
66 | #if defined(OSOpenBSD) || defined(OSFreeBSD) || defined(OSNetBSD) |
67 | #include <sys/param.h> |
68 | #include <sys/user.h> |
69 | #include <sys/proc.h> |
70 | #include <sys/stat.h> |
71 | #include <sys/sysctl.h> |
72 | #include <sys/types.h> |
73 | |
74 | #include <err.h> |
75 | #include <kvm.h> |
76 | #include <limits.h> |
77 | #endif |
78 | |
79 | #if defined(OShpux) |
80 | #include <sys/param.h> |
81 | #include <sys/pstat.h> |
82 | #endif |
83 | |
84 | #include <errno.h> |
85 | #include <stdio.h> |
86 | #include <stdlib.h> |
87 | #include <string.h> |
88 | #include <stdarg.h> |
89 | #include <signal.h> |
90 | #include <sys/stat.h> |
91 | #include <dirent.h> |
92 | #include <sys/time.h> |
93 | #include <unistd.h> |
94 | #include <getopt.h> |
95 | #include <pwd.h> |
96 | #include <grp.h> |
97 | #include <sys/ioctl.h> |
98 | #include <sys/types.h> |
99 | #include <sys/termios.h> |
100 | #include <fcntl.h> |
101 | #include <limits.h> |
102 | #include <assert.h> |
103 | #include <ctype.h> |
104 | |
105 | #include "headers.h" |
106 | |
107 | #ifdef HURD_IHASH_H |
108 | # include <hurd/ihash.h> |
109 | #endif |
110 | |
111 | static int testmode = 0; |
112 | static int quietmode = 0; |
113 | static int exitnodo = 1; |
114 | static int start = 0; |
115 | static int stop = 0; |
116 | static int background = 0; |
117 | static int mpidfile = 0; |
118 | static int signal_nr = 15; |
119 | static const char *signal_str = NULL; |
120 | static int user_id = -1; |
121 | static int runas_uid = -1; |
122 | static int runas_gid = -1; |
123 | static const char *userspec = NULL; |
124 | static char *changeuser = NULL; |
125 | static const char *changegroup = NULL; |
126 | static char *changeroot = NULL; |
127 | static const char *changedir = NULL; |
128 | static const char *cmdname = NULL; |
129 | static char *execname = NULL; |
130 | static char *startas = NULL; |
131 | static const char *pidfile = NULL; |
132 | static char what_stop[1024]; |
133 | static const char *schedule_str = NULL; |
134 | static const char *progname = ""; |
135 | static int nicelevel = 0; |
136 | |
137 | static struct stat exec_stat; |
138 | #if defined(OSHURD) |
139 | static struct proc_stat_list *procset; |
140 | #endif |
141 | |
142 | |
143 | struct pid_list { |
144 | struct pid_list *next; |
145 | pid_t pid; |
146 | }; |
147 | |
148 | static struct pid_list *found = NULL; |
149 | static struct pid_list *killed = NULL; |
150 | |
151 | struct schedule_item { |
152 | enum { sched_timeout, sched_signal, sched_goto, sched_forever } type; |
153 | int value; /* seconds, signal no., or index into array */ |
154 | /* sched_forever is only seen within parse_schedule and callees */ |
155 | }; |
156 | |
157 | static int schedule_length; |
158 | static struct schedule_item *schedule = NULL; |
159 | |
160 | static void *xmalloc(int size); |
161 | static void push(struct pid_list **list, pid_t pid); |
162 | static void do_help(void); |
163 | static void parse_options(int argc, char * const *argv); |
164 | static int pid_is_user(pid_t pid, uid_t uid); |
165 | static int pid_is_cmd(pid_t pid, const char *name); |
166 | static void check(pid_t pid); |
167 | static void do_pidfile(const char *name); |
168 | static void do_stop(int signal_nr, int quietmode, |
169 | int *n_killed, int *n_notkilled, int retry_nr); |
170 | #if defined(OSLinux) || defined(OShpux) |
171 | static int pid_is_exec(pid_t pid, const struct stat *esb); |
172 | #endif |
173 | |
174 | #ifdef __GNUC__ |
175 | static void fatal(const char *format, ...) |
176 | NONRETURNPRINTFFORMAT(1, 2); |
177 | static void badusage(const char *msg) |
178 | NONRETURNING; |
179 | #else |
180 | static void fatal(const char *format, ...); |
181 | static void badusage(const char *msg); |
182 | #endif |
183 | |
184 | /* This next part serves only to construct the TVCALC macro, which |
185 | * is used for doing arithmetic on struct timeval's. It works like this: |
186 | * TVCALC(result, expression); |
187 | * where result is a struct timeval (and must be an lvalue) and |
188 | * expression is the single expression for both components. In this |
189 | * expression you can use the special values TVELEM, which when fed a |
190 | * const struct timeval* gives you the relevant component, and |
191 | * TVADJUST. TVADJUST is necessary when subtracting timevals, to make |
192 | * it easier to renormalise. Whenver you subtract timeval elements, |
193 | * you must make sure that TVADJUST is added to the result of the |
194 | * subtraction (before any resulting multiplication or what have you). |
195 | * TVELEM must be linear in TVADJUST. |
196 | */ |
197 | typedef long tvselector(const struct timeval*); |
198 | static long tvselector_sec(const struct timeval *tv) { return tv->tv_sec; } |
199 | static long tvselector_usec(const struct timeval *tv) { return tv->tv_usec; } |
200 | #define TVCALC_ELEM(result, expr, sec, adj) \ |
201 | { \ |
202 | const long TVADJUST = adj; \ |
203 | long (*const TVELEM)(const struct timeval*) = tvselector_##sec; \ |
204 | (result).tv_##sec = (expr); \ |
205 | } |
206 | #define TVCALC(result,expr) \ |
207 | do { \ |
208 | TVCALC_ELEM(result, expr, sec, (-1)); \ |
209 | TVCALC_ELEM(result, expr, usec, (+1000000)); \ |
210 | (result).tv_sec += (result).tv_usec / 1000000; \ |
211 | (result).tv_usec %= 1000000; \ |
212 | } while(0) |
213 | |
214 | |
215 | static void |
216 | fatal(const char *format, ...) |
217 | { |
218 | va_list arglist; |
219 | |
220 | fprintf(stderr, "%s: ", progname); |
221 | va_start(arglist, format); |
222 | vfprintf(stderr, format, arglist); |
223 | va_end(arglist); |
224 | putc('\n', stderr); |
225 | exit(2); |
226 | } |
227 | |
228 | |
229 | static void * |
230 | xmalloc(int size) |
231 | { |
232 | void *ptr; |
233 | |
234 | ptr = malloc(size); |
235 | if (ptr) |
236 | return ptr; |
237 | fatal("malloc(%d) failed", size); |
238 | } |
239 | |
240 | |
241 | static void |
242 | xgettimeofday(struct timeval *tv) |
243 | { |
244 | if (gettimeofday(tv,0) != 0) |
245 | fatal("gettimeofday failed: %s", strerror(errno)); |
246 | } |
247 | |
248 | |
249 | static void |
250 | push(struct pid_list **list, pid_t pid) |
251 | { |
252 | struct pid_list *p; |
253 | |
254 | p = xmalloc(sizeof(*p)); |
255 | p->next = *list; |
256 | p->pid = pid; |
257 | *list = p; |
258 | } |
259 | |
260 | static void |
261 | clear(struct pid_list **list) |
262 | { |
263 | struct pid_list *here, *next; |
264 | |
265 | for (here = *list; here != NULL; here = next) { |
266 | next = here->next; |
267 | free(here); |
268 | } |
269 | |
270 | *list = NULL; |
271 | } |
272 | |
273 | static void |
274 | do_help(void) |
275 | { |
276 | printf( |
277 | "start-stop-daemon " VERSION " for Debian - small and fast C version written by\n" |
278 | "Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>, public domain.\n" |
279 | "\n" |
280 | "Usage:\n" |
281 | " start-stop-daemon -S|--start options ... -- arguments ...\n" |
282 | " start-stop-daemon -K|--stop options ...\n" |
283 | " start-stop-daemon -H|--help\n" |
284 | " start-stop-daemon -V|--version\n" |
285 | "\n" |
286 | "Options (at least one of --exec|--pidfile|--user is required):\n" |
287 | " -x|--exec <executable> program to start/check if it is running\n" |
288 | " -p|--pidfile <pid-file> pid file to check\n" |
289 | " -c|--chuid <name|uid[:group|gid]>\n" |
290 | " change to this user/group before starting process\n" |
291 | " -u|--user <username>|<uid> stop processes owned by this user\n" |
292 | " -g|--group <group|gid> run process as this group\n" |
293 | " -n|--name <process-name> stop processes with this name\n" |
294 | " -s|--signal <signal> signal to send (default TERM)\n" |
295 | " -a|--startas <pathname> program to start (default is <executable>)\n" |
296 | " -C|--chdir <directory> Change to <directory>(default is /)\n" |
297 | " -N|--nicelevel <incr> add incr to the process's nice level\n" |
298 | " -b|--background force the process to detach\n" |
299 | " -m|--make-pidfile create the pidfile before starting\n" |
300 | " -R|--retry <schedule> check whether processes die, and retry\n" |
301 | " -t|--test test mode, don't do anything\n" |
302 | " -o|--oknodo exit status 0 (not 1) if nothing done\n" |
303 | " -q|--quiet be more quiet\n" |
304 | " -v|--verbose be more verbose\n" |
305 | "Retry <schedule> is <item>|/<item>/... where <item> is one of\n" |
306 | " -<signal-num>|[-]<signal-name> send that signal\n" |
307 | " <timeout> wait that many seconds\n" |
308 | " forever repeat remainder forever\n" |
309 | "or <schedule> may be just <timeout>, meaning <signal>/<timeout>/KILL/<timeout>\n" |
310 | "\n" |
311 | "Exit status: 0 = done 1 = nothing done (=> 0 if --oknodo)\n" |
312 | " 3 = trouble 2 = with --retry, processes wouldn't die\n"); |
313 | } |
314 | |
315 | |
316 | static void |
317 | badusage(const char *msg) |
318 | { |
319 | if (msg) |
320 | fprintf(stderr, "%s: %s\n", progname, msg); |
321 | fprintf(stderr, "Try `%s --help' for more information.\n", progname); |
322 | exit(3); |
323 | } |
324 | |
325 | struct sigpair { |
326 | const char *name; |
327 | int signal; |
328 | }; |
329 | |
330 | const struct sigpair siglist[] = { |
331 | { "ABRT", SIGABRT }, |
332 | { "ALRM", SIGALRM }, |
333 | { "FPE", SIGFPE }, |
334 | { "HUP", SIGHUP }, |
335 | { "ILL", SIGILL }, |
336 | { "INT", SIGINT }, |
337 | { "KILL", SIGKILL }, |
338 | { "PIPE", SIGPIPE }, |
339 | { "QUIT", SIGQUIT }, |
340 | { "SEGV", SIGSEGV }, |
341 | { "TERM", SIGTERM }, |
342 | { "USR1", SIGUSR1 }, |
343 | { "USR2", SIGUSR2 }, |
344 | { "CHLD", SIGCHLD }, |
345 | { "CONT", SIGCONT }, |
346 | { "STOP", SIGSTOP }, |
347 | { "TSTP", SIGTSTP }, |
348 | { "TTIN", SIGTTIN }, |
349 | { "TTOU", SIGTTOU } |
350 | }; |
351 | |
352 | static int parse_integer(const char *string, int *value_r) { |
353 | unsigned long ul; |
354 | char *ep; |
355 | |
356 | if (!string[0]) |
357 | return -1; |
358 | |
359 | ul= strtoul(string,&ep,10); |
360 | if (ul > INT_MAX || *ep != '\0') |
361 | return -1; |
362 | |
363 | *value_r= ul; |
364 | return 0; |
365 | } |
366 | |
367 | static int parse_signal(const char *signal_str, int *signal_nr) |
368 | { |
369 | unsigned int i; |
370 | |
371 | if (parse_integer(signal_str, signal_nr) == 0) |
372 | return 0; |
373 | |
374 | for (i = 0; i < sizeof (siglist) / sizeof (siglist[0]); i++) { |
375 | if (strcmp (signal_str, siglist[i].name) == 0) { |
376 | *signal_nr = siglist[i].signal; |
377 | return 0; |
378 | } |
379 | } |
380 | return -1; |
381 | } |
382 | |
383 | static void |
384 | parse_schedule_item(const char *string, struct schedule_item *item) { |
385 | const char *after_hyph; |
386 | |
387 | if (!strcmp(string,"forever")) { |
388 | item->type = sched_forever; |
389 | } else if (isdigit(string[0])) { |
390 | item->type = sched_timeout; |
391 | if (parse_integer(string, &item->value) != 0) |
392 | badusage("invalid timeout value in schedule"); |
393 | } else if ((after_hyph = string + (string[0] == '-')) && |
394 | parse_signal(after_hyph, &item->value) == 0) { |
395 | item->type = sched_signal; |
396 | } else { |
397 | badusage("invalid schedule item (must be [-]<signal-name>, " |
398 | "-<signal-number>, <timeout> or `forever'"); |
399 | } |
400 | } |
401 | |
402 | static void |
403 | parse_schedule(const char *schedule_str) { |
404 | char item_buf[20]; |
405 | const char *slash; |
406 | int count, repeatat; |
407 | ptrdiff_t str_len; |
408 | |
409 | count = 0; |
410 | for (slash = schedule_str; *slash; slash++) |
411 | if (*slash == '/') |
412 | count++; |
413 | |
414 | schedule_length = (count == 0) ? 4 : count+1; |
415 | schedule = xmalloc(sizeof(*schedule) * schedule_length); |
416 | |
417 | if (count == 0) { |
418 | schedule[0].type = sched_signal; |
419 | schedule[0].value = signal_nr; |
420 | parse_schedule_item(schedule_str, &schedule[1]); |
421 | if (schedule[1].type != sched_timeout) { |
422 | badusage ("--retry takes timeout, or schedule list" |
423 | " of at least two items"); |
424 | } |
425 | schedule[2].type = sched_signal; |
426 | schedule[2].value = SIGKILL; |
427 | schedule[3]= schedule[1]; |
428 | } else { |
429 | count = 0; |
430 | repeatat = -1; |
431 | while (schedule_str != NULL) { |
432 | slash = strchr(schedule_str,'/'); |
433 | str_len = slash ? slash - schedule_str : strlen(schedule_str); |
434 | if (str_len >= (ptrdiff_t)sizeof(item_buf)) |
435 | badusage("invalid schedule item: far too long" |
436 | " (you must delimit items with slashes)"); |
437 | memcpy(item_buf, schedule_str, str_len); |
438 | item_buf[str_len] = 0; |
439 | schedule_str = slash ? slash+1 : NULL; |
440 | |
441 | parse_schedule_item(item_buf, &schedule[count]); |
442 | if (schedule[count].type == sched_forever) { |
443 | if (repeatat >= 0) |
444 | badusage("invalid schedule: `forever'" |
445 | " appears more than once"); |
446 | repeatat = count; |
447 | continue; |
448 | } |
449 | count++; |
450 | } |
451 | if (repeatat >= 0) { |
452 | schedule[count].type = sched_goto; |
453 | schedule[count].value = repeatat; |
454 | count++; |
455 | } |
456 | assert(count == schedule_length); |
457 | } |
458 | } |
459 | |
460 | static void |
461 | parse_options(int argc, char * const *argv) |
462 | { |
463 | static struct option longopts[] = { |
464 | { "help", 0, NULL, 'H'}, |
465 | { "stop", 0, NULL, 'K'}, |
466 | { "start", 0, NULL, 'S'}, |
467 | { "version", 0, NULL, 'V'}, |
468 | { "startas", 1, NULL, 'a'}, |
469 | { "name", 1, NULL, 'n'}, |
470 | { "oknodo", 0, NULL, 'o'}, |
471 | { "pidfile", 1, NULL, 'p'}, |
472 | { "quiet", 0, NULL, 'q'}, |
473 | { "signal", 1, NULL, 's'}, |
474 | { "test", 0, NULL, 't'}, |
475 | { "user", 1, NULL, 'u'}, |
476 | { "group", 1, NULL, 'g'}, |
477 | { "chroot", 1, NULL, 'r'}, |
478 | { "verbose", 0, NULL, 'v'}, |
479 | { "exec", 1, NULL, 'x'}, |
480 | { "chuid", 1, NULL, 'c'}, |
481 | { "nicelevel", 1, NULL, 'N'}, |
482 | { "background", 0, NULL, 'b'}, |
483 | { "make-pidfile", 0, NULL, 'm'}, |
484 | { "retry", 1, NULL, 'R'}, |
485 | { "chdir", 1, NULL, 'd'}, |
486 | { NULL, 0, NULL, 0} |
487 | }; |
488 | int c; |
489 | |
490 | for (;;) { |
491 | c = getopt_long(argc, argv, "HKSV:a:n:op:qr:s:tu:vx:c:N:bmR:g:d:", |
492 | longopts, (int *) 0); |
493 | if (c == -1) |
494 | break; |
495 | switch (c) { |
496 | case 'H': /* --help */ |
497 | do_help(); |
498 | exit(0); |
499 | case 'K': /* --stop */ |
500 | stop = 1; |
501 | break; |
502 | case 'S': /* --start */ |
503 | start = 1; |
504 | break; |
505 | case 'V': /* --version */ |
506 | printf("start-stop-daemon " VERSION "\n"); |
507 | exit(0); |
508 | case 'a': /* --startas <pathname> */ |
509 | startas = optarg; |
510 | break; |
511 | case 'n': /* --name <process-name> */ |
512 | cmdname = optarg; |
513 | break; |
514 | case 'o': /* --oknodo */ |
515 | exitnodo = 0; |
516 | break; |
517 | case 'p': /* --pidfile <pid-file> */ |
518 | pidfile = optarg; |
519 | break; |
520 | case 'q': /* --quiet */ |
521 | quietmode = 1; |
522 | break; |
523 | case 's': /* --signal <signal> */ |
524 | signal_str = optarg; |
525 | break; |
526 | case 't': /* --test */ |
527 | testmode = 1; |
528 | break; |
529 | case 'u': /* --user <username>|<uid> */ |
530 | userspec = optarg; |
531 | break; |
532 | case 'v': /* --verbose */ |
533 | quietmode = -1; |
534 | break; |
535 | case 'x': /* --exec <executable> */ |
536 | execname = optarg; |
537 | break; |
538 | case 'c': /* --chuid <username>|<uid> */ |
539 | /* we copy the string just in case we need the |
540 | * argument later. */ |
541 | changeuser = strdup(optarg); |
542 | changeuser = strtok(changeuser, ":"); |
543 | changegroup = strtok(NULL, ":"); |
544 | break; |
545 | case 'g': /* --group <group>|<gid> */ |
546 | changegroup = optarg; |
547 | break; |
548 | case 'r': /* --chroot /new/root */ |
549 | changeroot = optarg; |
550 | break; |
551 | case 'N': /* --nice */ |
552 | nicelevel = atoi(optarg); |
553 | break; |
554 | case 'b': /* --background */ |
555 | background = 1; |
556 | break; |
557 | case 'm': /* --make-pidfile */ |
558 | mpidfile = 1; |
559 | break; |
560 | case 'R': /* --retry <schedule>|<timeout> */ |
561 | schedule_str = optarg; |
562 | break; |
563 | case 'd': /* --chdir /new/dir */ |
564 | changedir = optarg; |
565 | break; |
566 | default: |
567 | badusage(NULL); /* message printed by getopt */ |
568 | } |
569 | } |
570 | |
571 | if (signal_str != NULL) { |
572 | if (parse_signal (signal_str, &signal_nr) != 0) |
573 | badusage("signal value must be numeric or name" |
574 | " of signal (KILL, INT, ...)"); |
575 | } |
576 | |
577 | if (schedule_str != NULL) { |
578 | parse_schedule(schedule_str); |
579 | } |
580 | |
581 | if (start == stop) |
582 | badusage("need one of --start or --stop"); |
583 | |
584 | if (!execname && !pidfile && !userspec && !cmdname) |
585 | badusage("need at least one of --exec, --pidfile, --user or --name"); |
586 | |
587 | if (!startas) |
588 | startas = execname; |
589 | |
590 | if (start && !startas) |
591 | badusage("--start needs --exec or --startas"); |
592 | |
593 | if (mpidfile && pidfile == NULL) |
594 | badusage("--make-pidfile is only relevant with --pidfile"); |
595 | |
596 | if (background && !start) |
597 | badusage("--background is only relevant with --start"); |
598 | |
599 | } |
600 | |
601 | #if defined(OSLinux) |
602 | static int |
603 | pid_is_exec(pid_t pid, const struct stat *esb) |
604 | { |
605 | struct stat sb; |
606 | char buf[32]; |
607 | |
608 | sprintf(buf, "/proc/%d/exe", pid); |
609 | if (stat(buf, &sb) != 0) |
610 | return 0; |
611 | return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino); |
612 | } |
613 | |
614 | |
615 | static int |
616 | pid_is_user(pid_t pid, uid_t uid) |
617 | { |
618 | struct stat sb; |
619 | char buf[32]; |
620 | |
621 | sprintf(buf, "/proc/%d", pid); |
622 | if (stat(buf, &sb) != 0) |
623 | return 0; |
624 | return (sb.st_uid == uid); |
625 | } |
626 | |
627 | |
628 | static int |
629 | pid_is_cmd(pid_t pid, const char *name) |
630 | { |
631 | char buf[32]; |
632 | FILE *f; |
633 | int c; |
634 | |
635 | sprintf(buf, "/proc/%d/stat", pid); |
636 | f = fopen(buf, "r"); |
637 | if (!f) |
638 | return 0; |
639 | while ((c = getc(f)) != EOF && c != '(') |
640 | ; |
641 | if (c != '(') { |
642 | fclose(f); |
643 | return 0; |
644 | } |
645 | /* this hopefully handles command names containing ')' */ |
646 | while ((c = getc(f)) != EOF && c == *name) |
647 | name++; |
648 | fclose(f); |
649 | return (c == ')' && *name == '\0'); |
650 | } |
651 | #endif /* OSLinux */ |
652 | |
653 | |
654 | #if defined(OSHURD) |
655 | static int |
656 | pid_is_user(pid_t pid, uid_t uid) |
657 | { |
658 | struct stat sb; |
659 | char buf[32]; |
660 | struct proc_stat *pstat; |
661 | |
662 | sprintf(buf, "/proc/%d", pid); |
663 | if (stat(buf, &sb) != 0) |
664 | return 0; |
665 | return (sb.st_uid == uid); |
666 | pstat = proc_stat_list_pid_proc_stat (procset, pid); |
667 | if (pstat == NULL) |
668 | fatal ("Error getting process information: NULL proc_stat struct"); |
669 | proc_stat_set_flags (pstat, PSTAT_PID | PSTAT_OWNER_UID); |
670 | return (pstat->owner_uid == uid); |
671 | } |
672 | |
673 | static int |
674 | pid_is_cmd(pid_t pid, const char *name) |
675 | { |
676 | struct proc_stat *pstat; |
677 | pstat = proc_stat_list_pid_proc_stat (procset, pid); |
678 | if (pstat == NULL) |
679 | fatal ("Error getting process information: NULL proc_stat struct"); |
680 | proc_stat_set_flags (pstat, PSTAT_PID | PSTAT_ARGS); |
681 | return (!strcmp (name, pstat->args)); |
682 | } |
683 | #endif /* OSHURD */ |
684 | |
685 | |
686 | static int |
687 | pid_is_running(pid_t pid) |
688 | { |
689 | struct stat sb; |
690 | char buf[32]; |
691 | |
692 | sprintf(buf, "/proc/%d", pid); |
693 | if (stat(buf, &sb) != 0) { |
694 | if (errno!=ENOENT) |
695 | fatal("Error stating %s: %s", buf, strerror(errno)); |
696 | return 0; |
697 | } |
698 | |
699 | return 1; |
700 | } |
701 | |
702 | static void |
703 | check(pid_t pid) |
704 | { |
705 | #if defined(OSLinux) || defined(OShpux) |
706 | if (execname && !pid_is_exec(pid, &exec_stat)) |
707 | #elif defined(OSHURD) || defined(OSFreeBSD) || defined(OSNetBSD) |
708 | /* I will try this to see if it works */ |
709 | if (execname && !pid_is_cmd(pid, execname)) |
710 | #endif |
711 | return; |
712 | if (userspec && !pid_is_user(pid, user_id)) |
713 | return; |
714 | if (cmdname && !pid_is_cmd(pid, cmdname)) |
715 | return; |
716 | if (start && !pid_is_running(pid)) |
717 | return; |
718 | push(&found, pid); |
719 | } |
720 | |
721 | static void |
722 | do_pidfile(const char *name) |
723 | { |
724 | FILE *f; |
725 | pid_t pid; |
726 | |
727 | f = fopen(name, "r"); |
728 | if (f) { |
729 | if (fscanf(f, "%d", &pid) == 1) |
730 | check(pid); |
731 | fclose(f); |
732 | } else if (errno != ENOENT) |
733 | fatal("open pidfile %s: %s", name, strerror(errno)); |
734 | |
735 | } |
736 | |
737 | /* WTA: this needs to be an autoconf check for /proc/pid existance. |
738 | */ |
739 | |
740 | #if defined(OSLinux) || defined (OSsunos) || defined(OSfreebsd) |
741 | static void |
742 | do_procinit(void) |
743 | { |
744 | DIR *procdir; |
745 | struct dirent *entry; |
746 | int foundany; |
747 | pid_t pid; |
748 | |
749 | procdir = opendir("/proc"); |
750 | if (!procdir) |
751 | fatal("opendir /proc: %s", strerror(errno)); |
752 | |
753 | foundany = 0; |
754 | while ((entry = readdir(procdir)) != NULL) { |
755 | if (sscanf(entry->d_name, "%d", &pid) != 1) |
756 | continue; |
757 | foundany++; |
758 | check(pid); |
759 | } |
760 | closedir(procdir); |
761 | if (!foundany) |
762 | fatal("nothing in /proc - not mounted?"); |
763 | } |
764 | #endif /* OSLinux */ |
765 | |
766 | |
767 | #if defined(OSHURD) |
768 | error_t |
769 | check_all(void *ptr) |
770 | { |
771 | struct proc_stat *pstat = ptr; |
772 | |
773 | check(pstat->pid); |
774 | return 0; |
775 | } |
776 | |
777 | static void |
778 | do_procinit(void) |
779 | { |
780 | struct ps_context *context; |
781 | error_t err; |
782 | |
783 | err = ps_context_create(getproc(), &context); |
784 | if (err) |
785 | error(1, err, "ps_context_create"); |
786 | |
787 | err = proc_stat_list_create(context, &procset); |
788 | if (err) |
789 | error(1, err, "proc_stat_list_create"); |
790 | |
791 | err = proc_stat_list_add_all(procset, 0, 0); |
792 | if (err) |
793 | error(1, err, "proc_stat_list_add_all"); |
794 | |
795 | /* Check all pids */ |
796 | ihash_iterate(context->procs, check_all); |
797 | } |
798 | #endif /* OSHURD */ |
799 | |
800 | |
801 | #if defined(OSOpenBSD) || defined(OSFreeBSD) || defined(OSNetBSD) |
802 | static int |
803 | pid_is_cmd(pid_t pid, const char *name) |
804 | { |
805 | kvm_t *kd; |
806 | int nentries, argv_len=0; |
807 | struct kinfo_proc *kp; |
808 | char errbuf[_POSIX2_LINE_MAX], buf[_POSIX2_LINE_MAX]; |
809 | char **pid_argv_p; |
810 | char *start_argv_0_p, *end_argv_0_p; |
811 | |
812 | |
813 | kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); |
814 | if (kd == 0) |
815 | errx(1, "%s", errbuf); |
816 | if ((kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &nentries)) == 0) |
817 | errx(1, "%s", kvm_geterr(kd)); |
818 | if ((pid_argv_p = kvm_getargv(kd, kp, argv_len)) == 0) |
819 | errx(1, "%s", kvm_geterr(kd)); |
820 | |
821 | start_argv_0_p = *pid_argv_p; |
822 | /* find and compare string */ |
823 | |
824 | /* find end of argv[0] then copy and cut of str there. */ |
825 | if ((end_argv_0_p = strchr(*pid_argv_p, ' ')) == 0 ) |
826 | /* There seems to be no space, so we have the command |
827 | * allready in its desired form. */ |
828 | start_argv_0_p = *pid_argv_p; |
829 | else { |
830 | /* Tests indicate that this never happens, since |
831 | * kvm_getargv itselfe cuts of tailing stuff. This is |
832 | * not what the manpage says, however. */ |
833 | strncpy(buf, *pid_argv_p, (end_argv_0_p - start_argv_0_p)); |
834 | buf[(end_argv_0_p - start_argv_0_p) + 1] = '\0'; |
835 | start_argv_0_p = buf; |
836 | } |
837 | |
838 | if (strlen(name) != strlen(start_argv_0_p)) |
839 | return 0; |
840 | return (strcmp(name, start_argv_0_p) == 0) ? 1 : 0; |
841 | } |
842 | |
843 | static int |
844 | pid_is_user(pid_t pid, uid_t uid) |
845 | { |
846 | kvm_t *kd; |
847 | int nentries; /* Value not used */ |
848 | uid_t proc_uid; |
849 | struct kinfo_proc *kp; |
850 | char errbuf[_POSIX2_LINE_MAX]; |
851 | |
852 | |
853 | kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); |
854 | if (kd == 0) |
855 | errx(1, "%s", errbuf); |
856 | if ((kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &nentries)) == 0) |
857 | errx(1, "%s", kvm_geterr(kd)); |
858 | if (kp->ki_ruid ) |
859 | kvm_read(kd, (u_long)&(kp->ki_ruid), |
860 | &proc_uid, sizeof(uid_t)); |
861 | else |
862 | return 0; |
863 | return (proc_uid == (uid_t)uid); |
864 | } |
865 | |
866 | static int |
867 | pid_is_exec(pid_t pid, const char *name) |
868 | { |
869 | kvm_t *kd; |
870 | int nentries; |
871 | struct kinfo_proc *kp; |
872 | char errbuf[_POSIX2_LINE_MAX], *pidexec; |
873 | |
874 | kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); |
875 | if (kd == 0) |
876 | errx(1, "%s", errbuf); |
877 | if ((kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &nentries)) == 0) |
878 | errx(1, "%s", kvm_geterr(kd)); |
879 | pidexec = kp->ki_comm; |
880 | if (strlen(name) != strlen(pidexec)) |
881 | return 0; |
882 | return (strcmp(name, pidexec) == 0) ? 1 : 0; |
883 | } |
884 | |
885 | |
886 | static void |
887 | do_procinit(void) |
888 | { |
889 | /* Nothing to do */ |
890 | } |
891 | |
892 | #endif /* OSOpenBSD */ |
893 | |
894 | |
895 | #if defined(OShpux) |
896 | static int |
897 | pid_is_user(pid_t pid, uid_t uid) |
898 | { |
899 | struct pst_status pst; |
900 | |
901 | if (pstat_getproc(&pst, sizeof(pst), (size_t) 0, (int) pid) < 0) |
902 | return 0; |
903 | return ((uid_t) pst.pst_uid == uid); |
904 | } |
905 | |
906 | static int |
907 | pid_is_cmd(pid_t pid, const char *name) |
908 | { |
909 | struct pst_status pst; |
910 | |
911 | if (pstat_getproc(&pst, sizeof(pst), (size_t) 0, (int) pid) < 0) |
912 | return 0; |
913 | return (strcmp(pst.pst_ucomm, name) == 0); |
914 | } |
915 | |
916 | static int |
917 | pid_is_exec(pid_t pid, const struct stat *esb) |
918 | { |
919 | struct pst_status pst; |
920 | |
921 | if (pstat_getproc(&pst, sizeof(pst), (size_t) 0, (int) pid) < 0) |
922 | return 0; |
923 | return ((dev_t) pst.pst_text.psf_fsid.psfs_id == esb->st_dev |
924 | && (ino_t) pst.pst_text.psf_fileid == esb->st_ino); |
925 | } |
926 | |
927 | static void |
928 | do_procinit(void) |
929 | { |
930 | struct pst_status pst[10]; |
931 | int i, count; |
932 | int idx = 0; |
933 | |
934 | while ((count = pstat_getproc(pst, sizeof(pst[0]), 10, idx)) > 0) { |
935 | for (i = 0; i < count; i++) |
936 | check(pst[i].pst_pid); |
937 | idx = pst[count - 1].pst_idx + 1; |
938 | } |
939 | } |
940 | #endif /* OShpux */ |
941 | |
942 | |
943 | static void |
944 | do_findprocs(void) |
945 | { |
946 | clear(&found); |
947 | |
948 | if (pidfile) |
949 | do_pidfile(pidfile); |
950 | else |
951 | do_procinit(); |
952 | } |
953 | |
954 | /* return 1 on failure */ |
955 | static void |
956 | do_stop(int signal_nr, int quietmode, int *n_killed, int *n_notkilled, int retry_nr) |
957 | { |
958 | struct pid_list *p; |
959 | |
960 | do_findprocs(); |
961 | |
962 | *n_killed = 0; |
963 | *n_notkilled = 0; |
964 | |
965 | if (!found) |
966 | return; |
967 | |
968 | clear(&killed); |
969 | |
970 | for (p = found; p; p = p->next) { |
971 | if (testmode) { |
972 | printf("Would send signal %d to %d.\n", |
973 | signal_nr, p->pid); |
974 | (*n_killed)++; |
975 | } else if (kill(p->pid, signal_nr) == 0) { |
976 | push(&killed, p->pid); |
977 | (*n_killed)++; |
978 | } else { |
979 | printf("%s: warning: failed to kill %d: %s\n", |
980 | progname, p->pid, strerror(errno)); |
981 | (*n_notkilled)++; |
982 | } |
983 | } |
984 | if (quietmode < 0 && killed) { |
985 | printf("Stopped %s (pid", what_stop); |
986 | for (p = killed; p; p = p->next) |
987 | printf(" %d", p->pid); |
988 | putchar(')'); |
989 | if (retry_nr > 0) |
990 | printf(", retry #%d", retry_nr); |
991 | printf(".\n"); |
992 | } |
993 | } |
994 | |
995 | |
996 | static void |
997 | set_what_stop(const char *str) |
998 | { |
999 | strncpy(what_stop, str, sizeof(what_stop)); |
1000 | what_stop[sizeof(what_stop)-1] = '\0'; |
1001 | } |
1002 | |
1003 | static int |
1004 | run_stop_schedule(void) |
1005 | { |
1006 | int r, position, n_killed, n_notkilled, value, ratio, anykilled, retry_nr; |
1007 | struct timeval stopat, before, after, interval, maxinterval; |
1008 | |
1009 | if (testmode) { |
1010 | if (schedule != NULL) { |
1011 | printf("Ignoring --retry in test mode\n"); |
1012 | schedule = NULL; |
1013 | } |
1014 | } |
1015 | |
1016 | if (cmdname) |
1017 | set_what_stop(cmdname); |
1018 | else if (execname) |
1019 | set_what_stop(execname); |
1020 | else if (pidfile) |
1021 | sprintf(what_stop, "process in pidfile `%.200s'", pidfile); |
1022 | else if (userspec) |
1023 | sprintf(what_stop, "process(es) owned by `%.200s'", userspec); |
1024 | else |
1025 | fatal("internal error, please report"); |
1026 | |
1027 | anykilled = 0; |
1028 | retry_nr = 0; |
1029 | |
1030 | if (schedule == NULL) { |
1031 | do_stop(signal_nr, quietmode, &n_killed, &n_notkilled, 0); |
1032 | if (n_notkilled > 0 && quietmode <= 0) |
1033 | printf("%d pids were not killed\n", n_notkilled); |
1034 | if (n_killed) |
1035 | anykilled = 1; |
1036 | goto x_finished; |
1037 | } |
1038 | |
1039 | for (position = 0; position < schedule_length; ) { |
1040 | value= schedule[position].value; |
1041 | n_notkilled = 0; |
1042 | |
1043 | switch (schedule[position].type) { |
1044 | |
1045 | case sched_goto: |
1046 | position = value; |
1047 | continue; |
1048 | |
1049 | case sched_signal: |
1050 | do_stop(value, quietmode, &n_killed, &n_notkilled, retry_nr++); |
1051 | if (!n_killed) |
1052 | goto x_finished; |
1053 | else |
1054 | anykilled = 1; |
1055 | goto next_item; |
1056 | |
1057 | case sched_timeout: |
1058 | /* We want to keep polling for the processes, to see if they've exited, |
1059 | * or until the timeout expires. |
1060 | * |
1061 | * This is a somewhat complicated algorithm to try to ensure that we |
1062 | * notice reasonably quickly when all the processes have exited, but |
1063 | * don't spend too much CPU time polling. In particular, on a fast |
1064 | * machine with quick-exiting daemons we don't want to delay system |
1065 | * shutdown too much, whereas on a slow one, or where processes are |
1066 | * taking some time to exit, we want to increase the polling |
1067 | * interval. |
1068 | * |
1069 | * The algorithm is as follows: we measure the elapsed time it takes |
1070 | * to do one poll(), and wait a multiple of this time for the next |
1071 | * poll. However, if that would put us past the end of the timeout |
1072 | * period we wait only as long as the timeout period, but in any case |
1073 | * we always wait at least MIN_POLL_INTERVAL (20ms). The multiple |
1074 | * (`ratio') starts out as 2, and increases by 1 for each poll to a |
1075 | * maximum of 10; so we use up to between 30% and 10% of the |
1076 | * machine's resources (assuming a few reasonable things about system |
1077 | * performance). |
1078 | */ |
1079 | xgettimeofday(&stopat); |
1080 | stopat.tv_sec += value; |
1081 | ratio = 1; |
1082 | for (;;) { |
1083 | xgettimeofday(&before); |
1084 | if (timercmp(&before,&stopat,>)) |
1085 | goto next_item; |
1086 | |
1087 | do_stop(0, 1, &n_killed, &n_notkilled, 0); |
1088 | if (!n_killed) |
1089 | goto x_finished; |
1090 | |
1091 | xgettimeofday(&after); |
1092 | |
1093 | if (!timercmp(&after,&stopat,<)) |
1094 | goto next_item; |
1095 | |
1096 | if (ratio < 10) |
1097 | ratio++; |
1098 | |
1099 | TVCALC(interval, ratio * (TVELEM(&after) - TVELEM(&before) + TVADJUST)); |
1100 | TVCALC(maxinterval, TVELEM(&stopat) - TVELEM(&after) + TVADJUST); |
1101 | |
1102 | if (timercmp(&interval,&maxinterval,>)) |
1103 | interval = maxinterval; |
1104 | |
1105 | if (interval.tv_sec == 0 && |
1106 | interval.tv_usec <= MIN_POLL_INTERVAL) |
1107 | interval.tv_usec = MIN_POLL_INTERVAL; |
1108 | |
1109 | r = select(0,0,0,0,&interval); |
1110 | if (r < 0 && errno != EINTR) |
1111 | fatal("select() failed for pause: %s", |
1112 | strerror(errno)); |
1113 | } |
1114 | |
1115 | default: |
1116 | assert(!"schedule[].type value must be valid"); |
1117 | |
1118 | } |
1119 | |
1120 | next_item: |
1121 | position++; |
1122 | } |
1123 | |
1124 | if (quietmode <= 0) |
1125 | printf("Program %s, %d process(es), refused to die.\n", |
1126 | what_stop, n_killed); |
1127 | |
1128 | return 2; |
1129 | |
1130 | x_finished: |
1131 | if (!anykilled) { |
1132 | if (quietmode <= 0) |
1133 | printf("No %s found running; none killed.\n", what_stop); |
1134 | return exitnodo; |
1135 | } else { |
1136 | return 0; |
1137 | } |
1138 | } |
1139 | |
1140 | |
1141 | int main(int argc, char **argv) NONRETURNING; |
1142 | int |
1143 | main(int argc, char **argv) |
1144 | { |
1145 | int devnull_fd = -1; |
1146 | #ifdef HAVE_TIOCNOTTY |
1147 | int tty_fd = -1; |
1148 | #endif |
1149 | progname = argv[0]; |
1150 | |
1151 | parse_options(argc, argv); |
1152 | argc -= optind; |
1153 | argv += optind; |
1154 | |
1155 | if (execname && stat(execname, &exec_stat)) |
1156 | fatal("stat %s: %s", execname, strerror(errno)); |
1157 | |
1158 | if (userspec && sscanf(userspec, "%d", &user_id) != 1) { |
1159 | struct passwd *pw; |
1160 | |
1161 | pw = getpwnam(userspec); |
1162 | if (!pw) |
1163 | fatal("user `%s' not found\n", userspec); |
1164 | |
1165 | user_id = pw->pw_uid; |
1166 | } |
1167 | |
1168 | if (changegroup && sscanf(changegroup, "%d", &runas_gid) != 1) { |
1169 | struct group *gr = getgrnam(changegroup); |
1170 | if (!gr) |
1171 | fatal("group `%s' not found\n", changegroup); |
1172 | runas_gid = gr->gr_gid; |
1173 | } |
1174 | if (changeuser && sscanf(changeuser, "%d", &runas_uid) != 1) { |
1175 | struct passwd *pw = getpwnam(changeuser); |
1176 | if (!pw) |
1177 | fatal("user `%s' not found\n", changeuser); |
1178 | runas_uid = pw->pw_uid; |
1179 | if (changegroup == NULL) { /* pass the default group of this user */ |
1180 | changegroup = ""; /* just empty */ |
1181 | runas_gid = pw->pw_gid; |
1182 | } |
1183 | } |
1184 | |
1185 | if (stop) { |
1186 | int i = run_stop_schedule(); |
1187 | exit(i); |
1188 | } |
1189 | |
1190 | do_findprocs(); |
1191 | |
1192 | if (found) { |
1193 | if (quietmode <= 0) |
1194 | printf("%s already running.\n", execname ? execname : "process"); |
1195 | exit(exitnodo); |
1196 | } |
1197 | if (testmode) { |
1198 | printf("Would start %s ", startas); |
1199 | while (argc-- > 0) |
1200 | printf("%s ", *argv++); |
1201 | if (changeuser != NULL) { |
1202 | printf(" (as user %s[%d]", changeuser, runas_uid); |
1203 | if (changegroup != NULL) |
1204 | printf(", and group %s[%d])", changegroup, runas_gid); |
1205 | else |
1206 | printf(")"); |
1207 | } |
1208 | if (changeroot != NULL) |
1209 | printf(" in directory %s", changeroot); |
1210 | if (nicelevel) |
1211 | printf(", and add %i to the priority", nicelevel); |
1212 | printf(".\n"); |
1213 | exit(0); |
1214 | } |
1215 | if (quietmode < 0) |
1216 | printf("Starting %s...\n", startas); |
1217 | *--argv = startas; |
1218 | if (background) { /* ok, we need to detach this process */ |
1219 | int i; |
1220 | if (quietmode < 0) |
1221 | printf("Detatching to start %s...", startas); |
1222 | i = fork(); |
1223 | if (i<0) { |
1224 | fatal("Unable to fork.\n"); |
1225 | } |
1226 | if (i) { /* parent */ |
1227 | if (quietmode < 0) |
1228 | printf("done.\n"); |
1229 | exit(0); |
1230 | } |
1231 | /* child continues here */ |
1232 | |
1233 | #ifdef HAVE_TIOCNOTTY |
1234 | tty_fd=open("/dev/tty", O_RDWR); |
1235 | #endif |
1236 | devnull_fd=open("/dev/null", O_RDWR); |
1237 | } |
1238 | if (nicelevel) { |
1239 | errno=0; |
1240 | if ((nice(nicelevel)==-1) && (errno!=0)) |
1241 | fatal("Unable to alter nice level by %i: %s", nicelevel, |
1242 | strerror(errno)); |
1243 | } |
1244 | if (mpidfile && pidfile != NULL) { /* user wants _us_ to make the pidfile :) */ |
1245 | FILE *pidf = fopen(pidfile, "w"); |
1246 | pid_t pidt = getpid(); |
1247 | if (pidf == NULL) |
1248 | fatal("Unable to open pidfile `%s' for writing: %s", pidfile, |
1249 | strerror(errno)); |
1250 | fprintf(pidf, "%d\n", pidt); |
1251 | fclose(pidf); |
1252 | } |
1253 | if (changeroot != NULL) { |
1254 | if (chdir(changeroot) < 0) |
1255 | fatal("Unable to chdir() to %s", changeroot); |
1256 | if (chroot(changeroot) < 0) |
1257 | fatal("Unable to chroot() to %s", changeroot); |
1258 | } |
1259 | if (changedir != NULL && chdir(changedir) < 0) |
1260 | fatal("Unable to chdir() to %s", changedir); |
1261 | if (changeuser != NULL) { |
1262 | if (setgid(runas_gid)) |
1263 | fatal("Unable to set gid to %d", runas_gid); |
1264 | if (initgroups(changeuser, runas_gid)) |
1265 | fatal("Unable to set initgroups() with gid %d", runas_gid); |
1266 | if (setuid(runas_uid)) |
1267 | fatal("Unable to set uid to %s", changeuser); |
1268 | } |
1269 | if (background) { /* continue background setup */ |
1270 | int i; |
1271 | #ifdef HAVE_TIOCNOTTY |
1272 | /* change tty */ |
1273 | ioctl(tty_fd, TIOCNOTTY, 0); |
1274 | close(tty_fd); |
1275 | #endif |
1276 | umask(022); /* set a default for dumb programs */ |
1277 | dup2(devnull_fd,0); /* stdin */ |
1278 | dup2(devnull_fd,1); /* stdout */ |
1279 | dup2(devnull_fd,2); /* stderr */ |
1280 | #if defined(OShpux) |
1281 | /* now close all extra fds */ |
1282 | for (i=sysconf(_SC_OPEN_MAX)-1; i>=3; --i) close(i); |
1283 | #else |
1284 | /* now close all extra fds */ |
1285 | for (i=getdtablesize()-1; i>=3; --i) close(i); |
1286 | #endif |
1287 | |
1288 | /* create a new session */ |
1289 | #ifdef HAVE_SETSID |
1290 | setsid(); |
1291 | #else |
1292 | setpgid(0,0); |
1293 | #endif |
1294 | } |
1295 | execv(startas, argv); |
1296 | fatal("Unable to start %s: %s", startas, strerror(errno)); |
1297 | } |
1298 |