/[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 2838 - (show annotations) (download) (as text)
Fri Aug 17 11:01:18 2007 UTC (6 years, 11 months ago) by uberlord
File MIME type: text/x-csrc
File size: 23365 byte(s)
Intergrate our standard options
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 'c': /* --chuid <username>|<uid> */
569 {
570 char *p = optarg;
571 char *cu = strsep (&p, ":");
572 struct passwd *pw = NULL;
573
574 changeuser = rc_xstrdup (cu);
575 if (sscanf (cu, "%d", &tid) != 1)
576 pw = getpwnam (cu);
577 else
578 pw = getpwuid (tid);
579
580 if (! pw)
581 eerrorx ("%s: user `%s' not found", applet, cu);
582 uid = pw->pw_uid;
583 if (! gid)
584 gid = pw->pw_gid;
585
586 if (p) {
587 struct group *gr = NULL;
588 char *cg = strsep (&p, ":");
589
590 if (sscanf (cg, "%d", &tid) != 1)
591 gr = getgrnam (cg);
592 else
593 gr = getgrgid (tid);
594
595 if (! gr)
596 eerrorx ("%s: group `%s' not found", applet, cg);
597 gid = gr->gr_gid;
598 }
599 }
600 break;
601
602 case 'd': /* --chdir /new/dir */
603 ch_dir = optarg;
604 break;
605
606 case 'g': /* --group <group>|<gid> */
607 {
608 struct group *gr = getgrnam (optarg);
609
610 if (sscanf (optarg, "%d", &tid) != 1)
611 gr = getgrnam (optarg);
612 else
613 gr = getgrgid (tid);
614
615 if (! gr)
616 eerrorx ("%s: group `%s' not found", applet, optarg);
617 gid = gr->gr_gid;
618 }
619 break;
620
621 case 'm': /* --make-pidfile */
622 makepidfile = true;
623 break;
624
625 case 'n': /* --name <process-name> */
626 cmd = optarg;
627 break;
628
629 case 'o': /* --oknodo */
630 oknodo = true;
631 break;
632
633 case 'p': /* --pidfile <pid-file> */
634 pidfile = optarg;
635 break;
636
637 case 'q': /* --quiet */
638 quiet = true;
639 break;
640
641 case 's': /* --signal <signal> */
642 sig = parse_signal (optarg);
643 break;
644
645 case 't': /* --test */
646 test = true;
647 break;
648
649 case 'u': /* --user <username>|<uid> */
650 if (sscanf (optarg, "%d", &tid) != 1) {
651 struct passwd *pw = getpwnam (optarg);
652 if (! pw)
653 eerrorx ("%s: user `%s' not found", applet, optarg);
654 uid = pw->pw_uid;
655 } else
656 uid = tid;
657 break;
658
659 case 'r': /* --chroot /new/root */
660 ch_root = optarg;
661 break;
662
663 case 'v': /* --verbose */
664 verbose = true;
665 break;
666
667 case 'a':
668 case 'x': /* --exec <executable> */
669 exec = optarg;
670 break;
671
672 case '1': /* --stdout /path/to/stdout.lgfile */
673 redirect_stdout = optarg;
674 break;
675
676 case '2': /* --stderr /path/to/stderr.logfile */
677 redirect_stderr = optarg;
678 break;
679
680 case_RC_COMMON_GETOPT
681 }
682
683 /* Respect RC as well as how we are called */
684 if (rc_is_env ("RC_QUIET", "yes") && ! verbose)
685 quiet = true;
686
687 /* Allow start-stop-daemon --signal HUP --exec /usr/sbin/dnsmasq
688 * instead of forcing --stop --oknodo as well */
689 if (! start && ! stop)
690 if (sig != SIGINT &&
691 sig != SIGTERM &&
692 sig != SIGQUIT &&
693 sig != SIGKILL)
694 {
695 oknodo = true;
696 stop = true;
697 }
698
699 if (start == stop)
700 eerrorx ("%s: need one of --start or --stop", applet);
701
702 if (start && ! exec)
703 eerrorx ("%s: --start needs --exec", applet);
704
705 if (stop && ! exec && ! pidfile && ! cmd && ! uid)
706 eerrorx ("%s: --stop needs --exec, --pidfile, --name or --user", applet);
707
708 if (makepidfile && ! pidfile)
709 eerrorx ("%s: --make-pidfile is only relevant with --pidfile", applet);
710
711 if (background && ! start)
712 eerrorx ("%s: --background is only relevant with --start", applet);
713
714 if ((redirect_stdout || redirect_stderr) && ! background)
715 eerrorx ("%s: --stdout and --stderr are only relevant with --background",
716 applet);
717
718 argc -= optind;
719 argv += optind;
720
721 /* Validate that the binary exists if we are starting */
722 if (exec && start) {
723 char *tmp;
724 if (ch_root)
725 tmp = rc_strcatpaths (ch_root, exec, (char *) NULL);
726 else
727 tmp = exec;
728 if (! rc_is_file (tmp)) {
729 eerror ("%s: %s does not exist", applet, tmp);
730 if (ch_root)
731 free (tmp);
732 exit (EXIT_FAILURE);
733 }
734 if (ch_root)
735 free (tmp);
736 }
737
738 if (stop) {
739 int result;
740
741 if (! schedule) {
742 if (test || oknodo)
743 parse_schedule ("0", sig);
744 else
745 parse_schedule (NULL, sig);
746 }
747
748 result = run_stop_schedule (exec, cmd, pidfile, uid, quiet, verbose, test);
749 if (test || oknodo)
750 return (result > 0 ? EXIT_SUCCESS : EXIT_FAILURE);
751 if (result < 1)
752 exit (result == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
753
754 if (pidfile && rc_is_file (pidfile))
755 unlink (pidfile);
756
757 if (svcname)
758 rc_set_service_daemon (svcname, exec, cmd, pidfile, false);
759
760 exit (EXIT_SUCCESS);
761 }
762
763 if (do_stop (exec, cmd, pidfile, uid, 0, true, false, true) > 0)
764 eerrorx ("%s: %s is already running", applet, exec);
765
766 if (test) {
767 if (quiet)
768 exit (EXIT_SUCCESS);
769
770 einfon ("Would start %s", exec);
771 while (argc-- > 0)
772 printf("%s ", *argv++);
773 printf ("\n");
774 eindent ();
775 if (uid != 0)
776 einfo ("as user id %d", uid);
777 if (gid != 0)
778 einfo ("as group id %d", gid);
779 if (ch_root)
780 einfo ("in root `%s'", ch_root);
781 if (ch_dir)
782 einfo ("in dir `%s'", ch_dir);
783 if (nicelevel != 0)
784 einfo ("with a priority of %d", nicelevel);
785 eoutdent ();
786 exit (EXIT_SUCCESS);
787 }
788
789 /* Ensure this is unset, so if the daemon does /etc/init.d/foo
790 Then we filter the environment accordingly */
791 unsetenv ("RC_SOFTLEVEL");
792
793 if (verbose) {
794 ebegin ("Detaching to start `%s'", exec);
795 eindent ();
796 }
797
798 if (background)
799 signal (SIGCHLD, handle_signal);
800
801 *--argv = exec;
802 if ((pid = fork ()) == -1)
803 eerrorx ("%s: fork: %s", applet, strerror (errno));
804
805 /* Child process - lets go! */
806 if (pid == 0) {
807 pid_t mypid = getpid ();
808
809 #ifdef TIOCNOTTY
810 tty_fd = open("/dev/tty", O_RDWR);
811 #endif
812
813 devnull_fd = open("/dev/null", O_RDWR);
814
815 if (nicelevel) {
816 if (setpriority (PRIO_PROCESS, mypid, nicelevel) == -1)
817 eerrorx ("%s: setpritory %d: %s", applet, nicelevel,
818 strerror(errno));
819 }
820
821 if (ch_root && chroot (ch_root) < 0)
822 eerrorx ("%s: chroot `%s': %s", applet, ch_root, strerror (errno));
823
824 if (ch_dir && chdir (ch_dir) < 0)
825 eerrorx ("%s: chdir `%s': %s", applet, ch_dir, strerror (errno));
826
827 if (makepidfile && pidfile) {
828 FILE *fp = fopen (pidfile, "w");
829 if (! fp)
830 eerrorx ("%s: fopen `%s': %s", applet, pidfile, strerror
831 (errno));
832 fprintf (fp, "%d\n", mypid);
833 fclose (fp);
834 }
835
836 #ifdef HAVE_PAM
837 if (changeuser != NULL)
838 pamr = pam_start ("start-stop-daemon", changeuser, &conv, &pamh);
839 else
840 pamr = pam_start ("start-stop-daemon", "nobody", &conv, &pamh);
841
842 if (pamr == PAM_SUCCESS)
843 pamr = pam_authenticate (pamh, PAM_SILENT);
844 if (pamr == PAM_SUCCESS)
845 pamr = pam_acct_mgmt (pamh, PAM_SILENT);
846 if (pamr == PAM_SUCCESS)
847 pamr = pam_open_session (pamh, PAM_SILENT);
848 if (pamr != PAM_SUCCESS)
849 eerrorx ("%s: pam error: %s", applet, pam_strerror(pamh, pamr));
850 #endif
851
852 if (gid && setgid (gid))
853 eerrorx ("%s: unable to set groupid to %d", applet, gid);
854 if (changeuser && initgroups (changeuser, gid))
855 eerrorx ("%s: initgroups (%s, %d)", applet, changeuser, gid);
856 if (uid && setuid (uid))
857 eerrorx ("%s: unable to set userid to %d", applet, uid);
858 else {
859 struct passwd *passwd = getpwuid (uid);
860 if (passwd) {
861 unsetenv ("HOME");
862 if (passwd->pw_dir)
863 setenv ("HOME", passwd->pw_dir, 1);
864 unsetenv ("USER");
865 if (passwd->pw_name)
866 setenv ("USER", passwd->pw_name, 1);
867 }
868 }
869
870 /* Close any fd's to the passwd database */
871 endpwent ();
872
873 #ifdef TIOCNOTTY
874 ioctl(tty_fd, TIOCNOTTY, 0);
875 close(tty_fd);
876 #endif
877
878 /* Clean the environment of any RC_ variables */
879 STRLIST_FOREACH (environ, env, i)
880 if (env && strncmp (env, "RC_", 3) != 0) {
881 /* For the path r, remove the rcscript bin dir from it */
882 if (strncmp (env, "PATH=" RC_LIBDIR "/bin:",
883 strlen ("PATH=" RC_LIBDIR "/bin:")) == 0)
884 {
885 char *path = env;
886 char *newpath;
887 int len;
888 path += strlen ("PATH=" RC_LIBDIR "/bin:");
889 len = sizeof (char *) * strlen (path) + 6;
890 newpath = rc_xmalloc (len);
891 snprintf (newpath, len, "PATH=%s", path);
892 newenv = rc_strlist_add (newenv, newpath);
893 free (newpath);
894 } else
895 newenv = rc_strlist_add (newenv, env);
896 }
897
898 umask (022);
899
900 stdout_fd = devnull_fd;
901 stderr_fd = devnull_fd;
902 if (redirect_stdout) {
903 if ((stdout_fd = open (redirect_stdout, O_WRONLY | O_CREAT | O_APPEND,
904 S_IRUSR | S_IWUSR)) == -1)
905 eerrorx ("%s: unable to open the logfile for stdout `%s': %s",
906 applet, redirect_stdout, strerror (errno));
907 }
908 if (redirect_stderr) {
909 if ((stderr_fd = open (redirect_stderr, O_WRONLY | O_CREAT | O_APPEND,
910 S_IRUSR | S_IWUSR)) == -1)
911 eerrorx ("%s: unable to open the logfile for stderr `%s': %s",
912 applet, redirect_stderr, strerror (errno));
913 }
914
915 if (background) {
916 /* Hmmm, some daemons may need stdin? */
917 dup2 (devnull_fd, STDIN_FILENO);
918 dup2 (stdout_fd, STDOUT_FILENO);
919 dup2 (stderr_fd, STDERR_FILENO);
920 }
921
922 for (i = getdtablesize () - 1; i >= 3; --i)
923 close(i);
924
925 setsid ();
926
927 execve (exec, argv, newenv);
928 #ifdef HAVE_PAM
929 if (pamr == PAM_SUCCESS)
930 pam_close_session (pamh, PAM_SILENT);
931 #endif
932 eerrorx ("%s: failed to exec `%s': %s", applet, exec, strerror (errno));
933 }
934
935 /* Parent process */
936 if (! background) {
937 /* As we're not backgrounding the process, wait for our pid to return */
938 int status = 0;
939 int savepid = pid;
940
941 errno = 0;
942 do {
943 pid = waitpid (savepid, &status, 0);
944 if (pid < 1) {
945 eerror ("waitpid %d: %s", savepid, strerror (errno));
946 return (-1);
947 }
948 } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
949
950 if (! WIFEXITED (status) || WEXITSTATUS (status) != 0) {
951 if (! quiet)
952 eerrorx ("%s: failed to start `%s'", applet, exec);
953 exit (EXIT_FAILURE);
954 }
955
956 pid = savepid;
957 }
958
959 /* Wait a little bit and check that process is still running
960 We do this as some badly written daemons fork and then barf */
961 if (START_WAIT > 0) {
962 struct timespec ts;
963 int nloops = START_WAIT / POLL_INTERVAL;
964 bool alive = false;
965 bool retestpid = false;
966
967 ts.tv_sec = 0;
968 ts.tv_nsec = POLL_INTERVAL;
969
970 while (nloops) {
971 if (nanosleep (&ts, NULL) == -1) {
972 if (errno == EINTR)
973 eerror ("%s: caught an interupt", applet);
974 else {
975 eerror ("%s: nanosleep: %s", applet, strerror (errno));
976 return (0);
977 }
978 }
979 nloops --;
980
981 /* This is knarly.
982 If we backgrounded then we know the exact pid.
983 Otherwise if we have a pidfile then it *may* know the exact pid.
984 Failing that, we'll have to query processes.
985 We sleep first as some programs like ntp like to fork, and write
986 their pidfile a LONG time later. */
987 if (background) {
988 if (kill (pid, 0) == 0)
989 alive = true;
990 } else {
991 if (pidfile) {
992 /* The pidfile may not have been written yet - give it some time */
993 if (get_pid (pidfile, true) == -1) {
994 alive = true;
995 retestpid = true;
996 } else {
997 retestpid = false;
998 if (do_stop (NULL, NULL, pidfile, uid, 0,
999 true, false, true) > 0)
1000 alive = true;
1001 }
1002 } else {
1003 if (do_stop (exec, cmd, NULL, uid, 0, true, false, true)
1004 > 0)
1005 alive = true;
1006 }
1007 }
1008
1009 if (! alive)
1010 eerrorx ("%s: %s died", applet, exec);
1011 }
1012
1013 if (retestpid) {
1014 if (do_stop (NULL, NULL, pidfile, uid, 0, true,
1015 false, true) < 1)
1016 eerrorx ("%s: %s died", applet, exec);
1017 }
1018 }
1019
1020 if (svcname)
1021 rc_set_service_daemon (svcname, exec, cmd, pidfile, true);
1022
1023 exit (EXIT_SUCCESS);
1024 }

  ViewVC Help
Powered by ViewVC 1.1.20