db_updater: Put parentheses back
[merlin.git] / daemonize.c
blob1d8cf17075c7d27d719e241a2c99a2468c5fc0dd
1 #define _GNU_SOURCE
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 #include <unistd.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <errno.h>
10 #include <signal.h>
11 #include <grp.h>
12 #include <pwd.h>
13 #include <string.h>
15 #include "daemonize.h"
17 static const char *daemon_pidfile;
20 * Really stupid generic sighandler...
22 void daemon_shutdown(void)
24 if (daemon_pidfile)
25 unlink(daemon_pidfile);
30 * Read a pid from "pidfile", which must contain one pid
31 * and one pid only
33 static int read_pid(const char *pidfile)
35 int pid = 0, fd;
36 unsigned char c;
38 fd = open(pidfile, O_RDONLY);
39 if (fd == -1)
40 return 0;
42 while ((read(fd, &c, 1)) > 0) {
43 if (c == '\n')
44 break;
45 pid = (pid * 10) + (c - '0');
47 close(fd);
49 return pid;
54 * Writes out the pidfile
56 static int write_pid(const char *pidfile, int pid)
58 FILE *fp;
60 if (!(fp = fopen(pidfile, "w")))
61 return 0;
63 fprintf(fp, "%d\n", pid);
64 fclose(fp);
66 return pid;
71 * Checks if a process with the pid found in *pidfile already exists.
72 * Returns 1 if it does, and 0 if it doesn't.
74 static int already_running(const char *pidfile)
76 int pid = read_pid(pidfile);
78 if (!pid)
79 return 0;
81 if (kill(pid, 0) < 0) {
82 if (errno == ESRCH) {
83 /* stale pidfile */
84 unlink(pidfile);
85 return 0;
88 fprintf(stderr, "Failed to signal process %d: %s\n",
89 pid, strerror(errno));
92 return pid;
95 static struct passwd *get_user_entry(const char *user)
97 struct passwd *pw;
99 if (!user || !*user)
100 return NULL;
102 while ((pw = getpwent())) {
103 if (!strcmp(pw->pw_name, user))
104 return pw;
107 fprintf(stderr, "No such user: %s\n", user);
108 exit(EXIT_FAILURE);
113 * Drop privileges neatly
115 * This code was taken from a patch I wrote for the Openwall
116 * distro. The patch was/is used in bind, nmap and dhclient
117 * shipped with Openwall
119 static unsigned drop_privs(struct passwd *pw)
121 /* XXX: is this enough? Where else is it present? */
122 #if defined(__GLIBC__)
123 /* group first, or we won't be able to swap uid */
124 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0)
125 return 0;
127 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0)
128 return 0;
129 #else
130 setgid(pw->pw_gid);
131 setuid(pw->pw_uid);
132 #endif
134 if (getegid() != pw->pw_gid || getgid() != pw->pw_uid)
135 return 0;
137 if (geteuid() != pw->pw_uid || getuid() != pw->pw_uid)
138 return 0;
140 return getuid();
143 int kill_daemon(const char *pidfile)
145 int pid = already_running(pidfile);
147 if (pid) {
148 printf("Signalling process with pid %d\n", pid);
149 kill(pid, SIGTERM);
150 sleep(3);
151 kill(pid, SIGKILL);
152 unlink(pidfile);
154 else
155 puts("No daemon running");
157 return 0;
160 int daemon_status(const char *pidfile)
162 int pid = already_running(pidfile);
163 if (pid) {
164 printf("Merlin is running\n");
165 return 0;
166 } else {
167 printf("Merlin is not running\n");
168 return 3;
173 * runas is the pseudo-user identity we assume
174 * jail is the directory we chdir() to before doing chroot(".")
175 * pidfile is written outside the jail.
176 * flags is a bitflag option specifier
178 int daemonize(const char *runas, const char *jail, const char *pidfile, int flags)
180 struct passwd *pw;
181 int pid = already_running(pidfile);
183 daemon_pidfile = strdup(pidfile);
185 if (pid > 0) {
186 fprintf(stderr, "Another Merlin instance is already running with pid %d\n", pid);
187 exit(EXIT_FAILURE);
190 /* don't drop privs or chdir if we're debugging */
191 if (flags & DMNZ_NOFORK)
192 return write_pid(pidfile, getpid());
194 if (jail && chdir(jail) < 0) {
195 fprintf(stderr, "Failed to chdir() to '%s': %s\n", jail, strerror(errno));
196 return -1;
199 pid = fork();
200 if (pid < 0) {
201 fprintf(stderr, "fork() failed: %s\n", strerror(errno));
202 exit(EXIT_FAILURE);
205 if (!pid) {
206 /* baby daemon goes here */
208 /* start a new process group */
209 setsid();
211 if (jail && flags & DMNZ_CHROOT && (chroot(".") < 0 || chdir("/") < 0)) {
212 fprintf(stderr, "chroot(%s) failed: %s", jail, strerror(errno));
213 exit(EXIT_FAILURE);
216 pw = get_user_entry(runas);
217 if (pw && drop_privs(pw) != pw->pw_uid) {
218 fprintf(stderr, "Failed to drop privileges to user %s", pw->pw_name);
219 exit(EXIT_FAILURE);
221 free(pw);
223 return 0;
226 if (write_pid(pidfile, pid) != pid) {
227 fprintf(stderr, "Failed to write pidfile '%s': %s\n",
228 pidfile, strerror(errno));
230 kill(pid, SIGTERM);
231 kill(pid, SIGKILL);
232 exit(EXIT_FAILURE);
235 if (flags & DMNZ_NOFORK)
236 return pid;
238 _exit(EXIT_SUCCESS);
239 return 0;