]> mj.ucw.cz Git - eval.git/blob - ucw/utils/daemon-helper.c
Added a paranoid test for evaluation order
[eval.git] / ucw / utils / daemon-helper.c
1 /*
2  *      A Simple Wrapper for Starting and Stopping of Daemons
3  *
4  *      (c) 2003 Martin Mares <mj@ucw.cz>
5  *
6  *      It would seem that we are reinventing the wheel and the
7  *      start-stop-daemon command present in most Linux distributions
8  *      is just what we need, but the usual "does the process already
9  *      exist?" strategies fail in presence of multiple running daemons.
10  *
11  *      Return codes:
12  *      101     already running
13  *      102     not running
14  */
15
16 #include "ucw/lib.h"
17
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <signal.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <getopt.h>
25 #include <errno.h>
26 #include <alloca.h>
27
28 enum action {
29   ACTION_NONE,
30   ACTION_START,
31   ACTION_STOP,
32   ACTION_FORCE_STOP,
33   ACTION_CHECK,
34   ACTION_RELOAD
35 };
36
37 static int action;
38
39 static struct option options[] = {
40   { "pid-file",         required_argument,      NULL, 'p' },
41   { "status-file",      required_argument,      NULL, 's' },
42   { "start",            no_argument,            &action, ACTION_START },
43   { "stop",             no_argument,            &action, ACTION_STOP },
44   { "force-stop",       no_argument,            &action, ACTION_FORCE_STOP },
45   { "check",            no_argument,            &action, ACTION_CHECK },
46   { "reload",           no_argument,            &action, ACTION_RELOAD },
47   { NULL,               no_argument,            NULL, 0 }
48 };
49
50 static void NONRET
51 usage(void)
52 {
53   fputs("\n\
54 Usage: daemon-helper --start <options> -- <daemon> <args>\n\
55    or: daemon-helper --stop <options>\n\
56    or: daemon-helper --force-stop <options>\n\
57    or: daemon-helper --reload <options>\n\
58    or: daemon-helper --check <options>\n\
59 \n\
60 Options:\n\
61 --pid-file <name>       Name of PID file for this daemon (mandatory)\n\
62 --status-file <name>    Status file used by the daemon (deleted just before starting)\n\
63 ", stderr);
64   exit(1);
65 }
66
67 int
68 main(int argc, char **argv)
69 {
70   int c, fd;
71   char *pidfile = NULL;
72   char *statfile = NULL;
73   struct flock fl;
74   char buf[64];
75
76   while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0)
77     switch (c)
78       {
79       case 0:
80         break;
81       case 'p':
82         pidfile = optarg;
83         break;
84       case 's':
85         statfile = optarg;
86         break;
87       default:
88         usage();
89       }
90   if (!pidfile)
91     usage();
92
93   bzero(&fl, sizeof(fl));
94   fl.l_type = F_WRLCK;
95   fl.l_whence = SEEK_SET;
96
97   switch (action)
98     {
99     case ACTION_START:
100       if (optind >= argc)
101         usage();
102       fd = open(pidfile, O_RDWR | O_CREAT, 0666);
103       if (fd < 0)
104         die("Unable to create %s: %m", pidfile);
105       if ((c = fcntl(fd, F_SETLK, &fl)) < 0)
106         {
107           if (errno == EAGAIN || errno == EACCES)
108             return 101;
109           else
110             die("fcntl lock on %s failed: %m", pidfile);
111         }
112       c = sprintf(buf, "%d\n", getpid());
113       if (write(fd, buf, c) != c)
114         die("write on %s failed: %m", pidfile);
115       if (ftruncate(fd, c) < 0)
116         die("truncate on %s failed: %m", pidfile);
117       if (statfile && unlink(statfile) < 0 && errno != ENOENT)
118         die("unlink(%s) failed: %m", statfile);
119       setsid();
120       /* Disconnect from stdin and stdout, leave stderr to the daemon. */
121       close(0);
122       open("/dev/null", O_RDWR, 0);
123       dup2(0, 1);
124       argv += optind;
125       argc -= optind;
126       char **a = alloca(sizeof(char *) * (argc+1));
127       memcpy(a, argv, sizeof(char *) * argc);
128       a[argc] = NULL;
129       execv(a[0], a);
130       die("Cannot execute %s: %m", a[0]);
131     case ACTION_STOP:
132     case ACTION_FORCE_STOP:
133     case ACTION_CHECK:
134     case ACTION_RELOAD:
135       if (optind < argc)
136         usage();
137       fd = open(pidfile, O_RDWR);
138       if (fd < 0)
139         {
140           if (errno == ENOENT)
141             return 102;
142           else
143             die("Unable to open %s: %m", pidfile);
144         }
145       if ((c = fcntl(fd, F_SETLK, &fl)) >= 0)
146         {
147         nopid:
148           unlink(pidfile);
149           return 102;
150         }
151       if (errno != EAGAIN && errno != EACCES)
152         die("fcntl lock on %s failed: %m", pidfile);
153       if ((c = read(fd, buf, sizeof(buf))) < 0)
154         die("read on %s failed: %m", pidfile);
155       if (!c)
156         goto nopid;
157       if (c >= (int) sizeof(buf) || sscanf(buf, "%d", &c) != 1)
158         die("PID file syntax error");
159       int sig = 0;
160       if (action == ACTION_CHECK || action == ACTION_RELOAD)
161         {
162           if (action == ACTION_RELOAD)
163             sig = SIGHUP;
164           if (kill(c, sig) < 0 && errno == ESRCH)
165             goto nopid;
166           return 0;
167         }
168       sig = (action == ACTION_STOP) ? SIGTERM : SIGQUIT;
169       if (kill(c, sig) < 0)
170         {
171           if (errno == ESRCH)
172             goto nopid;
173           die("Cannot kill process %d: %m", c);
174         }
175       if ((c = fcntl(fd, F_SETLKW, &fl)) < 0)
176         die("Cannot lock %s: %m", pidfile);
177       if (statfile)
178         unlink(statfile);
179       if (unlink(pidfile) < 0)
180         die("Cannot unlink %s: %m", pidfile);
181       return 0;
182     default:
183       usage();
184     }
185 }