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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2549 - (show annotations) (download) (as text)
Thu Apr 5 15:01:09 2007 UTC (11 years, 2 months ago) by uberlord
File MIME type: text/x-csrc
File size: 23733 byte(s)
We now compile on OpenBSD (no idea if it works or not yet)
1 /*
2 start-stop-daemon
3 Starts, stops, tests and signals daemons
4 Copyright 2007 Gentoo Foundation
5 Released under the GPLv2
6
7 This is essentially a ground up re-write of Debians
8 start-stop-daemon for cleaner code and to integrate into our RC
9 system so we can monitor daemons a little.
10 */
11
12 #define POLL_INTERVAL 20000
13 #define START_WAIT 100000
14
15 #include <sys/types.h>
16 #include <sys/ioctl.h>
17 #include <sys/resource.h>
18 #include <sys/stat.h>
19 #include <sys/termios.h>
20 #include <sys/time.h>
21 #include <sys/wait.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <getopt.h>
26 #include <grp.h>
27 #include <pwd.h>
28 #include <signal.h>
29 #include <stddef.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 #ifdef HAVE_PAM
36 #include <security/pam_appl.h>
37
38 /* We are not supporting authentication conversations */
39 static struct pam_conv conv = { NULL, NULL} ;
40 #endif
41
42 #include "einfo.h"
43 #include "rc.h"
44 #include "rc-misc.h"
45 #include "strlist.h"
46
47 typedef struct schedulelist
48 {
49 enum
50 {
51 schedule_timeout,
52 schedule_signal,
53 schedule_goto,
54 schedule_forever
55 } type;
56 int value;
57 struct schedulelist *gotolist;
58 struct schedulelist *next;
59 } schedulelist_t;
60 static schedulelist_t *schedule;
61
62 static char *progname;
63 static char *changeuser;
64 static char **newenv;
65
66 extern char **environ;
67
68 static void free_schedulelist (schedulelist_t **list)
69 {
70 schedulelist_t *here;
71 schedulelist_t *next;
72
73 for (here = *list; here; here = next)
74 {
75 next = here->next;
76 free (here);
77 }
78
79 *list = NULL;
80 }
81
82 static void cleanup (void)
83 {
84 if (changeuser)
85 free (changeuser);
86
87 if (schedule)
88 free_schedulelist (&schedule);
89
90 if (newenv)
91 rc_strlist_free (newenv);
92 }
93
94 static int parse_signal (const char *sig)
95 {
96 typedef struct signalpair
97 {
98 const char *name;
99 int signal;
100 } signalpair_t;
101
102 static const signalpair_t signallist[] = {
103 { "ABRT", SIGABRT },
104 { "ALRM", SIGALRM },
105 { "FPE", SIGFPE },
106 { "HUP", SIGHUP },
107 { "ILL", SIGILL },
108 { "INT", SIGINT },
109 { "KILL", SIGKILL },
110 { "PIPE", SIGPIPE },
111 { "QUIT", SIGQUIT },
112 { "SEGV", SIGSEGV },
113 { "TERM", SIGTERM },
114 { "USR1", SIGUSR1 },
115 { "USR2", SIGUSR2 },
116 { "CHLD", SIGCHLD },
117 { "CONT", SIGCONT },
118 { "STOP", SIGSTOP },
119 { "TSTP", SIGTSTP },
120 { "TTIN", SIGTTIN },
121 { "TTOU", SIGTTOU }
122 };
123
124 unsigned int i = 0;
125 char *s;
126
127 if (! sig || strlen (sig) == 0)
128 return (-1);
129
130 if (sscanf (sig, "%u", &i) == 1)
131 {
132 if (i > 0 && i < sizeof (signallist) / sizeof (signallist[0]))
133 return (i);
134 eerrorx ("%s: `%s' is not a valid signal", progname, sig);
135 }
136
137 if (strncmp (sig, "SIG", 3) == 0)
138 s = (char *) sig + 3;
139 else
140 s = NULL;
141
142 for (i = 0; i < sizeof (signallist) / sizeof (signallist[0]); i++)
143 if (strcmp (sig, signallist[i].name) == 0 ||
144 (s && strcmp (s, signallist[i].name) == 0))
145 return (signallist[i].signal);
146
147 eerrorx ("%s: `%s' is not a valid signal", progname, sig);
148 }
149
150 static void parse_schedule_item (schedulelist_t *item, const char *string)
151 {
152 const char *after_hyph;
153 int sig;
154
155 if (strcmp (string,"forever") == 0)
156 item->type = schedule_forever;
157 else if (isdigit (string[0]))
158 {
159 item->type = schedule_timeout;
160 errno = 0;
161 if (sscanf (string, "%d", &item->value) != 1)
162 eerrorx ("%s: invalid timeout value in schedule `%s'", progname,
163 string);
164 }
165 else if ((after_hyph = string + (string[0] == '-')) &&
166 ((sig = parse_signal (after_hyph)) != -1))
167 {
168 item->type = schedule_signal;
169 item->value = (int) sig;
170 }
171 else
172 eerrorx ("%s: invalid schedule item `%s'", progname, string);
173 }
174
175 static void parse_schedule (const char *string, int default_signal)
176 {
177 char buffer[20];
178 const char *slash;
179 int count = 0;
180 schedulelist_t *repeatat = NULL;
181 ptrdiff_t len;
182 schedulelist_t *next;
183
184 if (string)
185 for (slash = string; *slash; slash++)
186 if (*slash == '/')
187 count++;
188
189 if (schedule)
190 free_schedulelist (&schedule);
191
192 schedule = rc_xmalloc (sizeof (schedulelist_t));
193 schedule->gotolist = NULL;
194
195 if (count == 0)
196 {
197 schedule->type = schedule_signal;
198 schedule->value = default_signal;
199 schedule->next = rc_xmalloc (sizeof (schedulelist_t));
200 next = schedule->next;
201 next->type = schedule_timeout;
202 next->gotolist = NULL;
203 if (string)
204 {
205 if (sscanf (string, "%d", &next->value) != 1)
206 eerrorx ("%s: invalid timeout value in schedule", progname);
207 }
208 else
209 next->value = 5;
210 next->next = NULL;
211
212 return;
213 }
214
215 next = schedule;
216 while (string != NULL)
217 {
218 if ((slash = strchr (string, '/')))
219 len = slash - string;
220 else
221 len = strlen (string);
222
223 if (len >= (ptrdiff_t) sizeof (buffer))
224 eerrorx ("%s: invalid schedule item, far too long", progname);
225
226 memcpy (buffer, string, len);
227 buffer[len] = 0;
228 string = slash ? slash + 1 : NULL;
229
230 parse_schedule_item (next, buffer);
231 if (next->type == schedule_forever)
232 {
233 if (repeatat)
234 eerrorx ("%s: invalid schedule, `forever' appears more than once",
235 progname);
236
237 repeatat = next;
238 continue;
239 }
240
241 if (string)
242 {
243 next->next = rc_xmalloc (sizeof (schedulelist_t));
244 next = next->next;
245 next->gotolist = NULL;
246 }
247 }
248
249 if (repeatat)
250 {
251 next->next = rc_xmalloc (sizeof (schedulelist_t));
252 next = next->next;
253 next->type = schedule_goto;
254 next->value = 0;
255 next->gotolist = repeatat;
256 }
257
258 next->next = NULL;
259 return;
260 }
261
262 static pid_t get_pid (const char *pidfile, bool quiet)
263 {
264 FILE *fp;
265 pid_t pid;
266
267 if (! pidfile)
268 return (-1);
269
270 if ((fp = fopen (pidfile, "r")) == NULL)
271 {
272 if (! quiet)
273 eerror ("%s: fopen `%s': %s", progname, pidfile, strerror (errno));
274 return (-1);
275 }
276
277 if (fscanf (fp, "%d", &pid) != 1)
278 {
279 if (! quiet)
280 eerror ("%s: no pid found in `%s'", progname, pidfile);
281 fclose (fp);
282 return (-1);
283 }
284 fclose (fp);
285
286 return (pid);
287 }
288
289 /* return number of processed killed, -1 on error */
290 static int do_stop (const char *exec, const char *cmd,
291 const char *pidfile, uid_t uid,int sig,
292 bool quiet, bool verbose, bool test)
293 {
294 pid_t *pids;
295 bool killed;
296 int nkilled = 0;
297 pid_t pid = 0;
298 int i;
299
300 if (pidfile)
301 if ((pid = get_pid (pidfile, quiet)) == -1)
302 return (quiet ? 0 : -1);
303
304 if ((pids = rc_find_pids (exec, cmd, uid, pid)) == NULL)
305 return (0);
306
307 for (i = 0; pids[i]; i++)
308 {
309 if (test)
310 {
311 if (! quiet)
312 einfo ("Would send signal %d to PID %d", sig, pids[i]);
313 nkilled++;
314 continue;
315 }
316
317 if (verbose)
318 ebegin ("Sending signal %d to PID %d", sig, pids[i]);
319 errno = 0;
320 killed = (kill (pids[i], sig) == 0 || errno == ESRCH ? true : false);
321 if (! killed)
322 {
323 if (! quiet)
324 eerror ("%s: failed to send signal %d to PID %d: %s",
325 progname, sig, pids[i], strerror (errno));
326 if (verbose)
327 eend (1, NULL);
328 nkilled = -1;
329 }
330 else
331 {
332 if (verbose)
333 eend (0, NULL);
334 if (nkilled != -1)
335 nkilled++;
336 }
337 }
338
339 free (pids);
340 return (nkilled);
341 }
342
343 static int run_stop_schedule (const char *exec, const char *cmd,
344 const char *pidfile, uid_t uid,
345 bool quiet, bool verbose, bool test)
346 {
347 schedulelist_t *item = schedule;
348 int nkilled = 0;
349 int tkilled = 0;
350 int nrunning = 0;
351 struct timeval tv;
352 struct timeval now;
353 struct timeval stopat;
354
355 if (verbose)
356 {
357 if (pidfile)
358 einfo ("Will stop PID in pidfile `%s'", pidfile);
359 if (uid)
360 einfo ("Will stop processes owned by UID %d", uid);
361 if (exec)
362 einfo ("Will stop processes of `%s'", exec);
363 if (cmd)
364 einfo ("Will stop processes called `%s'", cmd);
365 }
366
367 while (item)
368 {
369 switch (item->type)
370 {
371 case schedule_goto:
372 item = item->gotolist;
373 continue;
374
375 case schedule_signal:
376 nrunning = 0;
377 nkilled = do_stop (exec, cmd, pidfile, uid, item->value,
378 quiet, verbose, test);
379 if (nkilled == 0)
380 {
381 if (tkilled == 0)
382 {
383 if (! quiet)
384 eerror ("%s: no matching processes found", progname);
385 }
386 return (tkilled);
387 }
388 else if (nkilled == -1)
389 return (0);
390
391 tkilled += nkilled;
392 break;
393 case schedule_timeout:
394 if (item->value < 1)
395 {
396 item = NULL;
397 break;
398 }
399
400 if (gettimeofday (&stopat, NULL) != 0)
401 {
402 eerror ("%s: gettimeofday: %s", progname, strerror (errno));
403 return (0);
404 }
405
406 stopat.tv_sec += item->value;
407 while (1)
408 {
409 if ((nrunning = do_stop (exec, cmd, pidfile,
410 uid, 0, true, false, true)) == 0)
411 return (true);
412
413 tv.tv_sec = 0;
414 tv.tv_usec = POLL_INTERVAL;
415 if (select (0, 0, 0, 0, &tv) < 0)
416 {
417 if (errno == EINTR)
418 eerror ("%s: caught an interupt", progname);
419 else
420 eerror ("%s: select: %s", progname, strerror (errno));
421 return (0);
422 }
423
424 if (gettimeofday (&now, NULL) != 0)
425 {
426 eerror ("%s: gettimeofday: %s", progname, strerror (errno));
427 return (0);
428 }
429 if (timercmp (&now, &stopat, >))
430 break;
431 }
432 break;
433
434 default:
435 eerror ("%s: invalid schedule item `%d'", progname, item->type);
436 return (0);
437 }
438
439 if (item)
440 item = item->next;
441 }
442
443 if (test || (tkilled > 0 && nrunning == 0))
444 return (nkilled);
445
446 if (! quiet)
447 {
448 if (nrunning == 1)
449 eerror ("%s: %d process refused to stop", progname, nrunning);
450 else
451 eerror ("%s: %d process(es) refused to stop", progname, nrunning);
452 }
453
454 return (-nrunning);
455 }
456
457 static void handle_signal (int sig)
458 {
459 int pid;
460 int status;
461 int serrno = errno;
462
463 switch (sig)
464 {
465 case SIGINT:
466 case SIGTERM:
467 case SIGQUIT:
468 eerrorx ("%s: caught signal %d, aborting", progname, sig);
469
470 case SIGCHLD:
471 while (1)
472 {
473 if ((pid = waitpid (-1, &status, WNOHANG)) < 0)
474 {
475 if (errno != ECHILD)
476 eerror ("%s: waitpid: %s", progname, strerror (errno));
477 break;
478 }
479 }
480 break;
481
482 default:
483 eerror ("%s: caught unknown signal %d", progname, sig);
484 }
485
486 /* Restore errno */
487 errno = serrno;
488 }
489
490 int main (int argc, char **argv)
491 {
492 int devnull_fd = -1;
493
494 #ifdef TIOCNOTTY
495 int tty_fd = -1;
496 #endif
497 #ifdef HAVE_PAM
498 pam_handle_t *pamh = NULL;
499 int pamr;
500 #endif
501
502 static struct option longopts[] = {
503 { "stop", 0, NULL, 'K'},
504 { "nicelevel", 1, NULL, 'N'},
505 { "retry", 1, NULL, 'R'},
506 { "start", 0, NULL, 'S'},
507 { "background", 0, NULL, 'b'},
508 { "chuid", 1, NULL, 'c'},
509 { "chdir", 1, NULL, 'd'},
510 { "group", 1, NULL, 'g'},
511 { "make-pidfile", 0, NULL, 'm'},
512 { "name", 1, NULL, 'n'},
513 { "oknodo", 0, NULL, 'o'},
514 { "pidfile", 1, NULL, 'p'},
515 { "quiet", 0, NULL, 'q'},
516 { "signal", 1, NULL, 's'},
517 { "test", 0, NULL, 't'},
518 { "user", 1, NULL, 'u'},
519 { "chroot", 1, NULL, 'r'},
520 { "verbose", 0, NULL, 'v'},
521 { "exec", 1, NULL, 'x'},
522 { "stdout", 1, NULL, '1'},
523 { "stderr", 1, NULL, '2'},
524 { NULL, 0, NULL, 0}
525 };
526 int c;
527 bool start = false;
528 bool stop = false;
529 bool oknodo = false;
530 bool test = false;
531 bool quiet = false;
532 bool verbose = false;
533 char *exec = NULL;
534 char *cmd = NULL;
535 char *pidfile = NULL;
536 int sig = SIGTERM;
537 uid_t uid = 0;
538 int nicelevel = 0;
539 bool background = false;
540 bool makepidfile = false;
541 uid_t ch_uid = 0;
542 gid_t ch_gid = 0;
543 char *ch_root = NULL;
544 char *ch_dir = NULL;
545 int tid = 0;
546 char *redirect_stderr = NULL;
547 char *redirect_stdout = NULL;
548 int stdout_fd;
549 int stderr_fd;
550 pid_t pid;
551 struct timeval tv;
552 int i;
553 char *svcname = getenv ("SVCNAME");
554 char *env;
555
556 progname = argv[0];
557 atexit (cleanup);
558
559 signal (SIGINT, handle_signal);
560 signal (SIGQUIT, handle_signal);
561 signal (SIGTERM, handle_signal);
562
563 while ((c = getopt_long (argc, argv,
564 "KN:R:Sbc:d:g:mn:op:qs:tu:r:vx:1:2:",
565 longopts, (int *) 0)) != -1)
566 switch (c)
567 {
568 case 'K': /* --stop */
569 stop = true;
570 break;
571
572 case 'N': /* --nice */
573 if (sscanf (optarg, "%d", &nicelevel) != 1)
574 eerrorx ("%s: invalid nice level `%s'", progname, optarg);
575 break;
576
577 case 'R': /* --retry <schedule>|<timeout> */
578 parse_schedule (optarg, sig);
579 break;
580
581 case 'S': /* --start */
582 start = true;
583 break;
584
585 case 'b': /* --background */
586 background = true;
587 break;
588
589 case 'c': /* --chuid <username>|<uid> */
590 /* we copy the string just in case we need the
591 * argument later. */
592 {
593 char *p = optarg;
594 char *cu = strsep (&p, ":");
595 changeuser = strdup (cu);
596 if (sscanf (cu, "%d", &tid) != 1)
597 {
598 struct passwd *pw = getpwnam (cu);
599 if (! pw)
600 eerrorx ("%s: user `%s' not found", progname, cu);
601 ch_uid = pw->pw_uid;
602 }
603 else
604 ch_uid = tid;
605 if (p)
606 {
607 char *cg = strsep (&p, ":");
608 if (sscanf (cg, "%d", &tid) != 1)
609 {
610 struct group *gr = getgrnam (cg);
611 if (! gr)
612 eerrorx ("%s: group `%s' not found", progname, cg);
613 ch_gid = gr->gr_gid;
614 }
615 else
616 ch_gid = tid;
617 }
618 }
619 break;
620
621 case 'd': /* --chdir /new/dir */
622 ch_dir = optarg;
623 break;
624
625 case 'g': /* --group <group>|<gid> */
626 if (sscanf (optarg, "%d", &tid) != 1)
627 {
628 struct group *gr = getgrnam (optarg);
629 if (! gr)
630 eerrorx ("%s: group `%s' not found", progname, optarg);
631 ch_gid = gr->gr_gid;
632 }
633 else
634 ch_gid = tid;
635 break;
636
637 case 'm': /* --make-pidfile */
638 makepidfile = true;
639 break;
640
641 case 'n': /* --name <process-name> */
642 cmd = optarg;
643 break;
644
645 case 'o': /* --oknodo */
646 oknodo = true;
647 break;
648
649 case 'p': /* --pidfile <pid-file> */
650 pidfile = optarg;
651 break;
652
653 case 'q': /* --quiet */
654 quiet = true;
655 break;
656
657 case 's': /* --signal <signal> */
658 sig = parse_signal (optarg);
659 break;
660
661 case 't': /* --test */
662 test = true;
663 break;
664
665 case 'u': /* --user <username>|<uid> */
666 if (sscanf (optarg, "%d", &tid) != 1)
667 {
668 struct passwd *pw = getpwnam (optarg);
669 if (! pw)
670 eerrorx ("%s: user `%s' not found", progname, optarg);
671 uid = pw->pw_uid;
672 }
673 else
674 uid = tid;
675 break;
676
677 case 'r': /* --chroot /new/root */
678 ch_root = optarg;
679 break;
680
681 case 'v': /* --verbose */
682 verbose = true;
683 break;
684
685 case 'x': /* --exec <executable> */
686 exec = optarg;
687 break;
688
689 case '1': /* --stdout /path/to/stdout.lgfile */
690 redirect_stdout = optarg;
691 break;
692
693 case '2': /* --stderr /path/to/stderr.logfile */
694 redirect_stderr = optarg;
695 break;
696
697 default:
698 exit (EXIT_FAILURE);
699 }
700
701 /* Respect RC as well as how we are called */
702 if (rc_is_env ("RC_QUIET", "yes") && ! verbose)
703 quiet = true;
704
705 if (start == stop)
706 eerrorx ("%s: need one of --start or --stop", progname);
707
708 if (start && ! exec)
709 eerrorx ("%s: --start needs --exec", progname);
710
711 if (stop && ! exec && ! pidfile && ! cmd && ! uid)
712 eerrorx ("%s: --stop needs --exec, --pidfile, --name or --user", progname);
713
714 if (makepidfile && ! pidfile)
715 eerrorx ("%s: --make-pidfile is only relevant with --pidfile", progname);
716
717 if (background && ! start)
718 eerrorx ("%s: --background is only relevant with --start", progname);
719
720 if ((redirect_stdout || redirect_stderr) && ! background)
721 eerrorx ("%s: --stdout and --stderr are only relevant with --background",
722 progname);
723
724 argc -= optind;
725 argv += optind;
726
727 /* Validate that the binary rc_exists if we are starting */
728 if (exec && start)
729 {
730 char *tmp;
731 if (ch_root)
732 tmp = rc_strcatpaths (ch_root, exec, (char *) NULL);
733 else
734 tmp = exec;
735 if (! rc_is_file (tmp))
736 {
737 eerror ("%s: %s does not exist", progname, tmp);
738 if (ch_root)
739 free (tmp);
740 exit (EXIT_FAILURE);
741 }
742 if (ch_root)
743 free (tmp);
744 }
745
746 if (stop)
747 {
748 int result;
749
750 if (! schedule)
751 {
752 if (test || oknodo)
753 parse_schedule ("0", sig);
754 else
755 parse_schedule (NULL, sig);
756 }
757
758 result = run_stop_schedule (exec, cmd, pidfile, uid, quiet, verbose, test);
759 if (test || oknodo)
760 return (result > 0 ? EXIT_SUCCESS : EXIT_FAILURE);
761 if (result < 1)
762 exit (result == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
763
764 if (pidfile && rc_is_file (pidfile))
765 unlink (pidfile);
766
767 if (svcname)
768 rc_set_service_daemon (svcname, exec, cmd, pidfile, false);
769
770 exit (EXIT_SUCCESS);
771 }
772
773 if (do_stop (exec, cmd, pidfile, uid, 0, true, false, true) > 0)
774 eerrorx ("%s: %s is already running", progname, exec);
775
776 if (test)
777 {
778 if (quiet)
779 exit (EXIT_SUCCESS);
780
781 einfon ("Would start %s", exec);
782 while (argc-- > 0)
783 printf("%s ", *argv++);
784 printf ("\n");
785 eindent ();
786 if (ch_uid != 0)
787 einfo ("as user %d", ch_uid);
788 if (ch_gid != 0)
789 einfo ("as group %d", ch_gid);
790 if (ch_root)
791 einfo ("in root `%s'", ch_root);
792 if (ch_dir)
793 einfo ("in dir `%s'", ch_dir);
794 if (nicelevel != 0)
795 einfo ("with a priority of %d", nicelevel);
796 eoutdent ();
797 exit (EXIT_SUCCESS);
798 }
799
800 /* Ensure this is unset, so if the daemon does /etc/init.d/foo
801 Then we filter the environment accordingly */
802 unsetenv ("RC_SOFTLEVEL");
803
804 if (verbose)
805 {
806 ebegin ("Detaching to start `%s'", exec);
807 eindent ();
808 }
809
810 if (background)
811 signal (SIGCHLD, handle_signal);
812
813 *--argv = exec;
814 if ((pid = fork ()) == -1)
815 eerrorx ("%s: fork: %s", progname, strerror (errno));
816
817 /* Child process - lets go! */
818 if (pid == 0)
819 {
820 pid_t mypid = getpid ();
821
822 #ifdef TIOCNOTTY
823 tty_fd = open("/dev/tty", O_RDWR);
824 #endif
825
826 devnull_fd = open("/dev/null", O_RDWR);
827
828 if (nicelevel)
829 {
830 if (setpriority (PRIO_PROCESS, mypid, nicelevel) == -1)
831 eerrorx ("%s: setpritory %d: %s", progname, nicelevel,
832 strerror(errno));
833 }
834
835 if (ch_root && chroot (ch_root) < 0)
836 eerrorx ("%s: chroot `%s': %s", progname, ch_root, strerror (errno));
837
838 if (ch_dir && chdir (ch_dir) < 0)
839 eerrorx ("%s: chdir `%s': %s", progname, ch_dir, strerror (errno));
840
841 if (makepidfile && pidfile)
842 {
843 FILE *fp = fopen (pidfile, "w");
844 if (! fp)
845 eerrorx ("%s: fopen `%s': %s", progname, pidfile, strerror
846 (errno));
847 fprintf (fp, "%d\n", mypid);
848 fclose (fp);
849 }
850
851 #ifdef HAVE_PAM
852 if (changeuser != NULL)
853 pamr = pam_start ("start-stop-daemon", changeuser, &conv, &pamh);
854 else
855 pamr = pam_start ("start-stop-daemon", "nobody", &conv, &pamh);
856
857 if (pamr == PAM_SUCCESS)
858 pamr = pam_authenticate (pamh, PAM_SILENT);
859 if (pamr == PAM_SUCCESS)
860 pamr = pam_acct_mgmt (pamh, PAM_SILENT);
861 if (pamr == PAM_SUCCESS)
862 pamr = pam_open_session (pamh, PAM_SILENT);
863 if (pamr != PAM_SUCCESS)
864 eerrorx ("%s: pam error: %s", progname, pam_strerror(pamh, pamr));
865 #endif
866
867 if ((ch_gid) && setgid(ch_gid))
868 eerrorx ("%s: unable to set groupid to %d", progname, ch_gid);
869 if (changeuser && ch_gid)
870 if (initgroups (changeuser, ch_gid))
871 eerrorx ("%s: initgroups (%s, %d)", progname, changeuser, ch_gid);
872 if (ch_uid && setuid (ch_uid))
873 eerrorx ("%s: unable to set userid to %d", progname, ch_uid);
874 else
875 {
876 struct passwd *passwd = getpwuid (ch_uid);
877 if (passwd)
878 {
879 unsetenv ("HOME");
880 if (passwd->pw_dir)
881 setenv ("HOME", passwd->pw_dir, 1);
882 unsetenv ("USER");
883 if (passwd->pw_name)
884 setenv ("USER", passwd->pw_name, 1);
885 }
886 }
887
888 /* Close any fd's to the passwd database */
889 endpwent ();
890
891 #ifdef TIOCNOTTY
892 ioctl(tty_fd, TIOCNOTTY, 0);
893 close(tty_fd);
894 #endif
895
896 /* Clean the environment of any RC_ variables */
897 STRLIST_FOREACH (environ, env, i)
898 if (env && strncmp (env, "RC_", 3) != 0)
899 {
900 /* For the path character, remove the rcscript bin dir from it */
901 if (strncmp (env, "PATH=" RC_LIBDIR "bin:",
902 strlen ("PATH=" RC_LIBDIR "bin:")) == 0)
903 {
904 char *path = env;
905 char *newpath;
906 int len;
907 path += strlen ("PATH=" RC_LIBDIR "bin:");
908 len = sizeof (char *) * strlen (path) + 6;
909 newpath = rc_xmalloc (len);
910 snprintf (newpath, len, "PATH=%s", path);
911 newenv = rc_strlist_add (newenv, newpath);
912 free (newpath);
913 }
914 else
915 newenv = rc_strlist_add (newenv, env);
916 }
917
918 umask (022);
919
920 stdout_fd = devnull_fd;
921 stderr_fd = devnull_fd;
922 if (redirect_stdout)
923 {
924 if ((stdout_fd = open (redirect_stdout, O_WRONLY | O_CREAT | O_APPEND,
925 S_IRUSR | S_IWUSR)) == -1)
926 eerrorx ("%s: unable to open the logfile for stdout `%s': %s",
927 progname, redirect_stdout, strerror (errno));
928 }
929 if (redirect_stderr)
930 {
931 if ((stderr_fd = open (redirect_stderr, O_WRONLY | O_CREAT | O_APPEND,
932 S_IRUSR | S_IWUSR)) == -1)
933 eerrorx ("%s: unable to open the logfile for stderr `%s': %s",
934 progname, redirect_stderr, strerror (errno));
935 }
936
937 dup2 (devnull_fd, STDIN_FILENO);
938 if (background)
939 {
940 dup2 (stdout_fd, STDOUT_FILENO);
941 dup2 (stderr_fd, STDERR_FILENO);
942 }
943
944 for (i = getdtablesize () - 1; i >= 3; --i)
945 close(i);
946
947 setsid ();
948
949 execve (exec, argv, newenv);
950 #ifdef HAVE_PAM
951 if (pamr == PAM_SUCCESS)
952 pam_close_session (pamh, PAM_SILENT);
953 #endif
954 eerrorx ("%s: failed to exec `%s': %s", progname, exec, strerror (errno));
955 }
956
957 /* Parent process */
958 if (! background)
959 {
960 /* As we're not backgrounding the process, wait for our pid to return */
961 int status = 0;
962 int savepid = pid;
963
964 errno = 0;
965 do
966 {
967 pid = waitpid (savepid, &status, 0);
968 if (pid < 1)
969 {
970 eerror ("waitpid %d: %s", savepid, strerror (errno));
971 return (-1);
972 }
973 } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
974
975 if (! WIFEXITED (status) || WEXITSTATUS (status) != 0)
976 {
977 if (! quiet)
978 eerrorx ("%s: failed to started `%s'", progname, exec);
979 exit (EXIT_FAILURE);
980 }
981
982 pid = savepid;
983 }
984
985 /* Wait a little bit and check that process is still running
986 We do this as some badly written daemons fork and then barf */
987 if (START_WAIT > 0)
988 {
989 struct timeval stopat;
990 struct timeval now;
991
992 if (gettimeofday (&stopat, NULL) != 0)
993 eerrorx ("%s: gettimeofday: %s", progname, strerror (errno));
994
995 stopat.tv_usec += START_WAIT;
996 while (1)
997 {
998 bool alive = false;
999
1000 tv.tv_sec = 0;
1001 tv.tv_usec = POLL_INTERVAL;
1002 if (select (0, 0, 0, 0, &tv) < 0)
1003 {
1004 /* Let our signal handler handle the interupt */
1005 if (errno != EINTR)
1006 eerrorx ("%s: select: %s", progname, strerror (errno));
1007 }
1008
1009 if (gettimeofday (&now, NULL) != 0)
1010 eerrorx ("%s: gettimeofday: %s", progname, strerror (errno));
1011
1012 /* This is knarly.
1013 If we backgrounded then we know the exact pid.
1014 Otherwise if we have a pidfile then it *may* know the exact pid.
1015 Failing that, we'll have to query processes.
1016 We sleep first as some programs like ntp like to fork, and write
1017 their pidfile a LONG time later. */
1018 if (background)
1019 {
1020 if (kill (pid, 0) == 0)
1021 alive = true;
1022 }
1023 else
1024 {
1025 if (pidfile && rc_exists (pidfile))
1026 {
1027 if (do_stop (NULL, NULL, pidfile, uid, 0, true, false, true) > 0)
1028 alive = true;
1029 }
1030 else
1031 {
1032 if (do_stop (exec, cmd, NULL, uid, 0, true, false, true) > 0)
1033 alive = true;
1034 }
1035 }
1036
1037 if (! alive)
1038 eerrorx ("%s: %s died", progname, exec);
1039
1040 if (timercmp (&now, &stopat, >))
1041 break;
1042 }
1043 }
1044
1045 if (svcname)
1046 rc_set_service_daemon (svcname, exec, cmd, pidfile, true);
1047
1048 exit (EXIT_SUCCESS);
1049 }

  ViewVC Help
Powered by ViewVC 1.1.20