2 * UCW Library -- Daemonization
4 * (c) 2012 Martin Mares <mj@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU Lesser General Public License.
11 #include <ucw/daemon.h>
12 #include <ucw/strtonum.h>
13 #include <ucw/process.h>
28 daemon_resolve_ugid(struct daemon_params *dp)
31 const char *u = dp->run_as_user;
32 struct passwd *pw = NULL;
38 const char *err = str_to_uns(&id, u, NULL, 10 | STN_WHOLE);
40 die("Cannot parse user `%s': %s", u, err);
48 die("No such user `%s'", u);
49 dp->run_as_uid = pw->pw_uid;
55 const char *g = dp->run_as_group;
62 const char *err = str_to_uns(&id, g, NULL, 10 | STN_WHOLE);
64 die("Cannot parse group `%s': %s", g, err);
72 die("No such group `%s'", g);
73 dp->run_as_gid = gr->gr_gid;
79 dp->run_as_gid = pw->pw_gid;
85 daemon_init(struct daemon_params *dp)
87 daemon_resolve_ugid(dp);
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);
96 dp->pid_fd = open(dp->pid_file, O_RDWR | O_CREAT, 0666);
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");
103 // Try to lock it with an exclusive lock
104 if (flock(dp->pid_fd, LOCK_EX | LOCK_NB) < 0)
106 if (errno == EINTR || errno == EWOULDBLOCK)
107 die("Daemon is already running (`%s' locked)", dp->pid_file);
109 die("Cannot lock `%s': %m", dp->pid_file);
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);
120 daemon_run(struct daemon_params *dp, void (*body)(struct daemon_params *dp))
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);
130 // Create a new session and close stdio
133 if (open("/dev/null", O_RDWR, 0) < 0 ||
135 die("Cannot redirect stdio to `/dev/null': %m");
137 // Set umask to a reasonable value
140 // Do not hold the current working directory
141 if (!(dp->flags & DAEMON_FLAG_PRESERVE_CWD))
144 die("Cannot chdir to root: %m");
150 die("Cannot fork: %m");
153 // We still keep the PID file open and thus locked
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);
173 daemon_exit(struct daemon_params *dp)
177 if (unlink(dp->pid_file) < 0)
178 msg(L_ERROR, "Cannot unlink PID file `%s': %m", dp->pid_file);
183 static enum daemon_control_status
184 daemon_control_err(struct daemon_control_params *dc, char *msg, ...)
188 vsnprintf(dc->error_msg, DAEMON_ERR_LEN, msg, args);
190 return DAEMON_STATUS_ERROR;
194 daemon_read_pid(struct daemon_control_params *dc, int will_wait)
196 int pid_fd = open(dc->pid_file, O_RDONLY);
201 daemon_control_err(dc, "Cannot open PID file `%s': %m", dc->pid_file);
205 if (flock(pid_fd, LOCK_EX | (will_wait ? 0 : LOCK_NB)) >= 0)
207 // The lock file is stale
212 if (errno != EINTR && errno != EWOULDBLOCK)
214 daemon_control_err(dc, "Cannot lock PID file `%s': %m", dc->pid_file);
219 int n = read(pid_fd, buf, sizeof(buf));
222 daemon_control_err(dc, "Error reading `%s': %m", dc->pid_file);
225 if (n == (int) sizeof(buf))
227 daemon_control_err(dc, "PID file `%s' is too long", dc->pid_file);
234 daemon_control_err(dc, "PID file `%s' does not contain a valid PID", dc->pid_file);
245 enum daemon_control_status
246 daemon_control(struct daemon_control_params *dc)
248 enum daemon_control_status st = DAEMON_STATUS_ERROR;
250 int guard_fd = open(dc->guard_file, O_RDWR | O_CREAT, 0666);
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);
257 int pid = daemon_read_pid(dc, 0);
263 case DAEMON_CONTROL_CHECK:
265 st = DAEMON_STATUS_OK;
267 st = DAEMON_STATUS_NOT_RUNNING;
269 case DAEMON_CONTROL_START:
271 st = DAEMON_STATUS_ALREADY_DONE;
277 daemon_control_err(dc, "Cannot fork: %m");
283 execvp(dc->argv[0], dc->argv);
284 fprintf(stderr, "Cannot execute `%s': %m\n", dc->argv[0]);
285 exit(DAEMON_STATUS_ERROR);
288 int ec = waitpid(pp, &stat, 0);
291 daemon_control_err(dc, "Cannot wait: %m");
294 if (WIFEXITED(stat) && WEXITSTATUS(stat) == DAEMON_STATUS_ERROR)
296 daemon_control_err(dc, "Cannot execute the daemon");
299 char ecmsg[EXIT_STATUS_MSG_SIZE];
300 if (format_exit_status(ecmsg, stat))
302 daemon_control_err(dc, "Daemon %s", ecmsg);
305 pid = daemon_read_pid(dc, 0);
307 daemon_control_err(dc, "Daemon failed to write the PID file `%s'", dc->pid_file);
309 st = DAEMON_STATUS_OK;
312 case DAEMON_CONTROL_STOP:
314 return DAEMON_STATUS_ALREADY_DONE;
315 int sig = dc->signal ? : SIGTERM;
316 if (kill(pid, sig) < 0)
318 daemon_control_err(dc, "Cannot send signal %d: %m", dc->signal);
321 pid = daemon_read_pid(dc, 1);
324 st = DAEMON_STATUS_OK;
326 case DAEMON_CONTROL_SIGNAL:
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);
332 st = DAEMON_STATUS_OK;
345 static void body(struct daemon_params *dp)
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());
351 msg(L_INFO, "Daemon is shutting down");
355 int main(int argc, char **argv)
357 struct daemon_params dp = {
358 .pid_file = "/tmp/123",
362 while ((opt = getopt(argc, argv, "p:u:g:")) >= 0)
366 dp.pid_file = optarg;
369 dp.run_as_user = optarg;
372 dp.run_as_group = optarg;
375 die("Invalid arguments");
379 daemon_run(&dp, body);
380 msg(L_INFO, "Main program has ended");