1 /* tinyproxy - A fast light-weight HTTP proxy
2 * Copyright (C) 2000 Robert James Kaes <rjkaes@users.sourceforge.net>
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.
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.
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.
19 /* Handles the creation/destruction of the various children required for
20 * processing incoming connections.
38 * Stores the internal data needed for each child (connection)
40 enum child_status_t { T_EMPTY, T_WAITING, T_CONNECTED };
43 unsigned int connects;
44 enum child_status_t status;
48 * A pointer to an array of children. A certain number of children are
49 * created when the program is started.
51 static struct child_s *child_ptr;
53 static struct child_config_s {
54 unsigned int maxclients, maxrequestsperchild;
55 unsigned int maxspareservers, minspareservers, startservers;
58 static unsigned int *servers_waiting; /* servers waiting for a connection */
61 * Lock/Unlock the "servers_waiting" variable so that two children cannot
62 * modify it at the same time.
64 #define SERVER_COUNT_LOCK() _child_lock_wait()
65 #define SERVER_COUNT_UNLOCK() _child_lock_release()
67 /* START OF LOCKING SECTION */
70 * These variables are required for the locking mechanism. Also included
71 * are the "private" functions for locking/unlocking.
73 static struct flock lock_it, unlock_it;
74 static int lock_fd = -1;
76 static void _child_lock_init (void)
78 char lock_file[] = "/tmp/tinyproxy.servers.lock.XXXXXX";
80 /* Only allow u+rw bits. This may be required for some versions
81 * of glibc so that mkstemp() doesn't make us vulnerable.
85 lock_fd = mkstemp (lock_file);
88 lock_it.l_type = F_WRLCK;
89 lock_it.l_whence = SEEK_SET;
93 unlock_it.l_type = F_UNLCK;
94 unlock_it.l_whence = SEEK_SET;
95 unlock_it.l_start = 0;
99 static void _child_lock_wait (void)
103 while ((rc = fcntl (lock_fd, F_SETLKW, &lock_it)) < 0) {
111 static void _child_lock_release (void)
113 if (fcntl (lock_fd, F_SETLKW, &unlock_it) < 0)
117 /* END OF LOCKING SECTION */
119 #define SERVER_INC() do { \
120 SERVER_COUNT_LOCK(); \
121 ++(*servers_waiting); \
122 DEBUG2("INC: servers_waiting: %d", *servers_waiting); \
123 SERVER_COUNT_UNLOCK(); \
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(); \
135 * Set the configuration values for the various child related settings.
137 short int child_configure (child_config_t type, unsigned int val)
140 case CHILD_MAXCLIENTS:
141 child_config.maxclients = val;
143 case CHILD_MAXSPARESERVERS:
144 child_config.maxspareservers = val;
146 case CHILD_MINSPARESERVERS:
147 child_config.minspareservers = val;
149 case CHILD_STARTSERVERS:
150 child_config.startservers = val;
152 case CHILD_MAXREQUESTSPERCHILD:
153 child_config.maxrequestsperchild = val;
156 DEBUG2 ("Invalid type (%d)", type);
164 * child signal handler for sighup
166 static void child_sighup_handler (int sig)
170 * Ignore the return value of reload_config for now.
171 * This should actually be handled somehow...
177 #endif /* FILTER_ENABLE */
182 * This is the main (per child) loop.
184 static void child_main (struct child_s *ptr)
187 struct sockaddr *cliaddr;
190 cliaddr = (struct sockaddr *)
191 safemalloc (sizeof(struct sockaddr_storage));
193 log_message (LOG_CRIT,
194 "Could not allocate memory for child address.");
200 while (!config.quit) {
201 ptr->status = T_WAITING;
203 clilen = sizeof(struct sockaddr_storage);
205 connfd = accept (listenfd, cliaddr, &clilen);
209 * Enable the TINYPROXY_DEBUG environment variable if you
210 * want to use the GDB debugger.
212 if (getenv ("TINYPROXY_DEBUG")) {
213 /* Pause for 10 seconds to allow us to connect debugger */
215 "Process has accepted connection: %ld\n",
216 (long int) ptr->tid);
218 fprintf (stderr, "Continuing process: %ld\n",
219 (long int) ptr->tid);
224 * Make sure no error occurred...
227 log_message (LOG_ERR,
228 "Accept returned an error (%s) ... retrying.",
233 ptr->status = T_CONNECTED;
237 handle_connection (connfd);
240 if (child_config.maxrequestsperchild != 0) {
241 DEBUG2 ("%u connections so far...", ptr->connects);
243 if (ptr->connects == child_config.maxrequestsperchild) {
244 log_message (LOG_NOTICE,
245 "Child has reached MaxRequestsPerChild (%u). "
246 "Killing child.", ptr->connects);
251 SERVER_COUNT_LOCK ();
252 if (*servers_waiting > child_config.maxspareservers) {
254 * There are too many spare children, kill ourself
257 log_message (LOG_NOTICE,
258 "Waiting servers (%d) exceeds MaxSpareServers (%d). "
261 child_config.maxspareservers);
262 SERVER_COUNT_UNLOCK ();
266 SERVER_COUNT_UNLOCK ();
272 ptr->status = T_EMPTY;
279 * Fork a child "child" (or in our case a process) and then start up the
280 * child_main() function.
282 static pid_t child_make (struct child_s *ptr)
286 if ((pid = fork ()) > 0)
287 return pid; /* parent */
290 * Reset the SIGNALS so that the child can be reaped.
292 set_signal_handler (SIGCHLD, SIG_DFL);
293 set_signal_handler (SIGTERM, SIG_DFL);
294 set_signal_handler (SIGHUP, child_sighup_handler);
296 child_main (ptr); /* never returns */
301 * Create a pool of children to handle incoming connections
303 short int child_pool_create (void)
308 * Make sure the number of MaxClients is not zero, since this
309 * variable determines the size of the array created for children
312 if (child_config.maxclients == 0) {
313 log_message (LOG_ERR,
314 "child_pool_create: \"MaxClients\" must be "
315 "greater than zero.");
318 if (child_config.startservers == 0) {
319 log_message (LOG_ERR,
320 "child_pool_create: \"StartServers\" must be "
321 "greater than zero.");
326 (struct child_s *) calloc_shared_memory (child_config.maxclients,
327 sizeof (struct child_s));
329 log_message (LOG_ERR,
330 "Could not allocate memory for children.");
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.");
341 *servers_waiting = 0;
344 * Create a "locking" file for use around the servers_waiting
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;
357 for (i = 0; i != child_config.maxclients; i++) {
358 child_ptr[i].status = T_EMPTY;
359 child_ptr[i].connects = 0;
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]);
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);
374 log_message (LOG_INFO,
375 "Creating child number %d of %d ...",
376 i + 1, child_config.startservers);
382 log_message (LOG_INFO, "Finished creating all children.");
388 * Keep the proper number of servers running. This is the birth of the
389 * servers. It monitors this at least once a second.
391 void child_main_loop (void)
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.",
406 child_config.minspareservers);
408 SERVER_COUNT_UNLOCK ();
410 for (i = 0; i != child_config.maxclients; i++) {
411 if (child_ptr[i].status == T_EMPTY) {
412 child_ptr[i].status = T_WAITING;
414 child_make (&child_ptr[i]);
415 if (child_ptr[i].tid < 0) {
416 log_message (LOG_NOTICE,
417 "Could not create child");
419 child_ptr[i].status = T_EMPTY;
429 SERVER_COUNT_UNLOCK ();
434 /* Handle log rotation if it was requested */
435 if (received_sighup) {
437 * Ignore the return value of reload_config for now.
438 * This should actually be handled somehow...
444 #endif /* FILTER_ENABLE */
446 /* propagate filter reload to all children */
447 child_kill_children (SIGHUP);
449 received_sighup = FALSE;
455 * Go through all the non-empty children and cancel them.
457 void child_kill_children (int sig)
461 for (i = 0; i != child_config.maxclients; i++) {
462 if (child_ptr[i].status != T_EMPTY)
463 kill (child_ptr[i].tid, sig);
467 int child_listening_sock (uint16_t port)
469 listenfd = listen_sock (config.ipAddr, port);
473 void child_close_sock (void)