sock: add addr argument to listen_sock()
[obnox/tinyproxy.git] / src / child.c
1 /* tinyproxy - A fast light-weight HTTP proxy
2  * Copyright (C) 2000 Robert James Kaes <rjkaes@users.sourceforge.net>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 /* Handles the creation/destruction of the various children required for
20  * processing incoming connections.
21  */
22
23 #include "main.h"
24
25 #include "child.h"
26 #include "daemon.h"
27 #include "filter.h"
28 #include "heap.h"
29 #include "log.h"
30 #include "reqs.h"
31 #include "sock.h"
32 #include "utils.h"
33 #include "conf.h"
34
35 static int listenfd;
36
37 /*
38  * Stores the internal data needed for each child (connection)
39  */
40 enum child_status_t { T_EMPTY, T_WAITING, T_CONNECTED };
41 struct child_s {
42         pid_t tid;
43         unsigned int connects;
44         enum child_status_t status;
45 };
46
47 /*
48  * A pointer to an array of children. A certain number of children are
49  * created when the program is started.
50  */
51 static struct child_s *child_ptr;
52
53 static struct child_config_s {
54         unsigned int maxclients, maxrequestsperchild;
55         unsigned int maxspareservers, minspareservers, startservers;
56 } child_config;
57
58 static unsigned int *servers_waiting;   /* servers waiting for a connection */
59
60 /*
61  * Lock/Unlock the "servers_waiting" variable so that two children cannot
62  * modify it at the same time.
63  */
64 #define SERVER_COUNT_LOCK()   _child_lock_wait()
65 #define SERVER_COUNT_UNLOCK() _child_lock_release()
66
67 /* START OF LOCKING SECTION */
68
69 /*
70  * These variables are required for the locking mechanism.  Also included
71  * are the "private" functions for locking/unlocking.
72  */
73 static struct flock lock_it, unlock_it;
74 static int lock_fd = -1;
75
76 static void _child_lock_init (void)
77 {
78         char lock_file[] = "/tmp/tinyproxy.servers.lock.XXXXXX";
79
80         /* Only allow u+rw bits. This may be required for some versions
81          * of glibc so that mkstemp() doesn't make us vulnerable.
82          */
83         umask (0177);
84
85         lock_fd = mkstemp (lock_file);
86         unlink (lock_file);
87
88         lock_it.l_type = F_WRLCK;
89         lock_it.l_whence = SEEK_SET;
90         lock_it.l_start = 0;
91         lock_it.l_len = 0;
92
93         unlock_it.l_type = F_UNLCK;
94         unlock_it.l_whence = SEEK_SET;
95         unlock_it.l_start = 0;
96         unlock_it.l_len = 0;
97 }
98
99 static void _child_lock_wait (void)
100 {
101         int rc;
102
103         while ((rc = fcntl (lock_fd, F_SETLKW, &lock_it)) < 0) {
104                 if (errno == EINTR)
105                         continue;
106                 else
107                         return;
108         }
109 }
110
111 static void _child_lock_release (void)
112 {
113         if (fcntl (lock_fd, F_SETLKW, &unlock_it) < 0)
114                 return;
115 }
116
117 /* END OF LOCKING SECTION */
118
119 #define SERVER_INC() do { \
120     SERVER_COUNT_LOCK(); \
121     ++(*servers_waiting); \
122     DEBUG2("INC: servers_waiting: %d", *servers_waiting); \
123     SERVER_COUNT_UNLOCK(); \
124 } while (0)
125
126 #define SERVER_DEC() do { \
127     SERVER_COUNT_LOCK(); \
128     assert(*servers_waiting > 0); \
129     --(*servers_waiting); \
130     DEBUG2("DEC: servers_waiting: %d", *servers_waiting); \
131     SERVER_COUNT_UNLOCK(); \
132 } while (0)
133
134 /*
135  * Set the configuration values for the various child related settings.
136  */
137 short int child_configure (child_config_t type, unsigned int val)
138 {
139         switch (type) {
140         case CHILD_MAXCLIENTS:
141                 child_config.maxclients = val;
142                 break;
143         case CHILD_MAXSPARESERVERS:
144                 child_config.maxspareservers = val;
145                 break;
146         case CHILD_MINSPARESERVERS:
147                 child_config.minspareservers = val;
148                 break;
149         case CHILD_STARTSERVERS:
150                 child_config.startservers = val;
151                 break;
152         case CHILD_MAXREQUESTSPERCHILD:
153                 child_config.maxrequestsperchild = val;
154                 break;
155         default:
156                 DEBUG2 ("Invalid type (%d)", type);
157                 return -1;
158         }
159
160         return 0;
161 }
162
163 /**
164  * child signal handler for sighup
165  */
166 static void child_sighup_handler (int sig)
167 {
168         if (sig == SIGHUP) {
169                 /*
170                  * Ignore the return value of reload_config for now.
171                  * This should actually be handled somehow...
172                  */
173                 reload_config ();
174
175 #ifdef FILTER_ENABLE
176                 filter_reload ();
177 #endif /* FILTER_ENABLE */
178         }
179 }
180
181 /*
182  * This is the main (per child) loop.
183  */
184 static void child_main (struct child_s *ptr)
185 {
186         int connfd;
187         struct sockaddr *cliaddr;
188         socklen_t clilen;
189
190         cliaddr = (struct sockaddr *)
191                         safemalloc (sizeof(struct sockaddr_storage));
192         if (!cliaddr) {
193                 log_message (LOG_CRIT,
194                              "Could not allocate memory for child address.");
195                 exit (0);
196         }
197
198         ptr->connects = 0;
199
200         while (!config.quit) {
201                 ptr->status = T_WAITING;
202
203                 clilen = sizeof(struct sockaddr_storage);
204
205                 connfd = accept (listenfd, cliaddr, &clilen);
206
207 #ifndef NDEBUG
208                 /*
209                  * Enable the TINYPROXY_DEBUG environment variable if you
210                  * want to use the GDB debugger.
211                  */
212                 if (getenv ("TINYPROXY_DEBUG")) {
213                         /* Pause for 10 seconds to allow us to connect debugger */
214                         fprintf (stderr,
215                                  "Process has accepted connection: %ld\n",
216                                  (long int) ptr->tid);
217                         sleep (10);
218                         fprintf (stderr, "Continuing process: %ld\n",
219                                  (long int) ptr->tid);
220                 }
221 #endif
222
223                 /*
224                  * Make sure no error occurred...
225                  */
226                 if (connfd < 0) {
227                         log_message (LOG_ERR,
228                                      "Accept returned an error (%s) ... retrying.",
229                                      strerror (errno));
230                         continue;
231                 }
232
233                 ptr->status = T_CONNECTED;
234
235                 SERVER_DEC ();
236
237                 handle_connection (connfd);
238                 ptr->connects++;
239
240                 if (child_config.maxrequestsperchild != 0) {
241                         DEBUG2 ("%u connections so far...", ptr->connects);
242
243                         if (ptr->connects == child_config.maxrequestsperchild) {
244                                 log_message (LOG_NOTICE,
245                                              "Child has reached MaxRequestsPerChild (%u). "
246                                              "Killing child.", ptr->connects);
247                                 break;
248                         }
249                 }
250
251                 SERVER_COUNT_LOCK ();
252                 if (*servers_waiting > child_config.maxspareservers) {
253                         /*
254                          * There are too many spare children, kill ourself
255                          * off.
256                          */
257                         log_message (LOG_NOTICE,
258                                      "Waiting servers (%d) exceeds MaxSpareServers (%d). "
259                                      "Killing child.",
260                                      *servers_waiting,
261                                      child_config.maxspareservers);
262                         SERVER_COUNT_UNLOCK ();
263
264                         break;
265                 } else {
266                         SERVER_COUNT_UNLOCK ();
267                 }
268
269                 SERVER_INC ();
270         }
271
272         ptr->status = T_EMPTY;
273
274         safefree (cliaddr);
275         exit (0);
276 }
277
278 /*
279  * Fork a child "child" (or in our case a process) and then start up the
280  * child_main() function.
281  */
282 static pid_t child_make (struct child_s *ptr)
283 {
284         pid_t pid;
285
286         if ((pid = fork ()) > 0)
287                 return pid;     /* parent */
288
289         /*
290          * Reset the SIGNALS so that the child can be reaped.
291          */
292         set_signal_handler (SIGCHLD, SIG_DFL);
293         set_signal_handler (SIGTERM, SIG_DFL);
294         set_signal_handler (SIGHUP, child_sighup_handler);
295
296         child_main (ptr);       /* never returns */
297         return -1;
298 }
299
300 /*
301  * Create a pool of children to handle incoming connections
302  */
303 short int child_pool_create (void)
304 {
305         unsigned int i;
306
307         /*
308          * Make sure the number of MaxClients is not zero, since this
309          * variable determines the size of the array created for children
310          * later on.
311          */
312         if (child_config.maxclients == 0) {
313                 log_message (LOG_ERR,
314                              "child_pool_create: \"MaxClients\" must be "
315                              "greater than zero.");
316                 return -1;
317         }
318         if (child_config.startservers == 0) {
319                 log_message (LOG_ERR,
320                              "child_pool_create: \"StartServers\" must be "
321                              "greater than zero.");
322                 return -1;
323         }
324
325         child_ptr =
326             (struct child_s *) calloc_shared_memory (child_config.maxclients,
327                                                      sizeof (struct child_s));
328         if (!child_ptr) {
329                 log_message (LOG_ERR,
330                              "Could not allocate memory for children.");
331                 return -1;
332         }
333
334         servers_waiting =
335             (unsigned int *) malloc_shared_memory (sizeof (unsigned int));
336         if (servers_waiting == MAP_FAILED) {
337                 log_message (LOG_ERR,
338                              "Could not allocate memory for child counting.");
339                 return -1;
340         }
341         *servers_waiting = 0;
342
343         /*
344          * Create a "locking" file for use around the servers_waiting
345          * variable.
346          */
347         _child_lock_init ();
348
349         if (child_config.startservers > child_config.maxclients) {
350                 log_message (LOG_WARNING,
351                              "Can not start more than \"MaxClients\" servers. "
352                              "Starting %u servers instead.",
353                              child_config.maxclients);
354                 child_config.startservers = child_config.maxclients;
355         }
356
357         for (i = 0; i != child_config.maxclients; i++) {
358                 child_ptr[i].status = T_EMPTY;
359                 child_ptr[i].connects = 0;
360         }
361
362         for (i = 0; i != child_config.startservers; i++) {
363                 DEBUG2 ("Trying to create child %d of %d", i + 1,
364                         child_config.startservers);
365                 child_ptr[i].status = T_WAITING;
366                 child_ptr[i].tid = child_make (&child_ptr[i]);
367
368                 if (child_ptr[i].tid < 0) {
369                         log_message (LOG_WARNING,
370                                      "Could not create child number %d of %d",
371                                      i, child_config.startservers);
372                         return -1;
373                 } else {
374                         log_message (LOG_INFO,
375                                      "Creating child number %d of %d ...",
376                                      i + 1, child_config.startservers);
377
378                         SERVER_INC ();
379                 }
380         }
381
382         log_message (LOG_INFO, "Finished creating all children.");
383
384         return 0;
385 }
386
387 /*
388  * Keep the proper number of servers running. This is the birth of the
389  * servers. It monitors this at least once a second.
390  */
391 void child_main_loop (void)
392 {
393         unsigned int i;
394
395         while (1) {
396                 if (config.quit)
397                         return;
398
399                 /* If there are not enough spare servers, create more */
400                 SERVER_COUNT_LOCK ();
401                 if (*servers_waiting < child_config.minspareservers) {
402                         log_message (LOG_NOTICE,
403                                      "Waiting servers (%d) is less than MinSpareServers (%d). "
404                                      "Creating new child.",
405                                      *servers_waiting,
406                                      child_config.minspareservers);
407
408                         SERVER_COUNT_UNLOCK ();
409
410                         for (i = 0; i != child_config.maxclients; i++) {
411                                 if (child_ptr[i].status == T_EMPTY) {
412                                         child_ptr[i].status = T_WAITING;
413                                         child_ptr[i].tid =
414                                             child_make (&child_ptr[i]);
415                                         if (child_ptr[i].tid < 0) {
416                                                 log_message (LOG_NOTICE,
417                                                              "Could not create child");
418
419                                                 child_ptr[i].status = T_EMPTY;
420                                                 break;
421                                         }
422
423                                         SERVER_INC ();
424
425                                         break;
426                                 }
427                         }
428                 } else {
429                         SERVER_COUNT_UNLOCK ();
430                 }
431
432                 sleep (5);
433
434                 /* Handle log rotation if it was requested */
435                 if (received_sighup) {
436                         /*
437                          * Ignore the return value of reload_config for now.
438                          * This should actually be handled somehow...
439                          */
440                         reload_config ();
441
442 #ifdef FILTER_ENABLE
443                         filter_reload ();
444 #endif /* FILTER_ENABLE */
445
446                         /* propagate filter reload to all children */
447                         child_kill_children (SIGHUP);
448
449                         received_sighup = FALSE;
450                 }
451         }
452 }
453
454 /*
455  * Go through all the non-empty children and cancel them.
456  */
457 void child_kill_children (int sig)
458 {
459         unsigned int i;
460
461         for (i = 0; i != child_config.maxclients; i++) {
462                 if (child_ptr[i].status != T_EMPTY)
463                         kill (child_ptr[i].tid, sig);
464         }
465 }
466
467 int child_listening_sock (uint16_t port)
468 {
469         listenfd = listen_sock (config.ipAddr, port);
470         return listenfd;
471 }
472
473 void child_close_sock (void)
474 {
475         close (listenfd);
476 }