2006-06-25 20:48:02 -04:00
|
|
|
/*
|
|
|
|
|
* HA-Proxy : High Availability-enabled HTTP/TCP proxy
|
2010-01-03 15:03:22 -05:00
|
|
|
* Copyright 2000-2010 Willy Tarreau <w@1wt.eu>.
|
2006-06-25 20:48:02 -04:00
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* Please refer to RFC2068 or RFC2616 for informations about HTTP protocol, and
|
|
|
|
|
* RFC2965 for informations about cookies usage. More generally, the IETF HTTP
|
|
|
|
|
* Working Group's web site should be consulted for protocol related changes :
|
|
|
|
|
*
|
|
|
|
|
* http://ftp.ics.uci.edu/pub/ietf/http/
|
|
|
|
|
*
|
|
|
|
|
* Pending bugs (may be not fixed because never reproduced) :
|
|
|
|
|
* - solaris only : sometimes, an HTTP proxy with only a dispatch address causes
|
|
|
|
|
* the proxy to terminate (no core) if the client breaks the connection during
|
|
|
|
|
* the response. Seen on 1.1.8pre4, but never reproduced. May not be related to
|
|
|
|
|
* the snprintf() bug since requests were simple (GET / HTTP/1.0), but may be
|
|
|
|
|
* related to missing setsid() (fixed in 1.1.15)
|
|
|
|
|
* - a proxy with an invalid config will prevent the startup even if disabled.
|
|
|
|
|
*
|
|
|
|
|
* ChangeLog has moved to the CHANGELOG file.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <netinet/tcp.h>
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
|
#include <netdb.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <sys/resource.h>
|
|
|
|
|
#include <time.h>
|
|
|
|
|
#include <syslog.h>
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG_FULL
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2006-06-29 11:53:05 -04:00
|
|
|
#include <common/appsession.h>
|
|
|
|
|
#include <common/base64.h>
|
|
|
|
|
#include <common/cfgparse.h>
|
|
|
|
|
#include <common/compat.h>
|
|
|
|
|
#include <common/config.h>
|
|
|
|
|
#include <common/defaults.h>
|
2007-10-28 06:14:07 -04:00
|
|
|
#include <common/errors.h>
|
2006-06-29 11:53:05 -04:00
|
|
|
#include <common/memory.h>
|
|
|
|
|
#include <common/mini-clist.h>
|
|
|
|
|
#include <common/regex.h>
|
|
|
|
|
#include <common/standard.h>
|
|
|
|
|
#include <common/time.h>
|
|
|
|
|
#include <common/uri_auth.h>
|
|
|
|
|
#include <common/version.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
#include <types/capture.h>
|
|
|
|
|
#include <types/global.h>
|
|
|
|
|
|
2010-01-29 11:50:44 -05:00
|
|
|
#include <proto/auth.h>
|
2007-06-16 18:36:03 -04:00
|
|
|
#include <proto/acl.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <proto/backend.h>
|
|
|
|
|
#include <proto/buffers.h>
|
2007-10-14 17:40:01 -04:00
|
|
|
#include <proto/checks.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <proto/client.h>
|
|
|
|
|
#include <proto/fd.h>
|
|
|
|
|
#include <proto/log.h>
|
2007-10-16 06:25:14 -04:00
|
|
|
#include <proto/protocols.h>
|
2006-12-24 11:47:20 -05:00
|
|
|
#include <proto/proto_http.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <proto/proxy.h>
|
|
|
|
|
#include <proto/queue.h>
|
|
|
|
|
#include <proto/server.h>
|
2007-05-13 13:43:47 -04:00
|
|
|
#include <proto/session.h>
|
2009-05-10 03:01:21 -04:00
|
|
|
#include <proto/signal.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <proto/stream_sock.h>
|
|
|
|
|
#include <proto/task.h>
|
|
|
|
|
|
2007-03-24 12:24:39 -04:00
|
|
|
#ifdef CONFIG_HAP_CTTPROXY
|
|
|
|
|
#include <proto/cttproxy.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/*********************************************************************/
|
|
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
2010-01-03 15:12:30 -05:00
|
|
|
/* list of config files */
|
|
|
|
|
static struct list cfg_cfgfiles = LIST_HEAD_INIT(cfg_cfgfiles);
|
2006-06-25 20:48:02 -04:00
|
|
|
char *progname = NULL; /* program name */
|
|
|
|
|
int pid; /* current process id */
|
2007-11-26 10:13:36 -05:00
|
|
|
int relative_pid = 1; /* process id starting at 1 */
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
/* global options */
|
|
|
|
|
struct global global = {
|
|
|
|
|
logfac1 : -1,
|
|
|
|
|
logfac2 : -1,
|
|
|
|
|
loglev1 : 7, /* max syslog level : debug */
|
|
|
|
|
loglev2 : 7,
|
2007-10-18 09:15:57 -04:00
|
|
|
.stats_sock = {
|
|
|
|
|
.maxconn = 10, /* 10 concurrent stats connections */
|
|
|
|
|
.perm = {
|
|
|
|
|
.ux = {
|
|
|
|
|
.uid = -1,
|
|
|
|
|
.gid = -1,
|
|
|
|
|
.mode = 0,
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-08-17 01:23:33 -04:00
|
|
|
},
|
|
|
|
|
.tune = {
|
|
|
|
|
.bufsize = BUFSIZE,
|
|
|
|
|
.maxrewrite = MAXREWRITE,
|
|
|
|
|
},
|
2006-06-25 20:48:02 -04:00
|
|
|
/* others NULL OK */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
|
|
|
|
int stopping; /* non zero means stopping in progress */
|
|
|
|
|
|
|
|
|
|
/* Here we store informations about the pids of the processes we may pause
|
|
|
|
|
* or kill. We will send them a signal every 10 ms until we can bind to all
|
|
|
|
|
* our ports. With 200 retries, that's about 2 seconds.
|
|
|
|
|
*/
|
|
|
|
|
#define MAX_START_RETRIES 200
|
|
|
|
|
static int nb_oldpids = 0;
|
|
|
|
|
static int *oldpids = NULL;
|
|
|
|
|
static int oldpids_sig; /* use USR1 or TERM */
|
|
|
|
|
|
|
|
|
|
/* this is used to drain data, and as a temporary buffer for sprintf()... */
|
|
|
|
|
char trash[BUFSIZE];
|
|
|
|
|
|
2010-02-26 05:12:27 -05:00
|
|
|
/* this buffer is always the same size as standard buffers and is used for
|
|
|
|
|
* swapping data inside a buffer.
|
|
|
|
|
*/
|
|
|
|
|
char *swap_buffer = NULL;
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
const int zero = 0;
|
|
|
|
|
const int one = 1;
|
2007-10-11 14:48:58 -04:00
|
|
|
const struct linger nolinger = { .l_onoff = 1, .l_linger = 0 };
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2010-03-12 15:58:54 -05:00
|
|
|
char hostname[MAX_HOSTNAME_LEN];
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
/* general purpose functions ***************************************/
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
|
|
|
|
void display_version()
|
|
|
|
|
{
|
|
|
|
|
printf("HA-Proxy version " HAPROXY_VERSION " " HAPROXY_DATE"\n");
|
2010-02-02 04:18:28 -05:00
|
|
|
printf("Copyright 2000-2010 Willy Tarreau <w@1wt.eu>\n\n");
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
2007-12-02 05:28:59 -05:00
|
|
|
void display_build_opts()
|
|
|
|
|
{
|
|
|
|
|
printf("Build options :"
|
|
|
|
|
#ifdef BUILD_TARGET
|
2008-01-02 14:48:34 -05:00
|
|
|
"\n TARGET = " BUILD_TARGET
|
2007-12-02 05:28:59 -05:00
|
|
|
#endif
|
|
|
|
|
#ifdef BUILD_CPU
|
2008-01-02 14:48:34 -05:00
|
|
|
"\n CPU = " BUILD_CPU
|
2007-12-02 05:28:59 -05:00
|
|
|
#endif
|
|
|
|
|
#ifdef BUILD_CC
|
2008-01-02 14:48:34 -05:00
|
|
|
"\n CC = " BUILD_CC
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef BUILD_CFLAGS
|
|
|
|
|
"\n CFLAGS = " BUILD_CFLAGS
|
2007-12-02 05:28:59 -05:00
|
|
|
#endif
|
2008-01-02 14:48:34 -05:00
|
|
|
#ifdef BUILD_OPTIONS
|
|
|
|
|
"\n OPTIONS = " BUILD_OPTIONS
|
2007-12-02 05:28:59 -05:00
|
|
|
#endif
|
2009-08-17 01:23:33 -04:00
|
|
|
"\n\nDefault settings :"
|
|
|
|
|
"\n maxconn = %d, bufsize = %d, maxrewrite = %d, maxpollevents = %d"
|
|
|
|
|
"\n\n",
|
|
|
|
|
DEFAULT_MAXCONN, BUFSIZE, MAXREWRITE, MAX_POLL_EVENTS);
|
2009-10-03 12:57:08 -04:00
|
|
|
|
2010-01-29 11:50:44 -05:00
|
|
|
printf("Encrypted password support via crypt(3): "
|
|
|
|
|
#ifdef CONFIG_HAP_CRYPT
|
|
|
|
|
"yes"
|
|
|
|
|
#else
|
|
|
|
|
"no"
|
|
|
|
|
#endif
|
|
|
|
|
"\n");
|
|
|
|
|
|
|
|
|
|
putchar('\n');
|
|
|
|
|
|
2009-10-03 12:57:08 -04:00
|
|
|
list_pollers(stdout);
|
|
|
|
|
putchar('\n');
|
2007-12-02 05:28:59 -05:00
|
|
|
}
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/*
|
|
|
|
|
* This function prints the command line usage and exits
|
|
|
|
|
*/
|
|
|
|
|
void usage(char *name)
|
|
|
|
|
{
|
|
|
|
|
display_version();
|
|
|
|
|
fprintf(stderr,
|
2009-06-22 10:02:30 -04:00
|
|
|
"Usage : %s [-f <cfgfile>]* [ -vdV"
|
2006-06-25 20:48:02 -04:00
|
|
|
"D ] [ -n <maxconn> ] [ -N <maxpconn> ]\n"
|
|
|
|
|
" [ -p <pidfile> ] [ -m <max megs> ]\n"
|
2007-12-02 05:28:59 -05:00
|
|
|
" -v displays version ; -vv shows known build options.\n"
|
2006-06-25 20:48:02 -04:00
|
|
|
" -d enters debug mode ; -db only disables background mode.\n"
|
|
|
|
|
" -V enters verbose mode (disables quiet mode)\n"
|
2009-05-18 10:29:51 -04:00
|
|
|
" -D goes daemon\n"
|
2006-06-25 20:48:02 -04:00
|
|
|
" -q quiet mode : don't display messages\n"
|
2009-06-22 10:02:30 -04:00
|
|
|
" -c check mode : only check config files and exit\n"
|
2006-06-25 20:48:02 -04:00
|
|
|
" -n sets the maximum total # of connections (%d)\n"
|
|
|
|
|
" -m limits the usable amount of memory (in MB)\n"
|
|
|
|
|
" -N sets the default, per-proxy maximum # of connections (%d)\n"
|
|
|
|
|
" -p writes pids of all children to this file\n"
|
|
|
|
|
#if defined(ENABLE_EPOLL)
|
|
|
|
|
" -de disables epoll() usage even when available\n"
|
|
|
|
|
#endif
|
2007-04-15 18:53:59 -04:00
|
|
|
#if defined(ENABLE_SEPOLL)
|
|
|
|
|
" -ds disables speculative epoll() usage even when available\n"
|
|
|
|
|
#endif
|
2007-04-09 06:03:06 -04:00
|
|
|
#if defined(ENABLE_KQUEUE)
|
|
|
|
|
" -dk disables kqueue() usage even when available\n"
|
|
|
|
|
#endif
|
2006-06-25 20:48:02 -04:00
|
|
|
#if defined(ENABLE_POLL)
|
|
|
|
|
" -dp disables poll() usage even when available\n"
|
2009-01-25 10:03:28 -05:00
|
|
|
#endif
|
2009-08-16 07:20:32 -04:00
|
|
|
#if defined(CONFIG_HAP_LINUX_SPLICE)
|
2009-01-25 10:03:28 -05:00
|
|
|
" -dS disables splice usage (broken on old kernels)\n"
|
2006-06-25 20:48:02 -04:00
|
|
|
#endif
|
|
|
|
|
" -sf/-st [pid ]* finishes/terminates old pids. Must be last arguments.\n"
|
|
|
|
|
"\n",
|
|
|
|
|
name, DEFAULT_MAXCONN, cfg_maxpconn);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
/* more specific functions ***************************************/
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* upon SIGUSR1, let's have a soft stop.
|
|
|
|
|
*/
|
|
|
|
|
void sig_soft_stop(int sig)
|
|
|
|
|
{
|
|
|
|
|
soft_stop();
|
2007-05-13 18:39:29 -04:00
|
|
|
pool_gc2();
|
2009-05-10 03:59:50 -04:00
|
|
|
signal_register(sig, SIG_IGN);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* upon SIGTTOU, we pause everything
|
|
|
|
|
*/
|
|
|
|
|
void sig_pause(int sig)
|
|
|
|
|
{
|
|
|
|
|
pause_proxies();
|
2007-05-13 18:39:29 -04:00
|
|
|
pool_gc2();
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* upon SIGTTIN, let's have a soft stop.
|
|
|
|
|
*/
|
|
|
|
|
void sig_listen(int sig)
|
|
|
|
|
{
|
|
|
|
|
listen_proxies();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* this function dumps every server's state when the process receives SIGHUP.
|
|
|
|
|
*/
|
|
|
|
|
void sig_dump_state(int sig)
|
|
|
|
|
{
|
|
|
|
|
struct proxy *p = proxy;
|
|
|
|
|
|
|
|
|
|
Warning("SIGHUP received, dumping servers states.\n");
|
|
|
|
|
while (p) {
|
|
|
|
|
struct server *s = p->srv;
|
|
|
|
|
|
|
|
|
|
send_log(p, LOG_NOTICE, "SIGHUP received, dumping servers states for proxy %s.\n", p->id);
|
|
|
|
|
while (s) {
|
|
|
|
|
snprintf(trash, sizeof(trash),
|
2009-04-11 14:44:08 -04:00
|
|
|
"SIGHUP: Server %s/%s is %s. Conn: %d act, %d pend, %lld tot.",
|
2006-06-25 20:48:02 -04:00
|
|
|
p->id, s->id,
|
|
|
|
|
(s->state & SRV_RUNNING) ? "UP" : "DOWN",
|
2009-10-04 08:52:57 -04:00
|
|
|
s->cur_sess, s->nbpend, s->counters.cum_sess);
|
2006-06-25 20:48:02 -04:00
|
|
|
Warning("%s\n", trash);
|
|
|
|
|
send_log(p, LOG_NOTICE, "%s\n", trash);
|
|
|
|
|
s = s->next;
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-17 05:27:09 -04:00
|
|
|
/* FIXME: those info are a bit outdated. We should be able to distinguish between FE and BE. */
|
|
|
|
|
if (!p->srv) {
|
|
|
|
|
snprintf(trash, sizeof(trash),
|
2009-04-11 14:44:08 -04:00
|
|
|
"SIGHUP: Proxy %s has no servers. Conn: act(FE+BE): %d+%d, %d pend (%d unass), tot(FE+BE): %lld+%lld.",
|
2007-09-17 05:27:09 -04:00
|
|
|
p->id,
|
2009-10-04 08:52:57 -04:00
|
|
|
p->feconn, p->beconn, p->totpend, p->nbpend, p->counters.cum_feconn, p->counters.cum_beconn);
|
2007-09-17 05:27:09 -04:00
|
|
|
} else if (p->srv_act == 0) {
|
2006-06-25 20:48:02 -04:00
|
|
|
snprintf(trash, sizeof(trash),
|
2009-04-11 14:44:08 -04:00
|
|
|
"SIGHUP: Proxy %s %s ! Conn: act(FE+BE): %d+%d, %d pend (%d unass), tot(FE+BE): %lld+%lld.",
|
2006-06-25 20:48:02 -04:00
|
|
|
p->id,
|
|
|
|
|
(p->srv_bck) ? "is running on backup servers" : "has no server available",
|
2009-10-04 08:52:57 -04:00
|
|
|
p->feconn, p->beconn, p->totpend, p->nbpend, p->counters.cum_feconn, p->counters.cum_beconn);
|
2006-06-25 20:48:02 -04:00
|
|
|
} else {
|
|
|
|
|
snprintf(trash, sizeof(trash),
|
|
|
|
|
"SIGHUP: Proxy %s has %d active servers and %d backup servers available."
|
2009-04-11 14:44:08 -04:00
|
|
|
" Conn: act(FE+BE): %d+%d, %d pend (%d unass), tot(FE+BE): %lld+%lld.",
|
2006-06-25 20:48:02 -04:00
|
|
|
p->id, p->srv_act, p->srv_bck,
|
2009-10-04 08:52:57 -04:00
|
|
|
p->feconn, p->beconn, p->totpend, p->nbpend, p->counters.cum_feconn, p->counters.cum_beconn);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
Warning("%s\n", trash);
|
|
|
|
|
send_log(p, LOG_NOTICE, "%s\n", trash);
|
|
|
|
|
|
|
|
|
|
p = p->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dump(int sig)
|
|
|
|
|
{
|
2007-04-29 04:41:56 -04:00
|
|
|
#if 0
|
2007-01-06 18:38:00 -05:00
|
|
|
struct task *t;
|
2006-06-25 20:48:02 -04:00
|
|
|
struct session *s;
|
2007-01-06 18:38:00 -05:00
|
|
|
struct rb_node *node;
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2007-01-06 18:38:00 -05:00
|
|
|
for(node = rb_first(&wait_queue[0]);
|
|
|
|
|
node != NULL; node = rb_next(node)) {
|
|
|
|
|
t = rb_entry(node, struct task, rb_node);
|
2006-06-25 20:48:02 -04:00
|
|
|
s = t->context;
|
|
|
|
|
qfprintf(stderr,"[dump] wq: task %p, still %ld ms, "
|
2008-12-07 10:27:56 -05:00
|
|
|
"cli=%d, srv=%d, req=%d, rep=%d\n",
|
2007-04-29 11:43:56 -04:00
|
|
|
s, tv_ms_remain(&now, &t->expire),
|
2008-12-07 10:27:56 -05:00
|
|
|
s->si[0].state,
|
|
|
|
|
s->si[1].state,
|
|
|
|
|
s->req->l, s->rep?s->rep->l:0);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
2007-04-29 04:41:56 -04:00
|
|
|
#endif
|
2007-05-13 13:43:47 -04:00
|
|
|
/* dump memory usage then free everything possible */
|
|
|
|
|
dump_pools();
|
|
|
|
|
pool_gc2();
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG_MEMORY
|
|
|
|
|
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();
|
2007-05-13 18:39:29 -04:00
|
|
|
pool_gc2();
|
2009-05-10 03:59:50 -04:00
|
|
|
/* If we are killed twice, we decide to die */
|
|
|
|
|
signal_register(sig, SIG_DFL);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
2007-05-13 18:39:29 -04:00
|
|
|
pool_gc2();
|
2009-05-10 03:59:50 -04:00
|
|
|
/* If we are killed twice, we decide to die */
|
|
|
|
|
signal_register(sig, SIG_DFL);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This function initializes all the necessary variables. It only returns
|
|
|
|
|
* if everything is OK. If something fails, it exits.
|
|
|
|
|
*/
|
|
|
|
|
void init(int argc, char **argv)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
int arg_mode = 0; /* MODE_DEBUG, ... */
|
|
|
|
|
char *old_argv = *argv;
|
|
|
|
|
char *tmp;
|
|
|
|
|
char *cfg_pidfile = NULL;
|
2009-07-20 03:30:05 -04:00
|
|
|
int err_code = 0;
|
2010-01-03 15:12:30 -05:00
|
|
|
struct wordlist *wl;
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Initialize the previously static variables.
|
|
|
|
|
*/
|
|
|
|
|
|
2009-01-25 07:56:13 -05:00
|
|
|
totalconn = actconn = maxfd = listeners = stopping = 0;
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAPROXY_MEMMAX
|
|
|
|
|
global.rlimit_memmax = HAPROXY_MEMMAX;
|
|
|
|
|
#endif
|
|
|
|
|
|
2008-06-23 08:00:57 -04:00
|
|
|
tv_update_date(-1,-1);
|
2006-06-25 20:48:02 -04:00
|
|
|
start_date = now;
|
|
|
|
|
|
2009-05-10 03:01:21 -04:00
|
|
|
signal_init();
|
2007-05-13 13:43:47 -04:00
|
|
|
init_task();
|
|
|
|
|
init_session();
|
2009-09-23 17:37:52 -04:00
|
|
|
/* warning, we init buffers later */
|
2007-05-13 14:19:55 -04:00
|
|
|
init_pendconn();
|
2006-12-24 11:47:20 -05:00
|
|
|
init_proto_http();
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2009-01-25 09:42:27 -05:00
|
|
|
global.tune.options |= GTUNE_USE_SELECT; /* select() is always available */
|
2006-06-25 20:48:02 -04:00
|
|
|
#if defined(ENABLE_POLL)
|
2009-01-25 09:42:27 -05:00
|
|
|
global.tune.options |= GTUNE_USE_POLL;
|
2006-06-25 20:48:02 -04:00
|
|
|
#endif
|
|
|
|
|
#if defined(ENABLE_EPOLL)
|
2009-01-25 09:42:27 -05:00
|
|
|
global.tune.options |= GTUNE_USE_EPOLL;
|
2006-06-25 20:48:02 -04:00
|
|
|
#endif
|
2007-04-15 18:53:59 -04:00
|
|
|
#if defined(ENABLE_SEPOLL)
|
2009-01-25 09:42:27 -05:00
|
|
|
global.tune.options |= GTUNE_USE_SEPOLL;
|
2007-04-15 18:53:59 -04:00
|
|
|
#endif
|
2007-04-09 06:03:06 -04:00
|
|
|
#if defined(ENABLE_KQUEUE)
|
2009-01-25 09:42:27 -05:00
|
|
|
global.tune.options |= GTUNE_USE_KQUEUE;
|
2007-04-09 06:03:06 -04:00
|
|
|
#endif
|
2009-08-16 07:20:32 -04:00
|
|
|
#if defined(CONFIG_HAP_LINUX_SPLICE)
|
2009-01-25 10:03:28 -05:00
|
|
|
global.tune.options |= GTUNE_USE_SPLICE;
|
|
|
|
|
#endif
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
pid = getpid();
|
|
|
|
|
progname = *argv;
|
|
|
|
|
while ((tmp = strchr(progname, '/')) != NULL)
|
|
|
|
|
progname = tmp + 1;
|
|
|
|
|
|
|
|
|
|
argc--; argv++;
|
|
|
|
|
while (argc > 0) {
|
|
|
|
|
char *flag;
|
|
|
|
|
|
|
|
|
|
if (**argv == '-') {
|
|
|
|
|
flag = *argv+1;
|
|
|
|
|
|
|
|
|
|
/* 1 arg */
|
|
|
|
|
if (*flag == 'v') {
|
|
|
|
|
display_version();
|
2007-12-02 05:28:59 -05:00
|
|
|
if (flag[1] == 'v') /* -vv */
|
|
|
|
|
display_build_opts();
|
2006-06-25 20:48:02 -04:00
|
|
|
exit(0);
|
|
|
|
|
}
|
|
|
|
|
#if defined(ENABLE_EPOLL)
|
|
|
|
|
else if (*flag == 'd' && flag[1] == 'e')
|
2009-01-25 09:42:27 -05:00
|
|
|
global.tune.options &= ~GTUNE_USE_EPOLL;
|
2006-06-25 20:48:02 -04:00
|
|
|
#endif
|
2007-04-15 18:53:59 -04:00
|
|
|
#if defined(ENABLE_SEPOLL)
|
|
|
|
|
else if (*flag == 'd' && flag[1] == 's')
|
2009-01-25 09:42:27 -05:00
|
|
|
global.tune.options &= ~GTUNE_USE_SEPOLL;
|
2007-04-15 18:53:59 -04:00
|
|
|
#endif
|
2006-06-25 20:48:02 -04:00
|
|
|
#if defined(ENABLE_POLL)
|
|
|
|
|
else if (*flag == 'd' && flag[1] == 'p')
|
2009-01-25 09:42:27 -05:00
|
|
|
global.tune.options &= ~GTUNE_USE_POLL;
|
2007-04-09 06:03:06 -04:00
|
|
|
#endif
|
2007-04-10 16:45:11 -04:00
|
|
|
#if defined(ENABLE_KQUEUE)
|
2007-04-09 06:03:06 -04:00
|
|
|
else if (*flag == 'd' && flag[1] == 'k')
|
2009-01-25 09:42:27 -05:00
|
|
|
global.tune.options &= ~GTUNE_USE_KQUEUE;
|
2009-01-25 10:03:28 -05:00
|
|
|
#endif
|
2009-08-16 07:20:32 -04:00
|
|
|
#if defined(CONFIG_HAP_LINUX_SPLICE)
|
2009-01-25 10:03:28 -05:00
|
|
|
else if (*flag == 'd' && flag[1] == 'S')
|
|
|
|
|
global.tune.options &= ~GTUNE_USE_SPLICE;
|
2006-06-25 20:48:02 -04:00
|
|
|
#endif
|
|
|
|
|
else if (*flag == 'V')
|
|
|
|
|
arg_mode |= MODE_VERBOSE;
|
|
|
|
|
else if (*flag == 'd' && flag[1] == 'b')
|
|
|
|
|
arg_mode |= MODE_FOREGROUND;
|
|
|
|
|
else if (*flag == 'd')
|
|
|
|
|
arg_mode |= MODE_DEBUG;
|
|
|
|
|
else if (*flag == 'c')
|
|
|
|
|
arg_mode |= MODE_CHECK;
|
|
|
|
|
else if (*flag == 'D')
|
2009-05-18 10:29:51 -04:00
|
|
|
arg_mode |= MODE_DAEMON;
|
2006-06-25 20:48:02 -04:00
|
|
|
else if (*flag == 'q')
|
|
|
|
|
arg_mode |= MODE_QUIET;
|
|
|
|
|
else if (*flag == 's' && (flag[1] == 'f' || flag[1] == 't')) {
|
|
|
|
|
/* list of pids to finish ('f') or terminate ('t') */
|
|
|
|
|
|
|
|
|
|
if (flag[1] == 'f')
|
|
|
|
|
oldpids_sig = SIGUSR1; /* finish then exit */
|
|
|
|
|
else
|
|
|
|
|
oldpids_sig = SIGTERM; /* terminate immediately */
|
|
|
|
|
argv++; argc--;
|
|
|
|
|
|
|
|
|
|
if (argc > 0) {
|
|
|
|
|
oldpids = calloc(argc, sizeof(int));
|
|
|
|
|
while (argc > 0) {
|
|
|
|
|
oldpids[nb_oldpids] = atol(*argv);
|
|
|
|
|
if (oldpids[nb_oldpids] <= 0)
|
|
|
|
|
usage(old_argv);
|
|
|
|
|
argc--; argv++;
|
|
|
|
|
nb_oldpids++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else { /* >=2 args */
|
|
|
|
|
argv++; argc--;
|
|
|
|
|
if (argc == 0)
|
|
|
|
|
usage(old_argv);
|
|
|
|
|
|
|
|
|
|
switch (*flag) {
|
|
|
|
|
case 'n' : cfg_maxconn = atol(*argv); break;
|
|
|
|
|
case 'm' : global.rlimit_memmax = atol(*argv); break;
|
|
|
|
|
case 'N' : cfg_maxpconn = atol(*argv); break;
|
2009-06-22 10:02:30 -04:00
|
|
|
case 'f' :
|
2010-01-03 15:12:30 -05:00
|
|
|
wl = (struct wordlist *)calloc(1, sizeof(*wl));
|
|
|
|
|
if (!wl) {
|
|
|
|
|
Alert("Cannot load configuration file %s : out of memory.\n", *argv);
|
2009-06-22 10:02:30 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
2010-01-03 15:12:30 -05:00
|
|
|
wl->s = *argv;
|
|
|
|
|
LIST_ADDQ(&cfg_cfgfiles, &wl->list);
|
2009-06-22 10:02:30 -04:00
|
|
|
break;
|
2006-06-25 20:48:02 -04:00
|
|
|
case 'p' : cfg_pidfile = *argv; break;
|
|
|
|
|
default: usage(old_argv);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
usage(old_argv);
|
|
|
|
|
argv++; argc--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
global.mode = MODE_STARTING | /* during startup, we want most of the alerts */
|
|
|
|
|
(arg_mode & (MODE_DAEMON | MODE_FOREGROUND | MODE_VERBOSE
|
|
|
|
|
| MODE_QUIET | MODE_CHECK | MODE_DEBUG));
|
|
|
|
|
|
2010-01-03 15:12:30 -05:00
|
|
|
if (LIST_ISEMPTY(&cfg_cfgfiles))
|
2006-06-25 20:48:02 -04:00
|
|
|
usage(old_argv);
|
|
|
|
|
|
2010-03-12 15:58:54 -05:00
|
|
|
/* NB: POSIX does not make it mandatory for gethostname() to NULL-terminate
|
|
|
|
|
* the string in case of truncation, and at least FreeBSD appears not to do
|
|
|
|
|
* it.
|
|
|
|
|
*/
|
|
|
|
|
memset(hostname, 0, sizeof(hostname));
|
|
|
|
|
gethostname(hostname, sizeof(hostname) - 1);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
have_appsession = 0;
|
|
|
|
|
global.maxsock = 10; /* reserve 10 fds ; will be incremented by socket eaters */
|
2009-06-22 09:48:36 -04:00
|
|
|
|
|
|
|
|
init_default_instance();
|
|
|
|
|
|
2010-01-03 15:12:30 -05:00
|
|
|
list_for_each_entry(wl, &cfg_cfgfiles, list) {
|
2009-12-06 07:10:44 -05:00
|
|
|
int ret;
|
|
|
|
|
|
2010-01-03 15:12:30 -05:00
|
|
|
ret = readcfgfile(wl->s);
|
2009-12-06 07:10:44 -05:00
|
|
|
if (ret == -1) {
|
|
|
|
|
Alert("Could not open configuration file %s : %s\n",
|
2010-01-03 15:12:30 -05:00
|
|
|
wl->s, strerror(errno));
|
2009-12-06 07:10:44 -05:00
|
|
|
exit(1);
|
|
|
|
|
}
|
2009-12-15 15:46:25 -05:00
|
|
|
if (ret & (ERR_ABORT|ERR_FATAL))
|
2010-01-03 15:12:30 -05:00
|
|
|
Alert("Error(s) found in configuration file : %s\n", wl->s);
|
2009-12-15 15:46:25 -05:00
|
|
|
err_code |= ret;
|
2009-07-20 03:30:05 -04:00
|
|
|
if (err_code & ERR_ABORT)
|
2009-06-22 10:02:30 -04:00
|
|
|
exit(1);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
2007-10-14 17:40:01 -04:00
|
|
|
|
2009-07-23 07:36:36 -04:00
|
|
|
err_code |= check_config_validity();
|
|
|
|
|
if (err_code & (ERR_ABORT|ERR_FATAL)) {
|
|
|
|
|
Alert("Fatal errors found in configuration.\n");
|
2009-06-22 09:48:36 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
if (global.mode & MODE_CHECK) {
|
2009-06-22 09:48:36 -04:00
|
|
|
qfprintf(stdout, "Configuration file is valid\n");
|
2006-06-25 20:48:02 -04:00
|
|
|
exit(0);
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-23 17:37:52 -04:00
|
|
|
/* now we know the buffer size, we can initialize the buffers */
|
|
|
|
|
init_buffer();
|
|
|
|
|
|
2009-06-22 09:48:36 -04:00
|
|
|
if (have_appsession)
|
|
|
|
|
appsession_init();
|
|
|
|
|
|
2007-10-14 17:40:01 -04:00
|
|
|
if (start_checks() < 0)
|
|
|
|
|
exit(1);
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
if (cfg_maxconn > 0)
|
|
|
|
|
global.maxconn = cfg_maxconn;
|
|
|
|
|
|
|
|
|
|
if (cfg_pidfile) {
|
2008-08-03 06:19:50 -04:00
|
|
|
free(global.pidfile);
|
2006-06-25 20:48:02 -04:00
|
|
|
global.pidfile = strdup(cfg_pidfile);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (global.maxconn == 0)
|
|
|
|
|
global.maxconn = DEFAULT_MAXCONN;
|
|
|
|
|
|
2009-01-18 15:44:07 -05:00
|
|
|
if (!global.maxpipes) {
|
|
|
|
|
/* maxpipes not specified. Count how many frontends and backends
|
|
|
|
|
* may be using splicing, and bound that to maxconn.
|
|
|
|
|
*/
|
|
|
|
|
struct proxy *cur;
|
|
|
|
|
int nbfe = 0, nbbe = 0;
|
|
|
|
|
|
|
|
|
|
for (cur = proxy; cur; cur = cur->next) {
|
|
|
|
|
if (cur->options2 & (PR_O2_SPLIC_ANY)) {
|
|
|
|
|
if (cur->cap & PR_CAP_FE)
|
|
|
|
|
nbfe += cur->maxconn;
|
|
|
|
|
if (cur->cap & PR_CAP_BE)
|
2009-01-25 04:42:05 -05:00
|
|
|
nbbe += cur->fullconn ? cur->fullconn : global.maxconn;
|
2009-01-18 15:44:07 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
global.maxpipes = MAX(nbfe, nbbe);
|
|
|
|
|
if (global.maxpipes > global.maxconn)
|
|
|
|
|
global.maxpipes = global.maxconn;
|
2009-01-25 08:06:58 -05:00
|
|
|
global.maxpipes /= 4;
|
2009-01-18 15:44:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
global.maxsock += global.maxconn * 2; /* each connection needs two sockets */
|
2009-01-18 14:39:42 -05:00
|
|
|
global.maxsock += global.maxpipes * 2; /* each pipe needs two FDs */
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2007-06-03 11:16:49 -04:00
|
|
|
if (global.tune.maxpollevents <= 0)
|
|
|
|
|
global.tune.maxpollevents = MAX_POLL_EVENTS;
|
|
|
|
|
|
2009-03-01 02:35:41 -05:00
|
|
|
if (global.tune.maxaccept == 0) {
|
2008-01-06 05:22:57 -05:00
|
|
|
if (global.nbproc > 1)
|
|
|
|
|
global.tune.maxaccept = 8; /* leave some conns to other processes */
|
|
|
|
|
else
|
2009-03-01 02:35:41 -05:00
|
|
|
global.tune.maxaccept = 100; /* accept many incoming conns at once */
|
2008-01-06 05:22:57 -05:00
|
|
|
}
|
|
|
|
|
|
2009-03-21 15:43:57 -04:00
|
|
|
if (global.tune.recv_enough == 0)
|
|
|
|
|
global.tune.recv_enough = MIN_RECV_AT_ONCE_ENOUGH;
|
|
|
|
|
|
2009-08-17 01:23:33 -04:00
|
|
|
if (global.tune.maxrewrite >= global.tune.bufsize / 2)
|
|
|
|
|
global.tune.maxrewrite = global.tune.bufsize / 2;
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
if (arg_mode & (MODE_DEBUG | MODE_FOREGROUND)) {
|
|
|
|
|
/* command line debug mode inhibits configuration mode */
|
|
|
|
|
global.mode &= ~(MODE_DAEMON | MODE_QUIET);
|
|
|
|
|
}
|
|
|
|
|
global.mode |= (arg_mode & (MODE_DAEMON | MODE_FOREGROUND | MODE_QUIET |
|
2007-10-18 07:53:22 -04:00
|
|
|
MODE_VERBOSE | MODE_DEBUG ));
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
if ((global.mode & MODE_DEBUG) && (global.mode & (MODE_DAEMON | MODE_QUIET))) {
|
|
|
|
|
Warning("<debug> mode incompatible with <quiet> and <daemon>. Keeping <debug> only.\n");
|
|
|
|
|
global.mode &= ~(MODE_DAEMON | MODE_QUIET);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((global.nbproc > 1) && !(global.mode & MODE_DAEMON)) {
|
|
|
|
|
if (!(global.mode & (MODE_FOREGROUND | MODE_DEBUG)))
|
|
|
|
|
Warning("<nbproc> is only meaningful in daemon mode. Setting limit to 1 process.\n");
|
|
|
|
|
global.nbproc = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (global.nbproc < 1)
|
|
|
|
|
global.nbproc = 1;
|
|
|
|
|
|
2010-02-26 05:12:27 -05:00
|
|
|
swap_buffer = (char *)calloc(1, global.tune.bufsize);
|
|
|
|
|
|
2009-10-18 01:25:52 -04:00
|
|
|
fdinfo = (struct fdinfo *)calloc(1,
|
|
|
|
|
sizeof(struct fdinfo) * (global.maxsock));
|
2006-06-25 20:48:02 -04:00
|
|
|
fdtab = (struct fdtab *)calloc(1,
|
|
|
|
|
sizeof(struct fdtab) * (global.maxsock));
|
|
|
|
|
for (i = 0; i < global.maxsock; i++) {
|
|
|
|
|
fdtab[i].state = FD_STCLOSE;
|
|
|
|
|
}
|
2007-04-08 10:39:58 -04:00
|
|
|
|
2007-04-15 18:25:25 -04:00
|
|
|
/*
|
|
|
|
|
* Note: we could register external pollers here.
|
|
|
|
|
* Built-in pollers have been registered before main().
|
|
|
|
|
*/
|
2007-04-08 10:39:58 -04:00
|
|
|
|
2009-01-25 09:42:27 -05:00
|
|
|
if (!(global.tune.options & GTUNE_USE_KQUEUE))
|
2007-04-09 06:03:06 -04:00
|
|
|
disable_poller("kqueue");
|
|
|
|
|
|
2009-01-25 09:42:27 -05:00
|
|
|
if (!(global.tune.options & GTUNE_USE_EPOLL))
|
2007-04-08 10:39:58 -04:00
|
|
|
disable_poller("epoll");
|
|
|
|
|
|
2009-01-25 09:42:27 -05:00
|
|
|
if (!(global.tune.options & GTUNE_USE_SEPOLL))
|
2007-04-15 18:53:59 -04:00
|
|
|
disable_poller("sepoll");
|
|
|
|
|
|
2009-01-25 09:42:27 -05:00
|
|
|
if (!(global.tune.options & GTUNE_USE_POLL))
|
2007-04-08 10:39:58 -04:00
|
|
|
disable_poller("poll");
|
|
|
|
|
|
2009-01-25 09:42:27 -05:00
|
|
|
if (!(global.tune.options & GTUNE_USE_SELECT))
|
2007-04-08 10:39:58 -04:00
|
|
|
disable_poller("select");
|
|
|
|
|
|
|
|
|
|
/* Note: we could disable any poller by name here */
|
|
|
|
|
|
2007-04-09 13:29:56 -04:00
|
|
|
if (global.mode & (MODE_VERBOSE|MODE_DEBUG))
|
|
|
|
|
list_pollers(stderr);
|
|
|
|
|
|
2007-04-08 10:39:58 -04:00
|
|
|
if (!init_pollers()) {
|
2007-04-09 13:29:56 -04:00
|
|
|
Alert("No polling mechanism available.\n");
|
2007-04-08 10:39:58 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
2007-04-09 13:29:56 -04:00
|
|
|
if (global.mode & (MODE_VERBOSE|MODE_DEBUG)) {
|
|
|
|
|
printf("Using %s() as the polling mechanism.\n", cur_poller.name);
|
2007-04-08 10:39:58 -04:00
|
|
|
}
|
|
|
|
|
|
2009-10-02 16:51:14 -04:00
|
|
|
if (!global.node)
|
|
|
|
|
global.node = strdup(hostname);
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void deinit(void)
|
|
|
|
|
{
|
2007-05-13 18:39:29 -04:00
|
|
|
struct proxy *p = proxy, *p0;
|
2006-06-25 20:48:02 -04:00
|
|
|
struct cap_hdr *h,*h_next;
|
|
|
|
|
struct server *s,*s_next;
|
|
|
|
|
struct listener *l,*l_next;
|
2007-06-16 18:36:03 -04:00
|
|
|
struct acl_cond *cond, *condb;
|
|
|
|
|
struct hdr_exp *exp, *expb;
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
|
|
|
struct acl *acl, *aclb;
|
2008-05-31 07:53:23 -04:00
|
|
|
struct switching_rule *rule, *ruleb;
|
2008-06-07 17:08:56 -04:00
|
|
|
struct redirect_rule *rdr, *rdrb;
|
2010-01-03 15:03:22 -05:00
|
|
|
struct wordlist *wl, *wlb;
|
2010-01-28 12:10:50 -05:00
|
|
|
struct cond_wordlist *cwl, *cwlb;
|
2008-05-31 07:53:23 -04:00
|
|
|
struct uri_auth *uap, *ua = NULL;
|
2007-06-16 18:36:03 -04:00
|
|
|
int i;
|
2008-05-31 07:53:23 -04:00
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
while (p) {
|
2008-08-03 06:19:50 -04:00
|
|
|
free(p->id);
|
|
|
|
|
free(p->check_req);
|
|
|
|
|
free(p->cookie_name);
|
|
|
|
|
free(p->cookie_domain);
|
|
|
|
|
free(p->url_param_name);
|
|
|
|
|
free(p->capture_name);
|
|
|
|
|
free(p->monitor_uri);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2008-08-03 06:19:50 -04:00
|
|
|
for (i = 0; i < HTTP_ERR_SIZE; i++)
|
2009-09-27 07:23:20 -04:00
|
|
|
chunk_destroy(&p->errmsg[i]);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2010-01-28 12:10:50 -05:00
|
|
|
list_for_each_entry_safe(cwl, cwlb, &p->req_add, list) {
|
|
|
|
|
LIST_DEL(&cwl->list);
|
|
|
|
|
free(cwl->s);
|
|
|
|
|
free(cwl);
|
2010-01-03 15:03:22 -05:00
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2010-01-28 12:10:50 -05:00
|
|
|
list_for_each_entry_safe(cwl, cwlb, &p->rsp_add, list) {
|
|
|
|
|
LIST_DEL(&cwl->list);
|
|
|
|
|
free(cwl->s);
|
|
|
|
|
free(cwl);
|
2010-01-03 15:03:22 -05:00
|
|
|
}
|
2007-06-16 18:36:03 -04:00
|
|
|
|
|
|
|
|
list_for_each_entry_safe(cond, condb, &p->block_cond, list) {
|
|
|
|
|
LIST_DEL(&cond->list);
|
|
|
|
|
prune_acl_cond(cond);
|
|
|
|
|
free(cond);
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-30 14:51:32 -05:00
|
|
|
list_for_each_entry_safe(cond, condb, &p->mon_fail_cond, list) {
|
|
|
|
|
LIST_DEL(&cond->list);
|
|
|
|
|
prune_acl_cond(cond);
|
|
|
|
|
free(cond);
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-16 18:36:03 -04:00
|
|
|
for (exp = p->req_exp; exp != NULL; ) {
|
2008-05-31 07:53:23 -04:00
|
|
|
if (exp->preg) {
|
2007-06-16 18:36:03 -04:00
|
|
|
regfree((regex_t *)exp->preg);
|
2008-05-31 07:53:23 -04:00
|
|
|
free((regex_t *)exp->preg);
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-16 18:36:03 -04:00
|
|
|
if (exp->replace && exp->action != ACT_SETBE)
|
|
|
|
|
free((char *)exp->replace);
|
|
|
|
|
expb = exp;
|
|
|
|
|
exp = exp->next;
|
|
|
|
|
free(expb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (exp = p->rsp_exp; exp != NULL; ) {
|
2008-05-31 07:53:23 -04:00
|
|
|
if (exp->preg) {
|
2007-06-16 18:36:03 -04:00
|
|
|
regfree((regex_t *)exp->preg);
|
2008-05-31 07:53:23 -04:00
|
|
|
free((regex_t *)exp->preg);
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-16 18:36:03 -04:00
|
|
|
if (exp->replace && exp->action != ACT_SETBE)
|
|
|
|
|
free((char *)exp->replace);
|
|
|
|
|
expb = exp;
|
|
|
|
|
exp = exp->next;
|
|
|
|
|
free(expb);
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-31 07:53:23 -04:00
|
|
|
/* build a list of unique uri_auths */
|
|
|
|
|
if (!ua)
|
|
|
|
|
ua = p->uri_auth;
|
|
|
|
|
else {
|
|
|
|
|
/* check if p->uri_auth is unique */
|
|
|
|
|
for (uap = ua; uap; uap=uap->next)
|
|
|
|
|
if (uap == p->uri_auth)
|
|
|
|
|
break;
|
|
|
|
|
|
2008-06-24 05:14:45 -04:00
|
|
|
if (!uap && p->uri_auth) {
|
2008-05-31 07:53:23 -04:00
|
|
|
/* add it, if it is */
|
|
|
|
|
p->uri_auth->next = ua;
|
|
|
|
|
ua = p->uri_auth;
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-06-16 18:36:03 -04:00
|
|
|
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
|
|
|
list_for_each_entry_safe(acl, aclb, &p->acl, list) {
|
|
|
|
|
LIST_DEL(&acl->list);
|
|
|
|
|
prune_acl(acl);
|
|
|
|
|
free(acl);
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-31 07:53:23 -04:00
|
|
|
list_for_each_entry_safe(rule, ruleb, &p->switching_rules, list) {
|
|
|
|
|
LIST_DEL(&rule->list);
|
|
|
|
|
prune_acl_cond(rule->cond);
|
|
|
|
|
free(rule->cond);
|
|
|
|
|
free(rule);
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-07 17:08:56 -04:00
|
|
|
list_for_each_entry_safe(rdr, rdrb, &p->redirect_rules, list) {
|
|
|
|
|
LIST_DEL(&rdr->list);
|
2010-01-03 14:03:03 -05:00
|
|
|
if (rdr->cond) {
|
|
|
|
|
prune_acl_cond(rdr->cond);
|
|
|
|
|
free(rdr->cond);
|
|
|
|
|
}
|
2008-06-07 17:08:56 -04:00
|
|
|
free(rdr->rdr_str);
|
|
|
|
|
free(rdr);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-03 06:19:50 -04:00
|
|
|
free(p->appsession_name);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
h = p->req_cap;
|
|
|
|
|
while (h) {
|
|
|
|
|
h_next = h->next;
|
2008-08-03 06:19:50 -04:00
|
|
|
free(h->name);
|
2007-05-13 16:46:04 -04:00
|
|
|
pool_destroy2(h->pool);
|
2006-06-25 20:48:02 -04:00
|
|
|
free(h);
|
|
|
|
|
h = h_next;
|
|
|
|
|
}/* end while(h) */
|
|
|
|
|
|
|
|
|
|
h = p->rsp_cap;
|
|
|
|
|
while (h) {
|
|
|
|
|
h_next = h->next;
|
2008-08-03 06:19:50 -04:00
|
|
|
free(h->name);
|
2007-05-13 16:46:04 -04:00
|
|
|
pool_destroy2(h->pool);
|
2006-06-25 20:48:02 -04:00
|
|
|
free(h);
|
|
|
|
|
h = h_next;
|
|
|
|
|
}/* end while(h) */
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
s = p->srv;
|
|
|
|
|
while (s) {
|
|
|
|
|
s_next = s->next;
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
|
|
|
|
|
|
|
|
if (s->check) {
|
|
|
|
|
task_delete(s->check);
|
|
|
|
|
task_free(s->check);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-03 06:19:50 -04:00
|
|
|
free(s->id);
|
|
|
|
|
free(s->cookie);
|
[MEDIUM] checks: support multi-packet health check responses
We are seeing both real servers repeatedly going on- and off-line with
a period of tens of seconds. Packet tracing, stracing, and adding
debug code to HAProxy itself has revealed that the real servers are
always responding correctly, but HAProxy is sometimes receiving only
part of the response.
It appears that the real servers are sending the test page as three
separate packets. HAProxy receives the contents of one, two, or three
packets, apparently randomly. Naturally, the health check only
succeeds when all three packets' data are seen by HAProxy. If HAProxy
and the real servers are modified to use a plain HTML page for the
health check, the response is in the form of a single packet and the
checks do not fail.
(...)
I've added buffer and length variables to struct server, and allocated
space with the rest of the server initialisation.
(...)
It seems to be working fine in my tests, and handles check responses
that are bigger than the buffer.
2010-03-16 11:50:46 -04:00
|
|
|
free(s->check_data);
|
2006-06-25 20:48:02 -04:00
|
|
|
free(s);
|
|
|
|
|
s = s_next;
|
|
|
|
|
}/* end while(s) */
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
l = p->listen;
|
|
|
|
|
while (l) {
|
|
|
|
|
l_next = l->next;
|
2010-02-05 14:31:44 -05:00
|
|
|
free(l->name);
|
|
|
|
|
free(l->counters);
|
2006-06-25 20:48:02 -04:00
|
|
|
free(l);
|
|
|
|
|
l = l_next;
|
|
|
|
|
}/* end while(l) */
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
|
|
|
|
2010-02-05 14:31:44 -05:00
|
|
|
free(p->desc);
|
|
|
|
|
free(p->fwdfor_hdr_name);
|
|
|
|
|
|
2010-01-29 11:58:21 -05:00
|
|
|
req_acl_free(&p->req_acl);
|
|
|
|
|
|
2007-05-13 16:46:04 -04:00
|
|
|
pool_destroy2(p->req_cap_pool);
|
|
|
|
|
pool_destroy2(p->rsp_cap_pool);
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
|
|
|
pool_destroy2(p->hdr_idx_pool);
|
2010-01-29 11:50:44 -05:00
|
|
|
|
2007-05-13 18:39:29 -04:00
|
|
|
p0 = p;
|
2006-06-25 20:48:02 -04:00
|
|
|
p = p->next;
|
2007-05-13 18:39:29 -04:00
|
|
|
free(p0);
|
2006-06-25 20:48:02 -04:00
|
|
|
}/* end while(p) */
|
2007-10-16 06:25:14 -04:00
|
|
|
|
2008-05-31 07:53:23 -04:00
|
|
|
while (ua) {
|
|
|
|
|
uap = ua;
|
|
|
|
|
ua = ua->next;
|
|
|
|
|
|
2008-08-03 06:19:50 -04:00
|
|
|
free(uap->uri_prefix);
|
|
|
|
|
free(uap->auth_realm);
|
2009-10-02 16:51:14 -04:00
|
|
|
free(uap->node);
|
|
|
|
|
free(uap->desc);
|
2008-05-31 07:53:23 -04:00
|
|
|
|
2010-01-29 13:29:32 -05:00
|
|
|
userlist_free(uap->userlist);
|
|
|
|
|
req_acl_free(&uap->req_acl);
|
|
|
|
|
|
2008-05-31 07:53:23 -04:00
|
|
|
free(uap);
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-29 11:50:44 -05:00
|
|
|
userlist_free(userlist);
|
|
|
|
|
|
2007-10-16 06:25:14 -04:00
|
|
|
protocol_unbind_all();
|
|
|
|
|
|
2008-08-03 06:19:50 -04:00
|
|
|
free(global.chroot); global.chroot = NULL;
|
|
|
|
|
free(global.pidfile); global.pidfile = NULL;
|
2009-10-02 16:51:14 -04:00
|
|
|
free(global.node); global.node = NULL;
|
|
|
|
|
free(global.desc); global.desc = NULL;
|
2008-08-03 06:19:50 -04:00
|
|
|
free(fdtab); fdtab = NULL;
|
|
|
|
|
free(oldpids); oldpids = NULL;
|
2008-05-31 07:53:23 -04:00
|
|
|
|
2010-01-03 15:12:30 -05:00
|
|
|
list_for_each_entry_safe(wl, wlb, &cfg_cfgfiles, list) {
|
|
|
|
|
LIST_DEL(&wl->list);
|
|
|
|
|
free(wl);
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-13 13:43:47 -04:00
|
|
|
pool_destroy2(pool2_session);
|
2007-05-13 13:56:02 -04:00
|
|
|
pool_destroy2(pool2_buffer);
|
2007-05-13 15:36:56 -04:00
|
|
|
pool_destroy2(pool2_requri);
|
2007-05-13 13:43:47 -04:00
|
|
|
pool_destroy2(pool2_task);
|
2007-05-13 15:45:51 -04:00
|
|
|
pool_destroy2(pool2_capture);
|
2007-05-13 15:29:55 -04:00
|
|
|
pool_destroy2(pool2_appsess);
|
2007-05-13 14:19:55 -04:00
|
|
|
pool_destroy2(pool2_pendconn);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
if (have_appsession) {
|
2007-05-13 15:29:55 -04:00
|
|
|
pool_destroy2(apools.serverid);
|
|
|
|
|
pool_destroy2(apools.sessid);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
|
|
|
|
|
|
|
|
deinit_pollers();
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
} /* end deinit() */
|
|
|
|
|
|
|
|
|
|
/* sends the signal <sig> to all pids found in <oldpids> */
|
|
|
|
|
static void tell_old_pids(int sig)
|
|
|
|
|
{
|
|
|
|
|
int p;
|
|
|
|
|
for (p = 0; p < nb_oldpids; p++)
|
|
|
|
|
kill(oldpids[p], sig);
|
|
|
|
|
}
|
|
|
|
|
|
2007-04-08 10:39:58 -04:00
|
|
|
/*
|
|
|
|
|
* Runs the polling loop
|
|
|
|
|
*
|
|
|
|
|
* FIXME:
|
|
|
|
|
* - we still use 'listeners' to check whether we want to stop or not.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
void run_poll_loop()
|
|
|
|
|
{
|
2008-07-06 18:09:58 -04:00
|
|
|
int next;
|
2007-04-08 10:39:58 -04:00
|
|
|
|
2008-06-23 08:00:57 -04:00
|
|
|
tv_update_date(0,1);
|
2007-04-08 10:39:58 -04:00
|
|
|
while (1) {
|
2009-05-10 03:01:21 -04:00
|
|
|
/* check if we caught some signals and process them */
|
|
|
|
|
signal_process_queue();
|
|
|
|
|
|
2008-06-29 16:40:23 -04:00
|
|
|
/* Check if we can expire some tasks */
|
|
|
|
|
wake_expired_tasks(&next);
|
|
|
|
|
|
|
|
|
|
/* Process a few tasks */
|
2007-05-12 16:35:00 -04:00
|
|
|
process_runnable_tasks(&next);
|
2007-04-08 10:39:58 -04:00
|
|
|
|
2008-06-29 16:40:23 -04:00
|
|
|
/* maintain all proxies in a consistent state. This should quickly
|
|
|
|
|
* become a task because it becomes expensive when there are huge
|
|
|
|
|
* numbers of proxies. */
|
|
|
|
|
maintain_proxies(&next);
|
|
|
|
|
|
2007-04-08 10:39:58 -04:00
|
|
|
/* stop when there's no connection left and we don't allow them anymore */
|
|
|
|
|
if (!actconn && listeners == 0)
|
|
|
|
|
break;
|
|
|
|
|
|
2008-06-29 16:40:23 -04:00
|
|
|
/* The poller will ensure it returns around <next> */
|
2008-07-06 18:09:58 -04:00
|
|
|
cur_poller.poll(&cur_poller, next);
|
2007-04-08 10:39:58 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
|
{
|
|
|
|
|
int err, retry;
|
|
|
|
|
struct rlimit limit;
|
|
|
|
|
FILE *pidfile = NULL;
|
|
|
|
|
init(argc, argv);
|
|
|
|
|
|
2009-05-10 03:59:50 -04:00
|
|
|
signal_register(SIGQUIT, dump);
|
|
|
|
|
signal_register(SIGUSR1, sig_soft_stop);
|
|
|
|
|
signal_register(SIGHUP, sig_dump_state);
|
2006-06-25 20:48:02 -04:00
|
|
|
#ifdef DEBUG_MEMORY
|
2009-05-10 03:59:50 -04:00
|
|
|
signal_register(SIGINT, sig_int);
|
|
|
|
|
signal_register(SIGTERM, sig_term);
|
2006-06-25 20:48:02 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* on very high loads, a sigpipe sometimes happen just between the
|
|
|
|
|
* getsockopt() which tells "it's OK to write", and the following write :-(
|
|
|
|
|
*/
|
2009-08-19 05:22:33 -04:00
|
|
|
#if !MSG_NOSIGNAL || defined(CONFIG_HAP_LINUX_SPLICE)
|
2006-06-25 20:48:02 -04:00
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* We will loop at most 100 times with 10 ms delay each time.
|
|
|
|
|
* That's at most 1 second. We only send a signal to old pids
|
|
|
|
|
* if we cannot grab at least one port.
|
|
|
|
|
*/
|
|
|
|
|
retry = MAX_START_RETRIES;
|
|
|
|
|
err = ERR_NONE;
|
|
|
|
|
while (retry >= 0) {
|
|
|
|
|
struct timeval w;
|
|
|
|
|
err = start_proxies(retry == 0 || nb_oldpids == 0);
|
2007-12-20 17:05:50 -05:00
|
|
|
/* exit the loop on no error or fatal error */
|
|
|
|
|
if ((err & (ERR_RETRYABLE|ERR_FATAL)) != ERR_RETRYABLE)
|
2006-06-25 20:48:02 -04:00
|
|
|
break;
|
|
|
|
|
if (nb_oldpids == 0)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* FIXME-20060514: Solaris and OpenBSD do not support shutdown() on
|
|
|
|
|
* listening sockets. So on those platforms, it would be wiser to
|
|
|
|
|
* simply send SIGUSR1, which will not be undoable.
|
|
|
|
|
*/
|
|
|
|
|
tell_old_pids(SIGTTOU);
|
|
|
|
|
/* give some time to old processes to stop listening */
|
|
|
|
|
w.tv_sec = 0;
|
|
|
|
|
w.tv_usec = 10*1000;
|
|
|
|
|
select(0, NULL, NULL, NULL, &w);
|
|
|
|
|
retry--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Note: start_proxies() sends an alert when it fails. */
|
2009-02-04 11:05:23 -05:00
|
|
|
if ((err & ~ERR_WARN) != ERR_NONE) {
|
2009-06-09 08:36:00 -04:00
|
|
|
if (retry != MAX_START_RETRIES && nb_oldpids) {
|
|
|
|
|
protocol_unbind_all(); /* cleanup everything we can */
|
2006-06-25 20:48:02 -04:00
|
|
|
tell_old_pids(SIGTTIN);
|
2009-06-09 08:36:00 -04:00
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (listeners == 0) {
|
|
|
|
|
Alert("[%s.main()] No enabled listener found (check the <listen> keywords) ! Exiting.\n", argv[0]);
|
|
|
|
|
/* Note: we don't have to send anything to the old pids because we
|
|
|
|
|
* never stopped them. */
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-04 11:05:23 -05:00
|
|
|
if ((protocol_bind_all() & ~ERR_WARN) != ERR_NONE) {
|
2007-10-16 06:25:14 -04:00
|
|
|
Alert("[%s.main()] Some protocols failed to start their listeners! Exiting.\n", argv[0]);
|
|
|
|
|
protocol_unbind_all(); /* cleanup everything we can */
|
|
|
|
|
if (nb_oldpids)
|
|
|
|
|
tell_old_pids(SIGTTIN);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/* prepare pause/play signals */
|
2009-05-10 03:59:50 -04:00
|
|
|
signal_register(SIGTTOU, sig_pause);
|
|
|
|
|
signal_register(SIGTTIN, sig_listen);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
/* MODE_QUIET can inhibit alerts and warnings below this line */
|
|
|
|
|
|
|
|
|
|
global.mode &= ~MODE_STARTING;
|
|
|
|
|
if ((global.mode & MODE_QUIET) && !(global.mode & MODE_VERBOSE)) {
|
|
|
|
|
/* detach from the tty */
|
|
|
|
|
fclose(stdin); fclose(stdout); fclose(stderr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* open log & pid files before the chroot */
|
|
|
|
|
if (global.mode & MODE_DAEMON && global.pidfile != NULL) {
|
|
|
|
|
int pidfd;
|
|
|
|
|
unlink(global.pidfile);
|
|
|
|
|
pidfd = open(global.pidfile, O_CREAT | O_WRONLY | O_TRUNC, 0644);
|
|
|
|
|
if (pidfd < 0) {
|
|
|
|
|
Alert("[%s.main()] Cannot create pidfile %s\n", argv[0], global.pidfile);
|
|
|
|
|
if (nb_oldpids)
|
|
|
|
|
tell_old_pids(SIGTTIN);
|
2007-10-16 06:25:14 -04:00
|
|
|
protocol_unbind_all();
|
2006-06-25 20:48:02 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
pidfile = fdopen(pidfd, "w");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ulimits */
|
|
|
|
|
if (!global.rlimit_nofile)
|
|
|
|
|
global.rlimit_nofile = global.maxsock;
|
|
|
|
|
|
|
|
|
|
if (global.rlimit_nofile) {
|
|
|
|
|
limit.rlim_cur = limit.rlim_max = global.rlimit_nofile;
|
|
|
|
|
if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
|
|
|
|
|
Warning("[%s.main()] Cannot raise FD limit to %d.\n", argv[0], global.rlimit_nofile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (global.rlimit_memmax) {
|
|
|
|
|
limit.rlim_cur = limit.rlim_max =
|
|
|
|
|
global.rlimit_memmax * 1048576 / global.nbproc;
|
|
|
|
|
#ifdef RLIMIT_AS
|
|
|
|
|
if (setrlimit(RLIMIT_AS, &limit) == -1) {
|
|
|
|
|
Warning("[%s.main()] Cannot fix MEM limit to %d megs.\n",
|
|
|
|
|
argv[0], global.rlimit_memmax);
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
if (setrlimit(RLIMIT_DATA, &limit) == -1) {
|
|
|
|
|
Warning("[%s.main()] Cannot fix MEM limit to %d megs.\n",
|
|
|
|
|
argv[0], global.rlimit_memmax);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2007-03-24 12:24:39 -04:00
|
|
|
#ifdef CONFIG_HAP_CTTPROXY
|
|
|
|
|
if (global.last_checks & LSTCHK_CTTPROXY) {
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = check_cttproxy_version();
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
Alert("[%s.main()] Cannot enable cttproxy.\n%s",
|
|
|
|
|
argv[0],
|
|
|
|
|
(ret == -1) ? " Incorrect module version.\n"
|
|
|
|
|
: " Make sure you have enough permissions and that the module is loaded.\n");
|
2007-10-16 06:25:14 -04:00
|
|
|
protocol_unbind_all();
|
2007-03-24 12:24:39 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if ((global.last_checks & LSTCHK_NETADM) && global.uid) {
|
|
|
|
|
Alert("[%s.main()] Some configuration options require full privileges, so global.uid cannot be changed.\n"
|
2009-02-04 12:02:48 -05:00
|
|
|
"", argv[0]);
|
2007-10-16 06:25:14 -04:00
|
|
|
protocol_unbind_all();
|
2007-03-24 12:24:39 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-04 12:02:48 -05:00
|
|
|
/* If the user is not root, we'll still let him try the configuration
|
|
|
|
|
* but we inform him that unexpected behaviour may occur.
|
|
|
|
|
*/
|
|
|
|
|
if ((global.last_checks & LSTCHK_NETADM) && getuid())
|
|
|
|
|
Warning("[%s.main()] Some options which require full privileges"
|
|
|
|
|
" might not work well.\n"
|
|
|
|
|
"", argv[0]);
|
|
|
|
|
|
2007-10-15 12:57:08 -04:00
|
|
|
/* chroot if needed */
|
|
|
|
|
if (global.chroot != NULL) {
|
|
|
|
|
if (chroot(global.chroot) == -1) {
|
|
|
|
|
Alert("[%s.main()] Cannot chroot(%s).\n", argv[0], global.chroot);
|
|
|
|
|
if (nb_oldpids)
|
|
|
|
|
tell_old_pids(SIGTTIN);
|
2007-10-16 06:25:14 -04:00
|
|
|
protocol_unbind_all();
|
2007-10-15 12:57:08 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
chdir("/");
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
if (nb_oldpids)
|
|
|
|
|
tell_old_pids(oldpids_sig);
|
|
|
|
|
|
|
|
|
|
/* Note that any error at this stage will be fatal because we will not
|
|
|
|
|
* be able to restart the old pids.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* setgid / setuid */
|
|
|
|
|
if (global.gid && setgid(global.gid) == -1) {
|
|
|
|
|
Alert("[%s.main()] Cannot set gid %d.\n", argv[0], global.gid);
|
2007-10-16 06:25:14 -04:00
|
|
|
protocol_unbind_all();
|
2006-06-25 20:48:02 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (global.uid && setuid(global.uid) == -1) {
|
|
|
|
|
Alert("[%s.main()] Cannot set uid %d.\n", argv[0], global.uid);
|
2007-10-16 06:25:14 -04:00
|
|
|
protocol_unbind_all();
|
2006-06-25 20:48:02 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* check ulimits */
|
|
|
|
|
limit.rlim_cur = limit.rlim_max = 0;
|
|
|
|
|
getrlimit(RLIMIT_NOFILE, &limit);
|
|
|
|
|
if (limit.rlim_cur < global.maxsock) {
|
|
|
|
|
Warning("[%s.main()] FD limit (%d) too low for maxconn=%d/maxsock=%d. Please raise 'ulimit-n' to %d or more to avoid any trouble.\n",
|
2009-04-03 08:49:12 -04:00
|
|
|
argv[0], (int)limit.rlim_cur, global.maxconn, global.maxsock, global.maxsock);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (global.mode & MODE_DAEMON) {
|
2009-02-04 16:05:05 -05:00
|
|
|
struct proxy *px;
|
2006-06-25 20:48:02 -04:00
|
|
|
int ret = 0;
|
|
|
|
|
int proc;
|
|
|
|
|
|
|
|
|
|
/* the father launches the required number of processes */
|
|
|
|
|
for (proc = 0; proc < global.nbproc; proc++) {
|
|
|
|
|
ret = fork();
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
Alert("[%s.main()] Cannot fork.\n", argv[0]);
|
2007-10-16 06:25:14 -04:00
|
|
|
protocol_unbind_all();
|
2007-10-16 01:44:56 -04:00
|
|
|
exit(1); /* there has been an error */
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
else if (ret == 0) /* child breaks here */
|
|
|
|
|
break;
|
|
|
|
|
if (pidfile != NULL) {
|
|
|
|
|
fprintf(pidfile, "%d\n", ret);
|
|
|
|
|
fflush(pidfile);
|
|
|
|
|
}
|
2007-11-04 17:35:08 -05:00
|
|
|
relative_pid++; /* each child will get a different one */
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
/* close the pidfile both in children and father */
|
|
|
|
|
if (pidfile != NULL)
|
|
|
|
|
fclose(pidfile);
|
|
|
|
|
free(global.pidfile);
|
[BUG] fix double-free during clean exit
This patch fixes a nasty bug raported by both glibc and valgrind, which
leads into a problem that haproxy does not exit when a new instace
starts ap (-sf/-st).
==9299== Invalid free() / delete / delete[]
==9299== at 0x401D095: free (in
/usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==9299== by 0x804A377: deinit (haproxy.c:721)
==9299== by 0x804A883: main (haproxy.c:1014)
==9299== Address 0x41859E0 is 0 bytes inside a block of size 21 free'd
==9299== at 0x401D095: free (in
/usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==9299== by 0x804A84B: main (haproxy.c:985)
==9299==
6542 open("/dev/tty", O_RDWR|O_NONBLOCK|O_NOCTTY) = -1 ENOENT (No such file
or directory)
6542 writev(2, [{"*** glibc detected *** ", 23}, {"corrupted double-linked
list", 28}, {": 0x", 4}, {"6ff91878", 8}, {" ***\n", 5}], 5) = -1 EBADF (Bad
file descriptor)
I found this bug trying to find why, after one week with many restarts, I
finished with >100 haproxy process running. ;)
2007-10-11 12:30:14 -04:00
|
|
|
global.pidfile = NULL;
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2009-02-04 16:05:05 -05:00
|
|
|
/* we might have to unbind some proxies from some processes */
|
|
|
|
|
px = proxy;
|
|
|
|
|
while (px != NULL) {
|
|
|
|
|
if (px->bind_proc && px->state != PR_STSTOPPED) {
|
|
|
|
|
if (!(px->bind_proc & (1 << proc)))
|
|
|
|
|
stop_proxy(px);
|
|
|
|
|
}
|
|
|
|
|
px = px->next;
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
if (proc == global.nbproc)
|
|
|
|
|
exit(0); /* parent must leave */
|
|
|
|
|
|
|
|
|
|
/* if we're NOT in QUIET mode, we should now close the 3 first FDs to ensure
|
|
|
|
|
* that we can detach from the TTY. We MUST NOT do it in other cases since
|
|
|
|
|
* it would have already be done, and 0-2 would have been affected to listening
|
|
|
|
|
* sockets
|
|
|
|
|
*/
|
2008-11-16 01:40:34 -05:00
|
|
|
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
|
2006-06-25 20:48:02 -04:00
|
|
|
/* detach from the tty */
|
|
|
|
|
fclose(stdin); fclose(stdout); fclose(stderr);
|
2008-11-16 01:40:34 -05:00
|
|
|
global.mode &= ~MODE_VERBOSE;
|
2006-06-25 20:48:02 -04:00
|
|
|
global.mode |= MODE_QUIET; /* ensure that we won't say anything from now */
|
|
|
|
|
}
|
|
|
|
|
pid = getpid(); /* update child's pid */
|
|
|
|
|
setsid();
|
2007-04-09 13:29:56 -04:00
|
|
|
fork_poller();
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
2007-10-16 06:25:14 -04:00
|
|
|
protocol_enable_all();
|
2007-04-08 10:39:58 -04:00
|
|
|
/*
|
|
|
|
|
* That's it : the central polling loop. Run until we stop.
|
|
|
|
|
*/
|
|
|
|
|
run_poll_loop();
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
/* Free all Hash Keys and all Hash elements */
|
|
|
|
|
appsession_cleanup();
|
|
|
|
|
/* Do some cleanup */
|
|
|
|
|
deinit();
|
|
|
|
|
|
|
|
|
|
exit(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Local variables:
|
|
|
|
|
* c-indent-level: 8
|
|
|
|
|
* c-basic-offset: 8
|
|
|
|
|
* End:
|
|
|
|
|
*/
|