* released 1.2.4
* merged Alexander Lazic's and Klaus Wagner's work on application
cookie-based persistence. Since this is the first merge, this version is
not intended for general use and reports are more than welcome. Some
documentation is really needed though.
diff --git a/CHANGELOG b/CHANGELOG
index 8057f87..df96dd6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,12 @@
ChangeLog :
===========
+2005/01/22 : 1.2.4
+ - merged Alexander Lazic's and Klaus Wagner's work on application
+ cookie-based persistence. Since this is the first merge, this version is
+ not intended for general use and reports are more than welcome. Some
+ documentation is really needed though.
+
2005/01/22 : 1.2.3 (1.1.30)
- add an architecture guide to the documentation
- released without any changes
diff --git a/Makefile b/Makefile
index 7caf492..29a79fc 100644
--- a/Makefile
+++ b/Makefile
@@ -73,7 +73,7 @@
REGEX_OPTS=$(COPTS.$(REGEX))
CPU_OPTS=$(COPTS.$(CPU))
-COPTS=$(CPU_OPTS) $(TARGET_OPTS) $(REGEX_OPTS) $(SMALL_OPTS)
+COPTS=-I. $(CPU_OPTS) $(TARGET_OPTS) $(REGEX_OPTS) $(SMALL_OPTS)
LIBS=$(LIBS.$(TARGET)) $(LIBS.$(REGEX))
# - use -DSTATTIME=0 to disable statistics, else specify an interval in
@@ -84,12 +84,12 @@
all: haproxy
-haproxy: haproxy.o
+haproxy: src/list.o src/chtbl.o src/hashpjw.o haproxy.o
$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
- rm -f *.[oas] *~ *.rej core haproxy test nohup.out gmon.out
+ rm -f *.[oas] *~ *.rej core haproxy test nohup.out gmon.out src/*.[oas]
diff --git a/TODO b/TODO
index 46e2912..047468b 100644
--- a/TODO
+++ b/TODO
@@ -136,7 +136,7 @@
* listen [ip4.ip4.ip4.ip4]:port[-port]
* listen [ip6::...ip6]/port[-port]
- server xxx ipv4 | ipv4: | ipv4:port[-port] | ipv6/ | ipv6/port[-port]
-- appcookie
+* appcookie
- weighted round robin
- option to shutdown(listen_sock) when max connections reached
diff --git a/haproxy.c b/haproxy.c
index b6682b4..87c4145 100644
--- a/haproxy.c
+++ b/haproxy.c
@@ -57,7 +57,51 @@
#include <linux/netfilter_ipv4.h>
#endif
-#define HAPROXY_VERSION "1.2.3"
+#if defined(__dietlibc__)
+#include <strings.h>
+#endif
+
+#define TBLSIZ 10
+#define TBLCHKINT 5000 /* The time between two calls of appsession_refresh in ms */
+
+/*
+ These Parts are copied from
+
+ http://www.oreilly.com/catalog/masteralgoc/index.html
+ Mastering Algorithms with C
+ By Kyle Loudon
+ ISBN: 1-56592-453-3
+ Publishd by O'Reilly
+
+ We have added our own struct to these function.
+ */
+
+#include <include/list.h>
+#include <include/chtbl.h>
+#include <include/hashpjw.h>
+/* end of copied parts */
+
+struct app_pool {
+ void **sessid;
+ void **serverid;
+ int ses_waste, ses_use, ses_msize;
+ int ser_waste, ser_use, ser_msize;
+};
+
+struct app_pool apools;
+int have_appsession;
+
+/* Callback for hash_lookup */
+int match_str(const void *key1, const void *key2);
+
+/* Callback for destroy */
+void destroy(void *data);
+
+#if defined(DEBUG_HASH)
+static void print_table(const CHTbl *htbl);
+#endif
+
+#define HAPROXY_VERSION "1.2.4"
#define HAPROXY_DATE "2005/01/22"
/* this is for libc5 for example */
@@ -236,8 +280,10 @@
#define sizeof_session sizeof(struct session)
#define sizeof_buffer sizeof(struct buffer)
#define sizeof_fdtab sizeof(struct fdtab)
+#define sizeof_curappsession CAPTURE_LEN /* current_session pool */
#define sizeof_requri REQURI_LEN
#define sizeof_capture CAPTURE_LEN
+#define sizeof_appsess sizeof(struct appsessions)
/* different possible states for the sockets */
#define FD_STCLOSE 0
@@ -501,7 +547,12 @@
struct server *srv, *cursrv; /* known servers, current server */
int nbservers; /* # of servers */
char *cookie_name; /* name of the cookie to look for */
- int cookie_len; /* strlen(cookie_len), computed only once */
+ int cookie_len; /* strlen(cookie_name), computed only once */
+ char *appsession_name; /* name of the cookie to look for */
+ int appsession_name_len; /* strlen(appsession_name), computed only once */
+ int appsession_len; /* length of the appsession cookie value to be used */
+ int appsession_timeout;
+ CHTbl htbl_proxy; /* Per Proxy hashtable */
char *capture_name; /* beginning of the name of the cookie to capture */
int capture_namelen; /* length of the cookie name to match */
int capture_len; /* length of the string to be captured */
@@ -598,7 +649,8 @@
**pool_fdtab = NULL,
**pool_requri = NULL,
**pool_task = NULL,
- **pool_capture = NULL;
+ **pool_capture = NULL,
+ **pool_appsess = NULL;
struct proxy *proxy = NULL; /* list of all existing proxies */
struct fdtab *fdtab = NULL; /* array of all the file descriptors */
@@ -745,6 +797,10 @@
int event_srv_write(int fd);
int process_session(struct task *t);
+static int appsession_task_init(void);
+static int appsession_init(void);
+static int appsession_refresh(struct task *t);
+
/*********************************************************************/
/* general purpose functions ***************************************/
/*********************************************************************/
@@ -1605,7 +1661,9 @@
int connect_server(struct session *s) {
int fd;
- // fprintf(stderr,"connect_server : s=%p\n",s);
+#ifdef DEBUG_FULL
+ fprintf(stderr,"connect_server : s=%p\n",s);
+#endif
if (s->flags & SN_DIRECT) { /* srv cannot be null */
s->srv_addr = s->srv->addr;
@@ -1729,7 +1787,9 @@
struct buffer *b = s->req;
int ret, max;
- // fprintf(stderr,"event_cli_read : fd=%d, s=%p\n", fd, s);
+#ifdef DEBUG_FULL
+ fprintf(stderr,"event_cli_read : fd=%d, s=%p\n", fd, s);
+#endif
if (fdtab[fd].state != FD_STERROR) {
while (1) {
@@ -1824,7 +1884,9 @@
struct buffer *b = s->rep;
int ret, max;
- // fprintf(stderr,"event_srv_read : fd=%d, s=%p\n", fd, s);
+#ifdef DEBUG_FULL
+ fprintf(stderr,"event_srv_read : fd=%d, s=%p\n", fd, s);
+#endif
if (fdtab[fd].state != FD_STERROR) {
while (1) {
@@ -1918,7 +1980,9 @@
struct buffer *b = s->rep;
int ret, max;
- // fprintf(stderr,"event_cli_write : fd=%d, s=%p\n", fd, s);
+#ifdef DEBUG_FULL
+ fprintf(stderr,"event_cli_write : fd=%d, s=%p\n", fd, s);
+#endif
if (b->l == 0) { /* let's realign the buffer to optimize I/O */
b->r = b->w = b->h = b->lr = b->data;
@@ -2005,7 +2069,9 @@
struct buffer *b = s->req;
int ret, max;
- //fprintf(stderr,"event_srv_write : fd=%d, s=%p\n", fd, s);
+#ifdef DEBUG_FULL
+ fprintf(stderr,"event_srv_write : fd=%d, s=%p\n", fd, s);
+#endif
if (b->l == 0) { /* let's realign the buffer to optimize I/O */
b->r = b->w = b->h = b->lr = b->data;
@@ -2694,6 +2760,9 @@
int c = t->cli_state;
struct buffer *req = t->req;
struct buffer *rep = t->rep;
+ int method_checked = 0;
+ appsess *asession_temp = NULL;
+ appsess local_asession;
#ifdef DEBUG_FULL
fprintf(stderr,"process_cli: c=%s, s=%s\n", cli_stnames[c], srv_stnames[s]);
@@ -2707,6 +2776,7 @@
while (req->lr < req->r) { /* this loop only sees one header at each iteration */
char *ptr;
int delete_header;
+ char *request_line = NULL;
ptr = req->lr;
@@ -2815,6 +2885,88 @@
* req->r = end of data (not used at this stage)
*/
+ if (!method_checked && (t->proxy->appsession_name != NULL) &&
+ ((memcmp(req->h, "GET ", 4) == 0) || (memcmp(req->h, "POST ", 4) == 0)) &&
+ ((request_line = memchr(req->h, ';', req->lr - req->h)) != NULL)) {
+
+ /* skip ; */
+ request_line++;
+
+ /* look if we have a jsessionid */
+
+ if (strncasecmp(request_line, t->proxy->appsession_name, t->proxy->appsession_name_len) == 0) {
+
+ /* skip jsessionid= */
+ request_line += t->proxy->appsession_name_len + 1;
+
+ /* First try if we allready have an appsession */
+ asession_temp = &local_asession;
+
+ if ((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL) {
+ Alert("Not enough memory process_cli():asession_temp->sessid:calloc().\n");
+ send_log(t->proxy, LOG_ALERT, "Not enough Memory process_cli():asession_temp->sessid:calloc().\n");
+ return 0;
+ }
+
+ /* Copy the sessionid */
+ memcpy(asession_temp->sessid, request_line, t->proxy->appsession_len);
+ asession_temp->sessid[t->proxy->appsession_len] = 0;
+ asession_temp->serverid = NULL;
+
+ /* only do insert, if lookup fails */
+ if (chtbl_lookup(&(t->proxy->htbl_proxy), (void *)&asession_temp)) {
+ if ((asession_temp = pool_alloc(appsess)) == NULL) {
+ Alert("Not enough memory process_cli():asession:calloc().\n");
+ send_log(t->proxy, LOG_ALERT, "Not enough memory process_cli():asession:calloc().\n");
+ return 0;
+ }
+ asession_temp->sessid = local_asession.sessid;
+ asession_temp->serverid = local_asession.serverid;
+ chtbl_insert(&(t->proxy->htbl_proxy), (void *) asession_temp);
+ } /* end if(chtbl_lookup()) */
+ else{
+ /*free wasted memory;*/
+ pool_free_to(apools.sessid, local_asession.sessid);
+ }
+
+ tv_delayfrom(&asession_temp->expire, &now, t->proxy->appsession_timeout);
+ asession_temp->request_count++;
+
+#if defined(DEBUG_HASH)
+ print_table(&(t->proxy->htbl_proxy));
+#endif
+
+ if (asession_temp->serverid == NULL) {
+ Alert("Found Application Session without matching server.\n");
+ } else {
+ struct server *srv = t->proxy->srv;
+ while (srv) {
+ if (strcmp(srv->id, asession_temp->serverid) == 0) {
+ if (srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST) {
+ /* we found the server and it's usable */
+ t->flags &= ~SN_CK_MASK;
+ t->flags |= SN_CK_VALID | SN_DIRECT;
+ t->srv = srv;
+ break;
+ }else {
+ t->flags &= ~SN_CK_MASK;
+ t->flags |= SN_CK_DOWN;
+ }
+ }/* end if(strcmp()) */
+ srv = srv->next;
+ }/* end while(srv) */
+ }/* end else of if (asession_temp->serverid == NULL) */
+
+ method_checked = 1;
+ }/* end if(strncasecmp(request_line,t->proxy->appsession_name,apssesion_name_len) == 0) */
+ else {
+ //fprintf(stderr,">>>>>>>>>>>>>>>>>>>>>>NO SESSION\n");
+ }
+ }/* end if(!method_checked ...) */
+ else{
+ //printf("No Methode-Header with Session-String\n");
+ }
+
if (t->logs.logwait & LW_REQ) {
/* we have a complete HTTP request that we must log */
int urilen;
@@ -2932,7 +3084,8 @@
* remove it. If no application cookie persists in the header, we
* *MUST* delete it
*/
- if (!delete_header && (t->proxy->cookie_name != NULL || t->proxy->capture_name != NULL)
+ if (!delete_header &&
+ (t->proxy->cookie_name != NULL || t->proxy->capture_name != NULL || t->proxy->appsession_name !=NULL)
&& !(t->flags & SN_CLDENY) && (ptr >= req->h + 8)
&& (strncasecmp(req->h, "Cookie: ", 8) == 0)) {
char *p1, *p2, *p3, *p4;
@@ -3001,12 +3154,12 @@
if ((t->logs.cli_cookie = pool_alloc(capture)) == NULL) {
Alert("HTTP logging : out of memory.\n");
+ } else {
+ if (log_len > t->proxy->capture_len)
+ log_len = t->proxy->capture_len;
+ memcpy(t->logs.cli_cookie, p1, log_len);
+ t->logs.cli_cookie[log_len] = 0;
}
-
- if (log_len > t->proxy->capture_len)
- log_len = t->proxy->capture_len;
- memcpy(t->logs.cli_cookie, p1, log_len);
- t->logs.cli_cookie[log_len] = 0;
}
if ((p2 - p1 == t->proxy->cookie_len) && (t->proxy->cookie_name != NULL) &&
@@ -3051,8 +3204,7 @@
t->flags |= SN_CK_VALID | SN_DIRECT;
t->srv = srv;
break;
- }
- else {
+ } else {
/* we found a server, but it's down */
t->flags &= ~SN_CK_MASK;
t->flags |= SN_CK_DOWN;
@@ -3086,8 +3238,7 @@
del_cookie = p1;
del_colon = colon;
}
- }
- else {
+ } else {
/* now we know that we must keep this cookie since it's
* not ours. But if we wanted to delete our cookie
* earlier, we cannot remove the complete header, but we
@@ -3102,6 +3253,66 @@
del_cookie = del_colon = NULL;
}
}
+
+ if ((t->proxy->appsession_name != NULL) &&
+ (memcmp(p1, t->proxy->appsession_name, p2 - p1) == 0)) {
+ /* first, let's see if the cookie is our appcookie*/
+
+ /* Cool... it's the right one */
+
+ asession_temp = &local_asession;
+
+ if ((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL) {
+ Alert("Not enough memory process_cli():asession->sessid:malloc().\n");
+ send_log(t->proxy, LOG_ALERT, "Not enough memory process_cli():asession->sessid:malloc().\n");
+ return 0;
+ }
+
+ memcpy(asession_temp->sessid, p3, t->proxy->appsession_len);
+ asession_temp->sessid[t->proxy->appsession_len] = 0;
+ asession_temp->serverid = NULL;
+
+ /* only do insert, if lookup fails */
+ if (chtbl_lookup(&(t->proxy->htbl_proxy), (void *) &asession_temp) != 0) {
+ if ((asession_temp = pool_alloc(appsess)) == NULL) {
+ Alert("Not enough memory process_cli():asession:calloc().\n");
+ send_log(t->proxy, LOG_ALERT, "Not enough memory process_cli():asession:calloc().\n");
+ return 0;
+ }
+
+ asession_temp->sessid = local_asession.sessid;
+ asession_temp->serverid = local_asession.serverid;
+ chtbl_insert(&(t->proxy->htbl_proxy), (void *) asession_temp);
+ }
+ else{
+ /* free wasted memory */
+ pool_free_to(apools.sessid, local_asession.sessid);
+ }
+
+ if (asession_temp->serverid == NULL) {
+ Alert("Found Application Session without matching server.\n");
+ } else {
+ struct server *srv = t->proxy->srv;
+ while (srv) {
+ if(strcmp(srv->id, asession_temp->serverid) == 0) {
+ if (srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST) {
+ /* we found the server and it's usable */
+ t->flags &= ~SN_CK_MASK;
+ t->flags |= SN_CK_VALID | SN_DIRECT;
+ t->srv = srv;
+ break;
+ } else {
+ t->flags &= ~SN_CK_MASK;
+ t->flags |= SN_CK_DOWN;
+ }
+ }
+ srv = srv->next;
+ }/* end while(srv) */
+ }/* end else if server == NULL */
+
+ tv_delayfrom(&asession_temp->expire, &now, t->proxy->appsession_timeout);
+ break;
+ }/* end if ((t->proxy->appsession_name != NULL) ... */
}
/* we'll have to look for another cookie ... */
@@ -3413,6 +3624,8 @@
int c = t->cli_state;
struct buffer *req = t->req;
struct buffer *rep = t->rep;
+ appsess *asession_temp = NULL;
+ appsess local_asession;
#ifdef DEBUG_FULL
fprintf(stderr,"process_srv: c=%s, s=%s\n", cli_stnames[c], srv_stnames[s]);
@@ -3829,7 +4042,7 @@
/* check for server cookies */
if (!delete_header /*&& (t->proxy->options & PR_O_COOK_ANY)*/
- && (t->proxy->cookie_name != NULL || t->proxy->capture_name != NULL)
+ && (t->proxy->cookie_name != NULL || t->proxy->capture_name != NULL || t->proxy->appsession_name !=NULL)
&& (strncasecmp(rep->h, "Set-Cookie: ", 12) == 0)) {
char *p1, *p2, *p3, *p4;
@@ -3915,6 +4128,58 @@
}
break;
}
+
+ /* first, let's see if the cookie is our appcookie*/
+ if ((t->proxy->appsession_name != NULL) &&
+ (memcmp(p1, t->proxy->appsession_name, p2 - p1) == 0)) {
+
+ /* Cool... it's the right one */
+
+ size_t server_id_len = strlen(t->srv->id)+1;
+ asession_temp = &local_asession;
+
+ if((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL){
+ Alert("Not enought Memory process_srv():asession->sessid:malloc().\n");
+ send_log(t->proxy, LOG_ALERT, "Not enought Memory process_srv():asession->sessid:malloc().\n");
+ }
+ memcpy(asession_temp->sessid, p3, t->proxy->appsession_len);
+ asession_temp->sessid[t->proxy->appsession_len] = 0;
+ asession_temp->serverid = NULL;
+
+ /* only do insert, if lookup fails */
+ if (chtbl_lookup(&(t->proxy->htbl_proxy), (void *) &asession_temp) != 0) {
+ if ((asession_temp = pool_alloc(appsess)) == NULL) {
+ Alert("Not enought Memory process_srv():asession:calloc().\n");
+ send_log(t->proxy, LOG_ALERT, "Not enought Memory process_srv():asession:calloc().\n");
+ return 0;
+ }
+ asession_temp->sessid = local_asession.sessid;
+ asession_temp->serverid = local_asession.serverid;
+ chtbl_insert(&(t->proxy->htbl_proxy), (void *) asession_temp);
+ }/* end if(chtbl_lookup()) */
+ else
+ {
+ /* free wasted memory */
+ pool_free_to(apools.sessid, local_asession.sessid);
+ } /* end else from if(chtbl_lookup()) */
+
+ if(asession_temp->serverid == NULL){
+ if((asession_temp->serverid = pool_alloc_from(apools.serverid, apools.ser_msize)) == NULL){
+ Alert("Not enought Memory process_srv():asession->sessid:malloc().\n");
+ send_log(t->proxy, LOG_ALERT, "Not enought Memory process_srv():asession->sessid:malloc().\n");
+ }
+ asession_temp->serverid[0] = '\0';
+ }
+
+ if(asession_temp->serverid[0] == '\0') memcpy(asession_temp->serverid,t->srv->id,server_id_len);
+
+ tv_delayfrom(&asession_temp->expire, &now, t->proxy->appsession_timeout);
+
+#if defined(DEBUG_HASH)
+ print_table(&(t->proxy->htbl_proxy));
+#endif
+ break;
+ }/* end if ((t->proxy->appsession_name != NULL) ... */
else {
// fprintf(stderr,"Ignoring unknown cookie : ");
// write(2, p1, p2-p1);
@@ -4828,8 +5093,41 @@
s->req->l, s->rep?s->rep->l:0, s->cli_fd
);
}
+}
+
+static void fast_stop(void)
+{
+ struct proxy *p;
+ p = proxy;
+ while (p) {
+ p->grace = 0;
+ p = p->next;
+ }
+ soft_stop();
}
+void sig_int(int sig) {
+ /* This would normally be a hard stop,
+ but we want to be sure about deallocation,
+ and so on, so we do a soft stop with
+ 0 GRACE time
+ */
+ fast_stop();
+ /* If we are killed twice, we decide to die*/
+ signal(sig, SIG_DFL);
+}
+
+void sig_term(int sig) {
+ /* This would normally be a hard stop,
+ but we want to be sure about deallocation,
+ and so on, so we do a soft stop with
+ 0 GRACE time
+ */
+ fast_stop();
+ /* If we are killed twice, we decide to die*/
+ signal(sig, SIG_DFL);
+}
+
void chain_regex(struct hdr_exp **head, regex_t *preg, int action, char *replace) {
struct hdr_exp *exp;
@@ -5003,6 +5301,7 @@
int cfg_parse_listen(char *file, int linenum, char **args) {
static struct proxy *curproxy = NULL;
struct server *newsrv = NULL;
+ int rc;
if (!strcmp(args[0], "listen")) { /* new proxy */
if (!*args[1]) {
@@ -5195,7 +5494,36 @@
file, linenum);
return -1;
}
- }
+ }/* end else if (!strcmp(args[0], "cookie")) */
+ else if (!strcmp(args[0], "appsession")) { /* cookie name */
+// if (curproxy == &defproxy) {
+// Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+// return -1;
+// }
+
+ if (curproxy->appsession_name != NULL) {
+// Alert("parsing [%s:%d] : cookie name already specified. Continuing.\n",
+// file, linenum);
+// return 0;
+ free(curproxy->appsession_name);
+ }
+
+ if (*(args[5]) == 0) {
+ Alert("parsing [%s:%d] : '%s' expects 'appsession' <cookie_name> 'len' <len> 'timeout' <timeout>.\n",
+ file, linenum, args[0]);
+ return -1;
+ }
+ have_appsession = 1;
+ curproxy->appsession_name = strdup(args[1]);
+ curproxy->appsession_name_len = strlen(curproxy->appsession_name);
+ curproxy->appsession_len = atoi(args[3]);
+ curproxy->appsession_timeout = atoi(args[5]);
+ rc = chtbl_init(&(curproxy->htbl_proxy), TBLSIZ, hashpjw, match_str, destroy);
+ if (rc) {
+ Alert("Error Init Appsession Hashtable.\n");
+ return -1;
+ }
+ } /* Url App Session */
else if (!strcmp(args[0], "capture")) {
if (!strcmp(args[1], "cookie")) { /* name of a cookie to capture */
// if (curproxy == &defproxy) {
@@ -6438,10 +6766,13 @@
gethostname(hostname, MAX_HOSTNAME_LEN);
+ have_appsession = 0;
if (readcfgfile(cfg_cfgfile) < 0) {
Alert("Error reading configuration file : %s\n", cfg_cfgfile);
exit(1);
}
+ if (have_appsession)
+ appsession_init();
if (global.mode & MODE_CHECK) {
qfprintf(stdout, "Configuration file is valid : %s\n", cfg_cfgfile);
@@ -6576,6 +6907,154 @@
return 0;
}
+int match_str(const void *key1, const void *key2){
+
+ appsess *temp1,*temp2;
+ temp1 = (appsess *)key1;
+ temp2 = (appsess *)key2;
+
+ //fprintf(stdout,">>>>>>>>>>>>>>temp1->sessid :%s:\n",temp1->sessid);
+ //fprintf(stdout,">>>>>>>>>>>>>>temp2->sessid :%s:\n",temp2->sessid);
+
+ return (strcmp(temp1->sessid,temp2->sessid) == 0);
+}/* end match_str */
+
+void destroy(void *data){
+ appsess *temp1;
+
+ //printf("destroy called\n");
+ temp1 = (appsess *)data;
+
+ if (temp1->sessid)
+ pool_free_to(apools.sessid, temp1->sessid);
+
+ if (temp1->serverid)
+ pool_free_to(apools.serverid, temp1->serverid);
+
+ pool_free(appsess, temp1);
+} /* end destroy */
+
+void appsession_cleanup( void )
+{
+ struct proxy *p = proxy;
+
+ while(p) {
+ chtbl_destroy(&(p->htbl_proxy));
+ p = p->next;
+ }
+}/* end appsession_cleanup() */
+
+void pool_destroy(void **pool)
+{
+ void *temp, *next;
+ next = pool;
+ while (next) {
+ temp = next;
+ next = *(void **)temp;
+ free(temp);
+ }
+}/* end pool_destroy() */
+
+void deinit(void){
+ struct proxy *p = proxy;
+ struct cap_hdr *h,*h_next;
+ struct server *s,*s_next;
+ struct listener *l,*l_next;
+
+ while (p) {
+ if (p->id)
+ free(p->id);
+
+ if (p->check_req)
+ free(p->check_req);
+
+ if (p->cookie_name)
+ free(p->cookie_name);
+
+ if (p->capture_name)
+ free(p->capture_name);
+
+ /* only strup if the user have set in config.
+ When should we free it?!
+ if(p->errmsg.msg400) free(p->errmsg.msg400);
+ if(p->errmsg.msg403) free(p->errmsg.msg403);
+ if(p->errmsg.msg408) free(p->errmsg.msg408);
+ if(p->errmsg.msg500) free(p->errmsg.msg500);
+ if(p->errmsg.msg502) free(p->errmsg.msg502);
+ if(p->errmsg.msg503) free(p->errmsg.msg503);
+ if(p->errmsg.msg504) free(p->errmsg.msg504);
+ */
+ if (p->appsession_name)
+ free(p->appsession_name);
+
+ h = p->req_cap;
+ while (h) {
+ h_next = h->next;
+ if (h->name)
+ free(h->name);
+ pool_destroy(h->pool);
+ free(h);
+ h = h_next;
+ }/* end while(h) */
+
+ h = p->rsp_cap;
+ while (h) {
+ h_next = h->next;
+ if (h->name)
+ free(h->name);
+
+ pool_destroy(h->pool);
+ free(h);
+ h = h_next;
+ }/* end while(h) */
+
+ s = p->srv;
+ while (s) {
+ s_next = s->next;
+ if(s->id)
+ free(s->id);
+
+ if(s->cookie)
+ free(s->cookie);
+
+ free(s);
+ s = s_next;
+ }/* end while(s) */
+
+ l = p->listen;
+ while (l) {
+ l_next = l->next;
+ free(l);
+ l = l_next;
+ }/* end while(l) */
+
+ pool_destroy((void **) p->req_cap_pool);
+ pool_destroy((void **) p->rsp_cap_pool);
+ p = p->next;
+ }/* end while(p) */
+
+ if (global.chroot) free(global.chroot);
+ if (global.pidfile) free(global.pidfile);
+
+ if (ReadEvent) free(ReadEvent);
+ if (WriteEvent) free(WriteEvent);
+ if (StaticReadEvent) free(StaticReadEvent);
+ if (StaticWriteEvent) free(StaticWriteEvent);
+ if (fdtab) free(fdtab);
+
+ pool_destroy(pool_session);
+ pool_destroy(pool_buffer);
+ pool_destroy(pool_fdtab);
+ pool_destroy(pool_requri);
+ pool_destroy(pool_task);
+ pool_destroy(pool_capture);
+ pool_destroy(pool_appsess);
+
+ if (have_appsession) {
+ pool_destroy(apools.serverid);
+ pool_destroy(apools.sessid);
+ }
+} /* end deinit() */
int main(int argc, char **argv) {
FILE *pidfile = NULL;
@@ -6590,6 +7069,8 @@
signal(SIGQUIT, dump);
signal(SIGUSR1, sig_soft_stop);
signal(SIGHUP, sig_dump_state);
+ signal(SIGINT, sig_int);
+ signal(SIGTERM, sig_term);
/* on very high loads, a sigpipe sometimes happen just between the
* getsockopt() which tells "it's OK to write", and the following write :-(
@@ -6676,5 +7157,152 @@
select_loop();
+ /* Free all Hash Keys and all Hash elements */
+ appsession_cleanup();
+ /* Do some cleanup */
+ deinit();
+
exit(0);
}
+
+#if defined(DEBUG_HASH)
+static void print_table(const CHTbl *htbl) {
+
+ ListElmt *element;
+ int i;
+ appsess *asession;
+
+ /*****************************************************************************
+ * *
+ * Display the chained hash table. *
+ * *
+ *****************************************************************************/
+
+ fprintf(stdout, "Table size is %d\n", chtbl_size(htbl));
+
+ for (i = 0; i < TBLSIZ; i++) {
+ fprintf(stdout, "Bucket[%03d]\n", i);
+
+ for (element = list_head(&htbl->table[i]); element != NULL; element = list_next(element)) {
+ //fprintf(stdout, "%c", *(char *)list_data(element));
+ asession = (appsess *)list_data(element);
+ fprintf(stdout, "ELEM :%s:", asession->sessid);
+ fprintf(stdout, " Server :%s: \n", asession->serverid);
+ //fprintf(stdout, " Server request_count :%li:\n",asession->request_count);
+ }
+
+ fprintf(stdout, "\n");
+ }
+ return;
+} /* end print_table */
+#endif
+
+static int appsession_init(void)
+{
+ static int initialized = 0;
+ int idlen;
+ struct server *s;
+ struct proxy *p = proxy;
+
+ if (!initialized) {
+ if (!appsession_task_init()) {
+ apools.sessid = NULL;
+ apools.serverid = NULL;
+ apools.ser_waste = 0;
+ apools.ser_use = 0;
+ apools.ser_msize = sizeof(void *);
+ apools.ses_waste = 0;
+ apools.ses_use = 0;
+ apools.ses_msize = sizeof(void *);
+ while (p) {
+ s = p->srv;
+ if (apools.ses_msize < p->appsession_len)
+ apools.ses_msize = p->appsession_len;
+ while (s) {
+ idlen = strlen(s->id);
+ if (apools.ser_msize < idlen)
+ apools.ser_msize = idlen;
+ s = s->next;
+ }
+ p = p->next;
+ }
+ apools.ser_msize ++; /* we use strings, so reserve space for '\0' */
+ apools.ses_msize ++;
+ }
+ else {
+ fprintf(stderr, "appsession_task_init failed\n");
+ return -1;
+ }
+ initialized ++;
+ }
+ return 0;
+}
+
+static int appsession_task_init(void)
+{
+ static int initialized = 0;
+ struct task *t;
+ if (!initialized) {
+ if ((t = pool_alloc(task)) == NULL)
+ return -1;
+ t->next = t->prev = t->rqnext = NULL;
+ t->wq = LIST_HEAD(wait_queue);
+ t->state = TASK_IDLE;
+ t->context = NULL;
+ tv_delayfrom(&t->expire, &now, TBLCHKINT);
+ task_queue(t);
+ t->process = appsession_refresh;
+ initialized ++;
+ }
+ return 0;
+}
+
+static int appsession_refresh(struct task *t) {
+ struct proxy *p = proxy;
+ CHTbl *htbl;
+ ListElmt *element, *last;
+ int i;
+ appsess *asession;
+ void *data;
+
+ while (p) {
+ if (p->appsession_name != NULL) {
+ htbl = &p->htbl_proxy;
+ /* if we ever give up the use of TBLSIZ, we need to change this */
+ for (i = 0; i < TBLSIZ; i++) {
+ last = NULL;
+ for (element = list_head(&htbl->table[i]); element != NULL; element = list_next(element)) {
+ asession = (appsess *)list_data(element);
+ if (tv_cmp2_ms(&asession->expire, &now) <= 0) {
+ if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
+ int len;
+ /*
+ on Linux NULL pointers are catched by sprintf, on solaris -> segfault
+ */
+ len = sprintf(trash, "appsession_refresh: cleaning up expired Session '%s' on Server %s\n",
+ asession->sessid, asession->serverid?asession->serverid:"(null)");
+ write(1, trash, len);
+ }
+ /* delete the expired element from within the hash table */
+ if ((list_rem_next(&htbl->table[i], last, (void **)&data) == 0)
+ && (htbl->table[i].destroy != NULL)) {
+ htbl->table[i].destroy(data);
+ }
+ if (last == NULL) {/* patient lost his head, get a new one */
+ element = list_head(&htbl->table[i]);
+ if (element == NULL) break; /* no heads left, go to next patient */
+ }
+ else
+ element = last;
+ }/* end if (tv_cmp2_ms(&asession->expire, &now) <= 0) */
+ else
+ last = element;
+ }/* end for (element = list_head(&htbl->table[i]); element != NULL; element = list_next(element)) */
+ }
+ }
+ p = p->next;
+ }
+ tv_delayfrom(&t->expire, &now, TBLCHKINT); /* check expiration every 5 seconds */
+ return TBLCHKINT;
+} /* end appsession_refresh */
+
diff --git a/include/chtbl.h b/include/chtbl.h
new file mode 100644
index 0000000..87b777a
--- /dev/null
+++ b/include/chtbl.h
@@ -0,0 +1,62 @@
+/*
+ This File is copied from
+
+ http://www.oreilly.com/catalog/masteralgoc/index.html
+ Mastering Algorithms with C
+ By Kyle Loudon
+ ISBN: 1-56592-453-3
+ Publishd by O'Reilly
+
+ */
+
+/*****************************************************************************
+* *
+* ------------------------------- chtbl.h -------------------------------- *
+* *
+*****************************************************************************/
+
+#ifndef CHTBL_H
+#define CHTBL_H
+
+#include <stdlib.h>
+
+#include "list.h"
+
+/*****************************************************************************
+* *
+* Define a structure for chained hash tables. *
+* *
+*****************************************************************************/
+
+typedef struct CHTbl_ {
+
+ int buckets;
+
+ int (*h)(const void *key);
+ int (*match)(const void *key1, const void *key2);
+ void (*destroy)(void *data);
+
+ int size;
+ List *table;
+} CHTbl;
+
+/*****************************************************************************
+ * *
+ * --------------------------- Public Interface --------------------------- *
+ * *
+ *****************************************************************************/
+
+int chtbl_init(CHTbl *htbl, int buckets, int (*h)(const void *key), int
+ (*match)(const void *key1, const void *key2), void (*destroy)(void *data));
+
+void chtbl_destroy(CHTbl *htbl);
+
+int chtbl_insert(CHTbl *htbl, const void *data);
+
+int chtbl_remove(CHTbl *htbl, void **data);
+
+int chtbl_lookup(const CHTbl *htbl, void **data);
+
+#define chtbl_size(htbl) ((htbl)->size)
+
+#endif
diff --git a/include/hashpjw.h b/include/hashpjw.h
new file mode 100644
index 0000000..a20b5b3
--- /dev/null
+++ b/include/hashpjw.h
@@ -0,0 +1,47 @@
+/*
+ This File is copied from
+
+ http://www.oreilly.com/catalog/masteralgoc/index.html
+ Mastering Algorithms with C
+ By Kyle Loudon
+ ISBN: 1-56592-453-3
+ Publishd by O'Reilly
+
+ We have added our own struct to these function.
+ */
+
+/*****************************************************************************
+* *
+* ------------------------------- hashpjw.h ------------------------------ *
+* *
+*****************************************************************************/
+
+#ifndef HASHPJW_H
+#define HASHPJW_H
+
+#include <sys/time.h>
+
+typedef struct appsessions {
+ char *sessid;
+ char *serverid;
+ struct timeval expire; /* next expiration time for this application session */
+ unsigned long int request_count;
+} appsess; /* end struct appsessions */
+
+/*****************************************************************************
+* *
+* Define a table size for demonstration purposes only. *
+* *
+*****************************************************************************/
+
+#define PRIME_TBLSIZ 1699
+
+/*****************************************************************************
+* *
+* --------------------------- Public Interface --------------------------- *
+* *
+*****************************************************************************/
+
+int hashpjw(const void *key);
+
+#endif
diff --git a/include/list.h b/include/list.h
new file mode 100644
index 0000000..ae7f086
--- /dev/null
+++ b/include/list.h
@@ -0,0 +1,78 @@
+/*
+ This File is copied from
+
+ http://www.oreilly.com/catalog/masteralgoc/index.html
+ Mastering Algorithms with C
+ By Kyle Loudon
+ ISBN: 1-56592-453-3
+ Publishd by O'Reilly
+
+ */
+
+/*****************************************************************************
+* *
+* -------------------------------- list.h -------------------------------- *
+* *
+*****************************************************************************/
+
+#ifndef LIST_H
+#define LIST_H
+
+#include <stdlib.h>
+
+/*****************************************************************************
+ * *
+ * Define a structure for linked list elements. *
+ * *
+ *****************************************************************************/
+
+typedef struct ListElmt_ {
+
+ void *data;
+ struct ListElmt_ *next;
+} ListElmt;
+
+/*****************************************************************************
+ * *
+ * Define a structure for linked lists. *
+ * *
+ *****************************************************************************/
+
+typedef struct List_ {
+ int size;
+ int (*match)(const void *key1, const void *key2);
+ void (*destroy)(void *data);
+
+ ListElmt *head;
+ ListElmt *tail;
+} List;
+
+/*****************************************************************************
+ * *
+ * --------------------------- Public Interface --------------------------- *
+ * *
+ *****************************************************************************/
+
+void list_init(List *list, void (*destroy)(void *data));
+
+void list_destroy(List *list);
+
+int list_ins_next(List *list, ListElmt *element, const void *data);
+
+int list_rem_next(List *list, ListElmt *element, void **data);
+
+#define list_size(list) ((list)->size)
+
+#define list_head(list) ((list)->head)
+
+#define list_tail(list) ((list)->tail)
+
+#define list_is_head(list, element) ((element) == (list)->head ? 1 : 0)
+
+#define list_is_tail(element) ((element)->next == NULL ? 1 : 0)
+
+#define list_data(element) ((element)->data)
+
+#define list_next(element) ((element)->next)
+
+#endif
diff --git a/src/chtbl.c b/src/chtbl.c
new file mode 100644
index 0000000..481b3ac
--- /dev/null
+++ b/src/chtbl.c
@@ -0,0 +1,260 @@
+/*
+ This File is copied from
+
+ http://www.oreilly.com/catalog/masteralgoc/index.html
+ Mastering Algorithms with C
+ By Kyle Loudon
+ ISBN: 1-56592-453-3
+ Publishd by O'Reilly
+
+ */
+
+/*****************************************************************************
+ * *
+ * ------------------------------- chtbl.c -------------------------------- *
+ * *
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <include/list.h>
+#include <include/chtbl.h>
+
+/*****************************************************************************
+ * *
+ * ------------------------------ chtbl_init ------------------------------ *
+ * *
+ *****************************************************************************/
+
+int chtbl_init(CHTbl *htbl, int buckets, int (*h)(const void *key), int
+ (*match)(const void *key1, const void *key2), void (*destroy)(void*data)) {
+
+ int i;
+
+ /*****************************************************************************
+ * *
+ * Allocate space for the hash table. *
+ * *
+ *****************************************************************************/
+
+ if ((htbl->table = (List *)malloc(buckets * sizeof(List))) == NULL)
+ return -1;
+
+ /*****************************************************************************
+ * *
+ * Initialize the buckets. *
+ * *
+ *****************************************************************************/
+
+ htbl->buckets = buckets;
+
+ for (i = 0; i < htbl->buckets; i++)
+ list_init(&htbl->table[i], destroy);
+
+ /*****************************************************************************
+ * *
+ * Encapsulate the functions. *
+ * *
+ *****************************************************************************/
+
+ htbl->h = h;
+ htbl->match = match;
+ htbl->destroy = destroy;
+
+ /*****************************************************************************
+ * *
+ * Initialize the number of elements in the table. *
+ * *
+ *****************************************************************************/
+
+ htbl->size = 0;
+
+ return 0;
+} /* end chtbl_init () */
+
+/*****************************************************************************
+ * *
+ * ---------------------------- chtbl_destroy ----------------------------- *
+ * *
+ *****************************************************************************/
+
+void chtbl_destroy(CHTbl *htbl) {
+
+ int i;
+
+ /*****************************************************************************
+ * *
+ * Destroy each bucket. *
+ * *
+ *****************************************************************************/
+
+ for (i = 0; i < htbl->buckets; i++) {
+ list_destroy(&htbl->table[i]);
+ } /* end for () */
+
+ /*****************************************************************************
+ * *
+ * Free the storage allocated for the hash table. *
+ * *
+ *****************************************************************************/
+
+ free(htbl->table);
+
+ /*****************************************************************************
+ * *
+ * No operations are allowed now, but clear the structure as a precaution. *
+ * *
+ *****************************************************************************/
+
+ memset(htbl, 0, sizeof(CHTbl));
+
+ return;
+} /* end chtbl_destroy() */
+
+/*****************************************************************************
+ * *
+ * ----------------------------- chtbl_insert ----------------------------- *
+ * *
+ *****************************************************************************/
+
+int chtbl_insert(CHTbl *htbl, const void *data) {
+
+ void *temp;
+ int bucket,retval;
+
+ /*****************************************************************************
+ * *
+ * Do nothing if the data is already in the table. *
+ * *
+ *****************************************************************************/
+
+ temp = (void *)data;
+
+ if (chtbl_lookup(htbl, &temp) == 0)
+ return 1;
+
+ /*****************************************************************************
+ * *
+ * Hash the key. *
+ * *
+ *****************************************************************************/
+
+ bucket = htbl->h(data) % htbl->buckets;
+
+ /*****************************************************************************
+ * *
+ * Insert the data into the bucket. *
+ * *
+ *****************************************************************************/
+
+ if ((retval = list_ins_next(&htbl->table[bucket], NULL, data)) == 0)
+ htbl->size++;
+
+ return retval;
+} /* end chtbl_insert() */
+
+/*****************************************************************************
+ * *
+ * ----------------------------- chtbl_remove ----------------------------- *
+ * *
+ *****************************************************************************/
+
+int chtbl_remove(CHTbl *htbl, void **data) {
+
+ ListElmt *element, *prev;
+ int bucket;
+
+ /*****************************************************************************
+ * *
+ * Hash the key. *
+ * *
+ *****************************************************************************/
+
+ bucket = htbl->h(*data) % htbl->buckets;
+
+ /*****************************************************************************
+ * *
+ * Search for the data in the bucket. *
+ * *
+ *****************************************************************************/
+
+ prev = NULL;
+
+ for (element = list_head(&htbl->table[bucket]); element != NULL; element = list_next(element)) {
+ if (htbl->match(*data, list_data(element))) {
+
+ /***********************************************************************
+ * *
+ * Remove the data from the bucket. *
+ * *
+ ***********************************************************************/
+
+ if (list_rem_next(&htbl->table[bucket], prev, data) == 0) {
+ htbl->size--;
+ return 0;
+ } /* end if() */
+ else {
+ return -1;
+ }/* end else */
+ }/* end if (htbl->match(*data, list_data(element))) */
+
+ prev = element;
+ }/* end for() */
+
+ /*****************************************************************************
+ * *
+ * Return that the data was not found. *
+ * *
+ *****************************************************************************/
+
+ return -1;
+} /* end int chtbl_remove(CHTbl *htbl, void **data) */
+
+/*****************************************************************************
+ * *
+ * ----------------------------- chtbl_lookup ----------------------------- *
+ * *
+ *****************************************************************************/
+
+int chtbl_lookup(const CHTbl *htbl, void **data) {
+
+ ListElmt *element;
+ int bucket;
+
+ /*****************************************************************************
+ * *
+ * Hash the key. *
+ * *
+ *****************************************************************************/
+
+ bucket = htbl->h(*data) % htbl->buckets;
+
+ /*****************************************************************************
+ * *
+ * Search for the data in the bucket. *
+ * *
+ *****************************************************************************/
+
+ for (element = list_head(&htbl->table[bucket]); element != NULL; element = list_next(element)) {
+ if (htbl->match(*data, list_data(element))) {
+
+ /***********************************************************************
+ * *
+ * Pass back the data from the table. *
+ * *
+ ***********************************************************************/
+
+ *data = list_data(element);
+ return 0;
+ }/* end if() */
+ }/* end for() */
+
+ /*****************************************************************************
+ * *
+ * Return that the data was not found. *
+ * *
+ *****************************************************************************/
+
+ return -1;
+} /* end chtbl_lookup() */
diff --git a/src/hashpjw.c b/src/hashpjw.c
new file mode 100644
index 0000000..ef7f209
--- /dev/null
+++ b/src/hashpjw.c
@@ -0,0 +1,62 @@
+/*
+ This File is copied from
+
+ http://www.oreilly.com/catalog/masteralgoc/index.html
+ Mastering Algorithms with C
+ By Kyle Loudon
+ ISBN: 1-56592-453-3
+ Publishd by O'Reilly
+
+ We have added our own struct to these function.
+ */
+
+/*****************************************************************************
+* *
+* ------------------------------- hashpjw.c ------------------------------ *
+* *
+*****************************************************************************/
+
+#include <include/hashpjw.h>
+
+/*****************************************************************************
+* *
+* -------------------------------- hashpjw ------------------------------- *
+* *
+*****************************************************************************/
+
+int hashpjw(const void *key) {
+
+ const char *ptr;
+ unsigned int val;
+ AppSess *appsession_temp;
+
+ /*****************************************************************************
+ * *
+ * Hash the key by performing a number of bit operations on it. *
+ * *
+ *****************************************************************************/
+
+ val = 0;
+ appsession_temp = (AppSess *)key;
+ ptr = appsession_temp->sessid;
+
+ while (*ptr != '\0') {
+
+ int tmp;
+
+ val = (val << 4) + (*ptr);
+
+ if((tmp = (val & 0xf0000000))) {
+ val = val ^ (tmp >> 24);
+ val = val ^ tmp;
+ }
+ ptr++;
+ }/* end while */
+
+ /*****************************************************************************
+ * *
+ * In practice, replace PRIME_TBLSIZ with the actual table size. *
+ * *
+ *****************************************************************************/
+ return val % PRIME_TBLSIZ;
+}/* end hashpjw */
diff --git a/src/list.c b/src/list.c
new file mode 100644
index 0000000..364ed14
--- /dev/null
+++ b/src/list.c
@@ -0,0 +1,228 @@
+/*
+ This File is copied from
+
+ http://www.oreilly.com/catalog/masteralgoc/index.html
+ Mastering Algorithms with C
+ By Kyle Loudon
+ ISBN: 1-56592-453-3
+ Publishd by O'Reilly
+
+ */
+
+/*****************************************************************************
+ * *
+ * -------------------------------- list.c -------------------------------- *
+ * *
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <include/list.h>
+
+/*****************************************************************************
+* *
+* ------------------------------- list_init ------------------------------ *
+* *
+*****************************************************************************/
+
+void list_init(List *list, void (*destroy)(void *data)) {
+
+ /*****************************************************************************
+ * *
+ * Initialize the list. *
+ * *
+ *****************************************************************************/
+
+ list->size = 0;
+ list->destroy = destroy;
+ list->head = NULL;
+ list->tail = NULL;
+ return;
+} /* end list_init() */
+
+/*****************************************************************************
+ * *
+ * ----------------------------- list_destroy ----------------------------- *
+ * *
+ *****************************************************************************/
+
+void list_destroy(List *list) {
+
+ void *data;
+ int rc;
+
+ /*****************************************************************************
+ * *
+ * Remove each element. *
+ * *
+ *****************************************************************************/
+
+ while (list_size(list) > 0) {
+
+ rc = list_rem_next(list, NULL, (void **)&data);
+
+ if (( rc == 0) && (list->destroy != NULL)) {
+
+ /***********************************************************************
+ * *
+ * Call a user-defined function to free dynamically allocated data. *
+ * *
+ ***********************************************************************/
+
+ list->destroy(data);
+ }/* end if() */
+ }/* end while() */
+
+ /*****************************************************************************
+ * *
+ * No operations are allowed now, but clear the structure as a precaution. *
+ * *
+ *****************************************************************************/
+
+ memset(list, 0, sizeof(List));
+ return;
+} /* void list_destroy(List *list) */
+
+/*****************************************************************************
+ * *
+ * ----------------------------- list_ins_next ---------------------------- *
+ * *
+ *****************************************************************************/
+
+int list_ins_next(List *list, ListElmt *element, const void *data) {
+
+ ListElmt *new_element;
+
+ /*****************************************************************************
+ * *
+ * Allocate storage for the element. *
+ * *
+ *****************************************************************************/
+
+ if ((new_element = (ListElmt *)malloc(sizeof(ListElmt))) == NULL)
+ return -1;
+
+ /*****************************************************************************
+ * *
+ * Insert the element into the list. *
+ * *
+ *****************************************************************************/
+
+ new_element->data = (void *)data;
+
+ if (element == NULL) {
+
+ /**************************************************************************
+ * *
+ * Handle insertion at the head of the list. *
+ * *
+ **************************************************************************/
+
+ if (list_size(list) == 0)
+ list->tail = new_element;
+
+ new_element->next = list->head;
+ list->head = new_element;
+ }/* end if (element == NULL) */
+ else {
+
+ /**************************************************************************
+ * *
+ * Handle insertion somewhere other than at the head. *
+ * *
+ **************************************************************************/
+
+ if (element->next == NULL)
+ list->tail = new_element;
+
+ new_element->next = element->next;
+ element->next = new_element;
+ }/* end else */
+
+ /*****************************************************************************
+ * *
+ * Adjust the size of the list to account for the inserted element. *
+ * *
+ *****************************************************************************/
+
+ list->size++;
+ return 0;
+} /* end list_ins_next() */
+
+/*****************************************************************************
+ * *
+ * ----------------------------- list_rem_next ---------------------------- *
+ * *
+ *****************************************************************************/
+
+int list_rem_next(List *list, ListElmt *element, void **data) {
+
+ ListElmt *old_element;
+
+ /*****************************************************************************
+ * *
+ * Do not allow removal from an empty list. *
+ * *
+ *****************************************************************************/
+
+ if (list_size(list) == 0)
+ return -1;
+
+ /*****************************************************************************
+ * *
+ * Remove the element from the list. *
+ * *
+ *****************************************************************************/
+
+ if (element == NULL) {
+
+ /**************************************************************************
+ * *
+ * Handle removal from the head of the list. *
+ * *
+ **************************************************************************/
+
+ *data = list->head->data;
+ old_element = list->head;
+ list->head = list->head->next;
+
+ if (list_size(list) == 1)
+ list->tail = NULL;
+ }/* end if (element == NULL) */
+ else {
+
+ /**************************************************************************
+ * *
+ * Handle removal from somewhere other than the head. *
+ * *
+ **************************************************************************/
+
+ if (element->next == NULL)
+ return -1;
+
+ *data = element->next->data;
+ old_element = element->next;
+ element->next = element->next->next;
+
+ if (element->next == NULL)
+ list->tail = element;
+ }/* end else */
+
+ /*****************************************************************************
+ * *
+ * Free the storage allocated by the abstract data type. *
+ * *
+ *****************************************************************************/
+
+ free(old_element);
+
+ /*****************************************************************************
+ * *
+ * Adjust the size of the list to account for the removed element. *
+ * *
+ *****************************************************************************/
+
+ list->size--;
+ return 0;
+}