haproxy/src/haproxy.c

1828 lines
51 KiB
C
Raw Normal View History

/*
* HA-Proxy : High Availability-enabled HTTP/TCP proxy
[RELEASE] Released version 1.5-dev22 Released version 1.5-dev22 with the following main changes : - MEDIUM: tcp-check new feature: connect - MEDIUM: ssl: Set verify 'required' as global default for servers side. - MINOR: ssl: handshake optim for long certificate chains. - BUG/MINOR: pattern: pattern comparison executed twice - BUG/MEDIUM: map: segmentation fault with the stats's socket command "set map ..." - BUG/MEDIUM: pattern: Segfault in binary parser - MINOR: pattern: move functions for grouping pat_match_* and pat_parse_* and add documentation. - MINOR: standard: The parse_binary() returns the length consumed and his documentation is updated - BUG/MINOR: payload: the patterns of the acl "req.ssl_ver" are no parsed with the good function. - BUG/MEDIUM: pattern: "pat_parse_dotted_ver()" set bad expect_type. - BUG/MINOR: sample: The c_str2int converter does not fail if the entry is not an integer - BUG/MEDIUM: http/auth: Sometimes the authentication credentials can be mix between two requests - MINOR: doc: Bad cli function name. - MINOR: http: smp_fetch_capture_header_* fetch captured headers - BUILD: last release inadvertently prepended a "+" in front of the date - BUG/MEDIUM: stream-int: fix the keep-alive idle connection handler - BUG/MEDIUM: backend: do not re-initialize the connection's context upon reuse - BUG: Revert "OPTIM/MEDIUM: epoll: fuse active events into polled ones during polling changes" - BUG/MINOR: checks: successful check completion must not re-enable MAINT servers - MINOR: http: try to stick to same server after status 401/407 - BUG/MINOR: http: always disable compression on HTTP/1.0 - OPTIM: poll: restore polling after a poll/stop/want sequence - OPTIM: http: don't stop polling for read on the client side after a request - BUG/MEDIUM: checks: unchecked servers could not be enabled anymore - BUG/MEDIUM: stats: the web interface must check the tracked servers before enabling - BUG/MINOR: channel: CHN_INFINITE_FORWARD must be unsigned - BUG/MINOR: stream-int: do not clear the owner upon unregister - MEDIUM: stats: add support for HTTP keep-alive on the stats page - BUG/MEDIUM: stats: fix HTTP/1.0 breakage introduced in previous patch - Revert "MEDIUM: stats: add support for HTTP keep-alive on the stats page" - MAJOR: channel: add a new flag CF_WAKE_WRITE to notify the task of writes - OPTIM: session: set the READ_DONTWAIT flag when connecting - BUG/MINOR: http: don't clear the SI_FL_DONT_WAKE flag between requests - MINOR: session: factor out the connect time measurement - MEDIUM: session: prepare to support earlier transitions to the established state - MEDIUM: stream-int: make si_connect() return an established state when possible - MINOR: checks: use an inline function for health_adjust() - OPTIM: session: put unlikely() around the freewheeling code - MEDIUM: config: report a warning when multiple servers have the same name - BUG: Revert "OPTIM: poll: restore polling after a poll/stop/want sequence" - BUILD/MINOR: listener: remove a glibc warning on accept4() - BUG/MAJOR: connection: fix mismatch between rcv_buf's API and usage - BUILD: listener: fix recent accept4() again - BUG/MAJOR: ssl: fix breakage caused by recent fix abf08d9 - BUG/MEDIUM: polling: ensure we update FD status when there's no more activity - MEDIUM: listener: fix polling management in the accept loop - MINOR: protocol: improve the proto->drain() API - MINOR: connection: add a new conn_drain() function - MEDIUM: tcp: report in tcp_drain() that lingering is already disabled on close - MEDIUM: connection: update callers of ctrl->drain() to use conn_drain() - MINOR: connection: add more error codes to report connection errors - MEDIUM: tcp: report connection error at the connection level - MEDIUM: checks: make use of chk_report_conn_err() for connection errors - BUG/MEDIUM: unique_id: HTTP request counter is not stable - DOC: fix misleading information about SIGQUIT - BUG/MAJOR: fix freezes during compression - BUG/MEDIUM: stream-interface: don't wake the task up before end of transfer - BUILD: fix VERDATE exclusion regex - CLEANUP: polling: rename "spec_e" to "state" - DOC: add a diagram showing polling state transitions - REORG: polling: rename "spec_e" to "state" and "spec_p" to "cache" - REORG: polling: rename "fd_spec" to "fd_cache" - REORG: polling: rename the cache allocation functions - REORG: polling: rename "fd_process_spec_events()" to "fd_process_cached_events()" - MAJOR: polling: rework the whole polling system - MAJOR: connection: remove the CO_FL_WAIT_{RD,WR} flags - MEDIUM: connection: remove conn_{data,sock}_poll_{recv,send} - MEDIUM: connection: add check for readiness in I/O handlers - MEDIUM: stream-interface: the polling flags must always be updated in chk_snd_conn - MINOR: stream-interface: no need to call fd_stop_both() on error - MEDIUM: connection: no need to recheck FD state - CLEANUP: connection: use conn_ctrl_ready() instead of checking the flag - CLEANUP: connection: use conn_xprt_ready() instead of checking the flag - CLEANUP: connection: fix comments in connection.h to reflect new behaviour. - OPTIM: raw-sock: don't speculate after a short read if polling is enabled - MEDIUM: polling: centralize polled events processing - MINOR: polling: create function fd_compute_new_polled_status() - MINOR: cli: add more information to the "show info" output - MEDIUM: listener: add support for limiting the session rate in addition to the connection rate - MEDIUM: listener: apply a limit on the session rate submitted to SSL - REORG: stats: move the stats socket states to dumpstats.c - MINOR: cli: add the new "show pools" command - BUG/MEDIUM: counters: flush content counters after each request - BUG/MEDIUM: counters: fix stick-table entry leak when using track-sc2 in connection - MINOR: tools: add very basic support for composite pointers - MEDIUM: counters: stop relying on session flags at all - BUG/MINOR: cli: fix missing break in command line parser - BUG/MINOR: config: correctly report when log-format headers require HTTP mode - MAJOR: http: update connection mode configuration - MEDIUM: http: make keep-alive + httpclose be passive mode - MAJOR: http: switch to keep-alive mode by default - BUG/MEDIUM: http: fix regression caused by recent switch to keep-alive by default - BUG/MEDIUM: listener: improve detection of non-working accept4() - BUILD: listener: add fcntl.h and unistd.h - BUG/MINOR: raw_sock: correctly set the MSG_MORE flag
2014-02-02 18:41:29 -05:00
* Copyright 2000-2014 Willy Tarreau <w@1wt.eu>.
*
* 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 <sys/wait.h>
#include <time.h>
#include <syslog.h>
#include <grp.h>
#ifdef USE_CPU_AFFINITY
#define __USE_GNU
#include <sched.h>
#undef __USE_GNU
#endif
#ifdef DEBUG_FULL
#include <assert.h>
#endif
#include <common/appsession.h>
#include <common/base64.h>
#include <common/cfgparse.h>
#include <common/chunk.h>
#include <common/compat.h>
#include <common/config.h>
#include <common/defaults.h>
#include <common/errors.h>
#include <common/memory.h>
#include <common/mini-clist.h>
MAJOR: namespace: add Linux network namespace support This patch makes it possible to create binds and servers in separate namespaces. This can be used to proxy between multiple completely independent virtual networks (with possibly overlapping IP addresses) and a non-namespace-aware proxy implementation that supports the proxy protocol (v2). The setup is something like this: net1 on VLAN 1 (namespace 1) -\ net2 on VLAN 2 (namespace 2) -- haproxy ==== proxy (namespace 0) net3 on VLAN 3 (namespace 3) -/ The proxy is configured to make server connections through haproxy and sending the expected source/target addresses to haproxy using the proxy protocol. The network namespace setup on the haproxy node is something like this: = 8< = $ cat setup.sh ip netns add 1 ip link add link eth1 type vlan id 1 ip link set eth1.1 netns 1 ip netns exec 1 ip addr add 192.168.91.2/24 dev eth1.1 ip netns exec 1 ip link set eth1.$id up ... = 8< = = 8< = $ cat haproxy.cfg frontend clients bind 127.0.0.1:50022 namespace 1 transparent default_backend scb backend server mode tcp server server1 192.168.122.4:2222 namespace 2 send-proxy-v2 = 8< = A bind line creates the listener in the specified namespace, and connections originating from that listener also have their network namespace set to that of the listener. A server line either forces the connection to be made in a specified namespace or may use the namespace from the client-side connection if that was set. For more documentation please read the documentation included in the patch itself. Signed-off-by: KOVACS Tamas <ktamas@balabit.com> Signed-off-by: Sarkozi Laszlo <laszlo.sarkozi@balabit.com> Signed-off-by: KOVACS Krisztian <hidden@balabit.com>
2014-11-17 09:11:45 -05:00
#include <common/namespace.h>
#include <common/regex.h>
#include <common/standard.h>
#include <common/time.h>
#include <common/uri_auth.h>
#include <common/version.h>
#include <types/capture.h>
#include <types/global.h>
#include <types/proto_tcp.h>
#include <types/acl.h>
#include <types/peers.h>
#include <proto/auth.h>
#include <proto/acl.h>
#include <proto/arg.h>
#include <proto/backend.h>
#include <proto/channel.h>
#include <proto/checks.h>
#include <proto/connection.h>
#include <proto/fd.h>
#include <proto/hdr_idx.h>
#ifdef USE_LUA
#include <proto/hlua.h>
#endif
#include <proto/listener.h>
#include <proto/log.h>
#include <proto/pattern.h>
#include <proto/protocol.h>
#include <proto/proto_http.h>
#include <proto/proxy.h>
#include <proto/queue.h>
#include <proto/server.h>
#include <proto/session.h>
#include <proto/signal.h>
#include <proto/task.h>
#ifdef CONFIG_HAP_CTTPROXY
#include <proto/cttproxy.h>
#endif
#ifdef USE_OPENSSL
#include <proto/ssl_sock.h>
#endif
/*********************************************************************/
extern const struct comp_algo comp_algos[];
/*********************************************************************/
/* list of config files */
static struct list cfg_cfgfiles = LIST_HEAD_INIT(cfg_cfgfiles);
int pid; /* current process id */
2007-11-26 10:13:36 -05:00
int relative_pid = 1; /* process id starting at 1 */
/* global options */
struct global global = {
.nbproc = 1,
.req_count = 0,
.logsrvs = LIST_HEAD_INIT(global.logsrvs),
#ifdef DEFAULT_MAXZLIBMEM
.maxzlibmem = DEFAULT_MAXZLIBMEM * 1024U * 1024U,
#else
.maxzlibmem = 0,
#endif
.comp_rate_lim = 0,
.ssl_server_verify = SSL_SERVER_VERIFY_REQUIRED,
.unix_bind = {
.ux = {
.uid = -1,
.gid = -1,
.mode = 0,
}
},
.tune = {
.bufsize = BUFSIZE,
.maxrewrite = MAXREWRITE,
.chksize = BUFSIZE,
MAJOR: session: only wake up as many sessions as available buffers permit We've already experimented with three wake up algorithms when releasing buffers : the first naive one used to wake up far too many sessions, causing many of them not to get any buffer. The second approach which was still in use prior to this patch consisted in waking up either 1 or 2 sessions depending on the number of FDs we had released. And this was still inaccurate. The third one tried to cover the accuracy issues of the second and took into consideration the number of FDs the sessions would be willing to use, but most of the time we ended up waking up too many of them for nothing, or deadlocking by lack of buffers. This patch completely removes the need to allocate two buffers at once. Instead it splits allocations into critical and non-critical ones and implements a reserve in the pool for this. The deadlock situation happens when all buffers are be allocated for requests pending in a maxconn-limited server queue, because then there's no more way to allocate buffers for responses, and these responses are critical to release the servers's connection in order to release the pending requests. In fact maxconn on a server creates a dependence between sessions and particularly between oldest session's responses and latest session's requests. Thus, it is mandatory to get a free buffer for a response in order to release a server connection which will permit to release a request buffer. Since we definitely have non-symmetrical buffers, we need to implement this logic in the buffer allocation mechanism. What this commit does is implement a reserve of buffers which can only be allocated for responses and that will never be allocated for requests. This is made possible by the requester indicating how much margin it wants to leave after the allocation succeeds. Thus it is a cooperative allocation mechanism : the requester (process_session() in general) prefers not to get a buffer in order to respect other's need for response buffers. The session management code always knows if a buffer will be used for requests or responses, so that is not difficult : - either there's an applet on the initiator side and we really need the request buffer (since currently the applet is called in the context of the session) - or we have a connection and we really need the response buffer (in order to support building and sending an error message back) This reserve ensures that we don't take all allocatable buffers for requests waiting in a queue. The downside is that all the extra buffers are really allocated to ensure they can be allocated. But with small values it is not an issue. With this change, we don't observe any more deadlocks even when running with maxconn 1 on a server under severely constrained memory conditions. The code becomes a bit tricky, it relies on the scheduler's run queue to estimate how many sessions are already expected to run so that it doesn't wake up everyone with too few resources. A better solution would probably consist in having two queues, one for urgent requests and one for normal requests. A failed allocation for a session dealing with an error, a connection event, or the need for a response (or request when there's an applet on the left) would go to the urgent request queue, while other requests would go to the other queue. Urgent requests would be served from 1 entry in the pool, while the regular ones would be served only according to the reserve. Despite not yet having this, it works remarkably well. This mechanism is quite efficient, we don't perform too many wake up calls anymore. For 1 million sessions elapsed during massive memory contention, we observe about 4.5M calls to process_session() compared to 4.0M without memory constraints. Previously we used to observe up to 16M calls, which rougly means 12M failures. During a test run under high memory constraints (limit enforced to 27 MB instead of the 58 MB normally needed), performance used to drop by 53% prior to this patch. Now with this patch instead it *increases* by about 1.5%. The best effect of this change is that by limiting the memory usage to about 2/3 to 3/4 of what is needed by default, it's possible to increase performance by up to about 18% mainly due to the fact that pools are reused more often and remain hot in the CPU cache (observed on regular HTTP traffic with 20k objects, buffers.limit = maxconn/10, buffers.reserve = limit/2). Below is an example of scenario which used to cause a deadlock previously : - connection is received - two buffers are allocated in process_session() then released - one is allocated when receiving an HTTP request - the second buffer is allocated then released in process_session() for request parsing then connection establishment. - poll() says we can send, so the request buffer is sent and released - process session gets notified that the connection is now established and allocates two buffers then releases them - all other sessions do the same till one cannot get the request buffer without hitting the margin - and now the server responds. stream_interface allocates the response buffer and manages to get it since it's higher priority being for a response. - but process_session() cannot allocate the request buffer anymore => We could end up with all buffers used by responses so that none may be allocated for a request in process_session(). When the applet processing leaves the session context, the test will have to be changed so that we always allocate a response buffer regardless of the left side (eg: H2->H1 gateway). A final improvement would consists in being able to only retry the failed I/O operation without waking up a task, but to date all experiments to achieve this have proven not to be reliable enough.
2014-11-26 19:11:56 -05:00
.reserved_bufs = RESERVED_BUFS,
#ifdef USE_OPENSSL
.sslcachesize = SSLCACHESIZE,
.ssl_default_dh_param = SSL_DEFAULT_DH_PARAM,
#ifdef DEFAULT_SSL_MAX_RECORD
.ssl_max_record = DEFAULT_SSL_MAX_RECORD,
#endif
#endif
#ifdef USE_ZLIB
.zlibmemlevel = 8,
.zlibwindowsize = MAX_WBITS,
#endif
.comp_maxlevel = 1,
#ifdef DEFAULT_IDLE_TIMER
.idle_timer = DEFAULT_IDLE_TIMER,
#else
.idle_timer = 1000, /* 1 second */
#endif
},
#ifdef USE_OPENSSL
#ifdef DEFAULT_MAXSSLCONN
.maxsslconn = DEFAULT_MAXSSLCONN,
#endif
#endif
/* others NULL OK */
};
/*********************************************************************/
int stopping; /* non zero means stopping in progress */
int jobs = 0; /* number of active jobs (conns, listeners, active tasks, ...) */
/* 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 *oldpids = NULL;
static int oldpids_sig; /* use USR1 or TERM */
/* this is used to drain data, and as a temporary buffer for sprintf()... */
struct chunk trash = { };
/* this buffer is always the same size as standard buffers and is used for
* swapping data inside a buffer.
*/
char *swap_buffer = NULL;
int nb_oldpids = 0;
const int zero = 0;
const int one = 1;
const struct linger nolinger = { .l_onoff = 1, .l_linger = 0 };
char hostname[MAX_HOSTNAME_LEN];
char localpeer[MAX_HOSTNAME_LEN];
/* used from everywhere just to drain results we don't want to read and which
* recent versions of gcc increasingly and annoyingly complain about.
*/
int shut_your_big_mouth_gcc_int = 0;
/* list of the temporarily limited listeners because of lack of resource */
struct list global_listener_queue = LIST_HEAD_INIT(global_listener_queue);
struct task *global_listener_queue_task;
static struct task *manage_global_listener_queue(struct task *t);
/* bitfield of a few warnings to emit just once (WARN_*) */
unsigned int warned = 0;
/*********************************************************************/
/* general purpose functions ***************************************/
/*********************************************************************/
void display_version()
{
printf("HA-Proxy version " HAPROXY_VERSION " " HAPROXY_DATE"\n");
[RELEASE] Released version 1.5-dev22 Released version 1.5-dev22 with the following main changes : - MEDIUM: tcp-check new feature: connect - MEDIUM: ssl: Set verify 'required' as global default for servers side. - MINOR: ssl: handshake optim for long certificate chains. - BUG/MINOR: pattern: pattern comparison executed twice - BUG/MEDIUM: map: segmentation fault with the stats's socket command "set map ..." - BUG/MEDIUM: pattern: Segfault in binary parser - MINOR: pattern: move functions for grouping pat_match_* and pat_parse_* and add documentation. - MINOR: standard: The parse_binary() returns the length consumed and his documentation is updated - BUG/MINOR: payload: the patterns of the acl "req.ssl_ver" are no parsed with the good function. - BUG/MEDIUM: pattern: "pat_parse_dotted_ver()" set bad expect_type. - BUG/MINOR: sample: The c_str2int converter does not fail if the entry is not an integer - BUG/MEDIUM: http/auth: Sometimes the authentication credentials can be mix between two requests - MINOR: doc: Bad cli function name. - MINOR: http: smp_fetch_capture_header_* fetch captured headers - BUILD: last release inadvertently prepended a "+" in front of the date - BUG/MEDIUM: stream-int: fix the keep-alive idle connection handler - BUG/MEDIUM: backend: do not re-initialize the connection's context upon reuse - BUG: Revert "OPTIM/MEDIUM: epoll: fuse active events into polled ones during polling changes" - BUG/MINOR: checks: successful check completion must not re-enable MAINT servers - MINOR: http: try to stick to same server after status 401/407 - BUG/MINOR: http: always disable compression on HTTP/1.0 - OPTIM: poll: restore polling after a poll/stop/want sequence - OPTIM: http: don't stop polling for read on the client side after a request - BUG/MEDIUM: checks: unchecked servers could not be enabled anymore - BUG/MEDIUM: stats: the web interface must check the tracked servers before enabling - BUG/MINOR: channel: CHN_INFINITE_FORWARD must be unsigned - BUG/MINOR: stream-int: do not clear the owner upon unregister - MEDIUM: stats: add support for HTTP keep-alive on the stats page - BUG/MEDIUM: stats: fix HTTP/1.0 breakage introduced in previous patch - Revert "MEDIUM: stats: add support for HTTP keep-alive on the stats page" - MAJOR: channel: add a new flag CF_WAKE_WRITE to notify the task of writes - OPTIM: session: set the READ_DONTWAIT flag when connecting - BUG/MINOR: http: don't clear the SI_FL_DONT_WAKE flag between requests - MINOR: session: factor out the connect time measurement - MEDIUM: session: prepare to support earlier transitions to the established state - MEDIUM: stream-int: make si_connect() return an established state when possible - MINOR: checks: use an inline function for health_adjust() - OPTIM: session: put unlikely() around the freewheeling code - MEDIUM: config: report a warning when multiple servers have the same name - BUG: Revert "OPTIM: poll: restore polling after a poll/stop/want sequence" - BUILD/MINOR: listener: remove a glibc warning on accept4() - BUG/MAJOR: connection: fix mismatch between rcv_buf's API and usage - BUILD: listener: fix recent accept4() again - BUG/MAJOR: ssl: fix breakage caused by recent fix abf08d9 - BUG/MEDIUM: polling: ensure we update FD status when there's no more activity - MEDIUM: listener: fix polling management in the accept loop - MINOR: protocol: improve the proto->drain() API - MINOR: connection: add a new conn_drain() function - MEDIUM: tcp: report in tcp_drain() that lingering is already disabled on close - MEDIUM: connection: update callers of ctrl->drain() to use conn_drain() - MINOR: connection: add more error codes to report connection errors - MEDIUM: tcp: report connection error at the connection level - MEDIUM: checks: make use of chk_report_conn_err() for connection errors - BUG/MEDIUM: unique_id: HTTP request counter is not stable - DOC: fix misleading information about SIGQUIT - BUG/MAJOR: fix freezes during compression - BUG/MEDIUM: stream-interface: don't wake the task up before end of transfer - BUILD: fix VERDATE exclusion regex - CLEANUP: polling: rename "spec_e" to "state" - DOC: add a diagram showing polling state transitions - REORG: polling: rename "spec_e" to "state" and "spec_p" to "cache" - REORG: polling: rename "fd_spec" to "fd_cache" - REORG: polling: rename the cache allocation functions - REORG: polling: rename "fd_process_spec_events()" to "fd_process_cached_events()" - MAJOR: polling: rework the whole polling system - MAJOR: connection: remove the CO_FL_WAIT_{RD,WR} flags - MEDIUM: connection: remove conn_{data,sock}_poll_{recv,send} - MEDIUM: connection: add check for readiness in I/O handlers - MEDIUM: stream-interface: the polling flags must always be updated in chk_snd_conn - MINOR: stream-interface: no need to call fd_stop_both() on error - MEDIUM: connection: no need to recheck FD state - CLEANUP: connection: use conn_ctrl_ready() instead of checking the flag - CLEANUP: connection: use conn_xprt_ready() instead of checking the flag - CLEANUP: connection: fix comments in connection.h to reflect new behaviour. - OPTIM: raw-sock: don't speculate after a short read if polling is enabled - MEDIUM: polling: centralize polled events processing - MINOR: polling: create function fd_compute_new_polled_status() - MINOR: cli: add more information to the "show info" output - MEDIUM: listener: add support for limiting the session rate in addition to the connection rate - MEDIUM: listener: apply a limit on the session rate submitted to SSL - REORG: stats: move the stats socket states to dumpstats.c - MINOR: cli: add the new "show pools" command - BUG/MEDIUM: counters: flush content counters after each request - BUG/MEDIUM: counters: fix stick-table entry leak when using track-sc2 in connection - MINOR: tools: add very basic support for composite pointers - MEDIUM: counters: stop relying on session flags at all - BUG/MINOR: cli: fix missing break in command line parser - BUG/MINOR: config: correctly report when log-format headers require HTTP mode - MAJOR: http: update connection mode configuration - MEDIUM: http: make keep-alive + httpclose be passive mode - MAJOR: http: switch to keep-alive mode by default - BUG/MEDIUM: http: fix regression caused by recent switch to keep-alive by default - BUG/MEDIUM: listener: improve detection of non-working accept4() - BUILD: listener: add fcntl.h and unistd.h - BUG/MINOR: raw_sock: correctly set the MSG_MORE flag
2014-02-02 18:41:29 -05:00
printf("Copyright 2000-2014 Willy Tarreau <w@1wt.eu>\n\n");
}
void display_build_opts()
{
printf("Build options :"
#ifdef BUILD_TARGET
"\n TARGET = " BUILD_TARGET
#endif
#ifdef BUILD_CPU
"\n CPU = " BUILD_CPU
#endif
#ifdef BUILD_CC
"\n CC = " BUILD_CC
#endif
#ifdef BUILD_CFLAGS
"\n CFLAGS = " BUILD_CFLAGS
#endif
#ifdef BUILD_OPTIONS
"\n OPTIONS = " BUILD_OPTIONS
#endif
"\n\nDefault settings :"
"\n maxconn = %d, bufsize = %d, maxrewrite = %d, maxpollevents = %d"
"\n\n",
DEFAULT_MAXCONN, BUFSIZE, MAXREWRITE, MAX_POLL_EVENTS);
printf("Encrypted password support via crypt(3): "
#ifdef CONFIG_HAP_CRYPT
"yes"
#else
"no"
#endif
"\n");
#ifdef USE_ZLIB
printf("Built with zlib version : " ZLIB_VERSION "\n");
#else /* USE_ZLIB */
printf("Built without zlib support (USE_ZLIB not set)\n");
#endif
printf("Compression algorithms supported :");
{
int i;
for (i = 0; comp_algos[i].name; i++) {
printf("%s %s", (i == 0 ? "" : ","), comp_algos[i].name);
}
if (i == 0) {
printf("none");
}
}
printf("\n");
#ifdef USE_OPENSSL
printf("Built with OpenSSL version : "
#ifdef OPENSSL_IS_BORINGSSL
"BoringSSL\n");
#else /* OPENSSL_IS_BORINGSSL */
OPENSSL_VERSION_TEXT "\n");
printf("Running on OpenSSL version : %s%s\n",
SSLeay_version(SSLEAY_VERSION),
((OPENSSL_VERSION_NUMBER ^ SSLeay()) >> 8) ? " (VERSIONS DIFFER!)" : "");
#endif
printf("OpenSSL library supports TLS extensions : "
#if OPENSSL_VERSION_NUMBER < 0x00907000L
"no (library version too old)"
#elif defined(OPENSSL_NO_TLSEXT)
"no (disabled via OPENSSL_NO_TLSEXT)"
#else
"yes"
#endif
"\n");
printf("OpenSSL library supports SNI : "
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
"yes"
#else
#ifdef OPENSSL_NO_TLSEXT
"no (because of OPENSSL_NO_TLSEXT)"
#else
"no (version might be too old, 0.9.8f min needed)"
#endif
#endif
"\n");
printf("OpenSSL library supports prefer-server-ciphers : "
#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
"yes"
#else
"no (0.9.7 or later needed)"
#endif
"\n");
#else /* USE_OPENSSL */
printf("Built without OpenSSL support (USE_OPENSSL not set)\n");
#endif
#ifdef USE_PCRE
printf("Built with PCRE version : %s", pcre_version());
printf("\nPCRE library supports JIT : ");
#ifdef USE_PCRE_JIT
{
int r;
pcre_config(PCRE_CONFIG_JIT, &r);
if (r)
printf("yes");
else
printf("no (libpcre build without JIT?)");
}
#else
printf("no (USE_PCRE_JIT not set)");
#endif
printf("\n");
#else
printf("Built without PCRE support (using libc's regex instead)\n");
#endif
#if defined(CONFIG_HAP_TRANSPARENT) || defined(CONFIG_HAP_CTTPROXY)
printf("Built with transparent proxy support using:"
#if defined(CONFIG_HAP_CTTPROXY)
" CTTPROXY"
#endif
#if defined(IP_TRANSPARENT)
" IP_TRANSPARENT"
#endif
#if defined(IPV6_TRANSPARENT)
" IPV6_TRANSPARENT"
#endif
#if defined(IP_FREEBIND)
" IP_FREEBIND"
#endif
#if defined(IP_BINDANY)
" IP_BINDANY"
#endif
#if defined(IPV6_BINDANY)
" IPV6_BINDANY"
#endif
#if defined(SO_BINDANY)
" SO_BINDANY"
#endif
"\n");
#endif
MAJOR: namespace: add Linux network namespace support This patch makes it possible to create binds and servers in separate namespaces. This can be used to proxy between multiple completely independent virtual networks (with possibly overlapping IP addresses) and a non-namespace-aware proxy implementation that supports the proxy protocol (v2). The setup is something like this: net1 on VLAN 1 (namespace 1) -\ net2 on VLAN 2 (namespace 2) -- haproxy ==== proxy (namespace 0) net3 on VLAN 3 (namespace 3) -/ The proxy is configured to make server connections through haproxy and sending the expected source/target addresses to haproxy using the proxy protocol. The network namespace setup on the haproxy node is something like this: = 8< = $ cat setup.sh ip netns add 1 ip link add link eth1 type vlan id 1 ip link set eth1.1 netns 1 ip netns exec 1 ip addr add 192.168.91.2/24 dev eth1.1 ip netns exec 1 ip link set eth1.$id up ... = 8< = = 8< = $ cat haproxy.cfg frontend clients bind 127.0.0.1:50022 namespace 1 transparent default_backend scb backend server mode tcp server server1 192.168.122.4:2222 namespace 2 send-proxy-v2 = 8< = A bind line creates the listener in the specified namespace, and connections originating from that listener also have their network namespace set to that of the listener. A server line either forces the connection to be made in a specified namespace or may use the namespace from the client-side connection if that was set. For more documentation please read the documentation included in the patch itself. Signed-off-by: KOVACS Tamas <ktamas@balabit.com> Signed-off-by: Sarkozi Laszlo <laszlo.sarkozi@balabit.com> Signed-off-by: KOVACS Krisztian <hidden@balabit.com>
2014-11-17 09:11:45 -05:00
#if defined(CONFIG_HAP_NS)
printf("Built with network namespace support\n");
#endif
putchar('\n');
list_pollers(stdout);
putchar('\n');
}
/*
* This function prints the command line usage and exits
*/
void usage(char *name)
{
display_version();
fprintf(stderr,
"Usage : %s [-f <cfgfile>]* [ -vdV"
"D ] [ -n <maxconn> ] [ -N <maxpconn> ]\n"
" [ -p <pidfile> ] [ -m <max megs> ] [ -C <dir> ]\n"
" -v displays version ; -vv shows known build options.\n"
" -d enters debug mode ; -db only disables background mode.\n"
" -dM[<byte>] poisons memory with <byte> (defaults to 0x50)\n"
" -V enters verbose mode (disables quiet mode)\n"
" -D goes daemon ; -C changes to <dir> before loading files.\n"
" -q quiet mode : don't display messages\n"
" -c check mode : only check config files and exit\n"
" -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"
" -L set local peer name (default to hostname)\n"
" -p writes pids of all children to this file\n"
#if defined(ENABLE_EPOLL)
" -de disables epoll() usage even when available\n"
#endif
#if defined(ENABLE_KQUEUE)
" -dk disables kqueue() usage even when available\n"
#endif
#if defined(ENABLE_POLL)
" -dp disables poll() usage even when available\n"
#endif
#if defined(CONFIG_HAP_LINUX_SPLICE)
" -dS disables splice usage (broken on old kernels)\n"
#endif
#if defined(USE_GETADDRINFO)
" -dG disables getaddrinfo() usage\n"
#endif
" -dV disables SSL verify on servers side\n"
" -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. Note that soft_stop() broadcasts
* a signal zero to all subscribers. This means that it's as easy as
* subscribing to signal 0 to get informed about an imminent shutdown.
*/
void sig_soft_stop(struct sig_handler *sh)
{
soft_stop();
signal_unregister_handler(sh);
pool_gc2();
}
/*
* upon SIGTTOU, we pause everything
*/
void sig_pause(struct sig_handler *sh)
{
pause_proxies();
pool_gc2();
}
/*
* upon SIGTTIN, let's have a soft stop.
*/
void sig_listen(struct sig_handler *sh)
{
resume_proxies();
}
/*
* this function dumps every server's state when the process receives SIGHUP.
*/
void sig_dump_state(struct sig_handler *sh)
{
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) {
chunk_printf(&trash,
"SIGHUP: Server %s/%s is %s. Conn: %d act, %d pend, %lld tot.",
p->id, s->id,
(s->state != SRV_ST_STOPPED) ? "UP" : "DOWN",
s->cur_sess, s->nbpend, s->counters.cum_sess);
Warning("%s\n", trash.str);
send_log(p, LOG_NOTICE, "%s\n", trash.str);
s = s->next;
}
/* FIXME: those info are a bit outdated. We should be able to distinguish between FE and BE. */
if (!p->srv) {
chunk_printf(&trash,
"SIGHUP: Proxy %s has no servers. Conn: act(FE+BE): %d+%d, %d pend (%d unass), tot(FE+BE): %lld+%lld.",
p->id,
p->feconn, p->beconn, p->totpend, p->nbpend, p->fe_counters.cum_conn, p->be_counters.cum_conn);
} else if (p->srv_act == 0) {
chunk_printf(&trash,
"SIGHUP: Proxy %s %s ! Conn: act(FE+BE): %d+%d, %d pend (%d unass), tot(FE+BE): %lld+%lld.",
p->id,
(p->srv_bck) ? "is running on backup servers" : "has no server available",
p->feconn, p->beconn, p->totpend, p->nbpend, p->fe_counters.cum_conn, p->be_counters.cum_conn);
} else {
chunk_printf(&trash,
"SIGHUP: Proxy %s has %d active servers and %d backup servers available."
" Conn: act(FE+BE): %d+%d, %d pend (%d unass), tot(FE+BE): %lld+%lld.",
p->id, p->srv_act, p->srv_bck,
p->feconn, p->beconn, p->totpend, p->nbpend, p->fe_counters.cum_conn, p->be_counters.cum_conn);
}
Warning("%s\n", trash.str);
send_log(p, LOG_NOTICE, "%s\n", trash.str);
p = p->next;
}
}
void dump(struct sig_handler *sh)
{
/* dump memory usage then free everything possible */
dump_pools();
pool_gc2();
}
/*
* 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 arg_mode = 0; /* MODE_DEBUG, ... */
char *tmp;
char *cfg_pidfile = NULL;
int err_code = 0;
struct wordlist *wl;
char *progname;
char *change_dir = NULL;
struct tm curtime;
chunk_init(&trash, malloc(global.tune.bufsize), global.tune.bufsize);
alloc_trash_buffers(global.tune.bufsize);
/* 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);
memset(localpeer, 0, sizeof(localpeer));
memcpy(localpeer, hostname, (sizeof(hostname) > sizeof(localpeer) ? sizeof(localpeer) : sizeof(hostname)) - 1);
/*
* Initialize the previously static variables.
*/
totalconn = actconn = maxfd = listeners = stopping = 0;
#ifdef HAPROXY_MEMMAX
global.rlimit_memmax = HAPROXY_MEMMAX;
#endif
tv_update_date(-1,-1);
start_date = now;
srandom(now_ms - getpid());
/* Get the numeric timezone. */
get_localtime(start_date.tv_sec, &curtime);
strftime(localtimezone, 6, "%z", &curtime);
signal_init();
if (init_acl() != 0)
exit(1);
init_task();
init_session();
init_connection();
/* warning, we init buffers later */
init_pendconn();
init_proto_http();
#ifdef USE_LUA
/* Initialise lua. */
hlua_init();
#endif
global.tune.options |= GTUNE_USE_SELECT; /* select() is always available */
#if defined(ENABLE_POLL)
global.tune.options |= GTUNE_USE_POLL;
#endif
#if defined(ENABLE_EPOLL)
global.tune.options |= GTUNE_USE_EPOLL;
#endif
#if defined(ENABLE_KQUEUE)
global.tune.options |= GTUNE_USE_KQUEUE;
#endif
#if defined(CONFIG_HAP_LINUX_SPLICE)
global.tune.options |= GTUNE_USE_SPLICE;
#endif
#if defined(USE_GETADDRINFO)
global.tune.options |= GTUNE_USE_GAI;
#endif
pid = getpid();
progname = *argv;
while ((tmp = strchr(progname, '/')) != NULL)
progname = tmp + 1;
/* the process name is used for the logs only */
global.log_tag = strdup(progname);
argc--; argv++;
while (argc > 0) {
char *flag;
if (**argv == '-') {
flag = *argv+1;
/* 1 arg */
if (*flag == 'v') {
display_version();
if (flag[1] == 'v') /* -vv */
display_build_opts();
exit(0);
}
#if defined(ENABLE_EPOLL)
else if (*flag == 'd' && flag[1] == 'e')
global.tune.options &= ~GTUNE_USE_EPOLL;
#endif
#if defined(ENABLE_POLL)
else if (*flag == 'd' && flag[1] == 'p')
global.tune.options &= ~GTUNE_USE_POLL;
#endif
#if defined(ENABLE_KQUEUE)
else if (*flag == 'd' && flag[1] == 'k')
global.tune.options &= ~GTUNE_USE_KQUEUE;
#endif
#if defined(CONFIG_HAP_LINUX_SPLICE)
else if (*flag == 'd' && flag[1] == 'S')
global.tune.options &= ~GTUNE_USE_SPLICE;
#endif
#if defined(USE_GETADDRINFO)
else if (*flag == 'd' && flag[1] == 'G')
global.tune.options &= ~GTUNE_USE_GAI;
#endif
else if (*flag == 'd' && flag[1] == 'V')
global.ssl_server_verify = SSL_SERVER_VERIFY_NONE;
else if (*flag == 'V')
arg_mode |= MODE_VERBOSE;
else if (*flag == 'd' && flag[1] == 'b')
arg_mode |= MODE_FOREGROUND;
else if (*flag == 'd' && flag[1] == 'M')
mem_poison_byte = flag[2] ? strtol(flag + 2, NULL, 0) : 'P';
else if (*flag == 'd')
arg_mode |= MODE_DEBUG;
else if (*flag == 'c')
arg_mode |= MODE_CHECK;
else if (*flag == 'D') {
arg_mode |= MODE_DAEMON;
if (flag[1] == 's') /* -Ds */
arg_mode |= MODE_SYSTEMD;
}
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(progname);
argc--; argv++;
nb_oldpids++;
}
}
}
else { /* >=2 args */
argv++; argc--;
if (argc == 0)
usage(progname);
switch (*flag) {
case 'C' : change_dir = *argv; break;
case 'n' : cfg_maxconn = atol(*argv); break;
case 'm' : global.rlimit_memmax = atol(*argv); break;
case 'N' : cfg_maxpconn = atol(*argv); break;
case 'L' : strncpy(localpeer, *argv, sizeof(localpeer) - 1); break;
case 'f' :
wl = (struct wordlist *)calloc(1, sizeof(*wl));
if (!wl) {
Alert("Cannot load configuration file %s : out of memory.\n", *argv);
exit(1);
}
wl->s = *argv;
LIST_ADDQ(&cfg_cfgfiles, &wl->list);
break;
case 'p' : cfg_pidfile = *argv; break;
default: usage(progname);
}
}
}
else
usage(progname);
argv++; argc--;
}
global.mode = MODE_STARTING | /* during startup, we want most of the alerts */
(arg_mode & (MODE_DAEMON | MODE_SYSTEMD | MODE_FOREGROUND | MODE_VERBOSE
| MODE_QUIET | MODE_CHECK | MODE_DEBUG));
if (LIST_ISEMPTY(&cfg_cfgfiles))
usage(progname);
if (change_dir && chdir(change_dir) < 0) {
Alert("Could not change to directory %s : %s\n", change_dir, strerror(errno));
exit(1);
}
have_appsession = 0;
global.maxsock = 10; /* reserve 10 fds ; will be incremented by socket eaters */
init_default_instance();
list_for_each_entry(wl, &cfg_cfgfiles, list) {
int ret;
ret = readcfgfile(wl->s);
if (ret == -1) {
Alert("Could not open configuration file %s : %s\n",
wl->s, strerror(errno));
exit(1);
}
if (ret & (ERR_ABORT|ERR_FATAL))
Alert("Error(s) found in configuration file : %s\n", wl->s);
err_code |= ret;
if (err_code & ERR_ABORT)
exit(1);
}
pattern_finalize_config();
err_code |= check_config_validity();
if (err_code & (ERR_ABORT|ERR_FATAL)) {
Alert("Fatal errors found in configuration.\n");
exit(1);
}
MAJOR: namespace: add Linux network namespace support This patch makes it possible to create binds and servers in separate namespaces. This can be used to proxy between multiple completely independent virtual networks (with possibly overlapping IP addresses) and a non-namespace-aware proxy implementation that supports the proxy protocol (v2). The setup is something like this: net1 on VLAN 1 (namespace 1) -\ net2 on VLAN 2 (namespace 2) -- haproxy ==== proxy (namespace 0) net3 on VLAN 3 (namespace 3) -/ The proxy is configured to make server connections through haproxy and sending the expected source/target addresses to haproxy using the proxy protocol. The network namespace setup on the haproxy node is something like this: = 8< = $ cat setup.sh ip netns add 1 ip link add link eth1 type vlan id 1 ip link set eth1.1 netns 1 ip netns exec 1 ip addr add 192.168.91.2/24 dev eth1.1 ip netns exec 1 ip link set eth1.$id up ... = 8< = = 8< = $ cat haproxy.cfg frontend clients bind 127.0.0.1:50022 namespace 1 transparent default_backend scb backend server mode tcp server server1 192.168.122.4:2222 namespace 2 send-proxy-v2 = 8< = A bind line creates the listener in the specified namespace, and connections originating from that listener also have their network namespace set to that of the listener. A server line either forces the connection to be made in a specified namespace or may use the namespace from the client-side connection if that was set. For more documentation please read the documentation included in the patch itself. Signed-off-by: KOVACS Tamas <ktamas@balabit.com> Signed-off-by: Sarkozi Laszlo <laszlo.sarkozi@balabit.com> Signed-off-by: KOVACS Krisztian <hidden@balabit.com>
2014-11-17 09:11:45 -05:00
#ifdef CONFIG_HAP_NS
err_code |= netns_init();
if (err_code & (ERR_ABORT|ERR_FATAL)) {
Alert("Failed to initialize namespace support.\n");
exit(1);
}
#endif
if (global.mode & MODE_CHECK) {
struct peers *pr;
struct proxy *px;
for (pr = peers; pr; pr = pr->next)
if (pr->peers_fe)
break;
for (px = proxy; px; px = px->next)
if (px->state == PR_STNEW && !LIST_ISEMPTY(&px->conf.listeners))
break;
if (pr || px) {
/* At least one peer or one listener has been found */
qfprintf(stdout, "Configuration file is valid\n");
exit(0);
}
qfprintf(stdout, "Configuration file has no error but will not start (no listener) => exit(2).\n");
exit(2);
}
global_listener_queue_task = task_new();
if (!global_listener_queue_task) {
Alert("Out of memory when initializing global task\n");
exit(1);
}
/* very simple initialization, users will queue the task if needed */
global_listener_queue_task->context = NULL; /* not even a context! */
global_listener_queue_task->process = manage_global_listener_queue;
global_listener_queue_task->expire = TICK_ETERNITY;
CLEANUP: channel: use "channel" instead of "buffer" in function names This is a massive rename of most functions which should make use of the word "channel" instead of the word "buffer" in their names. In concerns the following ones (new names) : unsigned long long channel_forward(struct channel *buf, unsigned long long bytes); static inline void channel_init(struct channel *buf) static inline int channel_input_closed(struct channel *buf) static inline int channel_output_closed(struct channel *buf) static inline void channel_check_timeouts(struct channel *b) static inline void channel_erase(struct channel *buf) static inline void channel_shutr_now(struct channel *buf) static inline void channel_shutw_now(struct channel *buf) static inline void channel_abort(struct channel *buf) static inline void channel_stop_hijacker(struct channel *buf) static inline void channel_auto_connect(struct channel *buf) static inline void channel_dont_connect(struct channel *buf) static inline void channel_auto_close(struct channel *buf) static inline void channel_dont_close(struct channel *buf) static inline void channel_auto_read(struct channel *buf) static inline void channel_dont_read(struct channel *buf) unsigned long long channel_forward(struct channel *buf, unsigned long long bytes) Some functions provided by channel.[ch] have kept their "buffer" name because they are really designed to act on the buffer according to some information gathered from the channel. They have been moved together to the same place in the file for better readability but they were not changed at all. The "buffer" memory pool was also renamed "channel".
2012-08-27 18:06:31 -04:00
/* now we know the buffer size, we can initialize the channels and buffers */
init_channel();
init_buffer();
if (have_appsession)
appsession_init();
if (start_checks() < 0)
exit(1);
if (cfg_maxconn > 0)
global.maxconn = cfg_maxconn;
if (cfg_pidfile) {
free(global.pidfile);
global.pidfile = strdup(cfg_pidfile);
}
/* Now we want to compute the maxconn and possibly maxsslconn values.
* It's a bit tricky. If memmax is not set, maxconn defaults to
* DEFAULT_MAXCONN and maxsslconn defaults to DEFAULT_MAXSSLCONN.
*
* If memmax is set, then it depends on which values are set. If
* maxsslconn is set, we use memmax to determine how many cleartext
* connections may be added, and set maxconn to the sum of the two.
* If maxconn is set and not maxsslconn, maxsslconn is computed from
* the remaining amount of memory between memmax and the cleartext
* connections. If neither are set, then it is considered that all
* connections are SSL-capable, and maxconn is computed based on this,
* then maxsslconn accordingly. We need to know if SSL is used on the
* frontends, backends, or both, because when it's used on both sides,
* we need twice the value for maxsslconn, but we only count the
* handshake once since it is not performed on the two sides at the
* same time (frontend-side is terminated before backend-side begins).
* The SSL stack is supposed to have filled ssl_session_cost and
* ssl_handshake_cost during its initialization. In any case, if
* SYSTEM_MAXCONN is set, we still enforce it as an upper limit for
* maxconn in order to protect the system.
*/
if (!global.rlimit_memmax) {
if (global.maxconn == 0) {
global.maxconn = DEFAULT_MAXCONN;
if (global.mode & (MODE_VERBOSE|MODE_DEBUG))
fprintf(stderr, "Note: setting global.maxconn to %d.\n", global.maxconn);
}
}
#ifdef USE_OPENSSL
else if (!global.maxconn && !global.maxsslconn &&
(global.ssl_used_frontend || global.ssl_used_backend)) {
/* memmax is set, compute everything automatically. Here we want
* to ensure that all SSL connections will be served. We take
* care of the number of sides where SSL is used, and consider
* the worst case : SSL used on both sides and doing a handshake
* simultaneously. Note that we can't have more than maxconn
* handshakes at a time by definition, so for the worst case of
* two SSL conns per connection, we count a single handshake.
*/
int sides = !!global.ssl_used_frontend + !!global.ssl_used_backend;
int64_t mem = global.rlimit_memmax * 1048576ULL;
mem -= global.tune.sslcachesize * 200; // about 200 bytes per SSL cache entry
mem -= global.maxzlibmem;
mem = mem * MEM_USABLE_RATIO;
global.maxconn = mem /
((SESSION_MAX_COST + 2 * global.tune.bufsize) + // session + 2 buffers per session
sides * global.ssl_session_max_cost + // SSL buffers, one per side
global.ssl_handshake_max_cost); // 1 handshake per connection max
global.maxconn = round_2dig(global.maxconn);
#ifdef SYSTEM_MAXCONN
if (global.maxconn > DEFAULT_MAXCONN)
global.maxconn = DEFAULT_MAXCONN;
#endif /* SYSTEM_MAXCONN */
global.maxsslconn = sides * global.maxconn;
if (global.mode & (MODE_VERBOSE|MODE_DEBUG))
fprintf(stderr, "Note: setting global.maxconn to %d and global.maxsslconn to %d.\n",
global.maxconn, global.maxsslconn);
}
else if (!global.maxsslconn &&
(global.ssl_used_frontend || global.ssl_used_backend)) {
/* memmax and maxconn are known, compute maxsslconn automatically.
* maxsslconn being forced, we don't know how many of it will be
* on each side if both sides are being used. The worst case is
* when all connections use only one SSL instance because
* handshakes may be on two sides at the same time.
*/
int sides = !!global.ssl_used_frontend + !!global.ssl_used_backend;
int64_t mem = global.rlimit_memmax * 1048576ULL;
int64_t sslmem;
mem -= global.tune.sslcachesize * 200; // about 200 bytes per SSL cache entry
mem -= global.maxzlibmem;
mem = mem * MEM_USABLE_RATIO;
sslmem = mem - global.maxconn * (int64_t)(SESSION_MAX_COST + 2 * global.tune.bufsize);
global.maxsslconn = sslmem / (global.ssl_session_max_cost + global.ssl_handshake_max_cost);
global.maxsslconn = round_2dig(global.maxsslconn);
if (sslmem <= 0 || global.maxsslconn < sides) {
Alert("Cannot compute the automatic maxsslconn because global.maxconn is already too "
"high for the global.memmax value (%d MB). The absolute maximum possible value "
"without SSL is %d, but %d was found and SSL is in use.\n",
global.rlimit_memmax,
(int)(mem / (SESSION_MAX_COST + 2 * global.tune.bufsize)),
global.maxconn);
exit(1);
}
if (global.maxsslconn > sides * global.maxconn)
global.maxsslconn = sides * global.maxconn;
if (global.mode & (MODE_VERBOSE|MODE_DEBUG))
fprintf(stderr, "Note: setting global.maxsslconn to %d\n", global.maxsslconn);
}
#endif
else if (!global.maxconn) {
/* memmax and maxsslconn are known/unused, compute maxconn automatically */
int sides = !!global.ssl_used_frontend + !!global.ssl_used_backend;
int64_t mem = global.rlimit_memmax * 1048576ULL;
int64_t clearmem;
if (global.ssl_used_frontend || global.ssl_used_backend)
mem -= global.tune.sslcachesize * 200; // about 200 bytes per SSL cache entry
mem -= global.maxzlibmem;
mem = mem * MEM_USABLE_RATIO;
clearmem = mem;
if (sides)
clearmem -= (global.ssl_session_max_cost + global.ssl_handshake_max_cost) * (int64_t)global.maxsslconn;
global.maxconn = clearmem / (SESSION_MAX_COST + 2 * global.tune.bufsize);
global.maxconn = round_2dig(global.maxconn);
#ifdef SYSTEM_MAXCONN
if (global.maxconn > DEFAULT_MAXCONN)
global.maxconn = DEFAULT_MAXCONN;
#endif /* SYSTEM_MAXCONN */
if (clearmem <= 0 || !global.maxconn) {
Alert("Cannot compute the automatic maxconn because global.maxsslconn is already too "
"high for the global.memmax value (%d MB). The absolute maximum possible value "
"is %d, but %d was found.\n",
global.rlimit_memmax,
(int)(mem / (global.ssl_session_max_cost + global.ssl_handshake_max_cost)),
global.maxsslconn);
exit(1);
}
if (global.mode & (MODE_VERBOSE|MODE_DEBUG)) {
if (sides && global.maxsslconn > sides * global.maxconn) {
fprintf(stderr, "Note: global.maxsslconn is forced to %d which causes global.maxconn "
"to be limited to %d. Better reduce global.maxsslconn to get more "
"room for extra connections.\n", global.maxsslconn, global.maxconn);
}
fprintf(stderr, "Note: setting global.maxconn to %d\n", global.maxconn);
}
}
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)
nbbe += cur->fullconn ? cur->fullconn : global.maxconn;
}
}
global.maxpipes = MAX(nbfe, nbbe);
if (global.maxpipes > global.maxconn)
global.maxpipes = global.maxconn;
global.maxpipes /= 4;
}
global.hardmaxconn = global.maxconn; /* keep this max value */
global.maxsock += global.maxconn * 2; /* each connection needs two sockets */
global.maxsock += global.maxpipes * 2; /* each pipe needs two FDs */
if (global.stats_fe)
global.maxsock += global.stats_fe->maxconn;
if (peers) {
/* peers also need to bypass global maxconn */
struct peers *p = peers;
for (p = peers; p; p = p->next)
if (p->peers_fe)
global.maxsock += p->peers_fe->maxconn;
}
if (global.tune.maxpollevents <= 0)
global.tune.maxpollevents = MAX_POLL_EVENTS;
if (global.tune.recv_enough == 0)
global.tune.recv_enough = MIN_RECV_AT_ONCE_ENOUGH;
if (global.tune.maxrewrite >= global.tune.bufsize / 2)
global.tune.maxrewrite = global.tune.bufsize / 2;
if (arg_mode & (MODE_DEBUG | MODE_FOREGROUND)) {
/* command line debug mode inhibits configuration mode */
global.mode &= ~(MODE_DAEMON | MODE_SYSTEMD | MODE_QUIET);
global.mode |= (arg_mode & (MODE_DEBUG | MODE_FOREGROUND));
}
if (arg_mode & (MODE_DAEMON | MODE_SYSTEMD)) {
/* command line daemon mode inhibits foreground and debug modes mode */
global.mode &= ~(MODE_DEBUG | MODE_FOREGROUND);
global.mode |= (arg_mode & (MODE_DAEMON | MODE_SYSTEMD));
}
global.mode |= (arg_mode & (MODE_QUIET | MODE_VERBOSE));
if ((global.mode & MODE_DEBUG) && (global.mode & (MODE_DAEMON | MODE_SYSTEMD | MODE_QUIET))) {
Warning("<debug> mode incompatible with <quiet>, <daemon> and <systemd>. Keeping <debug> only.\n");
global.mode &= ~(MODE_DAEMON | MODE_SYSTEMD | MODE_QUIET);
}
if ((global.nbproc > 1) && !(global.mode & (MODE_DAEMON | MODE_SYSTEMD))) {
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;
swap_buffer = (char *)calloc(1, global.tune.bufsize);
get_http_auth_buff = (char *)calloc(1, global.tune.bufsize);
static_table_key = calloc(1, sizeof(*static_table_key) + global.tune.bufsize);
fdinfo = (struct fdinfo *)calloc(1,
sizeof(struct fdinfo) * (global.maxsock));
fdtab = (struct fdtab *)calloc(1,
sizeof(struct fdtab) * (global.maxsock));
/*
* Note: we could register external pollers here.
* Built-in pollers have been registered before main().
*/
if (!(global.tune.options & GTUNE_USE_KQUEUE))
disable_poller("kqueue");
if (!(global.tune.options & GTUNE_USE_EPOLL))
disable_poller("epoll");
if (!(global.tune.options & GTUNE_USE_POLL))
disable_poller("poll");
if (!(global.tune.options & GTUNE_USE_SELECT))
disable_poller("select");
/* Note: we could disable any poller by name here */
if (global.mode & (MODE_VERBOSE|MODE_DEBUG))
list_pollers(stderr);
if (!init_pollers()) {
Alert("No polling mechanism available.\n"
" It is likely that haproxy was built with TARGET=generic and that FD_SETSIZE\n"
" is too low on this platform to support maxconn and the number of listeners\n"
" and servers. You should rebuild haproxy specifying your system using TARGET=\n"
" in order to support other polling systems (poll, epoll, kqueue) or reduce the\n"
" global maxconn setting to accommodate the system's limitation. For reference,\n"
" FD_SETSIZE=%d on this system, global.maxconn=%d resulting in a maximum of\n"
" %d file descriptors. You should thus reduce global.maxconn by %d. Also,\n"
" check build settings using 'haproxy -vv'.\n\n",
FD_SETSIZE, global.maxconn, global.maxsock, (global.maxsock + 1 - FD_SETSIZE) / 2);
exit(1);
}
if (global.mode & (MODE_VERBOSE|MODE_DEBUG)) {
printf("Using %s() as the polling mechanism.\n", cur_poller.name);
}
if (!global.node)
global.node = strdup(hostname);
}
static void deinit_acl_cond(struct acl_cond *cond)
{
struct acl_term_suite *suite, *suiteb;
struct acl_term *term, *termb;
if (!cond)
return;
list_for_each_entry_safe(suite, suiteb, &cond->suites, list) {
list_for_each_entry_safe(term, termb, &suite->terms, list) {
LIST_DEL(&term->list);
free(term);
}
LIST_DEL(&suite->list);
free(suite);
}
free(cond);
}
static void deinit_tcp_rules(struct list *rules)
{
struct tcp_rule *trule, *truleb;
list_for_each_entry_safe(trule, truleb, rules, list) {
LIST_DEL(&trule->list);
deinit_acl_cond(trule->cond);
free(trule);
}
}
static void deinit_sample_arg(struct arg *p)
{
struct arg *p_back = p;
if (!p)
return;
while (p->type != ARGT_STOP) {
if (p->type == ARGT_STR || p->unresolved) {
free(p->data.str.str);
p->data.str.str = NULL;
p->unresolved = 0;
}
else if (p->type == ARGT_REG) {
if (p->data.reg) {
regex_free(p->data.reg);
free(p->data.reg);
p->data.reg = NULL;
}
}
p++;
}
if (p_back != empty_arg_list)
free(p_back);
}
static void deinit_stick_rules(struct list *rules)
{
struct sticking_rule *rule, *ruleb;
list_for_each_entry_safe(rule, ruleb, rules, list) {
LIST_DEL(&rule->list);
deinit_acl_cond(rule->cond);
if (rule->expr) {
struct sample_conv_expr *conv_expr, *conv_exprb;
list_for_each_entry_safe(conv_expr, conv_exprb, &rule->expr->conv_exprs, list)
deinit_sample_arg(conv_expr->arg_p);
deinit_sample_arg(rule->expr->arg_p);
free(rule->expr);
}
free(rule);
}
}
void deinit(void)
{
struct proxy *p = proxy, *p0;
struct cap_hdr *h,*h_next;
struct server *s,*s_next;
struct listener *l,*l_next;
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;
struct switching_rule *rule, *ruleb;
struct server_rule *srule, *sruleb;
struct redirect_rule *rdr, *rdrb;
struct wordlist *wl, *wlb;
struct cond_wordlist *cwl, *cwlb;
struct uri_auth *uap, *ua = NULL;
struct logsrv *log, *logb;
struct logformat_node *lf, *lfb;
struct bind_conf *bind_conf, *bind_back;
int i;
deinit_signals();
while (p) {
free(p->conf.file);
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);
free(p->rdp_cookie_name);
if (p->conf.logformat_string != default_http_log_format &&
p->conf.logformat_string != default_tcp_log_format &&
p->conf.logformat_string != clf_http_log_format)
free(p->conf.logformat_string);
free(p->conf.lfs_file);
free(p->conf.uniqueid_format_string);
free(p->conf.uif_file);
free(p->lbprm.map.srv);
for (i = 0; i < HTTP_ERR_SIZE; i++)
chunk_destroy(&p->errmsg[i]);
list_for_each_entry_safe(cwl, cwlb, &p->req_add, list) {
LIST_DEL(&cwl->list);
free(cwl->s);
free(cwl);
}
list_for_each_entry_safe(cwl, cwlb, &p->rsp_add, list) {
LIST_DEL(&cwl->list);
free(cwl->s);
free(cwl);
}
list_for_each_entry_safe(cond, condb, &p->block_rules, list) {
LIST_DEL(&cond->list);
prune_acl_cond(cond);
free(cond);
}
list_for_each_entry_safe(cond, condb, &p->mon_fail_cond, list) {
LIST_DEL(&cond->list);
prune_acl_cond(cond);
free(cond);
}
for (exp = p->req_exp; exp != NULL; ) {
if (exp->preg) {
regex_free(exp->preg);
free(exp->preg);
}
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; ) {
if (exp->preg) {
regex_free(exp->preg);
free(exp->preg);
}
if (exp->replace && exp->action != ACT_SETBE)
free((char *)exp->replace);
expb = exp;
exp = exp->next;
free(expb);
}
/* 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;
if (!uap && p->uri_auth) {
/* add it, if it is */
p->uri_auth->next = ua;
ua = p->uri_auth;
}
}
[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);
}
list_for_each_entry_safe(srule, sruleb, &p->server_rules, list) {
LIST_DEL(&srule->list);
prune_acl_cond(srule->cond);
free(srule->cond);
free(srule);
}
list_for_each_entry_safe(rule, ruleb, &p->switching_rules, list) {
LIST_DEL(&rule->list);
if (rule->cond) {
prune_acl_cond(rule->cond);
free(rule->cond);
}
free(rule);
}
list_for_each_entry_safe(rdr, rdrb, &p->redirect_rules, list) {
LIST_DEL(&rdr->list);
if (rdr->cond) {
prune_acl_cond(rdr->cond);
free(rdr->cond);
}
free(rdr->rdr_str);
list_for_each_entry_safe(lf, lfb, &rdr->rdr_fmt, list) {
LIST_DEL(&lf->list);
free(lf);
}
free(rdr);
}
list_for_each_entry_safe(log, logb, &p->logsrvs, list) {
LIST_DEL(&log->list);
free(log);
}
list_for_each_entry_safe(lf, lfb, &p->logformat, list) {
LIST_DEL(&lf->list);
free(lf);
}
deinit_tcp_rules(&p->tcp_req.inspect_rules);
deinit_tcp_rules(&p->tcp_req.l4_rules);
deinit_stick_rules(&p->storersp_rules);
deinit_stick_rules(&p->sticking_rules);
free(p->appsession_name);
h = p->req_cap;
while (h) {
h_next = h->next;
free(h->name);
pool_destroy2(h->pool);
free(h);
h = h_next;
}/* end while(h) */
h = p->rsp_cap;
while (h) {
h_next = h->next;
free(h->name);
pool_destroy2(h->pool);
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
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) {
task_delete(s->check.task);
task_free(s->check.task);
[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
}
MEDIUM: checks: Add supplementary agent checks Allow an auxiliary agent check to be run independently of the regular a regular health check. This is enabled by the agent-check server setting. The agent-port, which specifies the TCP port to use for the agent's connections, is required. The agent-inter, which specifies the interval between agent checks and timeout of agent checks, is optional. If not set the value for regular checks is used. e.g. server web1_1 127.0.0.1:80 check agent-port 10000 If either the health or agent check determines that a server is down then it is marked as being down, otherwise it is marked as being up. An agent health check performed by opening a TCP socket and reading an ASCII string. The string should have one of the following forms: * An ASCII representation of an positive integer percentage. e.g. "75%" Values in this format will set the weight proportional to the initial weight of a server as configured when haproxy starts. * The string "drain". This will cause the weight of a server to be set to 0, and thus it will not accept any new connections other than those that are accepted via persistence. * The string "down", optionally followed by a description string. Mark the server as down and log the description string as the reason. * The string "stopped", optionally followed by a description string. This currently has the same behaviour as "down". * The string "fail", optionally followed by a description string. This currently has the same behaviour as "down". Signed-off-by: Simon Horman <horms@verge.net.au>
2013-11-24 20:46:36 -05:00
if (s->agent.task) {
task_delete(s->agent.task);
task_free(s->agent.task);
}
[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->warmup) {
task_delete(s->warmup);
task_free(s->warmup);
}
free(s->id);
free(s->cookie);
free(s->check.bi);
free(s->check.bo);
MEDIUM: checks: Add supplementary agent checks Allow an auxiliary agent check to be run independently of the regular a regular health check. This is enabled by the agent-check server setting. The agent-port, which specifies the TCP port to use for the agent's connections, is required. The agent-inter, which specifies the interval between agent checks and timeout of agent checks, is optional. If not set the value for regular checks is used. e.g. server web1_1 127.0.0.1:80 check agent-port 10000 If either the health or agent check determines that a server is down then it is marked as being down, otherwise it is marked as being up. An agent health check performed by opening a TCP socket and reading an ASCII string. The string should have one of the following forms: * An ASCII representation of an positive integer percentage. e.g. "75%" Values in this format will set the weight proportional to the initial weight of a server as configured when haproxy starts. * The string "drain". This will cause the weight of a server to be set to 0, and thus it will not accept any new connections other than those that are accepted via persistence. * The string "down", optionally followed by a description string. Mark the server as down and log the description string as the reason. * The string "stopped", optionally followed by a description string. This currently has the same behaviour as "down". * The string "fail", optionally followed by a description string. This currently has the same behaviour as "down". Signed-off-by: Simon Horman <horms@verge.net.au>
2013-11-24 20:46:36 -05:00
free(s->agent.bi);
free(s->agent.bo);
free((char*)s->conf.file);
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
list_for_each_entry_safe(l, l_next, &p->conf.listeners, by_fe) {
unbind_listener(l);
delete_listener(l);
LIST_DEL(&l->by_fe);
LIST_DEL(&l->by_bind);
free(l->name);
free(l->counters);
free(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
/* Release unused SSL configs. */
list_for_each_entry_safe(bind_conf, bind_back, &p->conf.bind, by_fe) {
#ifdef USE_OPENSSL
ssl_sock_free_all_ctx(bind_conf);
free(bind_conf->ca_file);
free(bind_conf->ciphers);
free(bind_conf->ecdhe);
free(bind_conf->crl_file);
#endif /* USE_OPENSSL */
free(bind_conf->file);
free(bind_conf->arg);
LIST_DEL(&bind_conf->by_fe);
free(bind_conf);
}
free(p->desc);
free(p->fwdfor_hdr_name);
free_http_req_rules(&p->http_req_rules);
free_http_res_rules(&p->http_res_rules);
free(p->task);
pool_destroy2(p->req_cap_pool);
pool_destroy2(p->rsp_cap_pool);
pool_destroy2(p->table.pool);
p0 = p;
p = p->next;
free(p0);
}/* end while(p) */
while (ua) {
uap = ua;
ua = ua->next;
free(uap->uri_prefix);
free(uap->auth_realm);
free(uap->node);
free(uap->desc);
userlist_free(uap->userlist);
free_http_req_rules(&uap->http_req_rules);
free(uap);
}
userlist_free(userlist);
protocol_unbind_all();
free(global.log_send_hostname); global.log_send_hostname = NULL;
free(global.log_tag); global.log_tag = NULL;
free(global.chroot); global.chroot = NULL;
free(global.pidfile); global.pidfile = NULL;
free(global.node); global.node = NULL;
free(global.desc); global.desc = NULL;
free(fdinfo); fdinfo = NULL;
free(fdtab); fdtab = NULL;
free(oldpids); oldpids = NULL;
free(global_listener_queue_task); global_listener_queue_task = NULL;
list_for_each_entry_safe(log, logb, &global.logsrvs, list) {
LIST_DEL(&log->list);
free(log);
}
list_for_each_entry_safe(wl, wlb, &cfg_cfgfiles, list) {
LIST_DEL(&wl->list);
free(wl);
}
pool_destroy2(pool2_session);
pool_destroy2(pool2_connection);
pool_destroy2(pool2_buffer);
CLEANUP: channel: use "channel" instead of "buffer" in function names This is a massive rename of most functions which should make use of the word "channel" instead of the word "buffer" in their names. In concerns the following ones (new names) : unsigned long long channel_forward(struct channel *buf, unsigned long long bytes); static inline void channel_init(struct channel *buf) static inline int channel_input_closed(struct channel *buf) static inline int channel_output_closed(struct channel *buf) static inline void channel_check_timeouts(struct channel *b) static inline void channel_erase(struct channel *buf) static inline void channel_shutr_now(struct channel *buf) static inline void channel_shutw_now(struct channel *buf) static inline void channel_abort(struct channel *buf) static inline void channel_stop_hijacker(struct channel *buf) static inline void channel_auto_connect(struct channel *buf) static inline void channel_dont_connect(struct channel *buf) static inline void channel_auto_close(struct channel *buf) static inline void channel_dont_close(struct channel *buf) static inline void channel_auto_read(struct channel *buf) static inline void channel_dont_read(struct channel *buf) unsigned long long channel_forward(struct channel *buf, unsigned long long bytes) Some functions provided by channel.[ch] have kept their "buffer" name because they are really designed to act on the buffer according to some information gathered from the channel. They have been moved together to the same place in the file for better readability but they were not changed at all. The "buffer" memory pool was also renamed "channel".
2012-08-27 18:06:31 -04:00
pool_destroy2(pool2_channel);
pool_destroy2(pool2_requri);
pool_destroy2(pool2_task);
pool_destroy2(pool2_capture);
pool_destroy2(pool2_appsess);
pool_destroy2(pool2_pendconn);
pool_destroy2(pool2_sig_handlers);
pool_destroy2(pool2_hdr_idx);
if (have_appsession) {
pool_destroy2(apools.serverid);
pool_destroy2(apools.sessid);
}
[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();
} /* end deinit() */
/* sends the signal <sig> to all pids found in <oldpids>. Returns the number of
* pids the signal was correctly delivered to.
*/
static int tell_old_pids(int sig)
{
int p;
int ret = 0;
for (p = 0; p < nb_oldpids; p++)
if (kill(oldpids[p], sig) == 0)
ret++;
return ret;
}
/* Runs the polling loop */
void run_poll_loop()
{
int next;
tv_update_date(0,1);
while (1) {
/* Process a few tasks */
process_runnable_tasks();
/* check if we caught some signals and process them */
signal_process_queue();
/* Check if we can expire some tasks */
next = wake_expired_tasks();
/* stop when there's nothing left to do */
if (jobs == 0)
break;
/* The poller will ensure it returns around <next> */
cur_poller.poll(&cur_poller, next);
fd_process_cached_events();
}
}
/* This is the global management task for listeners. It enables listeners waiting
* for global resources when there are enough free resource, or at least once in
* a while. It is designed to be called as a task.
*/
static struct task *manage_global_listener_queue(struct task *t)
{
int next = TICK_ETERNITY;
/* queue is empty, nothing to do */
if (LIST_ISEMPTY(&global_listener_queue))
goto out;
/* If there are still too many concurrent connections, let's wait for
* some of them to go away. We don't need to re-arm the timer because
* each of them will scan the queue anyway.
*/
if (unlikely(actconn >= global.maxconn))
goto out;
/* We should periodically try to enable listeners waiting for a global
* resource here, because it is possible, though very unlikely, that
* they have been blocked by a temporary lack of global resource such
* as a file descriptor or memory and that the temporary condition has
* disappeared.
*/
dequeue_all_listeners(&global_listener_queue);
out:
t->expire = next;
task_queue(t);
return t;
}
int main(int argc, char **argv)
{
int err, retry;
struct rlimit limit;
char errmsg[100];
int pidfd = -1;
init(argc, argv);
signal_register_fct(SIGQUIT, dump, SIGQUIT);
signal_register_fct(SIGUSR1, sig_soft_stop, SIGUSR1);
signal_register_fct(SIGHUP, sig_dump_state, SIGHUP);
/* Always catch SIGPIPE even on platforms which define MSG_NOSIGNAL.
* Some recent FreeBSD setups report broken pipes, and MSG_NOSIGNAL
* was defined there, so let's stay on the safe side.
*/
signal_register_fct(SIGPIPE, NULL, 0);
/* 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
}
/* 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);
/* exit the loop on no error or fatal error */
if ((err & (ERR_RETRYABLE|ERR_FATAL)) != ERR_RETRYABLE)
break;
if (nb_oldpids == 0 || retry == 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.
*/
if (tell_old_pids(SIGTTOU) == 0) {
/* no need to wait if we can't contact old pids */
retry = 0;
continue;
}
/* 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. */
if ((err & ~ERR_WARN) != ERR_NONE) {
if (retry != MAX_START_RETRIES && nb_oldpids) {
protocol_unbind_all(); /* cleanup everything we can */
tell_old_pids(SIGTTIN);
}
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);
}
err = protocol_bind_all(errmsg, sizeof(errmsg));
if ((err & ~ERR_WARN) != ERR_NONE) {
if ((err & ERR_ALERT) || (err & ERR_WARN))
Alert("[%s.main()] %s.\n", argv[0], errmsg);
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);
} else if (err & ERR_WARN) {
Alert("[%s.main()] %s.\n", argv[0], errmsg);
}
/* prepare pause/play signals */
signal_register_fct(SIGTTOU, sig_pause, SIGTTOU);
signal_register_fct(SIGTTIN, sig_listen, SIGTTIN);
/* 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 | MODE_SYSTEMD) && global.pidfile != NULL) {
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);
protocol_unbind_all();
exit(1);
}
}
#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");
protocol_unbind_all();
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"
"", argv[0]);
protocol_unbind_all();
exit(1);
}
/* 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]);
/* chroot if needed */
if (global.chroot != NULL) {
if (chroot(global.chroot) == -1 || chdir("/") == -1) {
Alert("[%s.main()] Cannot chroot(%s).\n", argv[0], global.chroot);
if (nb_oldpids)
tell_old_pids(SIGTTIN);
protocol_unbind_all();
exit(1);
}
}
if (nb_oldpids)
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) {
if (getgroups(0, NULL) > 0 && setgroups(0, NULL) == -1)
Warning("[%s.main()] Failed to drop supplementary groups. Using 'gid'/'group'"
" without 'uid'/'user' is generally useless.\n", argv[0]);
if (setgid(global.gid) == -1) {
Alert("[%s.main()] Cannot set gid %d.\n", argv[0], global.gid);
protocol_unbind_all();
exit(1);
}
}
if (global.uid && setuid(global.uid) == -1) {
Alert("[%s.main()] Cannot set uid %d.\n", argv[0], global.uid);
protocol_unbind_all();
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",
argv[0], (int)limit.rlim_cur, global.maxconn, global.maxsock, global.maxsock);
}
if (global.mode & (MODE_DAEMON | MODE_SYSTEMD)) {
struct proxy *px;
int ret = 0;
int *children = calloc(global.nbproc, sizeof(int));
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]);
protocol_unbind_all();
exit(1); /* there has been an error */
}
else if (ret == 0) /* child breaks here */
break;
children[proc] = ret;
if (pidfd >= 0) {
char pidstr[100];
snprintf(pidstr, sizeof(pidstr), "%d\n", ret);
shut_your_big_mouth_gcc(write(pidfd, pidstr, strlen(pidstr)));
}
relative_pid++; /* each child will get a different one */
}
#ifdef USE_CPU_AFFINITY
if (proc < global.nbproc && /* child */
proc < 32 && /* only the first 32 processes may be pinned */
global.cpu_map[proc]) /* only do this if the process has a CPU map */
sched_setaffinity(0, sizeof(unsigned long), (void *)&global.cpu_map[proc]);
#endif
/* close the pidfile both in children and father */
if (pidfd >= 0) {
//lseek(pidfd, 0, SEEK_SET); /* debug: emulate eglibc bug */
close(pidfd);
}
/* We won't ever use this anymore */
free(oldpids); oldpids = NULL;
free(global.chroot); global.chroot = NULL;
free(global.pidfile); global.pidfile = NULL;
/* 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 & (1UL << proc)))
stop_proxy(px);
}
px = px->next;
}
if (proc == global.nbproc) {
if (global.mode & MODE_SYSTEMD) {
BUG/MEDIUM: Fix unhandled connections problem with systemd daemon mode and SO_REUSEPORT. Using the systemd daemon mode the parent doesn't exits but waits for his childs without closing its listening sockets. As linux 3.9 introduced a SO_REUSEPORT option (always enabled in haproxy if available) this will give unhandled connections problems after an haproxy reload with open connections. The problem is that when on reload a new parent is started (-Ds $oldchildspids), in haproxy.c main there's a call to start_proxies that, without SO_REUSEPORT, should fail (as the old processes are already listening) and so a SIGTOU is sent to old processes. On this signal the old childs will call (in pause_listener) a shutdown() on the listening fd. From my tests (if I understand it correctly) this affects the in kernel file (so the listen is really disabled for all the processes, also the parent). Instead, with SO_REUSEPORT, the call to start_proxies doesn't fail and so SIGTOU is never sent. Only SIGUSR1 is sent and the listen isn't disabled for the parent but only the childs will stop listening (with a call to close()) So, with SO_REUSEPORT, the old childs will close their listening sockets but will wait for the current connections to finish or timeout, and, as their parent has its listening socket open, the kernel will schedule some connections on it. These connections will never be accepted by the parent as it's in the waitpid loop. This fix will close all the listeners on the parent before entering the waitpid loop. Signed-off-by: Simone Gotti <simone.gotti@gmail.com>
2014-06-10 18:15:51 -04:00
protocol_unbind_all();
for (proc = 0; proc < global.nbproc; proc++)
while (waitpid(children[proc], NULL, 0) == -1 && errno == EINTR);
}
exit(0); /* parent must leave */
}
free(children);
children = NULL;
/* 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
*/
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
/* detach from the tty */
fclose(stdin); fclose(stdout); fclose(stderr);
global.mode &= ~MODE_VERBOSE;
global.mode |= MODE_QUIET; /* ensure that we won't say anything from now */
}
pid = getpid(); /* update child's pid */
setsid();
fork_poller();
}
protocol_enable_all();
/*
* That's it : the central polling loop. Run until we stop.
*/
run_poll_loop();
/* 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:
*/