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