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