diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/child.c | 503 | ||||
-rw-r--r-- | src/conf.c | 38 | ||||
-rw-r--r-- | src/conf.h | 1 | ||||
-rw-r--r-- | src/heap.c | 58 | ||||
-rw-r--r-- | src/heap.h | 6 | ||||
-rw-r--r-- | src/main.c | 11 | ||||
-rw-r--r-- | src/sblist.c | 80 | ||||
-rw-r--r-- | src/sblist.h | 92 | ||||
-rw-r--r-- | src/stats.c | 27 |
10 files changed, 315 insertions, 504 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index af2f621..3924909 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -48,10 +48,11 @@ tinyproxy_SOURCES = \ upstream.c upstream.h \ basicauth.c basicauth.h \ base64.c base64.h \ + sblist.c sblist.h \ connect-ports.c connect-ports.h EXTRA_tinyproxy_SOURCES = filter.c filter.h \ reverse-proxy.c reverse-proxy.h \ transparent-proxy.c transparent-proxy.h tinyproxy_DEPENDENCIES = @ADDITIONAL_OBJECTS@ -tinyproxy_LDADD = @ADDITIONAL_OBJECTS@ +tinyproxy_LDADD = @ADDITIONAL_OBJECTS@ -lpthread diff --git a/src/child.c b/src/child.c index 60f8ead..06a977e 100644 --- a/src/child.c +++ b/src/child.c @@ -31,176 +31,66 @@ #include "sock.h" #include "utils.h" #include "conf.h" +#include "sblist.h" +#include <pthread.h> static vector_t listen_fds; -/* - * Stores the internal data needed for each child (connection) - */ -enum child_status_t { T_EMPTY, T_WAITING, T_CONNECTED }; -struct child_s { - pid_t tid; - unsigned int connects; - enum child_status_t status; +union sockaddr_union { + struct sockaddr_in v4; + struct sockaddr_in6 v6; }; -/* - * 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 unsigned 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 */ +struct client { + union sockaddr_union addr; + int fd; +}; -/* - * 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; +struct child { + pthread_t thread; + struct client client; + volatile int done; +}; -static void _child_lock_init (void) +static void* child_thread(void* data) { - char lock_file[] = "/tmp/tinyproxy.servers.lock.XXXXXX"; - - /* Only allow u+rw bits. This may be required for some versions - * of glibc so that mkstemp() doesn't make us vulnerable. - */ - umask (0177); - - 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; + struct child *c = data; + handle_connection (c->client.fd); + c->done = 1; + return NULL; } -static void _child_lock_wait (void) -{ - int rc; +static sblist *childs; - while ((rc = fcntl (lock_fd, F_SETLKW, &lock_it)) < 0) { - if (errno == EINTR) - continue; - else - return; - } -} - -static void _child_lock_release (void) +static void collect_threads(void) { - if (fcntl (lock_fd, F_SETLKW, &unlock_it) < 0) - return; + size_t i; + for (i = 0; i < sblist_getsize(childs); ) { + struct child *c = *((struct child**)sblist_get(childs, i)); + if (c->done) { + pthread_join(c->thread, 0); + sblist_delete(childs, i); + safefree(c); + } else i++; + } } -/* 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(); \ - assert(*servers_waiting > 0); \ - --(*servers_waiting); \ - DEBUG2("DEC: servers_waiting: %d", *servers_waiting); \ - SERVER_COUNT_UNLOCK(); \ -} while (0) - /* - * Set the configuration values for the various child related settings. + * This is the main loop accepting new connections. */ -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; -} - -/** - * child signal handler for sighup - */ -static void child_sighup_handler (int sig) -{ - if (sig == SIGHUP) { - /* - * Ignore the return value of reload_config for now. - * This should actually be handled somehow... - */ - reload_config (); - -#ifdef FILTER_ENABLE - filter_reload (); -#endif /* FILTER_ENABLE */ - } -} - -/* - * This is the main (per child) loop. - */ -static void child_main (struct child_s *ptr) +void child_main_loop (void) { int connfd; - struct sockaddr *cliaddr; - socklen_t clilen; + union sockaddr_union cliaddr_storage; + struct sockaddr *cliaddr = (void*) &cliaddr_storage; + socklen_t clilen = sizeof(cliaddr_storage); fd_set rfds; - int maxfd = 0; ssize_t i; - int ret; + int ret, listenfd, maxfd, was_full = 0; + pthread_attr_t *attrp, attr; + struct child *child; - cliaddr = (struct sockaddr *) - safemalloc (sizeof(struct sockaddr_storage)); - if (!cliaddr) { - log_message (LOG_CRIT, - "Could not allocate memory for child address."); - exit (0); - } - - ptr->connects = 0; - srand(time(NULL)); + childs = sblist_new(sizeof (struct child*), config.maxclients); /* * We have to wait for connections on multiple fds, @@ -208,7 +98,37 @@ static void child_main (struct child_s *ptr) */ while (!config.quit) { - int listenfd = -1; + collect_threads(); + + if (sblist_getsize(childs) >= config.maxclients) { + if (!was_full) + log_message (LOG_NOTICE, + "Maximum number of connections reached. " + "Refusing new connections."); + was_full = 1; + usleep(16); + continue; + } + + was_full = 0; + listenfd = -1; + maxfd = 0; + + /* Handle log rotation if it was requested */ + if (received_sighup) { + /* + * Ignore the return value of reload_config for now. + * This should actually be handled somehow... + */ + reload_config (); + +#ifdef FILTER_ENABLE + filter_reload (); +#endif /* FILTER_ENABLE */ + + received_sighup = FALSE; + } + FD_ZERO(&rfds); @@ -220,17 +140,13 @@ static void child_main (struct child_s *ptr) log_message(LOG_ERR, "Failed to set the listening " "socket %d to non-blocking: %s", fd, strerror(errno)); - exit(1); + continue; } FD_SET(*fd, &rfds); maxfd = max(maxfd, *fd); } - ptr->status = T_WAITING; - - clilen = sizeof(struct sockaddr_storage); - ret = select(maxfd + 1, &rfds, NULL, NULL, NULL); if (ret == -1) { if (errno == EINTR) { @@ -238,7 +154,7 @@ static void child_main (struct child_s *ptr) } log_message (LOG_ERR, "error calling select: %s", strerror(errno)); - exit(1); + continue; } else if (ret == 0) { log_message (LOG_WARNING, "Strange: select returned 0 " "but we did not specify a timeout..."); @@ -269,7 +185,7 @@ static void child_main (struct child_s *ptr) log_message(LOG_ERR, "Failed to set listening " "socket %d to blocking for accept: %s", listenfd, strerror(errno)); - exit(1); + continue; } /* @@ -279,21 +195,6 @@ static void child_main (struct child_s *ptr) connfd = accept (listenfd, cliaddr, &clilen); -#ifndef NDEBUG - /* - * Enable the TINYPROXY_DEBUG environment variable if you - * want to use the GDB debugger. - */ - if (getenv ("TINYPROXY_DEBUG")) { - /* Pause for 10 seconds to allow us to connect debugger */ - fprintf (stderr, - "Process has accepted connection: %ld\n", - (long int) ptr->tid); - sleep (10); - fprintf (stderr, "Continuing process: %ld\n", - (long int) ptr->tid); - } -#endif /* * Make sure no error occurred... @@ -305,224 +206,37 @@ static void child_main (struct child_s *ptr) continue; } - ptr->status = T_CONNECTED; - - SERVER_DEC (); - - handle_connection (connfd); - ptr->connects++; - - if (child_config.maxrequestsperchild != 0) { - DEBUG2 ("%u connections so far...", ptr->connects); - - if (ptr->connects == child_config.maxrequestsperchild) { - log_message (LOG_NOTICE, - "Child has reached MaxRequestsPerChild (%u). " - "Killing child.", ptr->connects); - 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 (); + child = safemalloc(sizeof(struct child)); + if (!child) { +oom: + close(connfd); + log_message (LOG_CRIT, + "Could not allocate memory for child."); + usleep(16); /* prevent 100% CPU usage in OOM situation */ + continue; } - 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 pid_t child_make (struct child_s *ptr) -{ - pid_t pid; - - if ((pid = fork ()) > 0) - return pid; /* parent */ - - /* - * Reset the SIGNALS so that the child can be reaped. - */ - set_signal_handler (SIGCHLD, SIG_DFL); - set_signal_handler (SIGTERM, SIG_DFL); - set_signal_handler (SIGHUP, child_sighup_handler); - - 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 = - (struct child_s *) 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; - } + child->done = 0; - servers_waiting = - (unsigned int *) malloc_shared_memory (sizeof (unsigned int)); - if (servers_waiting == MAP_FAILED) { - 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 (); + if (!sblist_add(childs, &child)) { + free(child); + goto oom; } - } - - log_message (LOG_INFO, "Finished creating all children."); - return 0; -} + child->client.fd = connfd; + memcpy(&child->client.addr, &cliaddr_storage, sizeof(cliaddr_storage)); -/* - * 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) -{ - unsigned 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 (); + attrp = 0; + if (pthread_attr_init(&attr) == 0) { + attrp = &attr; + pthread_attr_setstacksize(attrp, 256*1024); } - sleep (5); - - /* Handle log rotation if it was requested */ - if (received_sighup) { - /* - * Ignore the return value of reload_config for now. - * This should actually be handled somehow... - */ - reload_config (); - -#ifdef FILTER_ENABLE - filter_reload (); -#endif /* FILTER_ENABLE */ - - /* propagate filter reload to all children */ - child_kill_children (SIGHUP); - - received_sighup = FALSE; - } + if (pthread_create(&child->thread, attrp, child_thread, child) != 0) { + sblist_delete(childs, sblist_getsize(childs) -1); + free(child); + goto oom; + } } } @@ -531,12 +245,25 @@ void child_main_loop (void) */ void child_kill_children (int sig) { - unsigned int i; - - for (i = 0; i != child_config.maxclients; i++) { - if (child_ptr[i].status != T_EMPTY) - kill (child_ptr[i].tid, sig); - } + size_t i; + + if (sig != SIGTERM) return; + + for (i = 0; i < sblist_getsize(childs); i++) { + struct child *c = *((struct child**)sblist_get(childs, i)); + if (!c->done) { + /* interrupt blocking operations. + this should cause the threads to shutdown orderly. */ + close(c->client.fd); + } + } + usleep(16); + collect_threads(); + if (sblist_getsize(childs) != 0) + log_message (LOG_CRIT, + "child_kill_children: %zu threads still alive!", + sblist_getsize(childs) + ); } @@ -27,7 +27,6 @@ #include "acl.h" #include "anonymous.h" -#include "child.h" #include "filter.h" #include "heap.h" #include "html-error.h" @@ -140,9 +139,6 @@ static HANDLE_FUNC (handle_listen); static HANDLE_FUNC (handle_logfile); static HANDLE_FUNC (handle_loglevel); static HANDLE_FUNC (handle_maxclients); -static HANDLE_FUNC (handle_maxrequestsperchild); -static HANDLE_FUNC (handle_maxspareservers); -static HANDLE_FUNC (handle_minspareservers); static HANDLE_FUNC (handle_pidfile); static HANDLE_FUNC (handle_port); #ifdef REVERSE_SUPPORT @@ -151,7 +147,6 @@ static HANDLE_FUNC (handle_reversemagic); static HANDLE_FUNC (handle_reverseonly); static HANDLE_FUNC (handle_reversepath); #endif -static HANDLE_FUNC (handle_startservers); static HANDLE_FUNC (handle_statfile); static HANDLE_FUNC (handle_stathost); static HANDLE_FUNC (handle_syslog); @@ -217,10 +212,6 @@ struct { /* integer arguments */ STDCONF ("port", INT, handle_port), STDCONF ("maxclients", INT, handle_maxclients), - STDCONF ("maxspareservers", INT, handle_maxspareservers), - STDCONF ("minspareservers", INT, handle_minspareservers), - STDCONF ("startservers", INT, handle_startservers), - STDCONF ("maxrequestsperchild", INT, handle_maxrequestsperchild), STDCONF ("timeout", INT, handle_timeout), STDCONF ("connectport", INT, handle_connectport), /* alphanumeric arguments */ @@ -805,34 +796,7 @@ static HANDLE_FUNC (handle_port) static HANDLE_FUNC (handle_maxclients) { - child_configure (CHILD_MAXCLIENTS, get_long_arg (line, &match[2])); - return 0; -} - -static HANDLE_FUNC (handle_maxspareservers) -{ - child_configure (CHILD_MAXSPARESERVERS, - get_long_arg (line, &match[2])); - return 0; -} - -static HANDLE_FUNC (handle_minspareservers) -{ - child_configure (CHILD_MINSPARESERVERS, - get_long_arg (line, &match[2])); - return 0; -} - -static HANDLE_FUNC (handle_startservers) -{ - child_configure (CHILD_STARTSERVERS, get_long_arg (line, &match[2])); - return 0; -} - -static HANDLE_FUNC (handle_maxrequestsperchild) -{ - child_configure (CHILD_MAXREQUESTSPERCHILD, - get_long_arg (line, &match[2])); + set_int_arg (&conf->maxclients, line, &match[2]); return 0; } @@ -45,6 +45,7 @@ struct config_s { char *stathost; unsigned int godaemon; /* boolean */ unsigned int quit; /* boolean */ + unsigned int maxclients; char *user; char *group; vector_t listen_addrs; @@ -97,61 +97,3 @@ char *debugging_strdup (const char *s, const char *file, unsigned long line) #endif /* !NDEBUG */ -/* - * Allocate a block of memory in the "shared" memory region. - * - * FIXME: This uses the most basic (and slowest) means of creating a - * shared memory location. It requires the use of a temporary file. We might - * want to look into something like MM (Shared Memory Library) for a better - * solution. - */ -void *malloc_shared_memory (size_t size) -{ - int fd; - void *ptr; - char buffer[32]; - - static const char *shared_file = "/tmp/tinyproxy.shared.XXXXXX"; - - assert (size > 0); - - strlcpy (buffer, shared_file, sizeof (buffer)); - - /* Only allow u+rw bits. This may be required for some versions - * of glibc so that mkstemp() doesn't make us vulnerable. - */ - umask (0177); - - if ((fd = mkstemp (buffer)) == -1) - return MAP_FAILED; - unlink (buffer); - - if (ftruncate (fd, size) == -1) - return MAP_FAILED; - ptr = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - - return ptr; -} - -/* - * Allocate a block of memory from the "shared" region an initialize it to - * zero. - */ -void *calloc_shared_memory (size_t nmemb, size_t size) -{ - void *ptr; - long length; - - assert (nmemb > 0); - assert (size > 0); - - length = nmemb * size; - - ptr = malloc_shared_memory (length); - if (ptr == MAP_FAILED) - return ptr; - - memset (ptr, 0, length); - - return ptr; -} @@ -52,10 +52,4 @@ extern char *debugging_strdup (const char *s, const char *file, #endif -/* - * Allocate memory from the "shared" region of memory. - */ -extern void *malloc_shared_memory (size_t size); -extern void *calloc_shared_memory (size_t nmemb, size_t size); - #endif @@ -65,6 +65,7 @@ takesig (int sig) received_sighup = TRUE; break; + case SIGINT: case SIGTERM: config.quit = TRUE; break; @@ -302,6 +303,7 @@ static void initialize_config_defaults (struct config_s *conf) conf->idletimeout = MAX_IDLE_TIME; conf->logf_name = NULL; conf->pidpath = NULL; + conf->maxclients = 100; } /** @@ -329,6 +331,8 @@ done: int main (int argc, char **argv) { + srand(time(NULL)); /* for hashmap seeds */ + /* Only allow u+rw bits. This may be required for some versions * of glibc so that mkstemp() doesn't make us vulnerable. */ @@ -407,13 +411,6 @@ main (int argc, char **argv) exit (EX_SOFTWARE); } - if (child_pool_create () < 0) { - fprintf (stderr, - "%s: Could not create the pool of children.\n", - argv[0]); - exit (EX_SOFTWARE); - } - /* These signals are only for the parent process. */ log_message (LOG_INFO, "Setting the various signals."); diff --git a/src/sblist.c b/src/sblist.c new file mode 100644 index 0000000..4ddc4aa --- /dev/null +++ b/src/sblist.c @@ -0,0 +1,80 @@ +#undef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#include "sblist.h" +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#define MY_PAGE_SIZE 4096 + +sblist* sblist_new(size_t itemsize, size_t blockitems) { + sblist* ret = (sblist*) malloc(sizeof(sblist)); + sblist_init(ret, itemsize, blockitems); + return ret; +} + +static void sblist_clear(sblist* l) { + l->items = NULL; + l->capa = 0; + l->count = 0; +} + +void sblist_init(sblist* l, size_t itemsize, size_t blockitems) { + if(l) { + l->blockitems = blockitems ? blockitems : MY_PAGE_SIZE / itemsize; + l->itemsize = itemsize; + sblist_clear(l); + } +} + +void sblist_free_items(sblist* l) { + if(l) { + if(l->items) free(l->items); + sblist_clear(l); + } +} + +void sblist_free(sblist* l) { + if(l) { + sblist_free_items(l); + free(l); + } +} + +char* sblist_item_from_index(sblist* l, size_t idx) { + return l->items + (idx * l->itemsize); +} + +void* sblist_get(sblist* l, size_t item) { + if(item < l->count) return (void*) sblist_item_from_index(l, item); + return NULL; +} + +int sblist_set(sblist* l, void* item, size_t pos) { + if(pos >= l->count) return 0; + memcpy(sblist_item_from_index(l, pos), item, l->itemsize); + return 1; +} + +int sblist_grow_if_needed(sblist* l) { + char* temp; + if(l->count == l->capa) { + temp = realloc(l->items, (l->capa + l->blockitems) * l->itemsize); + if(!temp) return 0; + l->capa += l->blockitems; + l->items = temp; + } + return 1; +} + +int sblist_add(sblist* l, void* item) { + if(!sblist_grow_if_needed(l)) return 0; + l->count++; + return sblist_set(l, item, l->count - 1); +} + +void sblist_delete(sblist* l, size_t item) { + if (l->count && item < l->count) { + memmove(sblist_item_from_index(l, item), sblist_item_from_index(l, item + 1), (sblist_getsize(l) - (item + 1)) * l->itemsize); + l->count--; + } +} diff --git a/src/sblist.h b/src/sblist.h new file mode 100644 index 0000000..02c33d7 --- /dev/null +++ b/src/sblist.h @@ -0,0 +1,92 @@ +#ifndef SBLIST_H +#define SBLIST_H + +/* this file is part of libulz, as of commit 8ab361a27743aaf025323ee43b8b8876dc054fdd + modified for direct inclusion in tinyproxy, and for this purpose released under + the license of tinyproxy. */ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stddef.h> +/* + * simple buffer list. + * + * this thing here is basically a generic dynamic array + * will realloc after every blockitems inserts + * can store items of any size. + * + * so think of it as a by-value list, as opposed to a typical by-ref list. + * you typically use it by having some struct on the stack, and pass a pointer + * to sblist_add, which will copy the contents into its internal memory. + * + */ + +typedef struct { + size_t itemsize; + size_t blockitems; + size_t count; + size_t capa; + char* items; +} sblist; + +#define sblist_getsize(X) ((X)->count) +#define sblist_get_count(X) ((X)->count) +#define sblist_empty(X) ((X)->count == 0) + +/* for dynamic style */ +sblist* sblist_new(size_t itemsize, size_t blockitems); +void sblist_free(sblist* l); + +/*for static style*/ +void sblist_init(sblist* l, size_t itemsize, size_t blockitems); +void sblist_free_items(sblist* l); + +/* accessors */ +void* sblist_get(sblist* l, size_t item); +/* returns 1 on success, 0 on OOM */ +int sblist_add(sblist* l, void* item); +int sblist_set(sblist* l, void* item, size_t pos); +void sblist_delete(sblist* l, size_t item); +char* sblist_item_from_index(sblist* l, size_t idx); +int sblist_grow_if_needed(sblist* l); +int sblist_insert(sblist* l, void* item, size_t pos); +/* same as sblist_add, but returns list index of new item, or -1 */ +size_t sblist_addi(sblist* l, void* item); +void sblist_sort(sblist *l, int (*compar)(const void *, const void *)); +/* insert element into presorted list, returns listindex of new entry or -1*/ +size_t sblist_insert_sorted(sblist* l, void* o, int (*compar)(const void *, const void *)); + +#ifndef __COUNTER__ +#define __COUNTER__ __LINE__ +#endif + +#define __sblist_concat_impl( x, y ) x##y +#define __sblist_macro_concat( x, y ) __sblist_concat_impl( x, y ) +#define __sblist_iterator_name __sblist_macro_concat(sblist_iterator, __COUNTER__) + +/* use with custom iterator variable */ +#define sblist_iter_counter(LIST, ITER, PTR) \ + for(size_t ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < sblist_getsize(LIST); ITER++) + +/* use with custom iterator variable, which is predeclared */ +#define sblist_iter_counter2(LIST, ITER, PTR) \ + for(ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < sblist_getsize(LIST); ITER++) + +/* use with custom iterator variable, which is predeclared and signed */ +/* useful for a loop which can delete items from the list, and then decrease the iterator var. */ +#define sblist_iter_counter2s(LIST, ITER, PTR) \ + for(ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < (ssize_t) sblist_getsize(LIST); ITER++) + + +/* uses "magic" iterator variable */ +#define sblist_iter(LIST, PTR) sblist_iter_counter(LIST, __sblist_iterator_name, PTR) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/stats.c b/src/stats.c index c7b4423..93a30cf 100644 --- a/src/stats.c +++ b/src/stats.c @@ -33,6 +33,7 @@ #include "stats.h" #include "utils.h" #include "conf.h" +#include <pthread.h> struct stat_s { unsigned long int num_reqs; @@ -43,14 +44,16 @@ struct stat_s { }; static struct stat_s *stats; +static pthread_mutex_t stats_update_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t stats_file_lock = PTHREAD_MUTEX_INITIALIZER; /* * Initialize the statistics information to zero. */ void init_stats (void) { - stats = (struct stat_s *) malloc_shared_memory (sizeof (struct stat_s)); - if (stats == MAP_FAILED) + stats = (struct stat_s *) safemalloc (sizeof (struct stat_s)); + if (!stats) return; memset (stats, 0, sizeof (struct stat_s)); @@ -72,10 +75,15 @@ showstats (struct conn_s *connptr) snprintf (denied, sizeof (denied), "%lu", stats->num_denied); snprintf (refused, sizeof (refused), "%lu", stats->num_refused); + pthread_mutex_lock(&stats_file_lock); + if (!config.statpage || (!(statfile = fopen (config.statpage, "r")))) { message_buffer = (char *) safemalloc (MAXBUFFSIZE); - if (!message_buffer) + if (!message_buffer) { +err_minus_one: + pthread_mutex_unlock(&stats_file_lock); return -1; + } snprintf (message_buffer, MAXBUFFSIZE, @@ -105,13 +113,13 @@ showstats (struct conn_s *connptr) if (send_http_message (connptr, 200, "OK", message_buffer) < 0) { safefree (message_buffer); - return -1; + goto err_minus_one; } safefree (message_buffer); + pthread_mutex_unlock(&stats_file_lock); return 0; } - add_error_variable (connptr, "opens", opens); add_error_variable (connptr, "reqs", reqs); add_error_variable (connptr, "badconns", badconns); @@ -121,6 +129,7 @@ showstats (struct conn_s *connptr) send_http_headers (connptr, 200, "Statistic requested"); send_html_file (statfile, connptr); fclose (statfile); + pthread_mutex_unlock(&stats_file_lock); return 0; } @@ -131,6 +140,9 @@ showstats (struct conn_s *connptr) */ int update_stats (status_t update_level) { + int ret = 0; + + pthread_mutex_lock(&stats_update_lock); switch (update_level) { case STAT_BADCONN: ++stats->num_badcons; @@ -149,8 +161,9 @@ int update_stats (status_t update_level) ++stats->num_denied; break; default: - return -1; + ret = -1; } + pthread_mutex_unlock(&stats_update_lock); - return 0; + return ret; } |