2 * UCW Library -- Daemonization
4 * (c) 2012--2014 Martin Mares <mj@ucw.cz>
5 * (c) 2014 Pavel Charvat <pchar@ucw.cz>
7 * This software may be freely distributed and used according to the terms
8 * of the GNU Lesser General Public License.
12 #include <ucw/daemon.h>
13 #include <ucw/strtonum.h>
23 #include <sys/types.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_uint(&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_uint(&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_switch_ugid(struct daemon_params *dp)
87 if (dp->want_setgid && setresgid(dp->run_as_gid, dp->run_as_gid, dp->run_as_gid) < 0)
88 die("Cannot set GID to %d: %m", (int) dp->run_as_gid);
89 if (dp->want_setgid > 1 && initgroups(dp->run_as_user, dp->run_as_gid) < 0)
90 die("Cannot initialize groups: %m");
91 if (dp->want_setuid && setresuid(dp->run_as_uid, dp->run_as_uid, dp->run_as_uid) < 0)
92 die("Cannot set UID to %d: %m", (int) dp->run_as_uid);
96 daemon_init(struct daemon_params *dp)
98 daemon_resolve_ugid(dp);
100 if (dp->flags & DAEMON_FLAG_SIMULATE)
105 // Check that PID file path is absolute
106 if (!(dp->flags & DAEMON_FLAG_PRESERVE_CWD) && dp->pid_file[0] != '/')
107 die("Path to PID file `%s' must be absolute", dp->pid_file);
110 dp->pid_fd = open(dp->pid_file, O_RDWR | O_CREAT, 0666);
112 die("Cannot open `%s': %m", dp->pid_file);
113 int fl = fcntl(dp->pid_fd, F_GETFD);
114 if (fl < 0 || fcntl(dp->pid_fd, F_SETFD, fl | FD_CLOEXEC))
115 die("Cannot set FD_CLOEXEC: %m");
117 // Try to lock it with an exclusive lock
118 if (flock(dp->pid_fd, LOCK_EX | LOCK_NB) < 0)
120 if (errno == EINTR || errno == EWOULDBLOCK)
121 die("Daemon is already running (`%s' locked)", dp->pid_file);
123 die("Cannot lock `%s': %m", dp->pid_file);
126 // Make a note that the daemon is starting
127 if (ftruncate(dp->pid_fd, 0) < 0 ||
128 write(dp->pid_fd, "(starting)\n", 11) != 11)
129 die("Error writing `%s': %m", dp->pid_file);
134 daemon_run(struct daemon_params *dp, void (*body)(struct daemon_params *dp))
136 if (dp->flags & DAEMON_FLAG_SIMULATE)
142 // Switch GID and UID
143 daemon_switch_ugid(dp);
145 // Create a new session and close stdio
148 if (open("/dev/null", O_RDWR, 0) < 0 ||
150 die("Cannot redirect stdio to `/dev/null': %m");
152 // Set umask to a reasonable value
155 // Do not hold the current working directory
156 if (!(dp->flags & DAEMON_FLAG_PRESERVE_CWD))
159 die("Cannot chdir to root: %m");
162 // Create pipe to synchronize child process with master and avoid possible
163 // collision between writing of PID and daemon_exit()
165 if (dp->pid_file && pipe(pipe_fd) < 0)
166 die("Cannot create pipe: %m");
171 die("Cannot fork: %m");
174 // Wait for master process to finish writing of PID
179 if (read(pipe_fd[0], pipe_buf, 1) < 0)
180 die("Cannot read pipe: %m");
184 // We still keep the PID file open and thus locked
193 int c = snprintf(buf, sizeof(buf), "%d\n", (int) pid);
194 ASSERT(c <= (int) sizeof(buf));
195 if (lseek(dp->pid_fd, 0, SEEK_SET) < 0 ||
196 write(dp->pid_fd, buf, c) != c ||
197 ftruncate(dp->pid_fd, c) ||
198 close(dp->pid_fd) < 0)
199 die("Cannot write PID to `%s': %m", dp->pid_file);
206 daemon_exit(struct daemon_params *dp)
208 if (dp->flags & DAEMON_FLAG_SIMULATE)
213 if (ftruncate(dp->pid_fd, 0))
214 die("Error truncating `%s': %m", dp->pid_file);
223 static volatile sig_atomic_t terminate;
225 static void term_handler(int sig UNUSED)
227 msg(L_INFO | L_SIGHANDLER, "SIGTERM received, terminating in a while");
231 static void hup_handler(int sig UNUSED)
233 msg(L_INFO | L_SIGHANDLER, "SIGHUP received");
236 static void body(struct daemon_params *dp)
239 msg(L_INFO, "Daemon is running");
240 msg(L_INFO, "uid=%d/%d gid=%d/%d", (int) getuid(), (int) geteuid(), (int) getgid(), (int) getegid());
242 struct sigaction sa_term = { .sa_handler = term_handler };
243 struct sigaction sa_hup = { .sa_handler = hup_handler };
244 if (sigaction(SIGTERM, &sa_term, NULL) < 0 ||
245 sigaction(SIGHUP, &sa_hup, NULL) < 0)
252 msg(L_INFO, "Timeout elapsed, terminating in a while");
258 msg(L_INFO, "Daemon is shutting down");
262 int main(int argc, char **argv)
264 struct daemon_params dp = {
265 .pid_file = "/tmp/123",
269 while ((opt = getopt(argc, argv, "p:u:g:")) >= 0)
273 dp.pid_file = optarg;
276 dp.run_as_user = optarg;
279 dp.run_as_group = optarg;
282 die("Invalid arguments");
286 daemon_run(&dp, body);
287 msg(L_INFO, "Main program has ended");