]> mj.ucw.cz Git - libucw.git/blob - ucw/daemon-ctrl.c
8d9bf72fbc62b6a1dda99e7bef03d56a7f563410
[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)
36 {
37   int pid_fd = open(dc->pid_file, O_RDONLY);
38   if (pid_fd < 0)
39     {
40       if (errno == ENOENT)
41         return 0;
42       daemon_control_err(dc, "Cannot open PID file `%s': %m", dc->pid_file);
43       return -1;
44     }
45
46   if (flock(pid_fd, LOCK_EX | (will_wait ? 0 : LOCK_NB)) >= 0)
47     {
48       // The lock file is stale
49       close(pid_fd);
50       return 0;
51     }
52
53   if (errno != EINTR && errno != EWOULDBLOCK)
54     {
55       daemon_control_err(dc, "Cannot lock PID file `%s': %m", dc->pid_file);
56       goto fail;
57     }
58
59   char buf[16];
60   int n = read(pid_fd, buf, sizeof(buf));
61   if (n < 0)
62     {
63       daemon_control_err(dc, "Error reading `%s': %m", dc->pid_file);
64       goto fail;
65     }
66   if (n == (int) sizeof(buf))
67     {
68       daemon_control_err(dc, "PID file `%s' is too long", dc->pid_file);
69       goto fail;
70     }
71   buf[n] = 0;
72   int pid = atoi(buf);
73   if (!pid)
74     {
75       daemon_control_err(dc, "PID file `%s' does not contain a valid PID", dc->pid_file);
76       goto fail;
77     }
78   close(pid_fd);
79   return pid;
80
81 fail:
82   close(pid_fd);
83   return -1;
84 }
85
86 enum daemon_control_status
87 daemon_control(struct daemon_control_params *dc)
88 {
89   enum daemon_control_status st = DAEMON_STATUS_ERROR;
90
91   int guard_fd = open(dc->guard_file, O_RDWR | O_CREAT, 0666);
92   if (guard_fd < 0)
93     return daemon_control_err(dc, "Cannot open guard file `%s': %m", dc->guard_file);
94   if (flock(guard_fd, LOCK_EX) < 0)
95     return daemon_control_err(dc, "Cannot lock guard file `%s': %m", dc->guard_file);
96
97   // Read the PID file
98   int pid = daemon_read_pid(dc, 0);
99   if (pid < 0)
100     goto done;
101
102   switch (dc->action)
103     {
104     case DAEMON_CONTROL_CHECK:
105       if (pid)
106         st = DAEMON_STATUS_OK;
107       else
108         st = DAEMON_STATUS_NOT_RUNNING;
109       break;
110     case DAEMON_CONTROL_START:
111       if (pid)
112         st = DAEMON_STATUS_ALREADY_DONE;
113       else
114         {
115           pid_t pp = fork();
116           if (pp < 0)
117             {
118               daemon_control_err(dc, "Cannot fork: %m");
119               goto done;
120             }
121           if (pp)
122             {
123               close(guard_fd);
124               execvp(dc->argv[0], dc->argv);
125               fprintf(stderr, "Cannot execute `%s': %m\n", dc->argv[0]);
126               exit(DAEMON_STATUS_ERROR);
127             }
128           int stat;
129           int ec = waitpid(pp, &stat, 0);
130           if (ec < 0)
131             {
132               daemon_control_err(dc, "Cannot wait: %m");
133               goto done;
134             }
135           if (WIFEXITED(stat) && WEXITSTATUS(stat) == DAEMON_STATUS_ERROR)
136             {
137               daemon_control_err(dc, "Cannot execute the daemon");
138               goto done;
139             }
140           char ecmsg[EXIT_STATUS_MSG_SIZE];
141           if (format_exit_status(ecmsg, stat))
142             {
143               daemon_control_err(dc, "Daemon %s", ecmsg);
144               goto done;
145             }
146           pid = daemon_read_pid(dc, 0);
147           if (!pid)
148             daemon_control_err(dc, "Daemon failed to write the PID file `%s'", dc->pid_file);
149           else
150             st = DAEMON_STATUS_OK;
151         }
152       break;
153     case DAEMON_CONTROL_STOP:
154       if (!pid)
155         return DAEMON_STATUS_ALREADY_DONE;
156       int sig = dc->signal ? : SIGTERM;
157       if (kill(pid, sig) < 0)
158         {
159           daemon_control_err(dc, "Cannot send signal %d: %m", dc->signal);
160           goto done;
161         }
162       pid = daemon_read_pid(dc, 1);
163       ASSERT(pid <= 0);
164       if (!pid)
165         st = DAEMON_STATUS_OK;
166       break;
167     case DAEMON_CONTROL_SIGNAL:
168       if (!pid)
169         return DAEMON_STATUS_NOT_RUNNING;
170       if (kill(pid, dc->signal) < 0)
171         daemon_control_err(dc, "Cannot send signal %d: %m", dc->signal);
172       else
173         st = DAEMON_STATUS_OK;
174       break;
175     default:
176       ASSERT(0);
177     }
178
179 done:
180   close(guard_fd);
181   return st;
182 }