]> mj.ucw.cz Git - libucw.git/blob - ucw/daemon-ctrl.c
UCW::CGI: Announce supported methods correctly
[libucw.git] / ucw / daemon-ctrl.c
1 /*
2  *      UCW Library -- Daemon Control
3  *
4  *      (c) 2012 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #include <ucw/lib.h>
11 #include <ucw/daemon.h>
12 #include <ucw/process.h>
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <stdarg.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <signal.h>
21 #include <sys/file.h>
22 #include <sys/wait.h>
23
24 static enum daemon_control_status
25 daemon_control_err(struct daemon_control_params *dc, char *msg, ...)
26 {
27   va_list args;
28   va_start(args, msg);
29   vsnprintf(dc->error_msg, DAEMON_ERR_LEN, msg, args);
30   va_end(args);
31   return DAEMON_STATUS_ERROR;
32 }
33
34 static int
35 daemon_read_pid(struct daemon_control_params *dc, int will_wait, int *stalep)
36 {
37   *stalep = 0;
38
39   int pid_fd = open(dc->pid_file, O_RDONLY);
40   if (pid_fd < 0)
41     {
42       if (errno == ENOENT)
43         return 0;
44       daemon_control_err(dc, "Cannot open PID file `%s': %m", dc->pid_file);
45       return -1;
46     }
47
48   if (flock(pid_fd, LOCK_EX | (will_wait ? 0 : LOCK_NB)) >= 0)
49     {
50       // The lock file is stale
51       close(pid_fd);
52       *stalep = 1;
53       return 0;
54     }
55
56   if (errno != EINTR && errno != EWOULDBLOCK)
57     {
58       daemon_control_err(dc, "Cannot lock PID file `%s': %m", dc->pid_file);
59       goto fail;
60     }
61
62   char buf[16];
63   int n = read(pid_fd, buf, sizeof(buf));
64   if (n < 0)
65     {
66       daemon_control_err(dc, "Error reading `%s': %m", dc->pid_file);
67       goto fail;
68     }
69   if (n == (int) sizeof(buf))
70     {
71       daemon_control_err(dc, "PID file `%s' is too long", dc->pid_file);
72       goto fail;
73     }
74   buf[n] = 0;
75   int pid = atoi(buf);
76   if (!pid)
77     {
78       daemon_control_err(dc, "PID file `%s' does not contain a valid PID", dc->pid_file);
79       goto fail;
80     }
81   close(pid_fd);
82   return pid;
83
84 fail:
85   close(pid_fd);
86   return -1;
87 }
88
89 enum daemon_control_status
90 daemon_control(struct daemon_control_params *dc)
91 {
92   enum daemon_control_status st = DAEMON_STATUS_ERROR;
93   int sig, stale, stale2;
94
95   int guard_fd = open(dc->guard_file, O_RDWR | O_CREAT, 0666);
96   if (guard_fd < 0)
97     return daemon_control_err(dc, "Cannot open guard file `%s': %m", dc->guard_file);
98   if (flock(guard_fd, LOCK_EX) < 0)
99     return daemon_control_err(dc, "Cannot lock guard file `%s': %m", dc->guard_file);
100
101   // Read the PID file
102   int pid = daemon_read_pid(dc, 0, &stale);
103   if (pid < 0)
104     goto done;
105
106   switch (dc->action)
107     {
108     case DAEMON_CONTROL_CHECK:
109       if (pid)
110         st = DAEMON_STATUS_OK;
111       else if (stale)
112         st = DAEMON_STATUS_STALE;
113       else
114         st = DAEMON_STATUS_NOT_RUNNING;
115       break;
116     case DAEMON_CONTROL_START:
117       if (pid)
118         st = DAEMON_STATUS_ALREADY_DONE;
119       else
120         {
121           pid_t pp = fork();
122           if (pp < 0)
123             {
124               daemon_control_err(dc, "Cannot fork: %m");
125               goto done;
126             }
127           if (!pp)
128             {
129               close(guard_fd);
130               execvp(dc->argv[0], dc->argv);
131               fprintf(stderr, "Cannot execute `%s': %m\n", dc->argv[0]);
132               exit(DAEMON_STATUS_ERROR);
133             }
134           int stat;
135           int ec = waitpid(pp, &stat, 0);
136           if (ec < 0)
137             {
138               daemon_control_err(dc, "Cannot wait: %m");
139               goto done;
140             }
141           if (WIFEXITED(stat) && WEXITSTATUS(stat) == DAEMON_STATUS_ERROR)
142             {
143               daemon_control_err(dc, "Cannot execute the daemon");
144               goto done;
145             }
146           char ecmsg[EXIT_STATUS_MSG_SIZE];
147           if (format_exit_status(ecmsg, stat))
148             {
149               daemon_control_err(dc, "Daemon %s %s", dc->argv[0], ecmsg);
150               goto done;
151             }
152           pid = daemon_read_pid(dc, 0, &stale2);
153           if (!pid)
154             daemon_control_err(dc, "Daemon %s failed to write the PID file `%s'", dc->argv[0], dc->pid_file);
155           else
156             st = stale ? DAEMON_STATUS_STALE : DAEMON_STATUS_OK;
157         }
158       break;
159     case DAEMON_CONTROL_STOP:
160       if (!pid)
161         return stale ? DAEMON_STATUS_STALE : DAEMON_STATUS_ALREADY_DONE;
162       sig = dc->signal ? : SIGTERM;
163       if (kill(pid, sig) < 0)
164         {
165           daemon_control_err(dc, "Cannot send signal %d: %m", sig);
166           goto done;
167         }
168       pid = daemon_read_pid(dc, 1, &stale2);
169       ASSERT(pid <= 0);
170       if (!pid)
171         st = DAEMON_STATUS_OK;
172       break;
173     case DAEMON_CONTROL_SIGNAL:
174       if (!pid)
175         return DAEMON_STATUS_NOT_RUNNING;
176       sig = dc->signal ? : SIGHUP;
177       if (kill(pid, sig) < 0)
178         daemon_control_err(dc, "Cannot send signal %d: %m", sig);
179       else
180         st = DAEMON_STATUS_OK;
181       break;
182     default:
183       ASSERT(0);
184     }
185
186 done:
187   close(guard_fd);
188   return st;
189 }