1/* Process handling
2 *
3 * Copyright © 2006, Thomas Bernard
4 * Copyright © 2013, Benoît Knecht <benoit.knecht@fsfe.org>
5 *
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *     * Redistributions of source code must retain the above copyright
11 *       notice, this list of conditions and the following disclaimer.
12 *     * Redistributions in binary form must reproduce the above copyright
13 *       notice, this list of conditions and the following disclaimer in the
14 *       documentation and/or other materials provided with the distribution.
15 *     * The name of the author may not be used to endorse or promote products
16 *       derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <unistd.h>
33#include <fcntl.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <errno.h>
37#include <string.h>
38#include <signal.h>
39#include <sys/wait.h>
40
41#include "upnpglobalvars.h"
42#include "process.h"
43#include "config.h"
44#include "log.h"
45#include "utils.h"
46
47struct child *children = NULL;
48int number_of_children = 0;
49
50static int
51add_process_info(pid_t pid, struct client_cache_s *client)
52{
53	struct child *child;
54	int i;
55
56	for (i = 0; i < runtime_vars.max_connections; i++)
57	{
58		child = children+i;
59		if (child->pid)
60			continue;
61		child->pid = pid;
62		child->client = client;
63		child->age = uptime();
64		return 1;
65	}
66
67	return 0;
68}
69
70static inline int
71remove_process_info(pid_t pid)
72{
73	struct child *child;
74	int i;
75
76	for (i = 0; i < runtime_vars.max_connections; i++)
77	{
78		child = children+i;
79		if (child->pid != pid)
80			continue;
81		child->pid = 0;
82		if (child->client)
83			child->client->connections--;
84		return 1;
85	}
86
87	return 0;
88}
89
90pid_t
91process_fork(struct client_cache_s *client)
92{
93	if (number_of_children >= runtime_vars.max_connections)
94	{
95		DPRINTF(E_WARN, L_GENERAL, "Exceeded max connections [%d], not forking\n",
96			runtime_vars.max_connections);
97		errno = EAGAIN;
98		return -1;
99	}
100
101	pid_t pid = fork();
102	if (pid > 0)
103	{
104		if (client)
105			client->connections++;
106		if (add_process_info(pid, client))
107			number_of_children++;
108	}
109
110	return pid;
111}
112
113void
114process_handle_child_termination(int signal)
115{
116	pid_t pid;
117
118	while ((pid = waitpid(-1, NULL, WNOHANG)))
119	{
120		if (pid == -1)
121		{
122			if (errno == EINTR)
123				continue;
124			else
125				break;
126		}
127		if (remove_process_info(pid))
128			number_of_children--;
129	}
130}
131
132int
133process_daemonize(void)
134{
135	int pid;
136#ifndef USE_DAEMON
137	int i;
138
139	switch(fork())
140	{
141		/* fork error */
142		case -1:
143			perror("fork()");
144			exit(1);
145
146		/* child process */
147		case 0:
148		/* obtain a new process group */
149			if( (pid = setsid()) < 0)
150			{
151				perror("setsid()");
152				exit(1);
153			}
154
155			/* close all descriptors */
156			for (i=getdtablesize();i>=0;--i) close(i);
157
158			i = open("/dev/null",O_RDWR); /* open stdin */
159			dup(i); /* stdout */
160			dup(i); /* stderr */
161
162			umask(027);
163			chdir("/");
164
165			break;
166		/* parent process */
167		default:
168			exit(0);
169	}
170#else
171	if( daemon(0, 0) < 0 )
172		perror("daemon()");
173	pid = getpid();
174#endif
175	return pid;
176}
177
178int
179process_check_if_running(const char *fname)
180{
181	char buffer[64];
182	int pidfile;
183	pid_t pid;
184
185	if(!fname || *fname == '\0')
186		return -1;
187
188	if( (pidfile = open(fname, O_RDONLY)) < 0)
189		return 0;
190
191	memset(buffer, 0, 64);
192
193	if(read(pidfile, buffer, 63) > 0)
194	{
195		if( (pid = atol(buffer)) > 0)
196		{
197			if(!kill(pid, 0))
198			{
199				close(pidfile);
200				return -2;
201			}
202		}
203	}
204
205	close(pidfile);
206
207	return 0;
208}
209
210void
211process_reap_children(void)
212{
213	struct child *child;
214	int i;
215
216	for (i = 0; i < runtime_vars.max_connections; i++)
217	{
218		child = children+i;
219		if (child->pid)
220			kill(child->pid, SIGKILL);
221	}
222}
223