]> mj.ucw.cz Git - libucw.git/blob - ucw/daemon.c
9f3273df73b6d438e4116d75f0c90464b99d9aa6
[libucw.git] / ucw / daemon.c
1 /*
2  *      UCW Library -- Daemonization
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/strtonum.h>
13 #include <ucw/process.h>
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <stdarg.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <pwd.h>
21 #include <grp.h>
22 #include <errno.h>
23 #include <signal.h>
24 #include <sys/file.h>
25 #include <sys/wait.h>
26
27 static void
28 daemon_resolve_ugid(struct daemon_params *dp)
29 {
30   // Resolve user name
31   const char *u = dp->run_as_user;
32   struct passwd *pw = NULL;
33   if (u)
34     {
35       if (u[0] == '#')
36         {
37           uns id;
38           const char *err = str_to_uns(&id, u, NULL, 10 | STN_WHOLE);
39           if (err)
40             die("Cannot parse user `%s': %s", u, err);
41           dp->run_as_uid = id;
42           dp->want_setuid = 1;
43         }
44       else
45         {
46           pw = getpwnam(u);
47           if (!pw)
48             die("No such user `%s'", u);
49           dp->run_as_uid = pw->pw_uid;
50           dp->want_setuid = 1;
51         }
52     }
53
54   // Resolve group name
55   const char *g = dp->run_as_group;
56   struct group *gr;
57   if (g)
58     {
59       if (g[0] == '#')
60         {
61           uns id;
62           const char *err = str_to_uns(&id, g, NULL, 10 | STN_WHOLE);
63           if (err)
64             die("Cannot parse group `%s': %s", g, err);
65           dp->run_as_gid = id;
66           dp->want_setgid = 1;
67         }
68       else
69         {
70           gr = getgrnam(g);
71           if (!gr)
72             die("No such group `%s'", g);
73           dp->run_as_gid = gr->gr_gid;
74           dp->want_setgid = 1;
75         }
76     }
77   else if (pw)
78     {
79       dp->run_as_gid = pw->pw_gid;
80       dp->want_setgid = 2;
81     }
82 }
83
84 void
85 daemon_init(struct daemon_params *dp)
86 {
87   daemon_resolve_ugid(dp);
88
89   if (dp->pid_file)
90     {
91       // Check that PID file path is absolute
92       if (!(dp->flags & DAEMON_FLAG_PRESERVE_CWD) && dp->pid_file[0] != '/')
93         die("Path to PID file `%s' must be absolute", dp->pid_file);
94
95       // Open PID file
96       dp->pid_fd = open(dp->pid_file, O_RDWR | O_CREAT, 0666);
97       if (dp->pid_fd < 0)
98         die("Cannot open `%s': %m", dp->pid_file);
99       int fl = fcntl(dp->pid_fd, F_GETFD);
100       if (fl < 0 || fcntl(dp->pid_fd, F_SETFD, fl | FD_CLOEXEC))
101         die("Cannot set FD_CLOEXEC: %m");
102
103       // Try to lock it with an exclusive lock
104       if (flock(dp->pid_fd, LOCK_EX | LOCK_NB) < 0)
105         {
106           if (errno == EINTR || errno == EWOULDBLOCK)
107             die("Daemon is already running (`%s' locked)", dp->pid_file);
108           else
109             die("Cannot lock `%s': %m", dp->pid_file);
110         }
111
112       // Make a note that the daemon is starting
113       if (write(dp->pid_fd, "(starting)\n", 11) != 11 ||
114           ftruncate(dp->pid_fd, 11) < 0)
115         die("Error writing `%s': %m", dp->pid_file);
116     }
117 }
118
119 void
120 daemon_run(struct daemon_params *dp, void (*body)(struct daemon_params *dp))
121 {
122   // Switch GID and UID
123   if (dp->want_setgid && setresgid(dp->run_as_gid, dp->run_as_gid, dp->run_as_gid) < 0)
124     die("Cannot set GID to %d: %m", (int) dp->run_as_gid);
125   if (dp->want_setgid > 1 && initgroups(dp->run_as_user, dp->run_as_gid) < 0)
126     die("Cannot initialize groups: %m");
127   if (dp->want_setuid && setresuid(dp->run_as_uid, dp->run_as_uid, dp->run_as_uid) < 0)
128     die("Cannot set UID to %d: %m", (int) dp->run_as_uid);
129
130   // Create a new session and close stdio
131   setsid();
132   close(0);
133   if (open("/dev/null", O_RDWR, 0) < 0 ||
134       dup2(0, 1) < 0)
135     die("Cannot redirect stdio to `/dev/null': %m");
136
137   // Set umask to a reasonable value
138   umask(022);
139
140   // Do not hold the current working directory
141   if (!(dp->flags & DAEMON_FLAG_PRESERVE_CWD))
142     {
143       if (chdir("/") < 0)
144         die("Cannot chdir to root: %m");
145     }
146
147   // Fork
148   pid_t pid = fork();
149   if (pid < 0)
150     die("Cannot fork: %m");
151   if (!pid)
152     {
153       // We still keep the PID file open and thus locked
154       body(dp);
155       exit(0);
156     }
157
158   // Write PID
159   if (dp->pid_file)
160     {
161       char buf[32];
162       int c = snprintf(buf, sizeof(buf), "%d\n", (int) pid);
163       ASSERT(c <= (int) sizeof(buf));
164       if (lseek(dp->pid_fd, 0, SEEK_SET) < 0 ||
165           write(dp->pid_fd, buf, c) != c ||
166           ftruncate(dp->pid_fd, c) ||
167           close(dp->pid_fd) < 0)
168         die("Cannot write PID to `%s': %m", dp->pid_file);
169     }
170 }
171
172 void
173 daemon_exit(struct daemon_params *dp)
174 {
175   if (dp->pid_file)
176     {
177       if (unlink(dp->pid_file) < 0)
178         msg(L_ERROR, "Cannot unlink PID file `%s': %m", dp->pid_file);
179       close(dp->pid_fd);
180     }
181 }
182
183 static enum daemon_control_status
184 daemon_control_err(struct daemon_control_params *dc, char *msg, ...)
185 {
186   va_list args;
187   va_start(args, msg);
188   vsnprintf(dc->error_msg, DAEMON_ERR_LEN, msg, args);
189   va_end(args);
190   return DAEMON_STATUS_ERROR;
191 }
192
193 static int
194 daemon_read_pid(struct daemon_control_params *dc, int will_wait)
195 {
196   int pid_fd = open(dc->pid_file, O_RDONLY);
197   if (pid_fd < 0)
198     {
199       if (errno == ENOENT)
200         return 0;
201       daemon_control_err(dc, "Cannot open PID file `%s': %m", dc->pid_file);
202       return -1;
203     }
204
205   if (flock(pid_fd, LOCK_EX | (will_wait ? 0 : LOCK_NB)) >= 0)
206     {
207       // The lock file is stale
208       close(pid_fd);
209       return 0;
210     }
211
212   if (errno != EINTR && errno != EWOULDBLOCK)
213     {
214       daemon_control_err(dc, "Cannot lock PID file `%s': %m", dc->pid_file);
215       goto fail;
216     }
217
218   char buf[16];
219   int n = read(pid_fd, buf, sizeof(buf));
220   if (n < 0)
221     {
222       daemon_control_err(dc, "Error reading `%s': %m", dc->pid_file);
223       goto fail;
224     }
225   if (n == (int) sizeof(buf))
226     {
227       daemon_control_err(dc, "PID file `%s' is too long", dc->pid_file);
228       goto fail;
229     }
230   buf[n] = 0;
231   int pid = atoi(buf);
232   if (!pid)
233     {
234       daemon_control_err(dc, "PID file `%s' does not contain a valid PID", dc->pid_file);
235       goto fail;
236     }
237   close(pid_fd);
238   return pid;
239
240 fail:
241   close(pid_fd);
242   return -1;
243 }
244
245 enum daemon_control_status
246 daemon_control(struct daemon_control_params *dc)
247 {
248   enum daemon_control_status st = DAEMON_STATUS_ERROR;
249
250   int guard_fd = open(dc->guard_file, O_RDWR | O_CREAT, 0666);
251   if (guard_fd < 0)
252     return daemon_control_err(dc, "Cannot open guard file `%s': %m", dc->guard_file);
253   if (flock(guard_fd, LOCK_EX) < 0)
254     return daemon_control_err(dc, "Cannot lock guard file `%s': %m", dc->guard_file);
255
256   // Read the PID file
257   int pid = daemon_read_pid(dc, 0);
258   if (pid < 0)
259     goto done;
260
261   switch (dc->action)
262     {
263     case DAEMON_CONTROL_CHECK:
264       if (pid)
265         st = DAEMON_STATUS_OK;
266       else
267         st = DAEMON_STATUS_NOT_RUNNING;
268       break;
269     case DAEMON_CONTROL_START:
270       if (pid)
271         st = DAEMON_STATUS_ALREADY_DONE;
272       else
273         {
274           pid_t pp = fork();
275           if (pp < 0)
276             {
277               daemon_control_err(dc, "Cannot fork: %m");
278               goto done;
279             }
280           if (pp)
281             {
282               close(guard_fd);
283               execvp(dc->argv[0], dc->argv);
284               fprintf(stderr, "Cannot execute `%s': %m\n", dc->argv[0]);
285               exit(DAEMON_STATUS_ERROR);
286             }
287           int stat;
288           int ec = waitpid(pp, &stat, 0);
289           if (ec < 0)
290             {
291               daemon_control_err(dc, "Cannot wait: %m");
292               goto done;
293             }
294           if (WIFEXITED(stat) && WEXITSTATUS(stat) == DAEMON_STATUS_ERROR)
295             {
296               daemon_control_err(dc, "Cannot execute the daemon");
297               goto done;
298             }
299           char ecmsg[EXIT_STATUS_MSG_SIZE];
300           if (format_exit_status(ecmsg, stat))
301             {
302               daemon_control_err(dc, "Daemon %s", ecmsg);
303               goto done;
304             }
305           pid = daemon_read_pid(dc, 0);
306           if (!pid)
307             daemon_control_err(dc, "Daemon failed to write the PID file `%s'", dc->pid_file);
308           else
309             st = DAEMON_STATUS_OK;
310         }
311       break;
312     case DAEMON_CONTROL_STOP:
313       if (!pid)
314         return DAEMON_STATUS_ALREADY_DONE;
315       int sig = dc->signal ? : SIGTERM;
316       if (kill(pid, sig) < 0)
317         {
318           daemon_control_err(dc, "Cannot send signal %d: %m", dc->signal);
319           goto done;
320         }
321       pid = daemon_read_pid(dc, 1);
322       ASSERT(pid <= 0);
323       if (!pid)
324         st = DAEMON_STATUS_OK;
325       break;
326     case DAEMON_CONTROL_SIGNAL:
327       if (!pid)
328         return DAEMON_STATUS_NOT_RUNNING;
329       if (kill(pid, dc->signal) < 0)
330         daemon_control_err(dc, "Cannot send signal %d: %m", dc->signal);
331       else
332         st = DAEMON_STATUS_OK;
333       break;
334     default:
335       ASSERT(0);
336     }
337
338 done:
339   close(guard_fd);
340   return st;
341 }
342
343 #ifdef TEST
344
345 static void body(struct daemon_params *dp)
346 {
347   log_fork();
348   msg(L_INFO, "Daemon is running");
349   msg(L_INFO, "uid=%d/%d gid=%d/%d", (int) getuid(), (int) geteuid(), (int) getgid(), (int) getegid());
350   sleep(60);
351   msg(L_INFO, "Daemon is shutting down");
352   daemon_exit(dp);
353 }
354
355 int main(int argc, char **argv)
356 {
357   struct daemon_params dp = {
358     .pid_file = "/tmp/123",
359   };
360
361   int opt;
362   while ((opt = getopt(argc, argv, "p:u:g:")) >= 0)
363     switch (opt)
364       {
365       case 'p':
366         dp.pid_file = optarg;
367         break;
368       case 'u':
369         dp.run_as_user = optarg;
370         break;
371       case 'g':
372         dp.run_as_group = optarg;
373         break;
374       default:
375         die("Invalid arguments");
376       }
377
378   daemon_init(&dp);
379   daemon_run(&dp, body);
380   msg(L_INFO, "Main program has ended");
381   return 0;
382 }
383
384 #endif