/[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 2882 - (show annotations) (download) (as text)
Tue Sep 18 11:36:55 2007 UTC (7 years, 3 months ago) by uberlord
File MIME type: text/x-csrc
File size: 23125 byte(s)
    API change! rc_strlist_add and friends now take char *** instead of
    char ** and return a pointer to the item added instead of the new
    list head. This is so we can easily tell if the item was successfully
    added or not instead of iterating through the list looking for it.

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

  ViewVC Help
Powered by ViewVC 1.1.20