• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * This code is mainly taken from Doug Potter's page
3  *
4  * http://www-theorie.physik.unizh.ch/~dpotter/howto/daemonize
5  *
6  * I contacted him 2007-04-16 about the license for the original code,
7  * he replied it is Public Domain.  Use the URL above to get the original
8  * Public Domain version if you want it.
9  *
10  * This version is MIT like the rest of libwebsockets and is
11  * Copyright (c)2006 - 2013 Andy Green <andy@warmcat.com>
12  *
13  *
14  * You're much better advised to use systemd to daemonize stuff without needing
15  * this kind of support in the app itself.
16  */
17 
18 #include <stdlib.h>
19 #include <string.h>
20 #include <stdio.h>
21 #include <signal.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <limits.h>
26 #include <unistd.h>
27 #include <errno.h>
28 
29 #include <libwebsockets.h>
30 #include "private-lib-core.h"
31 
32 pid_t pid_daemon;
33 static char *lock_path;
34 
get_daemonize_pid()35 pid_t get_daemonize_pid()
36 {
37 	return pid_daemon;
38 }
39 
40 static void
child_handler(int signum)41 child_handler(int signum)
42 {
43 	int len, sent, fd;
44 	char sz[20];
45 
46 	switch (signum) {
47 
48 	case SIGALRM: /* timed out daemonizing */
49 		exit(0);
50 		break;
51 
52 	case SIGUSR1: /* positive confirmation we daemonized well */
53 
54 		if (!lock_path)
55 			exit(0);
56 
57 		/* Create the lock file as the current user */
58 
59 		fd = lws_open(lock_path, O_TRUNC | O_RDWR | O_CREAT, 0640);
60 		if (fd < 0) {
61 			fprintf(stderr,
62 			   "unable to create lock file %s, code=%d (%s)\n",
63 				lock_path, errno, strerror(errno));
64 			exit(0);
65 		}
66 		len = sprintf(sz, "%u", (unsigned int)pid_daemon);
67 		sent = write(fd, sz, len);
68 		if (sent != len)
69 			fprintf(stderr,
70 			  "unable to write pid to lock file %s, code=%d (%s)\n",
71 					     lock_path, errno, strerror(errno));
72 
73 		close(fd);
74 
75 		exit(0);
76 		//!!(sent == len));
77 
78 	case SIGCHLD: /* daemonization failed */
79 		exit(0);
80 		break;
81 	}
82 }
83 
lws_daemon_closing(int sigact)84 static void lws_daemon_closing(int sigact)
85 {
86 	if (getpid() == pid_daemon)
87 		if (lock_path) {
88 			unlink(lock_path);
89 			lws_free_set_NULL(lock_path);
90 		}
91 
92 	kill(getpid(), SIGKILL);
93 }
94 
95 /*
96  * You just need to call this from your main(), when it
97  * returns you are all set "in the background" decoupled
98  * from the console you were started from.
99  *
100  * The process context you called from has been terminated then.
101  */
102 
103 int
lws_daemonize(const char * _lock_path)104 lws_daemonize(const char *_lock_path)
105 {
106 	struct sigaction act;
107 	pid_t sid, parent;
108 
109 	/* already a daemon */
110 //	if (getppid() == 1)
111 //		return 1;
112 
113 	if (_lock_path) {
114 		int n;
115 
116 		int fd = lws_open(_lock_path, O_RDONLY);
117 		if (fd >= 0) {
118 			char buf[10];
119 
120 			n = read(fd, buf, sizeof(buf));
121 			close(fd);
122 			if (n) {
123 				int ret;
124 				n = atoi(buf);
125 				ret = kill(n, 0);
126 				if (ret >= 0) {
127 					fprintf(stderr,
128 					     "Daemon already running pid %d\n",
129 					     n);
130 					exit(1);
131 				}
132 				fprintf(stderr,
133 				    "Removing stale lock %s from dead pid %d\n",
134 							_lock_path, n);
135 				unlink(lock_path);
136 			}
137 		}
138 
139 		n = strlen(_lock_path) + 1;
140 		lock_path = lws_malloc(n, "daemonize lock");
141 		if (!lock_path) {
142 			fprintf(stderr, "Out of mem in lws_daemonize\n");
143 			return 1;
144 		}
145 		strcpy(lock_path, _lock_path);
146 	}
147 
148 	/* Trap signals that we expect to receive */
149 	signal(SIGCHLD, child_handler);	/* died */
150 	signal(SIGUSR1, child_handler); /* was happy */
151 	signal(SIGALRM, child_handler); /* timeout daemonizing */
152 
153 	/* Fork off the parent process */
154 	pid_daemon = fork();
155 	if ((int)pid_daemon < 0) {
156 		fprintf(stderr, "unable to fork daemon, code=%d (%s)",
157 		    errno, strerror(errno));
158 		exit(9);
159 	}
160 
161         /* If we got a good PID, then we can exit the parent process. */
162 	if (pid_daemon > 0) {
163 
164                /*
165                 * Wait for confirmation signal from the child via
166                 * SIGCHILD / USR1, or for two seconds to elapse
167                 * (SIGALRM).  pause() should not return.
168                 */
169                alarm(2);
170 
171                pause();
172                /* should not be reachable */
173                exit(1);
174        }
175 
176 	/* At this point we are executing as the child process */
177 	parent = getppid();
178 	pid_daemon = getpid();
179 
180 	/* Cancel certain signals */
181 	signal(SIGCHLD, SIG_DFL); /* A child process dies */
182 	signal(SIGTSTP, SIG_IGN); /* Various TTY signals */
183 	signal(SIGTTOU, SIG_IGN);
184 	signal(SIGTTIN, SIG_IGN);
185 	signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */
186 
187 	/* Change the file mode mask */
188 	umask(0);
189 
190 	/* Create a new SID for the child process */
191 	sid = setsid();
192 	if (sid < 0) {
193 		fprintf(stderr,
194 			"unable to create a new session, code %d (%s)",
195 			errno, strerror(errno));
196 		exit(2);
197 	}
198 
199 	/*
200 	 * Change the current working directory.  This prevents the current
201 	 * directory from being locked; hence not being able to remove it.
202 	 */
203 	if (chdir("/tmp") < 0) {
204 		fprintf(stderr,
205 			"unable to change directory to %s, code %d (%s)",
206 			"/", errno, strerror(errno));
207 		exit(3);
208 	}
209 
210 	/* Redirect standard files to /dev/null */
211 	if (!freopen("/dev/null", "r", stdin))
212 		fprintf(stderr, "unable to freopen() stdin, code %d (%s)",
213 						       errno, strerror(errno));
214 
215 	if (!freopen("/dev/null", "w", stdout))
216 		fprintf(stderr, "unable to freopen() stdout, code %d (%s)",
217 						       errno, strerror(errno));
218 
219 	if (!freopen("/dev/null", "w", stderr))
220 		fprintf(stderr, "unable to freopen() stderr, code %d (%s)",
221 						       errno, strerror(errno));
222 
223 	/* Tell the parent process that we are A-okay */
224 	kill(parent, SIGUSR1);
225 
226 	act.sa_handler = lws_daemon_closing;
227 	sigemptyset(&act.sa_mask);
228 	act.sa_flags = 0;
229 
230 	sigaction(SIGTERM, &act, NULL);
231 
232 	/* return to continue what is now "the daemon" */
233 
234 	return 0;
235 }
236 
237