diff options
Diffstat (limited to '')
| -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 | 
