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>
22 static void daemon_resolve_ugid(struct daemon_params *dp)
25 const char *u = dp->run_as_user;
26 struct passwd *pw = NULL;
32 const char *err = str_to_uns(&id, u, NULL, 10 | STN_WHOLE);
34 die("Cannot parse user `%s': %s", u, err);
42 die("No such user `%s'", u);
43 dp->run_as_uid = pw->pw_uid;
49 const char *g = dp->run_as_group;
56 const char *err = str_to_uns(&id, g, NULL, 10 | STN_WHOLE);
58 die("Cannot parse group `%s': %s", g, err);
66 die("No such group `%s'", g);
67 dp->run_as_gid = gr->gr_gid;
73 dp->run_as_gid = pw->pw_gid;
78 void daemon_init(struct daemon_params *dp)
80 daemon_resolve_ugid(dp);
84 // Check that PID file path is absolute
85 if (!(dp->flags & DAEMON_FLAG_PRESERVE_CWD) && dp->pid_file[0] != '/')
86 die("Path to PID file `%s' must be absolute", dp->pid_file);
89 dp->pid_fd = open(dp->pid_file, O_RDWR | O_CREAT, 0666);
91 die("Cannot open `%s': %m", dp->pid_file);
93 // Try to lock it with an exclusive lock
94 struct flock fl = { .l_type = F_WRLCK, .l_whence = SEEK_SET };
95 if (fcntl(dp->pid_fd, F_SETLK, &fl) < 0)
97 if (errno == EAGAIN || errno == EACCES)
98 die("Daemon is already running (`%s' locked)", dp->pid_file);
100 die("Cannot lock `%s': %m", dp->pid_file);
103 // Make a note that the daemon is starting
104 if (write(dp->pid_fd, "(starting)\n", 11) != 11 ||
105 ftruncate(dp->pid_fd, 11) < 0)
106 die("Error writing `%s': %m", dp->pid_file);
110 void daemon_run(struct daemon_params *dp, void (*body)(struct daemon_params *dp))
112 // Switch GID and UID
113 if (dp->want_setgid && setresgid(dp->run_as_gid, dp->run_as_gid, dp->run_as_gid) < 0)
114 die("Cannot set GID to %d: %m", (int) dp->run_as_gid);
115 if (dp->want_setgid > 1 && initgroups(dp->run_as_user, dp->run_as_gid) < 0)
116 die("Cannot initialize groups: %m");
117 if (dp->want_setuid && setresuid(dp->run_as_uid, dp->run_as_uid, dp->run_as_uid) < 0)
118 die("Cannot set UID to %d: %m", (int) dp->run_as_uid);
120 // Create a new session and close stdio
123 if (open("/dev/null", O_RDWR, 0) < 0 ||
125 die("Cannot redirect stdio to `/dev/null': %m");
127 // Set umask to a reasonable value
130 // Do not hold the current working directory
131 if (!(dp->flags & DAEMON_FLAG_PRESERVE_CWD))
134 die("Cannot chdir to root: %m");
140 die("Cannot fork: %m");
143 // The PID file is left open, so that it is kept locked
144 // FIXME: Downgrade the lock to shared
149 // Write PID and release the PID file
153 int c = snprintf(buf, sizeof(buf), "%d\n", (int) pid);
154 ASSERT(c <= (int) sizeof(buf));
155 if (lseek(dp->pid_fd, 0, SEEK_SET) < 0 ||
156 write(dp->pid_fd, buf, c) != c ||
157 ftruncate(dp->pid_fd, c) < 0 ||
158 close(dp->pid_fd) < 0)
159 die("Cannot write PID to `%s': %m", dp->pid_file);
163 void daemon_exit(struct daemon_params *dp)
167 if (unlink(dp->pid_file) < 0)
168 msg(L_ERROR, "Cannot unlink PID file `%s': %m", dp->pid_file);
175 static void body(struct daemon_params *dp)
178 msg(L_INFO, "Daemon is running");
180 msg(L_INFO, "Daemon is shutting down");
186 struct daemon_params dp = {
187 .pid_file = "/tmp/123",
191 daemon_run(&dp, body);
192 msg(L_INFO, "Main program has ended");