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 "config.h"
42 #include "event.h"
43 #include "upnpglobalvars.h"
44 #include "process.h"
45 #include "log.h"
46 
47 struct child *children = NULL;
48 int number_of_children = 0;
49 
50 static void
add_process_info(pid_t pid,struct client_cache_s * client)51 add_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 = time(NULL);
64 		break;
65 	}
66 }
67 
68 static inline void
remove_process_info(pid_t pid)69 remove_process_info(pid_t pid)
70 {
71 	struct child *child;
72 	int i;
73 
74 	for (i = 0; i < runtime_vars.max_connections; i++)
75 	{
76 		child = children+i;
77 		if (child->pid != pid)
78 			continue;
79 		child->pid = 0;
80 		if (child->client)
81 			child->client->connections--;
82 		break;
83 	}
84 }
85 
86 pid_t
process_fork(struct client_cache_s * client)87 process_fork(struct client_cache_s *client)
88 {
89 	if (number_of_children >= runtime_vars.max_connections)
90 	{
91 		DPRINTF(E_WARN, L_GENERAL, "Exceeded max connections [%d], not forking\n",
92 			runtime_vars.max_connections);
93 		errno = EAGAIN;
94 		return -1;
95 	}
96 
97 	pid_t pid = fork();
98 	if (pid > 0)
99 	{
100 		if (client)
101 			client->connections++;
102 		add_process_info(pid, client);
103 		number_of_children++;
104 	} else if (pid == 0)
105 		event_module.fini();
106 	else
107 		DPRINTF(E_FATAL, L_GENERAL, "Fork() failed: %s\n", strerror(errno));
108 
109 	return pid;
110 }
111 
112 void
process_handle_child_termination(int signal)113 process_handle_child_termination(int signal)
114 {
115 	pid_t pid;
116 
117 	while ((pid = waitpid(-1, NULL, WNOHANG)))
118 	{
119 		if (pid == -1)
120 		{
121 			if (errno == EINTR)
122 				continue;
123 			else
124 				break;
125 		}
126 		number_of_children--;
127 		remove_process_info(pid);
128 	}
129 }
130 
131 int
process_daemonize(void)132 process_daemonize(void)
133 {
134 	int pid;
135 #ifndef USE_DAEMON
136 	int i;
137 
138 	switch(fork())
139 	{
140 		/* fork error */
141 		case -1:
142 			perror("fork()");
143 			exit(1);
144 
145 		/* child process */
146 		case 0:
147 		/* obtain a new process group */
148 			if( (pid = setsid()) < 0)
149 			{
150 				perror("setsid()");
151 				exit(1);
152 			}
153 
154 			/* close all descriptors */
155 			for (i=getdtablesize();i>=0;--i) close(i);
156 
157 			i = open("/dev/null",O_RDWR); /* open stdin */
158 			dup(i); /* stdout */
159 			dup(i); /* stderr */
160 
161 			umask(027);
162 			chdir("/");
163 
164 			break;
165 		/* parent process */
166 		default:
167 			exit(0);
168 	}
169 #else
170 	if( daemon(0, 0) < 0 )
171 		perror("daemon()");
172 	pid = getpid();
173 #endif
174 	return pid;
175 }
176 
177 int
process_check_if_running(const char * fname)178 process_check_if_running(const char *fname)
179 {
180 	char buffer[64];
181 	int pidfile;
182 	pid_t pid;
183 
184 	if(!fname || *fname == '\0')
185 		return -1;
186 
187 	if( (pidfile = open(fname, O_RDONLY)) < 0)
188 		return 0;
189 
190 	memset(buffer, 0, 64);
191 
192 	if(read(pidfile, buffer, 63) > 0)
193 	{
194 		if( (pid = atol(buffer)) > 0)
195 		{
196 			if(!kill(pid, 0))
197 			{
198 				close(pidfile);
199 				return -2;
200 			}
201 		}
202 	}
203 
204 	close(pidfile);
205 
206 	return 0;
207 }
208 
209 void
process_reap_children(void)210 process_reap_children(void)
211 {
212 	struct child *child;
213 	int i;
214 
215 	for (i = 0; i < runtime_vars.max_connections; i++)
216 	{
217 		child = children+i;
218 		if (child->pid)
219 			kill(child->pid, SIGKILL);
220 	}
221 }
222