diff options
author | Robert James Kaes <rjkaes@users.sourceforge.net> | 2002-05-26 18:45:26 +0000 |
---|---|---|
committer | Robert James Kaes <rjkaes@users.sourceforge.net> | 2002-05-26 18:45:26 +0000 |
commit | b3e657a00e70fbf328792ab1dd2da9724ffa9b98 (patch) | |
tree | a11ea61f7fd38db21bcc8956daaa4a43d7d44e07 /src | |
parent | 391a408eee5351b4c4714f9edfe358d924ea80bc (diff) |
Changed from using a threading model to a standard pre-forked model.
Therefore the thread.c file has been removed and this file replaces it.
These files are really just the thread.c and thread.h files with all the
threading stuff replaced with fork() code. Most of the code is identical.
Diffstat (limited to 'src')
-rw-r--r-- | src/child.c | 405 | ||||
-rw-r--r-- | src/child.h | 37 |
2 files changed, 442 insertions, 0 deletions
diff --git a/src/child.c b/src/child.c new file mode 100644 index 0000000..9ac74e4 --- /dev/null +++ b/src/child.c @@ -0,0 +1,405 @@ +/* $Id: child.c,v 1.1 2002-05-26 18:45:26 rjkaes Exp $ + * + * Handles the creation/destruction of the various children required for + * processing incoming connections. + * + * Copyright (C) 2000 Robert James Kaes (rjkaes@flarenet.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include "tinyproxy.h" + +#include "child.h" +#include "heap.h" +#include "log.h" +#include "reqs.h" +#include "sock.h" +#include "utils.h" + +static int listenfd; +static socklen_t addrlen; + +/* + * Stores the internal data needed for each child (connection) + */ +struct child_s { + pid_t tid; + unsigned int connects; + enum { T_EMPTY, T_WAITING, T_CONNECTED } status; +}; + +/* + * A pointer to an array of children. A certain number of children are + * created when the program is started. + */ +static struct child_s *child_ptr; + +static struct child_config_s { + unsigned int maxclients, maxrequestsperchild; + unsigned int maxspareservers, minspareservers, startservers; +} child_config; + +static int* servers_waiting; /* servers waiting for a connection */ + +/* + * Lock/Unlock the "servers_waiting" variable so that two children cannot + * modify it at the same time. + */ +#define SERVER_COUNT_LOCK() _child_lock_wait() +#define SERVER_COUNT_UNLOCK() _child_lock_release() + +/* START OF LOCKING SECTION */ + +/* + * These variables are required for the locking mechanism. Also included + * are the "private" functions for locking/unlocking. + */ +static struct flock lock_it, unlock_it; +static int lock_fd = -1; + +static void +_child_lock_init(void) +{ + char lock_file[] = "/tmp/tinyproxy.servers.lock.XXXXXX"; + + lock_fd = mkstemp(lock_file); + unlink(lock_file); + + lock_it.l_type = F_WRLCK; + lock_it.l_whence = SEEK_SET; + lock_it.l_start = 0; + lock_it.l_len = 0; + + unlock_it.l_type = F_UNLCK; + unlock_it.l_whence = SEEK_SET; + unlock_it.l_start = 0; + unlock_it.l_len = 0; +} + +static void +_child_lock_wait(void) +{ + int rc; + + while ((rc = fcntl(lock_fd, F_SETLKW, &lock_it)) < 0) { + if (errno == EINTR) + continue; + else + return; + } +} + +static void +_child_lock_release(void) +{ + if (fcntl(lock_fd, F_SETLKW, &unlock_it) < 0) + return; +} + +/* END OF LOCKING SECTION */ + +#define SERVER_INC() do { \ + SERVER_COUNT_LOCK(); \ + ++(*servers_waiting); \ + DEBUG2("INC: servers_waiting: %d", *servers_waiting); \ + SERVER_COUNT_UNLOCK(); \ +} while (0) + +#define SERVER_DEC() do { \ + SERVER_COUNT_LOCK(); \ + --(*servers_waiting); \ + DEBUG2("DEC: servers_waiting: %d", *servers_waiting); \ + SERVER_COUNT_UNLOCK(); \ +} while (0) + +/* + * Set the configuration values for the various child related settings. + */ +short int +child_configure(child_config_t type, unsigned int val) +{ + switch (type) { + case CHILD_MAXCLIENTS: + child_config.maxclients = val; + break; + case CHILD_MAXSPARESERVERS: + child_config.maxspareservers = val; + break; + case CHILD_MINSPARESERVERS: + child_config.minspareservers = val; + break; + case CHILD_STARTSERVERS: + child_config.startservers = val; + break; + case CHILD_MAXREQUESTSPERCHILD: + child_config.maxrequestsperchild = val; + break; + default: + DEBUG2("Invalid type (%d)", type); + return -1; + } + + return 0; +} + +/* + * This is the main (per child) loop. + */ +static void +child_main(struct child_s* ptr) +{ + int connfd; + struct sockaddr *cliaddr; + socklen_t clilen; + + cliaddr = safemalloc(addrlen); + if (!cliaddr) + exit(0); + + ptr->connects = 0; + + while (!config.quit) { + ptr->status = T_WAITING; + + clilen = addrlen; + + connfd = accept(listenfd, cliaddr, &clilen); + + /* + * Make sure no error occurred... + */ + if (connfd < 0) { + log_message(LOG_ERR, "Accept returned an error (%s) ... retrying.", strerror(errno)); + continue; + } + + ptr->status = T_CONNECTED; + + SERVER_DEC(); + + handle_connection(connfd); + + if (child_config.maxrequestsperchild != 0) { + ptr->connects++; + + DEBUG2("%u connections so far...", ptr->connects); + + if (ptr->connects >= child_config.maxrequestsperchild) { + log_message(LOG_NOTICE, + "Child has reached MaxRequestsPerChild (%u > %u). Killing child.", + ptr->connects, + child_config.maxrequestsperchild); + + break; + } + } + + SERVER_COUNT_LOCK(); + if (*servers_waiting > child_config.maxspareservers) { + /* + * There are too many spare children, kill ourself + * off. + */ + log_message(LOG_NOTICE, + "Waiting servers (%d) exceeds MaxSpareServers (%d). Killing child.", + *servers_waiting, child_config.maxspareservers); + SERVER_COUNT_UNLOCK(); + + break; + } else { + SERVER_COUNT_UNLOCK(); + } + + SERVER_INC(); + } + + ptr->status = T_EMPTY; + + safefree(cliaddr); + exit(0); +} + +/* + * Fork a child "child" (or in our case a process) and then start up the + * child_main() function. + */ +static int +child_make(struct child_s* ptr) +{ + pid_t pid; + + if ((pid = fork()) > 0) + return pid; /* parent */ + + child_main(ptr); /* never returns */ + return -1; +} + +/* + * Create a pool of children to handle incoming connections + */ +short int +child_pool_create(void) +{ + unsigned int i; + + /* + * Make sure the number of MaxClients is not zero, since this + * variable determines the size of the array created for children + * later on. + */ + if (child_config.maxclients == 0) { + log_message(LOG_ERR, + "child_pool_create: \"MaxClients\" must be greater than zero."); + return -1; + } + if (child_config.startservers == 0) { + log_message(LOG_ERR, + "child_pool_create: \"StartServers\" must be greater than zero."); + return -1; + } + + child_ptr = calloc_shared_memory(child_config.maxclients, + sizeof(struct child_s)); + if (!child_ptr) { + log_message(LOG_ERR, "Could not allocate memory for children."); + return -1; + } + + servers_waiting = malloc_shared_memory(sizeof(int)); + if (!servers_waiting) { + log_message(LOG_ERR, "Could not allocate memory for child counting."); + return -1; + } + *servers_waiting = 0; + + /* + * Create a "locking" file for use around the servers_waiting + * variable. + */ + _child_lock_init(); + + if (child_config.startservers > child_config.maxclients) { + log_message(LOG_WARNING, + "Can not start more than \"MaxClients\" servers. Starting %u servers instead.", + child_config.maxclients); + child_config.startservers = child_config.maxclients; + } + + for (i = 0; i < child_config.maxclients; i++) { + child_ptr[i].status = T_EMPTY; + child_ptr[i].connects = 0; + } + + for (i = 0; i < child_config.startservers; i++) { + DEBUG2("Trying to create child %d of %d", i + 1, child_config.startservers); + child_ptr[i].status = T_WAITING; + child_ptr[i].tid = child_make(&child_ptr[i]); + + if (child_ptr[i].tid < 0) { + log_message(LOG_WARNING, + "Could not create child number %d of %d", + i, child_config.startservers); + return -1; + } else { + log_message(LOG_INFO, + "Creating child number %d of %d ...", + i + 1, child_config.startservers); + + SERVER_INC(); + } + } + + log_message(LOG_INFO, "Finished creating all children."); + + return 0; +} + +/* + * Keep the proper number of servers running. This is the birth of the + * servers. It monitors this at least once a second. + */ +void +child_main_loop(void) +{ + int i; + + while (1) { + if (config.quit) + return; + + /* If there are not enough spare servers, create more */ + SERVER_COUNT_LOCK(); + if (*servers_waiting < child_config.minspareservers) { + log_message(LOG_NOTICE, + "Waiting servers (%d) is less than MinSpareServers (%d). Creating new child.", + *servers_waiting, child_config.minspareservers); + + SERVER_COUNT_UNLOCK(); + + for (i = 0; i < child_config.maxclients; i++) { + if (child_ptr[i].status == T_EMPTY) { + child_ptr[i].status = T_WAITING; + child_ptr[i].tid = child_make(&child_ptr[i]); + if (child_ptr[i].tid < 0) { + log_message(LOG_NOTICE, + "Could not create child"); + + child_ptr[i].status = T_EMPTY; + break; + } + + SERVER_INC(); + + break; + } + } + } else { + SERVER_COUNT_UNLOCK(); + } + + sleep(5); + + /* Handle log rotation if it was requested */ + if (log_rotation_request) { + rotate_log_files(); + log_rotation_request = FALSE; + } + } +} + +/* + * Go through all the non-empty children and cancel them. + */ +void +child_kill_children(void) +{ + int i; + + for (i = 0; i < child_config.maxclients; i++) { + if (child_ptr[i].status != T_EMPTY) + kill(child_ptr[i].tid, SIGTERM); + } +} + +int +child_listening_sock(uint16_t port) +{ + listenfd = listen_sock(port, &addrlen); + return listenfd; +} + +void +child_close_sock(void) +{ + close(listenfd); +} diff --git a/src/child.h b/src/child.h new file mode 100644 index 0000000..2ae5b3d --- /dev/null +++ b/src/child.h @@ -0,0 +1,37 @@ +/* $Id: child.h,v 1.1 2002-05-26 18:45:26 rjkaes Exp $ + * + * See 'child.c' for more information. + * + * Copyright (C) 2002 Robert James Kaes (rjkaes@flarenet.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef TINYPROXY_CHILD_H +#define TINYPROXY_CHILD_H + +typedef enum { + CHILD_MAXCLIENTS, + CHILD_MAXSPARESERVERS, + CHILD_MINSPARESERVERS, + CHILD_STARTSERVERS, + CHILD_MAXREQUESTSPERCHILD +} child_config_t; + +extern short int child_pool_create(void); +extern int child_listening_sock(uint16_t port); +extern void child_close_sock(void); +extern void child_main_loop(void); +extern void child_kill_children(void); + +extern short int child_configure(child_config_t type, unsigned int val); + +#endif |