/[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 2547 - (hide annotations) (download) (as text)
Thu Apr 5 11:18:42 2007 UTC (11 years, 8 months ago) by uberlord
File MIME type: text/x-csrc
File size: 23678 byte(s)
    Rewrite the core parts in C. We now provide librc so other programs can
    query runlevels, services and state without using bash. We also provide
    libeinfo so other programs can easily use our informational functions.

    As such, we have dropped the requirement of using bash as the init script
    shell. We now use /bin/sh and have strived to make the scripts as portable
    as possible. Shells that work are bash and dash. busybox works provided
    you disable s-s-d. If you have WIPE_TMP set to yes in conf.d/bootmisc you
    should disable find too.
    zsh and ksh do not work at this time.

    Networking support is currently being re-vamped also as it was heavily bash
    array based. As such, a new config format is available like so
    config_eth0="1.2.3.4/24 5.6.7.8/16"
    or like so
    config_eth0="'1.2.3.4 netmask 255.255.255.0' '5.6.7.8 netmask 255.255.0.0'"

    We will still support the old bash array format provided that /bin/sh IS
    a link it bash.

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

  ViewVC Help
Powered by ViewVC 1.1.20