/[gentoo-alt]/trunk/baselayout-prefix/src/start-stop-daemon.c
Gentoo

Contents of /trunk/baselayout-prefix/src/start-stop-daemon.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1688 - (show annotations) (download) (as text)
Thu Mar 31 14:12:31 2011 UTC (3 years, 6 months ago) by grobian
File MIME type: text/x-csrc
File size: 36060 byte(s)
applied baselayout-1.12.5-aix.patch

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

  ViewVC Help
Powered by ViewVC 1.1.20