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

  ViewVC Help
Powered by ViewVC 1.1.20