summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am3
-rw-r--r--src/child.c503
-rw-r--r--src/conf.c38
-rw-r--r--src/conf.h1
-rw-r--r--src/heap.c58
-rw-r--r--src/heap.h6
-rw-r--r--src/main.c11
-rw-r--r--src/sblist.c80
-rw-r--r--src/sblist.h92
-rw-r--r--src/stats.c27
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)
+ );
}
diff --git a/src/conf.c b/src/conf.c
index 5ebf179..d7cf5b6 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -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;
}
diff --git a/src/conf.h b/src/conf.h
index beb2b01..44f12d7 100644
--- a/src/conf.h
+++ b/src/conf.h
@@ -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;
diff --git a/src/heap.c b/src/heap.c
index c7d8560..0611c39 100644
--- a/src/heap.c
+++ b/src/heap.c
@@ -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;
-}
diff --git a/src/heap.h b/src/heap.h
index f3cf671..da64461 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -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
diff --git a/src/main.c b/src/main.c
index 43170c5..06465b1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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;
}