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
32pid_t pid_daemon;
33static char *lock_path;
34
35pid_t get_daemonize_pid()
36{
37	return pid_daemon;
38}
39
40static void
41child_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 = (int)write(fd, sz, (size_t)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
84static 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
103int
104lws_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 = (int)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 = (int)strlen(_lock_path) + 1;
140		lock_path = lws_malloc((unsigned int)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