2006-06-25 20:48:02 -04:00
|
|
|
/*
|
|
|
|
|
* HA-Proxy : High Availability-enabled HTTP/TCP proxy
|
2016-05-10 09:36:58 -04:00
|
|
|
* Copyright 2000-2016 Willy Tarreau <willy@haproxy.org>.
|
2006-06-25 20:48:02 -04:00
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* Please refer to RFC2068 or RFC2616 for informations about HTTP protocol, and
|
|
|
|
|
* RFC2965 for informations about cookies usage. More generally, the IETF HTTP
|
|
|
|
|
* Working Group's web site should be consulted for protocol related changes :
|
|
|
|
|
*
|
|
|
|
|
* http://ftp.ics.uci.edu/pub/ietf/http/
|
|
|
|
|
*
|
|
|
|
|
* Pending bugs (may be not fixed because never reproduced) :
|
|
|
|
|
* - solaris only : sometimes, an HTTP proxy with only a dispatch address causes
|
|
|
|
|
* the proxy to terminate (no core) if the client breaks the connection during
|
|
|
|
|
* the response. Seen on 1.1.8pre4, but never reproduced. May not be related to
|
|
|
|
|
* the snprintf() bug since requests were simple (GET / HTTP/1.0), but may be
|
|
|
|
|
* related to missing setsid() (fixed in 1.1.15)
|
|
|
|
|
* - a proxy with an invalid config will prevent the startup even if disabled.
|
|
|
|
|
*
|
|
|
|
|
* ChangeLog has moved to the CHANGELOG file.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
2015-12-08 16:43:09 -05:00
|
|
|
#define _GNU_SOURCE
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <ctype.h>
|
2016-05-13 17:52:56 -04:00
|
|
|
#include <dirent.h>
|
|
|
|
|
#include <sys/stat.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#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>
|
2013-02-12 04:53:52 -05:00
|
|
|
#include <sys/wait.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <time.h>
|
|
|
|
|
#include <syslog.h>
|
BUG/MEDIUM: remove supplementary groups when changing gid
Without it, haproxy will retain the group membership of root, which may
give more access than intended to the process. For example, haproxy would
still be in the wheel group on Fedora 18, as seen with :
# haproxy -f /etc/haproxy/haproxy.cfg
# ps a -o pid,user,group,command | grep hapr
3545 haproxy haproxy haproxy -f /etc/haproxy/haproxy.cfg
4356 root root grep --color=auto hapr
# grep Group /proc/3545/status
Groups: 0 1 2 3 4 6 10
# getent group wheel
wheel:x:10:root,misc
[WT: The issue has been investigated by independent security research team
and realized by itself not being able to allow security exploitation.
Additionally, dropping groups is not allowed to unprivileged users,
though this mode of deployment is quite common. Thus a warning is
emitted in this case to inform the user. The fix could be backported
into all supported versions as the issue has always been there. ]
2013-01-12 12:35:19 -05:00
|
|
|
#include <grp.h>
|
2012-11-16 10:12:27 -05:00
|
|
|
#ifdef USE_CPU_AFFINITY
|
|
|
|
|
#include <sched.h>
|
2015-09-17 15:26:40 -04:00
|
|
|
#ifdef __FreeBSD__
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
|
#include <sys/cpuset.h>
|
|
|
|
|
#endif
|
2012-11-16 10:12:27 -05:00
|
|
|
#endif
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
#ifdef DEBUG_FULL
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2006-06-29 11:53:05 -04:00
|
|
|
#include <common/base64.h>
|
|
|
|
|
#include <common/cfgparse.h>
|
2012-08-24 13:22:53 -04:00
|
|
|
#include <common/chunk.h>
|
2006-06-29 11:53:05 -04:00
|
|
|
#include <common/compat.h>
|
|
|
|
|
#include <common/config.h>
|
|
|
|
|
#include <common/defaults.h>
|
2007-10-28 06:14:07 -04:00
|
|
|
#include <common/errors.h>
|
2006-06-29 11:53:05 -04:00
|
|
|
#include <common/memory.h>
|
|
|
|
|
#include <common/mini-clist.h>
|
2014-11-17 09:11:45 -05:00
|
|
|
#include <common/namespace.h>
|
2006-06-29 11:53:05 -04:00
|
|
|
#include <common/regex.h>
|
|
|
|
|
#include <common/standard.h>
|
|
|
|
|
#include <common/time.h>
|
|
|
|
|
#include <common/uri_auth.h>
|
|
|
|
|
#include <common/version.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
#include <types/capture.h>
|
2015-11-05 07:35:03 -05:00
|
|
|
#include <types/compression.h>
|
MAJOR: filters: Add filters support
This patch adds the support of filters in HAProxy. The main idea is to have a
way to "easely" extend HAProxy by adding some "modules", called filters, that
will be able to change HAProxy behavior in a programmatic way.
To do so, many entry points has been added in code to let filters to hook up to
different steps of the processing. A filter must define a flt_ops sutrctures
(see include/types/filters.h for details). This structure contains all available
callbacks that a filter can define:
struct flt_ops {
/*
* Callbacks to manage the filter lifecycle
*/
int (*init) (struct proxy *p);
void (*deinit)(struct proxy *p);
int (*check) (struct proxy *p);
/*
* Stream callbacks
*/
void (*stream_start) (struct stream *s);
void (*stream_accept) (struct stream *s);
void (*session_establish)(struct stream *s);
void (*stream_stop) (struct stream *s);
/*
* HTTP callbacks
*/
int (*http_start) (struct stream *s, struct http_msg *msg);
int (*http_start_body) (struct stream *s, struct http_msg *msg);
int (*http_start_chunk) (struct stream *s, struct http_msg *msg);
int (*http_data) (struct stream *s, struct http_msg *msg);
int (*http_last_chunk) (struct stream *s, struct http_msg *msg);
int (*http_end_chunk) (struct stream *s, struct http_msg *msg);
int (*http_chunk_trailers)(struct stream *s, struct http_msg *msg);
int (*http_end_body) (struct stream *s, struct http_msg *msg);
void (*http_end) (struct stream *s, struct http_msg *msg);
void (*http_reset) (struct stream *s, struct http_msg *msg);
int (*http_pre_process) (struct stream *s, struct http_msg *msg);
int (*http_post_process) (struct stream *s, struct http_msg *msg);
void (*http_reply) (struct stream *s, short status,
const struct chunk *msg);
};
To declare and use a filter, in the configuration, the "filter" keyword must be
used in a listener/frontend section:
frontend test
...
filter <FILTER-NAME> [OPTIONS...]
The filter referenced by the <FILTER-NAME> must declare a configuration parser
on its own name to fill flt_ops and filter_conf field in the proxy's
structure. An exemple will be provided later to make it perfectly clear.
For now, filters cannot be used in backend section. But this is only a matter of
time. Documentation will also be added later. This is the first commit of a long
list about filters.
It is possible to have several filters on the same listener/frontend. These
filters are stored in an array of at most MAX_FILTERS elements (define in
include/types/filters.h). Again, this will be replaced later by a list of
filters.
The filter API has been highly refactored. Main changes are:
* Now, HA supports an infinite number of filters per proxy. To do so, filters
are stored in list.
* Because filters are stored in list, filters state has been moved from the
channel structure to the filter structure. This is cleaner because there is no
more info about filters in channel structure.
* It is possible to defined filters on backends only. For such filters,
stream_start/stream_stop callbacks are not called. Of course, it is possible
to mix frontend and backend filters.
* Now, TCP streams are also filtered. All callbacks without the 'http_' prefix
are called for all kind of streams. In addition, 2 new callbacks were added to
filter data exchanged through a TCP stream:
- tcp_data: it is called when new data are available or when old unprocessed
data are still waiting.
- tcp_forward_data: it is called when some data can be consumed.
* New callbacks attached to channel were added:
- channel_start_analyze: it is called when a filter is ready to process data
exchanged through a channel. 2 new analyzers (a frontend and a backend)
are attached to channels to call this callback. For a frontend filter, it
is called before any other analyzer. For a backend filter, it is called
when a backend is attached to a stream. So some processing cannot be
filtered in that case.
- channel_analyze: it is called before each analyzer attached to a channel,
expects analyzers responsible for data sending.
- channel_end_analyze: it is called when all other analyzers have finished
their processing. A new analyzers is attached to channels to call this
callback. For a TCP stream, this is always the last one called. For a HTTP
one, the callback is called when a request/response ends, so it is called
one time for each request/response.
* 'session_established' callback has been removed. Everything that is done in
this callback can be handled by 'channel_start_analyze' on the response
channel.
* 'http_pre_process' and 'http_post_process' callbacks have been replaced by
'channel_analyze'.
* 'http_start' callback has been replaced by 'http_headers'. This new one is
called just before headers sending and parsing of the body.
* 'http_end' callback has been replaced by 'channel_end_analyze'.
* It is possible to set a forwarder for TCP channels. It was already possible to
do it for HTTP ones.
* Forwarders can partially consumed forwardable data. For this reason a new
HTTP message state was added before HTTP_MSG_DONE : HTTP_MSG_ENDING.
Now all filters can define corresponding callbacks (http_forward_data
and tcp_forward_data). Each filter owns 2 offsets relative to buf->p, next and
forward, to track, respectively, input data already parsed but not forwarded yet
by the filter and parsed data considered as forwarded by the filter. A any time,
we have the warranty that a filter cannot parse or forward more input than
previous ones. And, of course, it cannot forward more input than it has
parsed. 2 macros has been added to retrieve these offets: FLT_NXT and FLT_FWD.
In addition, 2 functions has been added to change the 'next size' and the
'forward size' of a filter. When a filter parses input data, it can alter these
data, so the size of these data can vary. This action has an effet on all
previous filters that must be handled. To do so, the function
'filter_change_next_size' must be called, passing the size variation. In the
same spirit, if a filter alter forwarded data, it must call the function
'filter_change_forward_size'. 'filter_change_next_size' can be called in
'http_data' and 'tcp_data' callbacks and only these ones. And
'filter_change_forward_size' can be called in 'http_forward_data' and
'tcp_forward_data' callbacks and only these ones. The data changes are the
filter responsability, but with some limitation. It must not change already
parsed/forwarded data or data that previous filters have not parsed/forwarded
yet.
Because filters can be used on backends, when we the backend is set for a
stream, we add filters defined for this backend in the filter list of the
stream. But we must only do that when the backend and the frontend of the stream
are not the same. Else same filters are added a second time leading to undefined
behavior.
The HTTP compression code had to be moved.
So it simplifies http_response_forward_body function. To do so, the way the data
are forwarded has changed. Now, a filter (and only one) can forward data. In a
commit to come, this limitation will be removed to let all filters take part to
data forwarding. There are 2 new functions that filters should use to deal with
this feature:
* flt_set_http_data_forwarder: This function sets the filter (using its id)
that will forward data for the specified HTTP message. It is possible if it
was not already set by another filter _AND_ if no data was yet forwarded
(msg->msg_state <= HTTP_MSG_BODY). It returns -1 if an error occurs.
* flt_http_data_forwarder: This function returns the filter id that will
forward data for the specified HTTP message. If there is no forwarder set, it
returns -1.
When an HTTP data forwarder is set for the response, the HTTP compression is
disabled. Of course, this is not definitive.
2015-04-30 05:48:27 -04:00
|
|
|
#include <types/filters.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <types/global.h>
|
2011-07-15 00:14:09 -04:00
|
|
|
#include <types/acl.h>
|
2011-09-07 12:00:47 -04:00
|
|
|
#include <types/peers.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2007-06-16 18:36:03 -04:00
|
|
|
#include <proto/acl.h>
|
2015-04-19 03:59:31 -04:00
|
|
|
#include <proto/applet.h>
|
2012-10-19 13:49:09 -04:00
|
|
|
#include <proto/arg.h>
|
2015-04-19 03:59:31 -04:00
|
|
|
#include <proto/auth.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <proto/backend.h>
|
2012-08-24 13:22:53 -04:00
|
|
|
#include <proto/channel.h>
|
2007-10-14 17:40:01 -04:00
|
|
|
#include <proto/checks.h>
|
2012-10-26 14:10:28 -04:00
|
|
|
#include <proto/connection.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <proto/fd.h>
|
MAJOR: filters: Add filters support
This patch adds the support of filters in HAProxy. The main idea is to have a
way to "easely" extend HAProxy by adding some "modules", called filters, that
will be able to change HAProxy behavior in a programmatic way.
To do so, many entry points has been added in code to let filters to hook up to
different steps of the processing. A filter must define a flt_ops sutrctures
(see include/types/filters.h for details). This structure contains all available
callbacks that a filter can define:
struct flt_ops {
/*
* Callbacks to manage the filter lifecycle
*/
int (*init) (struct proxy *p);
void (*deinit)(struct proxy *p);
int (*check) (struct proxy *p);
/*
* Stream callbacks
*/
void (*stream_start) (struct stream *s);
void (*stream_accept) (struct stream *s);
void (*session_establish)(struct stream *s);
void (*stream_stop) (struct stream *s);
/*
* HTTP callbacks
*/
int (*http_start) (struct stream *s, struct http_msg *msg);
int (*http_start_body) (struct stream *s, struct http_msg *msg);
int (*http_start_chunk) (struct stream *s, struct http_msg *msg);
int (*http_data) (struct stream *s, struct http_msg *msg);
int (*http_last_chunk) (struct stream *s, struct http_msg *msg);
int (*http_end_chunk) (struct stream *s, struct http_msg *msg);
int (*http_chunk_trailers)(struct stream *s, struct http_msg *msg);
int (*http_end_body) (struct stream *s, struct http_msg *msg);
void (*http_end) (struct stream *s, struct http_msg *msg);
void (*http_reset) (struct stream *s, struct http_msg *msg);
int (*http_pre_process) (struct stream *s, struct http_msg *msg);
int (*http_post_process) (struct stream *s, struct http_msg *msg);
void (*http_reply) (struct stream *s, short status,
const struct chunk *msg);
};
To declare and use a filter, in the configuration, the "filter" keyword must be
used in a listener/frontend section:
frontend test
...
filter <FILTER-NAME> [OPTIONS...]
The filter referenced by the <FILTER-NAME> must declare a configuration parser
on its own name to fill flt_ops and filter_conf field in the proxy's
structure. An exemple will be provided later to make it perfectly clear.
For now, filters cannot be used in backend section. But this is only a matter of
time. Documentation will also be added later. This is the first commit of a long
list about filters.
It is possible to have several filters on the same listener/frontend. These
filters are stored in an array of at most MAX_FILTERS elements (define in
include/types/filters.h). Again, this will be replaced later by a list of
filters.
The filter API has been highly refactored. Main changes are:
* Now, HA supports an infinite number of filters per proxy. To do so, filters
are stored in list.
* Because filters are stored in list, filters state has been moved from the
channel structure to the filter structure. This is cleaner because there is no
more info about filters in channel structure.
* It is possible to defined filters on backends only. For such filters,
stream_start/stream_stop callbacks are not called. Of course, it is possible
to mix frontend and backend filters.
* Now, TCP streams are also filtered. All callbacks without the 'http_' prefix
are called for all kind of streams. In addition, 2 new callbacks were added to
filter data exchanged through a TCP stream:
- tcp_data: it is called when new data are available or when old unprocessed
data are still waiting.
- tcp_forward_data: it is called when some data can be consumed.
* New callbacks attached to channel were added:
- channel_start_analyze: it is called when a filter is ready to process data
exchanged through a channel. 2 new analyzers (a frontend and a backend)
are attached to channels to call this callback. For a frontend filter, it
is called before any other analyzer. For a backend filter, it is called
when a backend is attached to a stream. So some processing cannot be
filtered in that case.
- channel_analyze: it is called before each analyzer attached to a channel,
expects analyzers responsible for data sending.
- channel_end_analyze: it is called when all other analyzers have finished
their processing. A new analyzers is attached to channels to call this
callback. For a TCP stream, this is always the last one called. For a HTTP
one, the callback is called when a request/response ends, so it is called
one time for each request/response.
* 'session_established' callback has been removed. Everything that is done in
this callback can be handled by 'channel_start_analyze' on the response
channel.
* 'http_pre_process' and 'http_post_process' callbacks have been replaced by
'channel_analyze'.
* 'http_start' callback has been replaced by 'http_headers'. This new one is
called just before headers sending and parsing of the body.
* 'http_end' callback has been replaced by 'channel_end_analyze'.
* It is possible to set a forwarder for TCP channels. It was already possible to
do it for HTTP ones.
* Forwarders can partially consumed forwardable data. For this reason a new
HTTP message state was added before HTTP_MSG_DONE : HTTP_MSG_ENDING.
Now all filters can define corresponding callbacks (http_forward_data
and tcp_forward_data). Each filter owns 2 offsets relative to buf->p, next and
forward, to track, respectively, input data already parsed but not forwarded yet
by the filter and parsed data considered as forwarded by the filter. A any time,
we have the warranty that a filter cannot parse or forward more input than
previous ones. And, of course, it cannot forward more input than it has
parsed. 2 macros has been added to retrieve these offets: FLT_NXT and FLT_FWD.
In addition, 2 functions has been added to change the 'next size' and the
'forward size' of a filter. When a filter parses input data, it can alter these
data, so the size of these data can vary. This action has an effet on all
previous filters that must be handled. To do so, the function
'filter_change_next_size' must be called, passing the size variation. In the
same spirit, if a filter alter forwarded data, it must call the function
'filter_change_forward_size'. 'filter_change_next_size' can be called in
'http_data' and 'tcp_data' callbacks and only these ones. And
'filter_change_forward_size' can be called in 'http_forward_data' and
'tcp_forward_data' callbacks and only these ones. The data changes are the
filter responsability, but with some limitation. It must not change already
parsed/forwarded data or data that previous filters have not parsed/forwarded
yet.
Because filters can be used on backends, when we the backend is set for a
stream, we add filters defined for this backend in the filter list of the
stream. But we must only do that when the backend and the frontend of the stream
are not the same. Else same filters are added a second time leading to undefined
behavior.
The HTTP compression code had to be moved.
So it simplifies http_response_forward_body function. To do so, the way the data
are forwarded has changed. Now, a filter (and only one) can forward data. In a
commit to come, this limitation will be removed to let all filters take part to
data forwarding. There are 2 new functions that filters should use to deal with
this feature:
* flt_set_http_data_forwarder: This function sets the filter (using its id)
that will forward data for the specified HTTP message. It is possible if it
was not already set by another filter _AND_ if no data was yet forwarded
(msg->msg_state <= HTTP_MSG_BODY). It returns -1 if an error occurs.
* flt_http_data_forwarder: This function returns the filter id that will
forward data for the specified HTTP message. If there is no forwarder set, it
returns -1.
When an HTTP data forwarder is set for the response, the HTTP compression is
disabled. Of course, this is not definitive.
2015-04-30 05:48:27 -04:00
|
|
|
#include <proto/filters.h>
|
2011-10-24 12:15:04 -04:00
|
|
|
#include <proto/hdr_idx.h>
|
2015-01-23 08:06:13 -05:00
|
|
|
#include <proto/hlua.h>
|
2012-09-12 16:58:11 -04:00
|
|
|
#include <proto/listener.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <proto/log.h>
|
2014-03-11 09:29:22 -04:00
|
|
|
#include <proto/pattern.h>
|
2012-09-12 16:58:11 -04:00
|
|
|
#include <proto/protocol.h>
|
2006-12-24 11:47:20 -05:00
|
|
|
#include <proto/proto_http.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <proto/proxy.h>
|
|
|
|
|
#include <proto/queue.h>
|
|
|
|
|
#include <proto/server.h>
|
2015-04-03 07:53:24 -04:00
|
|
|
#include <proto/session.h>
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
#include <proto/stream.h>
|
2009-05-10 03:01:21 -04:00
|
|
|
#include <proto/signal.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
#include <proto/task.h>
|
2015-04-13 17:40:55 -04:00
|
|
|
#include <proto/dns.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2012-09-07 11:30:07 -04:00
|
|
|
#ifdef USE_OPENSSL
|
|
|
|
|
#include <proto/ssl_sock.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2016-11-08 12:47:25 -05:00
|
|
|
#ifdef USE_WURFL
|
|
|
|
|
#include <proto/wurfl.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-06-01 07:57:22 -04:00
|
|
|
#ifdef USE_DEVICEATLAS
|
|
|
|
|
#include <import/da.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-05-12 11:23:58 -04:00
|
|
|
#ifdef USE_51DEGREES
|
2015-06-29 10:43:25 -04:00
|
|
|
#include <import/51d.h>
|
2015-05-12 11:23:58 -04:00
|
|
|
#endif
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/*********************************************************************/
|
|
|
|
|
|
2012-11-10 13:27:47 -05:00
|
|
|
extern const struct comp_algo comp_algos[];
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/*********************************************************************/
|
|
|
|
|
|
2010-01-03 15:12:30 -05:00
|
|
|
/* list of config files */
|
|
|
|
|
static struct list cfg_cfgfiles = LIST_HEAD_INIT(cfg_cfgfiles);
|
2006-06-25 20:48:02 -04:00
|
|
|
int pid; /* current process id */
|
2007-11-26 10:13:36 -05:00
|
|
|
int relative_pid = 1; /* process id starting at 1 */
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
/* global options */
|
|
|
|
|
struct global global = {
|
2012-11-15 11:38:15 -05:00
|
|
|
.nbproc = 1,
|
2012-04-05 12:02:55 -04:00
|
|
|
.req_count = 0,
|
2011-10-12 11:50:54 -04:00
|
|
|
.logsrvs = LIST_HEAD_INIT(global.logsrvs),
|
2015-11-26 10:34:56 -05:00
|
|
|
#if defined(USE_ZLIB) && defined(DEFAULT_MAXZLIBMEM)
|
2012-12-03 06:10:45 -05:00
|
|
|
.maxzlibmem = DEFAULT_MAXZLIBMEM * 1024U * 1024U,
|
2012-11-12 09:52:53 -05:00
|
|
|
#else
|
2012-11-07 10:12:57 -05:00
|
|
|
.maxzlibmem = 0,
|
2012-11-12 09:52:53 -05:00
|
|
|
#endif
|
2012-11-09 11:05:39 -05:00
|
|
|
.comp_rate_lim = 0,
|
2014-01-29 06:24:34 -05:00
|
|
|
.ssl_server_verify = SSL_SERVER_VERIFY_REQUIRED,
|
2010-10-22 11:59:25 -04:00
|
|
|
.unix_bind = {
|
|
|
|
|
.ux = {
|
|
|
|
|
.uid = -1,
|
|
|
|
|
.gid = -1,
|
|
|
|
|
.mode = 0,
|
|
|
|
|
}
|
|
|
|
|
},
|
2009-08-17 01:23:33 -04:00
|
|
|
.tune = {
|
|
|
|
|
.bufsize = BUFSIZE,
|
2015-09-28 07:53:23 -04:00
|
|
|
.maxrewrite = -1,
|
2010-10-04 14:39:20 -04:00
|
|
|
.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,
|
2015-04-29 10:24:50 -04:00
|
|
|
.pattern_cache = DEFAULT_PAT_LRU_SIZE,
|
2012-09-03 06:10:29 -04:00
|
|
|
#ifdef USE_OPENSSL
|
2012-11-14 05:32:56 -05:00
|
|
|
.sslcachesize = SSLCACHESIZE,
|
2014-06-12 08:58:40 -04:00
|
|
|
.ssl_default_dh_param = SSL_DEFAULT_DH_PARAM,
|
2014-02-12 08:55:41 -05:00
|
|
|
#ifdef DEFAULT_SSL_MAX_RECORD
|
|
|
|
|
.ssl_max_record = DEFAULT_SSL_MAX_RECORD,
|
|
|
|
|
#endif
|
2015-06-09 11:29:50 -04:00
|
|
|
.ssl_ctx_cache = DEFAULT_SSL_CTX_CACHE,
|
2012-09-03 06:10:29 -04:00
|
|
|
#endif
|
2012-11-07 10:54:34 -05:00
|
|
|
#ifdef USE_ZLIB
|
|
|
|
|
.zlibmemlevel = 8,
|
|
|
|
|
.zlibwindowsize = MAX_WBITS,
|
|
|
|
|
#endif
|
2012-11-09 06:33:10 -05:00
|
|
|
.comp_maxlevel = 1,
|
2014-02-12 10:35:14 -05:00
|
|
|
#ifdef DEFAULT_IDLE_TIMER
|
|
|
|
|
.idle_timer = DEFAULT_IDLE_TIMER,
|
|
|
|
|
#else
|
|
|
|
|
.idle_timer = 1000, /* 1 second */
|
|
|
|
|
#endif
|
2009-08-17 01:23:33 -04:00
|
|
|
},
|
2012-10-05 09:47:31 -04:00
|
|
|
#ifdef USE_OPENSSL
|
|
|
|
|
#ifdef DEFAULT_MAXSSLCONN
|
2012-09-06 05:58:37 -04:00
|
|
|
.maxsslconn = DEFAULT_MAXSSLCONN,
|
2012-10-05 09:47:31 -04:00
|
|
|
#endif
|
2015-06-01 07:57:22 -04:00
|
|
|
#endif
|
|
|
|
|
#ifdef USE_DEVICEATLAS
|
|
|
|
|
.deviceatlas = {
|
2015-12-02 06:22:53 -05:00
|
|
|
.loglevel = 0,
|
2015-06-01 07:57:22 -04:00
|
|
|
.jsonpath = 0,
|
2015-09-25 09:13:44 -04:00
|
|
|
.cookiename = 0,
|
|
|
|
|
.cookienamelen = 0,
|
|
|
|
|
.useragentid = 0,
|
|
|
|
|
.daset = 0,
|
2015-06-01 07:57:22 -04:00
|
|
|
.separator = '|',
|
|
|
|
|
},
|
2015-06-11 15:36:33 -04:00
|
|
|
#endif
|
|
|
|
|
#ifdef USE_51DEGREES
|
2015-06-29 10:43:25 -04:00
|
|
|
._51degrees = {
|
|
|
|
|
.property_separator = ',',
|
|
|
|
|
.property_names = LIST_HEAD_INIT(global._51degrees.property_names),
|
|
|
|
|
.data_file_path = NULL,
|
|
|
|
|
#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
|
|
|
|
|
.data_set = { },
|
|
|
|
|
#endif
|
2015-08-07 10:41:23 -04:00
|
|
|
.cache_size = 0,
|
2015-06-29 10:43:25 -04:00
|
|
|
},
|
2012-09-06 05:58:37 -04:00
|
|
|
#endif
|
2016-11-04 05:55:08 -04:00
|
|
|
#ifdef USE_WURFL
|
|
|
|
|
.wurfl = {
|
|
|
|
|
.data_file = NULL,
|
|
|
|
|
.cache_size = NULL,
|
|
|
|
|
.engine_mode = -1,
|
|
|
|
|
.useragent_priority = -1,
|
|
|
|
|
.information_list_separator = ',',
|
|
|
|
|
.information_list = LIST_HEAD_INIT(global.wurfl.information_list),
|
|
|
|
|
.patch_file_list = LIST_HEAD_INIT(global.wurfl.patch_file_list),
|
|
|
|
|
.handle = NULL,
|
|
|
|
|
},
|
|
|
|
|
#endif
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/* others NULL OK */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
|
|
|
|
int stopping; /* non zero means stopping in progress */
|
2010-08-31 09:39:26 -04:00
|
|
|
int jobs = 0; /* number of active jobs (conns, listeners, active tasks, ...) */
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
/* 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()... */
|
2012-10-29 11:51:55 -04:00
|
|
|
struct chunk trash = { };
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2010-02-26 05:12:27 -05:00
|
|
|
/* this buffer is always the same size as standard buffers and is used for
|
|
|
|
|
* swapping data inside a buffer.
|
|
|
|
|
*/
|
|
|
|
|
char *swap_buffer = NULL;
|
|
|
|
|
|
2010-08-25 06:58:59 -04:00
|
|
|
int nb_oldpids = 0;
|
2006-06-25 20:48:02 -04:00
|
|
|
const int zero = 0;
|
|
|
|
|
const int one = 1;
|
2007-10-11 14:48:58 -04:00
|
|
|
const struct linger nolinger = { .l_onoff = 1, .l_linger = 0 };
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2010-03-12 15:58:54 -05:00
|
|
|
char hostname[MAX_HOSTNAME_LEN];
|
2010-09-23 12:30:22 -04:00
|
|
|
char localpeer[MAX_HOSTNAME_LEN];
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2013-12-13 09:14:55 -05:00
|
|
|
/* 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;
|
|
|
|
|
|
2011-07-24 16:58:00 -04:00
|
|
|
/* list of the temporarily limited listeners because of lack of resource */
|
|
|
|
|
struct list global_listener_queue = LIST_HEAD_INIT(global_listener_queue);
|
2011-08-01 14:57:55 -04:00
|
|
|
struct task *global_listener_queue_task;
|
|
|
|
|
static struct task *manage_global_listener_queue(struct task *t);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2014-04-28 16:27:06 -04:00
|
|
|
/* bitfield of a few warnings to emit just once (WARN_*) */
|
|
|
|
|
unsigned int warned = 0;
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/*********************************************************************/
|
|
|
|
|
/* general purpose functions ***************************************/
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
|
|
|
|
void display_version()
|
|
|
|
|
{
|
|
|
|
|
printf("HA-Proxy version " HAPROXY_VERSION " " HAPROXY_DATE"\n");
|
2016-03-13 19:10:05 -04:00
|
|
|
printf("Copyright 2000-2016 Willy Tarreau <willy@haproxy.org>\n\n");
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
2007-12-02 05:28:59 -05:00
|
|
|
void display_build_opts()
|
|
|
|
|
{
|
|
|
|
|
printf("Build options :"
|
|
|
|
|
#ifdef BUILD_TARGET
|
2008-01-02 14:48:34 -05:00
|
|
|
"\n TARGET = " BUILD_TARGET
|
2007-12-02 05:28:59 -05:00
|
|
|
#endif
|
|
|
|
|
#ifdef BUILD_CPU
|
2008-01-02 14:48:34 -05:00
|
|
|
"\n CPU = " BUILD_CPU
|
2007-12-02 05:28:59 -05:00
|
|
|
#endif
|
|
|
|
|
#ifdef BUILD_CC
|
2008-01-02 14:48:34 -05:00
|
|
|
"\n CC = " BUILD_CC
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef BUILD_CFLAGS
|
|
|
|
|
"\n CFLAGS = " BUILD_CFLAGS
|
2007-12-02 05:28:59 -05:00
|
|
|
#endif
|
2008-01-02 14:48:34 -05:00
|
|
|
#ifdef BUILD_OPTIONS
|
|
|
|
|
"\n OPTIONS = " BUILD_OPTIONS
|
2007-12-02 05:28:59 -05:00
|
|
|
#endif
|
2009-08-17 01:23:33 -04:00
|
|
|
"\n\nDefault settings :"
|
|
|
|
|
"\n maxconn = %d, bufsize = %d, maxrewrite = %d, maxpollevents = %d"
|
|
|
|
|
"\n\n",
|
|
|
|
|
DEFAULT_MAXCONN, BUFSIZE, MAXREWRITE, MAX_POLL_EVENTS);
|
2009-10-03 12:57:08 -04:00
|
|
|
|
2010-01-29 11:50:44 -05:00
|
|
|
printf("Encrypted password support via crypt(3): "
|
|
|
|
|
#ifdef CONFIG_HAP_CRYPT
|
|
|
|
|
"yes"
|
|
|
|
|
#else
|
|
|
|
|
"no"
|
|
|
|
|
#endif
|
|
|
|
|
"\n");
|
|
|
|
|
|
2012-11-10 13:27:47 -05:00
|
|
|
#ifdef USE_ZLIB
|
|
|
|
|
printf("Built with zlib version : " ZLIB_VERSION "\n");
|
2016-09-12 17:42:14 -04:00
|
|
|
printf("Running on zlib version : %s\n", zlibVersion());
|
2015-10-13 10:46:28 -04:00
|
|
|
#elif defined(USE_SLZ)
|
|
|
|
|
printf("Built with libslz for stateless compression.\n");
|
2012-11-10 13:27:47 -05:00
|
|
|
#else /* USE_ZLIB */
|
2015-10-13 10:46:28 -04:00
|
|
|
printf("Built without compression support (neither USE_ZLIB nor USE_SLZ are set)\n");
|
2012-11-10 13:27:47 -05:00
|
|
|
#endif
|
|
|
|
|
printf("Compression algorithms supported :");
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
2015-03-28 11:40:46 -04:00
|
|
|
for (i = 0; comp_algos[i].cfg_name; i++) {
|
|
|
|
|
printf("%s %s(\"%s\")", (i == 0 ? "" : ","), comp_algos[i].cfg_name, comp_algos[i].ua_name);
|
2012-11-10 13:27:47 -05:00
|
|
|
}
|
|
|
|
|
if (i == 0) {
|
|
|
|
|
printf("none");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
printf("\n");
|
|
|
|
|
|
2012-09-10 01:16:05 -04:00
|
|
|
#ifdef USE_OPENSSL
|
2014-08-17 18:56:30 -04:00
|
|
|
printf("Built with OpenSSL version : "
|
|
|
|
|
#ifdef OPENSSL_IS_BORINGSSL
|
|
|
|
|
"BoringSSL\n");
|
|
|
|
|
#else /* OPENSSL_IS_BORINGSSL */
|
|
|
|
|
OPENSSL_VERSION_TEXT "\n");
|
2013-04-26 12:16:13 -04:00
|
|
|
printf("Running on OpenSSL version : %s%s\n",
|
|
|
|
|
SSLeay_version(SSLEAY_VERSION),
|
|
|
|
|
((OPENSSL_VERSION_NUMBER ^ SSLeay()) >> 8) ? " (VERSIONS DIFFER!)" : "");
|
2014-08-17 18:56:30 -04:00
|
|
|
#endif
|
2012-09-10 01:16:05 -04:00
|
|
|
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
|
2013-04-04 06:24:16 -04:00
|
|
|
|
|
|
|
|
#ifdef USE_PCRE
|
2016-09-12 17:42:07 -04:00
|
|
|
printf("Built with PCRE version : %s\n", (HAP_XSTRING(Z PCRE_PRERELEASE)[1] == 0)?
|
|
|
|
|
HAP_XSTRING(PCRE_MAJOR.PCRE_MINOR PCRE_DATE) :
|
|
|
|
|
HAP_XSTRING(PCRE_MAJOR.PCRE_MINOR) HAP_XSTRING(PCRE_PRERELEASE PCRE_DATE));
|
2016-09-12 17:42:00 -04:00
|
|
|
printf("Running on PCRE version : %s", pcre_version());
|
2013-04-14 18:41:40 -04:00
|
|
|
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?)");
|
|
|
|
|
}
|
2013-04-04 06:24:16 -04:00
|
|
|
#else
|
2013-04-14 18:41:40 -04:00
|
|
|
printf("no (USE_PCRE_JIT not set)");
|
2013-04-04 06:24:16 -04:00
|
|
|
#endif
|
2013-04-14 18:41:40 -04:00
|
|
|
printf("\n");
|
2013-04-04 06:24:16 -04:00
|
|
|
#else
|
|
|
|
|
printf("Built without PCRE support (using libc's regex instead)\n");
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-03-01 18:08:39 -05:00
|
|
|
#ifdef USE_LUA
|
|
|
|
|
printf("Built with Lua version : %s\n", LUA_RELEASE);
|
|
|
|
|
#else
|
|
|
|
|
printf("Built without Lua support\n");
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-08-20 13:35:14 -04:00
|
|
|
#if defined(CONFIG_HAP_TRANSPARENT)
|
2013-05-08 16:49:23 -04:00
|
|
|
printf("Built with transparent proxy support using:"
|
|
|
|
|
#if defined(IP_TRANSPARENT)
|
|
|
|
|
" IP_TRANSPARENT"
|
|
|
|
|
#endif
|
|
|
|
|
#if defined(IPV6_TRANSPARENT)
|
|
|
|
|
" IPV6_TRANSPARENT"
|
|
|
|
|
#endif
|
|
|
|
|
#if defined(IP_FREEBIND)
|
|
|
|
|
" IP_FREEBIND"
|
2013-05-08 17:22:39 -04:00
|
|
|
#endif
|
|
|
|
|
#if defined(IP_BINDANY)
|
|
|
|
|
" IP_BINDANY"
|
|
|
|
|
#endif
|
|
|
|
|
#if defined(IPV6_BINDANY)
|
|
|
|
|
" IPV6_BINDANY"
|
2013-05-08 17:30:23 -04:00
|
|
|
#endif
|
|
|
|
|
#if defined(SO_BINDANY)
|
|
|
|
|
" SO_BINDANY"
|
2013-05-08 16:49:23 -04:00
|
|
|
#endif
|
|
|
|
|
"\n");
|
|
|
|
|
#endif
|
2014-11-17 09:11:45 -05:00
|
|
|
|
|
|
|
|
#if defined(CONFIG_HAP_NS)
|
|
|
|
|
printf("Built with network namespace support\n");
|
|
|
|
|
#endif
|
2015-05-12 11:23:58 -04:00
|
|
|
|
2015-12-02 06:22:53 -05:00
|
|
|
#ifdef USE_DEVICEATLAS
|
|
|
|
|
printf("Built with DeviceAtlas support\n");
|
|
|
|
|
#endif
|
2015-05-12 11:23:58 -04:00
|
|
|
#ifdef USE_51DEGREES
|
|
|
|
|
printf("Built with 51Degrees support\n");
|
2016-11-04 05:55:08 -04:00
|
|
|
#endif
|
|
|
|
|
#ifdef USE_WURFL
|
|
|
|
|
printf("Built with WURFL support\n");
|
2015-05-12 11:23:58 -04:00
|
|
|
#endif
|
2010-01-29 11:50:44 -05:00
|
|
|
putchar('\n');
|
|
|
|
|
|
2009-10-03 12:57:08 -04:00
|
|
|
list_pollers(stdout);
|
|
|
|
|
putchar('\n');
|
2016-03-07 06:46:38 -05:00
|
|
|
list_filters(stdout);
|
|
|
|
|
putchar('\n');
|
2007-12-02 05:28:59 -05:00
|
|
|
}
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/*
|
|
|
|
|
* This function prints the command line usage and exits
|
|
|
|
|
*/
|
|
|
|
|
void usage(char *name)
|
|
|
|
|
{
|
|
|
|
|
display_version();
|
|
|
|
|
fprintf(stderr,
|
2016-05-13 17:52:56 -04:00
|
|
|
"Usage : %s [-f <cfgfile|cfgdir>]* [ -vdV"
|
2006-06-25 20:48:02 -04:00
|
|
|
"D ] [ -n <maxconn> ] [ -N <maxpconn> ]\n"
|
2015-10-08 05:58:48 -04:00
|
|
|
" [ -p <pidfile> ] [ -m <max megs> ] [ -C <dir> ] [-- <cfgfile>*]\n"
|
2007-12-02 05:28:59 -05:00
|
|
|
" -v displays version ; -vv shows known build options.\n"
|
2006-06-25 20:48:02 -04:00
|
|
|
" -d enters debug mode ; -db only disables background mode.\n"
|
2012-05-08 09:40:42 -04:00
|
|
|
" -dM[<byte>] poisons memory with <byte> (defaults to 0x50)\n"
|
2006-06-25 20:48:02 -04:00
|
|
|
" -V enters verbose mode (disables quiet mode)\n"
|
2011-09-10 13:26:56 -04:00
|
|
|
" -D goes daemon ; -C changes to <dir> before loading files.\n"
|
2006-06-25 20:48:02 -04:00
|
|
|
" -q quiet mode : don't display messages\n"
|
2009-06-22 10:02:30 -04:00
|
|
|
" -c check mode : only check config files and exit\n"
|
2006-06-25 20:48:02 -04:00
|
|
|
" -n sets the maximum total # of connections (%d)\n"
|
|
|
|
|
" -m limits the usable amount of memory (in MB)\n"
|
|
|
|
|
" -N sets the default, per-proxy maximum # of connections (%d)\n"
|
2010-09-23 12:30:22 -04:00
|
|
|
" -L set local peer name (default to hostname)\n"
|
2006-06-25 20:48:02 -04:00
|
|
|
" -p writes pids of all children to this file\n"
|
|
|
|
|
#if defined(ENABLE_EPOLL)
|
|
|
|
|
" -de disables epoll() usage even when available\n"
|
|
|
|
|
#endif
|
2007-04-09 06:03:06 -04:00
|
|
|
#if defined(ENABLE_KQUEUE)
|
|
|
|
|
" -dk disables kqueue() usage even when available\n"
|
|
|
|
|
#endif
|
2006-06-25 20:48:02 -04:00
|
|
|
#if defined(ENABLE_POLL)
|
|
|
|
|
" -dp disables poll() usage even when available\n"
|
2009-01-25 10:03:28 -05:00
|
|
|
#endif
|
2009-08-16 07:20:32 -04:00
|
|
|
#if defined(CONFIG_HAP_LINUX_SPLICE)
|
2009-01-25 10:03:28 -05:00
|
|
|
" -dS disables splice usage (broken on old kernels)\n"
|
2014-04-14 09:56:58 -04:00
|
|
|
#endif
|
|
|
|
|
#if defined(USE_GETADDRINFO)
|
|
|
|
|
" -dG disables getaddrinfo() usage\n"
|
2016-09-12 17:42:20 -04:00
|
|
|
#endif
|
|
|
|
|
#if defined(SO_REUSEPORT)
|
|
|
|
|
" -dR disables SO_REUSEPORT usage\n"
|
2006-06-25 20:48:02 -04:00
|
|
|
#endif
|
2016-11-07 15:03:16 -05:00
|
|
|
" -dr ignores server address resolution failures\n"
|
2014-01-29 06:24:34 -05:00
|
|
|
" -dV disables SSL verify on servers side\n"
|
2015-10-08 05:32:32 -04:00
|
|
|
" -sf/-st [pid ]* finishes/terminates old pids.\n"
|
2006-06-25 20:48:02 -04:00
|
|
|
"\n",
|
|
|
|
|
name, DEFAULT_MAXCONN, cfg_maxpconn);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
/* more specific functions ***************************************/
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
|
|
|
|
/*
|
2010-08-27 12:26:11 -04:00
|
|
|
* 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.
|
2006-06-25 20:48:02 -04:00
|
|
|
*/
|
2010-08-27 11:56:48 -04:00
|
|
|
void sig_soft_stop(struct sig_handler *sh)
|
2006-06-25 20:48:02 -04:00
|
|
|
{
|
|
|
|
|
soft_stop();
|
2010-08-27 11:56:48 -04:00
|
|
|
signal_unregister_handler(sh);
|
2007-05-13 18:39:29 -04:00
|
|
|
pool_gc2();
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* upon SIGTTOU, we pause everything
|
|
|
|
|
*/
|
2010-08-27 11:56:48 -04:00
|
|
|
void sig_pause(struct sig_handler *sh)
|
2006-06-25 20:48:02 -04:00
|
|
|
{
|
|
|
|
|
pause_proxies();
|
2007-05-13 18:39:29 -04:00
|
|
|
pool_gc2();
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* upon SIGTTIN, let's have a soft stop.
|
|
|
|
|
*/
|
2010-08-27 11:56:48 -04:00
|
|
|
void sig_listen(struct sig_handler *sh)
|
2006-06-25 20:48:02 -04:00
|
|
|
{
|
2011-07-24 12:28:10 -04:00
|
|
|
resume_proxies();
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* this function dumps every server's state when the process receives SIGHUP.
|
|
|
|
|
*/
|
2010-08-27 11:56:48 -04:00
|
|
|
void sig_dump_state(struct sig_handler *sh)
|
2006-06-25 20:48:02 -04:00
|
|
|
{
|
|
|
|
|
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) {
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_printf(&trash,
|
|
|
|
|
"SIGHUP: Server %s/%s is %s. Conn: %d act, %d pend, %lld tot.",
|
|
|
|
|
p->id, s->id,
|
2014-05-13 17:41:20 -04:00
|
|
|
(s->state != SRV_ST_STOPPED) ? "UP" : "DOWN",
|
2012-10-29 11:51:55 -04:00
|
|
|
s->cur_sess, s->nbpend, s->counters.cum_sess);
|
|
|
|
|
Warning("%s\n", trash.str);
|
|
|
|
|
send_log(p, LOG_NOTICE, "%s\n", trash.str);
|
2006-06-25 20:48:02 -04:00
|
|
|
s = s->next;
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-17 05:27:09 -04:00
|
|
|
/* FIXME: those info are a bit outdated. We should be able to distinguish between FE and BE. */
|
|
|
|
|
if (!p->srv) {
|
2012-10-29 11:51:55 -04:00
|
|
|
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);
|
2007-09-17 05:27:09 -04:00
|
|
|
} else if (p->srv_act == 0) {
|
2012-10-29 11:51:55 -04:00
|
|
|
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);
|
2006-06-25 20:48:02 -04:00
|
|
|
} else {
|
2012-10-29 11:51:55 -04:00
|
|
|
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);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
2012-10-29 11:51:55 -04:00
|
|
|
Warning("%s\n", trash.str);
|
|
|
|
|
send_log(p, LOG_NOTICE, "%s\n", trash.str);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
p = p->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-27 11:56:48 -04:00
|
|
|
void dump(struct sig_handler *sh)
|
2006-06-25 20:48:02 -04:00
|
|
|
{
|
2007-05-13 13:43:47 -04:00
|
|
|
/* dump memory usage then free everything possible */
|
|
|
|
|
dump_pools();
|
|
|
|
|
pool_gc2();
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
2016-05-13 17:52:56 -04:00
|
|
|
/* This function check if cfg_cfgfiles containes directories.
|
|
|
|
|
* If it find one, it add all the files (and only files) it containes
|
|
|
|
|
* in cfg_cfgfiles in place of the directory (and remove the directory).
|
|
|
|
|
* It add the files in lexical order.
|
|
|
|
|
* It add only files with .cfg extension.
|
|
|
|
|
* It doesn't add files with name starting with '.'
|
|
|
|
|
*/
|
|
|
|
|
void cfgfiles_expand_directories(void)
|
|
|
|
|
{
|
|
|
|
|
struct wordlist *wl, *wlb;
|
|
|
|
|
char *err = NULL;
|
|
|
|
|
|
|
|
|
|
list_for_each_entry_safe(wl, wlb, &cfg_cfgfiles, list) {
|
|
|
|
|
struct stat file_stat;
|
|
|
|
|
struct dirent **dir_entries = NULL;
|
|
|
|
|
int dir_entries_nb;
|
|
|
|
|
int dir_entries_it;
|
|
|
|
|
|
|
|
|
|
if (stat(wl->s, &file_stat)) {
|
|
|
|
|
Alert("Cannot open configuration file/directory %s : %s\n",
|
|
|
|
|
wl->s,
|
|
|
|
|
strerror(errno));
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!S_ISDIR(file_stat.st_mode))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* from this point wl->s is a directory */
|
|
|
|
|
|
|
|
|
|
dir_entries_nb = scandir(wl->s, &dir_entries, NULL, alphasort);
|
|
|
|
|
if (dir_entries_nb < 0) {
|
|
|
|
|
Alert("Cannot open configuration directory %s : %s\n",
|
|
|
|
|
wl->s,
|
|
|
|
|
strerror(errno));
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* for each element in the directory wl->s */
|
|
|
|
|
for (dir_entries_it = 0; dir_entries_it < dir_entries_nb; dir_entries_it++) {
|
|
|
|
|
struct dirent *dir_entry = dir_entries[dir_entries_it];
|
|
|
|
|
char *filename = NULL;
|
|
|
|
|
char *d_name_cfgext = strstr(dir_entry->d_name, ".cfg");
|
|
|
|
|
|
|
|
|
|
/* don't add filename that begin with .
|
|
|
|
|
* only add filename with .cfg extention
|
|
|
|
|
*/
|
|
|
|
|
if (dir_entry->d_name[0] == '.' ||
|
|
|
|
|
!(d_name_cfgext && d_name_cfgext[4] == '\0'))
|
|
|
|
|
goto next_dir_entry;
|
|
|
|
|
|
|
|
|
|
if (!memprintf(&filename, "%s/%s", wl->s, dir_entry->d_name)) {
|
|
|
|
|
Alert("Cannot load configuration files %s : out of memory.\n",
|
|
|
|
|
filename);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (stat(filename, &file_stat)) {
|
|
|
|
|
Alert("Cannot open configuration file %s : %s\n",
|
|
|
|
|
wl->s,
|
|
|
|
|
strerror(errno));
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* don't add anything else than regular file in cfg_cfgfiles
|
|
|
|
|
* this way we avoid loops
|
|
|
|
|
*/
|
|
|
|
|
if (!S_ISREG(file_stat.st_mode))
|
|
|
|
|
goto next_dir_entry;
|
|
|
|
|
|
|
|
|
|
if (!list_append_word(&wl->list, filename, &err)) {
|
|
|
|
|
Alert("Cannot load configuration files %s : %s\n",
|
|
|
|
|
filename,
|
|
|
|
|
err);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next_dir_entry:
|
|
|
|
|
free(filename);
|
|
|
|
|
free(dir_entry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(dir_entries);
|
|
|
|
|
|
|
|
|
|
/* remove the current directory (wl) from cfg_cfgfiles */
|
|
|
|
|
free(wl->s);
|
|
|
|
|
LIST_DEL(&wl->list);
|
|
|
|
|
free(wl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(err);
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/*
|
|
|
|
|
* 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;
|
2009-07-20 03:30:05 -04:00
|
|
|
int err_code = 0;
|
2016-05-13 17:52:55 -04:00
|
|
|
char *err_msg = NULL;
|
2010-01-03 15:12:30 -05:00
|
|
|
struct wordlist *wl;
|
2010-12-22 11:08:21 -05:00
|
|
|
char *progname;
|
2011-09-10 13:26:56 -04:00
|
|
|
char *change_dir = NULL;
|
MAJOR: filters: Add filters support
This patch adds the support of filters in HAProxy. The main idea is to have a
way to "easely" extend HAProxy by adding some "modules", called filters, that
will be able to change HAProxy behavior in a programmatic way.
To do so, many entry points has been added in code to let filters to hook up to
different steps of the processing. A filter must define a flt_ops sutrctures
(see include/types/filters.h for details). This structure contains all available
callbacks that a filter can define:
struct flt_ops {
/*
* Callbacks to manage the filter lifecycle
*/
int (*init) (struct proxy *p);
void (*deinit)(struct proxy *p);
int (*check) (struct proxy *p);
/*
* Stream callbacks
*/
void (*stream_start) (struct stream *s);
void (*stream_accept) (struct stream *s);
void (*session_establish)(struct stream *s);
void (*stream_stop) (struct stream *s);
/*
* HTTP callbacks
*/
int (*http_start) (struct stream *s, struct http_msg *msg);
int (*http_start_body) (struct stream *s, struct http_msg *msg);
int (*http_start_chunk) (struct stream *s, struct http_msg *msg);
int (*http_data) (struct stream *s, struct http_msg *msg);
int (*http_last_chunk) (struct stream *s, struct http_msg *msg);
int (*http_end_chunk) (struct stream *s, struct http_msg *msg);
int (*http_chunk_trailers)(struct stream *s, struct http_msg *msg);
int (*http_end_body) (struct stream *s, struct http_msg *msg);
void (*http_end) (struct stream *s, struct http_msg *msg);
void (*http_reset) (struct stream *s, struct http_msg *msg);
int (*http_pre_process) (struct stream *s, struct http_msg *msg);
int (*http_post_process) (struct stream *s, struct http_msg *msg);
void (*http_reply) (struct stream *s, short status,
const struct chunk *msg);
};
To declare and use a filter, in the configuration, the "filter" keyword must be
used in a listener/frontend section:
frontend test
...
filter <FILTER-NAME> [OPTIONS...]
The filter referenced by the <FILTER-NAME> must declare a configuration parser
on its own name to fill flt_ops and filter_conf field in the proxy's
structure. An exemple will be provided later to make it perfectly clear.
For now, filters cannot be used in backend section. But this is only a matter of
time. Documentation will also be added later. This is the first commit of a long
list about filters.
It is possible to have several filters on the same listener/frontend. These
filters are stored in an array of at most MAX_FILTERS elements (define in
include/types/filters.h). Again, this will be replaced later by a list of
filters.
The filter API has been highly refactored. Main changes are:
* Now, HA supports an infinite number of filters per proxy. To do so, filters
are stored in list.
* Because filters are stored in list, filters state has been moved from the
channel structure to the filter structure. This is cleaner because there is no
more info about filters in channel structure.
* It is possible to defined filters on backends only. For such filters,
stream_start/stream_stop callbacks are not called. Of course, it is possible
to mix frontend and backend filters.
* Now, TCP streams are also filtered. All callbacks without the 'http_' prefix
are called for all kind of streams. In addition, 2 new callbacks were added to
filter data exchanged through a TCP stream:
- tcp_data: it is called when new data are available or when old unprocessed
data are still waiting.
- tcp_forward_data: it is called when some data can be consumed.
* New callbacks attached to channel were added:
- channel_start_analyze: it is called when a filter is ready to process data
exchanged through a channel. 2 new analyzers (a frontend and a backend)
are attached to channels to call this callback. For a frontend filter, it
is called before any other analyzer. For a backend filter, it is called
when a backend is attached to a stream. So some processing cannot be
filtered in that case.
- channel_analyze: it is called before each analyzer attached to a channel,
expects analyzers responsible for data sending.
- channel_end_analyze: it is called when all other analyzers have finished
their processing. A new analyzers is attached to channels to call this
callback. For a TCP stream, this is always the last one called. For a HTTP
one, the callback is called when a request/response ends, so it is called
one time for each request/response.
* 'session_established' callback has been removed. Everything that is done in
this callback can be handled by 'channel_start_analyze' on the response
channel.
* 'http_pre_process' and 'http_post_process' callbacks have been replaced by
'channel_analyze'.
* 'http_start' callback has been replaced by 'http_headers'. This new one is
called just before headers sending and parsing of the body.
* 'http_end' callback has been replaced by 'channel_end_analyze'.
* It is possible to set a forwarder for TCP channels. It was already possible to
do it for HTTP ones.
* Forwarders can partially consumed forwardable data. For this reason a new
HTTP message state was added before HTTP_MSG_DONE : HTTP_MSG_ENDING.
Now all filters can define corresponding callbacks (http_forward_data
and tcp_forward_data). Each filter owns 2 offsets relative to buf->p, next and
forward, to track, respectively, input data already parsed but not forwarded yet
by the filter and parsed data considered as forwarded by the filter. A any time,
we have the warranty that a filter cannot parse or forward more input than
previous ones. And, of course, it cannot forward more input than it has
parsed. 2 macros has been added to retrieve these offets: FLT_NXT and FLT_FWD.
In addition, 2 functions has been added to change the 'next size' and the
'forward size' of a filter. When a filter parses input data, it can alter these
data, so the size of these data can vary. This action has an effet on all
previous filters that must be handled. To do so, the function
'filter_change_next_size' must be called, passing the size variation. In the
same spirit, if a filter alter forwarded data, it must call the function
'filter_change_forward_size'. 'filter_change_next_size' can be called in
'http_data' and 'tcp_data' callbacks and only these ones. And
'filter_change_forward_size' can be called in 'http_forward_data' and
'tcp_forward_data' callbacks and only these ones. The data changes are the
filter responsability, but with some limitation. It must not change already
parsed/forwarded data or data that previous filters have not parsed/forwarded
yet.
Because filters can be used on backends, when we the backend is set for a
stream, we add filters defined for this backend in the filter list of the
stream. But we must only do that when the backend and the frontend of the stream
are not the same. Else same filters are added a second time leading to undefined
behavior.
The HTTP compression code had to be moved.
So it simplifies http_response_forward_body function. To do so, the way the data
are forwarded has changed. Now, a filter (and only one) can forward data. In a
commit to come, this limitation will be removed to let all filters take part to
data forwarding. There are 2 new functions that filters should use to deal with
this feature:
* flt_set_http_data_forwarder: This function sets the filter (using its id)
that will forward data for the specified HTTP message. It is possible if it
was not already set by another filter _AND_ if no data was yet forwarded
(msg->msg_state <= HTTP_MSG_BODY). It returns -1 if an error occurs.
* flt_http_data_forwarder: This function returns the filter id that will
forward data for the specified HTTP message. If there is no forwarder set, it
returns -1.
When an HTTP data forwarder is set for the response, the HTTP compression is
disabled. Of course, this is not definitive.
2015-04-30 05:48:27 -04:00
|
|
|
struct proxy *px;
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_init(&trash, malloc(global.tune.bufsize), global.tune.bufsize);
|
2013-12-13 08:41:10 -05:00
|
|
|
alloc_trash_buffers(global.tune.bufsize);
|
2012-05-16 08:16:48 -04:00
|
|
|
|
2010-09-23 12:30:22 -04:00
|
|
|
/* NB: POSIX does not make it mandatory for gethostname() to NULL-terminate
|
|
|
|
|
* the string in case of truncation, and at least FreeBSD appears not to do
|
|
|
|
|
* it.
|
|
|
|
|
*/
|
|
|
|
|
memset(hostname, 0, sizeof(hostname));
|
|
|
|
|
gethostname(hostname, sizeof(hostname) - 1);
|
|
|
|
|
memset(localpeer, 0, sizeof(localpeer));
|
|
|
|
|
memcpy(localpeer, hostname, (sizeof(hostname) > sizeof(localpeer) ? sizeof(localpeer) : sizeof(hostname)) - 1);
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/*
|
|
|
|
|
* Initialize the previously static variables.
|
|
|
|
|
*/
|
|
|
|
|
|
2009-01-25 07:56:13 -05:00
|
|
|
totalconn = actconn = maxfd = listeners = stopping = 0;
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAPROXY_MEMMAX
|
2015-12-14 06:46:07 -05:00
|
|
|
global.rlimit_memmax_all = HAPROXY_MEMMAX;
|
2006-06-25 20:48:02 -04:00
|
|
|
#endif
|
|
|
|
|
|
2016-03-27 05:08:03 -04:00
|
|
|
tzset();
|
2008-06-23 08:00:57 -04:00
|
|
|
tv_update_date(-1,-1);
|
2006-06-25 20:48:02 -04:00
|
|
|
start_date = now;
|
|
|
|
|
|
2014-02-14 05:59:04 -05:00
|
|
|
srandom(now_ms - getpid());
|
|
|
|
|
|
2016-02-12 07:23:03 -05:00
|
|
|
init_log();
|
2009-05-10 03:01:21 -04:00
|
|
|
signal_init();
|
2013-01-11 09:49:37 -05:00
|
|
|
if (init_acl() != 0)
|
|
|
|
|
exit(1);
|
2007-05-13 13:43:47 -04:00
|
|
|
init_task();
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
init_stream();
|
2015-04-03 07:53:24 -04:00
|
|
|
init_session();
|
2012-10-26 14:10:28 -04:00
|
|
|
init_connection();
|
2009-09-23 17:37:52 -04:00
|
|
|
/* warning, we init buffers later */
|
2007-05-13 14:19:55 -04:00
|
|
|
init_pendconn();
|
2006-12-24 11:47:20 -05:00
|
|
|
init_proto_http();
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2015-01-23 08:06:13 -05:00
|
|
|
/* Initialise lua. */
|
|
|
|
|
hlua_init();
|
|
|
|
|
|
2009-01-25 09:42:27 -05:00
|
|
|
global.tune.options |= GTUNE_USE_SELECT; /* select() is always available */
|
2006-06-25 20:48:02 -04:00
|
|
|
#if defined(ENABLE_POLL)
|
2009-01-25 09:42:27 -05:00
|
|
|
global.tune.options |= GTUNE_USE_POLL;
|
2006-06-25 20:48:02 -04:00
|
|
|
#endif
|
|
|
|
|
#if defined(ENABLE_EPOLL)
|
2009-01-25 09:42:27 -05:00
|
|
|
global.tune.options |= GTUNE_USE_EPOLL;
|
2006-06-25 20:48:02 -04:00
|
|
|
#endif
|
2007-04-09 06:03:06 -04:00
|
|
|
#if defined(ENABLE_KQUEUE)
|
2009-01-25 09:42:27 -05:00
|
|
|
global.tune.options |= GTUNE_USE_KQUEUE;
|
2007-04-09 06:03:06 -04:00
|
|
|
#endif
|
2009-08-16 07:20:32 -04:00
|
|
|
#if defined(CONFIG_HAP_LINUX_SPLICE)
|
2009-01-25 10:03:28 -05:00
|
|
|
global.tune.options |= GTUNE_USE_SPLICE;
|
|
|
|
|
#endif
|
2014-04-14 09:56:58 -04:00
|
|
|
#if defined(USE_GETADDRINFO)
|
|
|
|
|
global.tune.options |= GTUNE_USE_GAI;
|
|
|
|
|
#endif
|
2016-09-12 17:42:20 -04:00
|
|
|
#if defined(SO_REUSEPORT)
|
|
|
|
|
global.tune.options |= GTUNE_USE_REUSEPORT;
|
|
|
|
|
#endif
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
pid = getpid();
|
|
|
|
|
progname = *argv;
|
|
|
|
|
while ((tmp = strchr(progname, '/')) != NULL)
|
|
|
|
|
progname = tmp + 1;
|
|
|
|
|
|
2010-12-22 11:08:21 -05:00
|
|
|
/* the process name is used for the logs only */
|
2015-10-01 07:18:13 -04:00
|
|
|
chunk_initstr(&global.log_tag, strdup(progname));
|
2010-12-22 11:08:21 -05:00
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
argc--; argv++;
|
|
|
|
|
while (argc > 0) {
|
|
|
|
|
char *flag;
|
|
|
|
|
|
|
|
|
|
if (**argv == '-') {
|
|
|
|
|
flag = *argv+1;
|
|
|
|
|
|
|
|
|
|
/* 1 arg */
|
|
|
|
|
if (*flag == 'v') {
|
|
|
|
|
display_version();
|
2007-12-02 05:28:59 -05:00
|
|
|
if (flag[1] == 'v') /* -vv */
|
|
|
|
|
display_build_opts();
|
2006-06-25 20:48:02 -04:00
|
|
|
exit(0);
|
|
|
|
|
}
|
|
|
|
|
#if defined(ENABLE_EPOLL)
|
|
|
|
|
else if (*flag == 'd' && flag[1] == 'e')
|
2009-01-25 09:42:27 -05:00
|
|
|
global.tune.options &= ~GTUNE_USE_EPOLL;
|
2006-06-25 20:48:02 -04:00
|
|
|
#endif
|
|
|
|
|
#if defined(ENABLE_POLL)
|
|
|
|
|
else if (*flag == 'd' && flag[1] == 'p')
|
2009-01-25 09:42:27 -05:00
|
|
|
global.tune.options &= ~GTUNE_USE_POLL;
|
2007-04-09 06:03:06 -04:00
|
|
|
#endif
|
2007-04-10 16:45:11 -04:00
|
|
|
#if defined(ENABLE_KQUEUE)
|
2007-04-09 06:03:06 -04:00
|
|
|
else if (*flag == 'd' && flag[1] == 'k')
|
2009-01-25 09:42:27 -05:00
|
|
|
global.tune.options &= ~GTUNE_USE_KQUEUE;
|
2009-01-25 10:03:28 -05:00
|
|
|
#endif
|
2009-08-16 07:20:32 -04:00
|
|
|
#if defined(CONFIG_HAP_LINUX_SPLICE)
|
2009-01-25 10:03:28 -05:00
|
|
|
else if (*flag == 'd' && flag[1] == 'S')
|
|
|
|
|
global.tune.options &= ~GTUNE_USE_SPLICE;
|
2014-04-14 09:56:58 -04:00
|
|
|
#endif
|
|
|
|
|
#if defined(USE_GETADDRINFO)
|
|
|
|
|
else if (*flag == 'd' && flag[1] == 'G')
|
|
|
|
|
global.tune.options &= ~GTUNE_USE_GAI;
|
2016-09-12 17:42:20 -04:00
|
|
|
#endif
|
|
|
|
|
#if defined(SO_REUSEPORT)
|
|
|
|
|
else if (*flag == 'd' && flag[1] == 'R')
|
|
|
|
|
global.tune.options &= ~GTUNE_USE_REUSEPORT;
|
2006-06-25 20:48:02 -04:00
|
|
|
#endif
|
2014-01-29 06:24:34 -05:00
|
|
|
else if (*flag == 'd' && flag[1] == 'V')
|
|
|
|
|
global.ssl_server_verify = SSL_SERVER_VERIFY_NONE;
|
2006-06-25 20:48:02 -04:00
|
|
|
else if (*flag == 'V')
|
|
|
|
|
arg_mode |= MODE_VERBOSE;
|
|
|
|
|
else if (*flag == 'd' && flag[1] == 'b')
|
|
|
|
|
arg_mode |= MODE_FOREGROUND;
|
2012-05-08 09:40:42 -04:00
|
|
|
else if (*flag == 'd' && flag[1] == 'M')
|
|
|
|
|
mem_poison_byte = flag[2] ? strtol(flag + 2, NULL, 0) : 'P';
|
2016-11-07 15:03:16 -05:00
|
|
|
else if (*flag == 'd' && flag[1] == 'r')
|
|
|
|
|
global.tune.options |= GTUNE_RESOLVE_DONTFAIL;
|
2006-06-25 20:48:02 -04:00
|
|
|
else if (*flag == 'd')
|
|
|
|
|
arg_mode |= MODE_DEBUG;
|
|
|
|
|
else if (*flag == 'c')
|
|
|
|
|
arg_mode |= MODE_CHECK;
|
2013-02-12 04:53:52 -05:00
|
|
|
else if (*flag == 'D') {
|
2009-05-18 10:29:51 -04:00
|
|
|
arg_mode |= MODE_DAEMON;
|
2013-02-12 04:53:52 -05:00
|
|
|
if (flag[1] == 's') /* -Ds */
|
|
|
|
|
arg_mode |= MODE_SYSTEMD;
|
|
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
else if (*flag == 'q')
|
|
|
|
|
arg_mode |= MODE_QUIET;
|
|
|
|
|
else if (*flag == 's' && (flag[1] == 'f' || flag[1] == 't')) {
|
|
|
|
|
/* list of pids to finish ('f') or terminate ('t') */
|
|
|
|
|
|
|
|
|
|
if (flag[1] == 'f')
|
|
|
|
|
oldpids_sig = SIGUSR1; /* finish then exit */
|
|
|
|
|
else
|
|
|
|
|
oldpids_sig = SIGTERM; /* terminate immediately */
|
|
|
|
|
|
2015-10-08 05:32:32 -04:00
|
|
|
while (argc > 1 && argv[1][0] != '-') {
|
|
|
|
|
oldpids = realloc(oldpids, (nb_oldpids + 1) * sizeof(int));
|
|
|
|
|
if (!oldpids) {
|
|
|
|
|
Alert("Cannot allocate old pid : out of memory.\n");
|
|
|
|
|
exit(1);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
2015-10-08 05:32:32 -04:00
|
|
|
argc--; argv++;
|
|
|
|
|
oldpids[nb_oldpids] = atol(*argv);
|
|
|
|
|
if (oldpids[nb_oldpids] <= 0)
|
|
|
|
|
usage(progname);
|
|
|
|
|
nb_oldpids++;
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
}
|
2015-10-08 05:58:48 -04:00
|
|
|
else if (flag[0] == '-' && flag[1] == 0) { /* "--" */
|
|
|
|
|
/* now that's a cfgfile list */
|
|
|
|
|
argv++; argc--;
|
|
|
|
|
while (argc > 0) {
|
2016-05-13 17:52:55 -04:00
|
|
|
if (!list_append_word(&cfg_cfgfiles, *argv, &err_msg)) {
|
|
|
|
|
Alert("Cannot load configuration file/directory %s : %s\n",
|
|
|
|
|
*argv,
|
|
|
|
|
err_msg);
|
2015-10-08 05:58:48 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
argv++; argc--;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
else { /* >=2 args */
|
|
|
|
|
argv++; argc--;
|
|
|
|
|
if (argc == 0)
|
2011-09-10 13:20:23 -04:00
|
|
|
usage(progname);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
switch (*flag) {
|
2011-09-10 13:26:56 -04:00
|
|
|
case 'C' : change_dir = *argv; break;
|
2006-06-25 20:48:02 -04:00
|
|
|
case 'n' : cfg_maxconn = atol(*argv); break;
|
2015-12-14 06:46:07 -05:00
|
|
|
case 'm' : global.rlimit_memmax_all = atol(*argv); break;
|
2006-06-25 20:48:02 -04:00
|
|
|
case 'N' : cfg_maxpconn = atol(*argv); break;
|
2010-09-23 12:30:22 -04:00
|
|
|
case 'L' : strncpy(localpeer, *argv, sizeof(localpeer) - 1); break;
|
2009-06-22 10:02:30 -04:00
|
|
|
case 'f' :
|
2016-05-13 17:52:55 -04:00
|
|
|
if (!list_append_word(&cfg_cfgfiles, *argv, &err_msg)) {
|
|
|
|
|
Alert("Cannot load configuration file/directory %s : %s\n",
|
|
|
|
|
*argv,
|
|
|
|
|
err_msg);
|
2009-06-22 10:02:30 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2006-06-25 20:48:02 -04:00
|
|
|
case 'p' : cfg_pidfile = *argv; break;
|
2011-09-10 13:20:23 -04:00
|
|
|
default: usage(progname);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2011-09-10 13:20:23 -04:00
|
|
|
usage(progname);
|
2006-06-25 20:48:02 -04:00
|
|
|
argv++; argc--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
global.mode = MODE_STARTING | /* during startup, we want most of the alerts */
|
2013-02-12 04:53:52 -05:00
|
|
|
(arg_mode & (MODE_DAEMON | MODE_SYSTEMD | MODE_FOREGROUND | MODE_VERBOSE
|
2006-06-25 20:48:02 -04:00
|
|
|
| MODE_QUIET | MODE_CHECK | MODE_DEBUG));
|
|
|
|
|
|
2011-09-10 13:26:56 -04:00
|
|
|
if (change_dir && chdir(change_dir) < 0) {
|
|
|
|
|
Alert("Could not change to directory %s : %s\n", change_dir, strerror(errno));
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-13 17:52:56 -04:00
|
|
|
/* handle cfgfiles that are actualy directories */
|
|
|
|
|
cfgfiles_expand_directories();
|
|
|
|
|
|
|
|
|
|
if (LIST_ISEMPTY(&cfg_cfgfiles))
|
|
|
|
|
usage(progname);
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
global.maxsock = 10; /* reserve 10 fds ; will be incremented by socket eaters */
|
2009-06-22 09:48:36 -04:00
|
|
|
|
|
|
|
|
init_default_instance();
|
|
|
|
|
|
2010-01-03 15:12:30 -05:00
|
|
|
list_for_each_entry(wl, &cfg_cfgfiles, list) {
|
2009-12-06 07:10:44 -05:00
|
|
|
int ret;
|
|
|
|
|
|
2010-01-03 15:12:30 -05:00
|
|
|
ret = readcfgfile(wl->s);
|
2009-12-06 07:10:44 -05:00
|
|
|
if (ret == -1) {
|
|
|
|
|
Alert("Could not open configuration file %s : %s\n",
|
2010-01-03 15:12:30 -05:00
|
|
|
wl->s, strerror(errno));
|
2009-12-06 07:10:44 -05:00
|
|
|
exit(1);
|
|
|
|
|
}
|
2009-12-15 15:46:25 -05:00
|
|
|
if (ret & (ERR_ABORT|ERR_FATAL))
|
2010-01-03 15:12:30 -05:00
|
|
|
Alert("Error(s) found in configuration file : %s\n", wl->s);
|
2009-12-15 15:46:25 -05:00
|
|
|
err_code |= ret;
|
2009-07-20 03:30:05 -04:00
|
|
|
if (err_code & ERR_ABORT)
|
2009-06-22 10:02:30 -04:00
|
|
|
exit(1);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
2007-10-14 17:40:01 -04:00
|
|
|
|
2014-03-11 09:29:22 -04:00
|
|
|
pattern_finalize_config();
|
2015-05-09 02:46:01 -04:00
|
|
|
#if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
|
|
|
|
|
tlskeys_finalize_config();
|
|
|
|
|
#endif
|
2014-03-11 09:29:22 -04:00
|
|
|
|
2009-07-23 07:36:36 -04:00
|
|
|
err_code |= check_config_validity();
|
|
|
|
|
if (err_code & (ERR_ABORT|ERR_FATAL)) {
|
|
|
|
|
Alert("Fatal errors found in configuration.\n");
|
2009-06-22 09:48:36 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2015-12-14 06:46:07 -05:00
|
|
|
/* recompute the amount of per-process memory depending on nbproc and
|
|
|
|
|
* the shared SSL cache size (allowed to exist in all processes).
|
|
|
|
|
*/
|
|
|
|
|
if (global.rlimit_memmax_all) {
|
|
|
|
|
#if defined (USE_OPENSSL) && !defined(USE_PRIVATE_CACHE)
|
|
|
|
|
int64_t ssl_cache_bytes = global.tune.sslcachesize * 200LL;
|
|
|
|
|
|
|
|
|
|
global.rlimit_memmax =
|
|
|
|
|
((((int64_t)global.rlimit_memmax_all * 1048576LL) -
|
|
|
|
|
ssl_cache_bytes) / global.nbproc +
|
|
|
|
|
ssl_cache_bytes + 1048575LL) / 1048576LL;
|
|
|
|
|
#else
|
|
|
|
|
global.rlimit_memmax = global.rlimit_memmax_all / global.nbproc;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
|
2016-11-02 10:33:15 -04:00
|
|
|
/* Apply server states */
|
|
|
|
|
apply_server_state();
|
|
|
|
|
|
|
|
|
|
for (px = proxy; px; px = px->next)
|
|
|
|
|
srv_compute_all_admin_states(px);
|
|
|
|
|
|
2016-11-02 10:34:05 -04:00
|
|
|
/* Apply servers' configured address */
|
|
|
|
|
err_code |= srv_init_addr();
|
|
|
|
|
if (err_code & (ERR_ABORT|ERR_FATAL)) {
|
|
|
|
|
Alert("Failed to initialize server(s) addr.\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
if (global.mode & MODE_CHECK) {
|
2012-02-02 11:48:18 -05:00
|
|
|
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)
|
2012-09-20 10:48:07 -04:00
|
|
|
if (px->state == PR_STNEW && !LIST_ISEMPTY(&px->conf.listeners))
|
2012-02-02 11:48:18 -05:00
|
|
|
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);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
2011-08-01 14:57:55 -04:00
|
|
|
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;
|
|
|
|
|
|
2012-08-27 18:06:31 -04:00
|
|
|
/* now we know the buffer size, we can initialize the channels and buffers */
|
2012-10-12 17:49:43 -04:00
|
|
|
init_buffer();
|
2015-06-01 07:57:22 -04:00
|
|
|
#if defined(USE_DEVICEATLAS)
|
|
|
|
|
init_deviceatlas();
|
|
|
|
|
#endif
|
2015-06-29 10:43:25 -04:00
|
|
|
#ifdef USE_51DEGREES
|
|
|
|
|
init_51degrees();
|
|
|
|
|
#endif
|
2016-11-04 05:55:08 -04:00
|
|
|
#ifdef USE_WURFL
|
|
|
|
|
ha_wurfl_init();
|
|
|
|
|
#endif
|
2009-09-23 17:37:52 -04:00
|
|
|
|
MAJOR: filters: Add filters support
This patch adds the support of filters in HAProxy. The main idea is to have a
way to "easely" extend HAProxy by adding some "modules", called filters, that
will be able to change HAProxy behavior in a programmatic way.
To do so, many entry points has been added in code to let filters to hook up to
different steps of the processing. A filter must define a flt_ops sutrctures
(see include/types/filters.h for details). This structure contains all available
callbacks that a filter can define:
struct flt_ops {
/*
* Callbacks to manage the filter lifecycle
*/
int (*init) (struct proxy *p);
void (*deinit)(struct proxy *p);
int (*check) (struct proxy *p);
/*
* Stream callbacks
*/
void (*stream_start) (struct stream *s);
void (*stream_accept) (struct stream *s);
void (*session_establish)(struct stream *s);
void (*stream_stop) (struct stream *s);
/*
* HTTP callbacks
*/
int (*http_start) (struct stream *s, struct http_msg *msg);
int (*http_start_body) (struct stream *s, struct http_msg *msg);
int (*http_start_chunk) (struct stream *s, struct http_msg *msg);
int (*http_data) (struct stream *s, struct http_msg *msg);
int (*http_last_chunk) (struct stream *s, struct http_msg *msg);
int (*http_end_chunk) (struct stream *s, struct http_msg *msg);
int (*http_chunk_trailers)(struct stream *s, struct http_msg *msg);
int (*http_end_body) (struct stream *s, struct http_msg *msg);
void (*http_end) (struct stream *s, struct http_msg *msg);
void (*http_reset) (struct stream *s, struct http_msg *msg);
int (*http_pre_process) (struct stream *s, struct http_msg *msg);
int (*http_post_process) (struct stream *s, struct http_msg *msg);
void (*http_reply) (struct stream *s, short status,
const struct chunk *msg);
};
To declare and use a filter, in the configuration, the "filter" keyword must be
used in a listener/frontend section:
frontend test
...
filter <FILTER-NAME> [OPTIONS...]
The filter referenced by the <FILTER-NAME> must declare a configuration parser
on its own name to fill flt_ops and filter_conf field in the proxy's
structure. An exemple will be provided later to make it perfectly clear.
For now, filters cannot be used in backend section. But this is only a matter of
time. Documentation will also be added later. This is the first commit of a long
list about filters.
It is possible to have several filters on the same listener/frontend. These
filters are stored in an array of at most MAX_FILTERS elements (define in
include/types/filters.h). Again, this will be replaced later by a list of
filters.
The filter API has been highly refactored. Main changes are:
* Now, HA supports an infinite number of filters per proxy. To do so, filters
are stored in list.
* Because filters are stored in list, filters state has been moved from the
channel structure to the filter structure. This is cleaner because there is no
more info about filters in channel structure.
* It is possible to defined filters on backends only. For such filters,
stream_start/stream_stop callbacks are not called. Of course, it is possible
to mix frontend and backend filters.
* Now, TCP streams are also filtered. All callbacks without the 'http_' prefix
are called for all kind of streams. In addition, 2 new callbacks were added to
filter data exchanged through a TCP stream:
- tcp_data: it is called when new data are available or when old unprocessed
data are still waiting.
- tcp_forward_data: it is called when some data can be consumed.
* New callbacks attached to channel were added:
- channel_start_analyze: it is called when a filter is ready to process data
exchanged through a channel. 2 new analyzers (a frontend and a backend)
are attached to channels to call this callback. For a frontend filter, it
is called before any other analyzer. For a backend filter, it is called
when a backend is attached to a stream. So some processing cannot be
filtered in that case.
- channel_analyze: it is called before each analyzer attached to a channel,
expects analyzers responsible for data sending.
- channel_end_analyze: it is called when all other analyzers have finished
their processing. A new analyzers is attached to channels to call this
callback. For a TCP stream, this is always the last one called. For a HTTP
one, the callback is called when a request/response ends, so it is called
one time for each request/response.
* 'session_established' callback has been removed. Everything that is done in
this callback can be handled by 'channel_start_analyze' on the response
channel.
* 'http_pre_process' and 'http_post_process' callbacks have been replaced by
'channel_analyze'.
* 'http_start' callback has been replaced by 'http_headers'. This new one is
called just before headers sending and parsing of the body.
* 'http_end' callback has been replaced by 'channel_end_analyze'.
* It is possible to set a forwarder for TCP channels. It was already possible to
do it for HTTP ones.
* Forwarders can partially consumed forwardable data. For this reason a new
HTTP message state was added before HTTP_MSG_DONE : HTTP_MSG_ENDING.
Now all filters can define corresponding callbacks (http_forward_data
and tcp_forward_data). Each filter owns 2 offsets relative to buf->p, next and
forward, to track, respectively, input data already parsed but not forwarded yet
by the filter and parsed data considered as forwarded by the filter. A any time,
we have the warranty that a filter cannot parse or forward more input than
previous ones. And, of course, it cannot forward more input than it has
parsed. 2 macros has been added to retrieve these offets: FLT_NXT and FLT_FWD.
In addition, 2 functions has been added to change the 'next size' and the
'forward size' of a filter. When a filter parses input data, it can alter these
data, so the size of these data can vary. This action has an effet on all
previous filters that must be handled. To do so, the function
'filter_change_next_size' must be called, passing the size variation. In the
same spirit, if a filter alter forwarded data, it must call the function
'filter_change_forward_size'. 'filter_change_next_size' can be called in
'http_data' and 'tcp_data' callbacks and only these ones. And
'filter_change_forward_size' can be called in 'http_forward_data' and
'tcp_forward_data' callbacks and only these ones. The data changes are the
filter responsability, but with some limitation. It must not change already
parsed/forwarded data or data that previous filters have not parsed/forwarded
yet.
Because filters can be used on backends, when we the backend is set for a
stream, we add filters defined for this backend in the filter list of the
stream. But we must only do that when the backend and the frontend of the stream
are not the same. Else same filters are added a second time leading to undefined
behavior.
The HTTP compression code had to be moved.
So it simplifies http_response_forward_body function. To do so, the way the data
are forwarded has changed. Now, a filter (and only one) can forward data. In a
commit to come, this limitation will be removed to let all filters take part to
data forwarding. There are 2 new functions that filters should use to deal with
this feature:
* flt_set_http_data_forwarder: This function sets the filter (using its id)
that will forward data for the specified HTTP message. It is possible if it
was not already set by another filter _AND_ if no data was yet forwarded
(msg->msg_state <= HTTP_MSG_BODY). It returns -1 if an error occurs.
* flt_http_data_forwarder: This function returns the filter id that will
forward data for the specified HTTP message. If there is no forwarder set, it
returns -1.
When an HTTP data forwarder is set for the response, the HTTP compression is
disabled. Of course, this is not definitive.
2015-04-30 05:48:27 -04:00
|
|
|
for (px = proxy; px; px = px->next) {
|
|
|
|
|
err_code |= flt_init(px);
|
|
|
|
|
if (err_code & (ERR_ABORT|ERR_FATAL)) {
|
|
|
|
|
Alert("Failed to initialize filters for proxy '%s'.\n",
|
|
|
|
|
px->id);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-14 17:40:01 -04:00
|
|
|
if (start_checks() < 0)
|
|
|
|
|
exit(1);
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
if (cfg_maxconn > 0)
|
|
|
|
|
global.maxconn = cfg_maxconn;
|
|
|
|
|
|
|
|
|
|
if (cfg_pidfile) {
|
2008-08-03 06:19:50 -04:00
|
|
|
free(global.pidfile);
|
2006-06-25 20:48:02 -04:00
|
|
|
global.pidfile = strdup(cfg_pidfile);
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-15 15:45:22 -05:00
|
|
|
/* 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
|
2015-01-28 13:03:21 -05:00
|
|
|
* 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.
|
2015-01-15 15:45:22 -05:00
|
|
|
*/
|
|
|
|
|
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 /
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
((STREAM_MAX_COST + 2 * global.tune.bufsize) + // stream + 2 buffers per stream
|
2015-01-15 15:45:22 -05:00
|
|
|
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);
|
2015-01-28 13:03:21 -05:00
|
|
|
#ifdef SYSTEM_MAXCONN
|
|
|
|
|
if (global.maxconn > DEFAULT_MAXCONN)
|
|
|
|
|
global.maxconn = DEFAULT_MAXCONN;
|
|
|
|
|
#endif /* SYSTEM_MAXCONN */
|
2015-01-15 15:45:22 -05:00
|
|
|
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;
|
|
|
|
|
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
sslmem = mem - global.maxconn * (int64_t)(STREAM_MAX_COST + 2 * global.tune.bufsize);
|
2015-01-15 15:45:22 -05:00
|
|
|
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,
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
(int)(mem / (STREAM_MAX_COST + 2 * global.tune.bufsize)),
|
2015-01-15 15:45:22 -05:00
|
|
|
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;
|
|
|
|
|
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
global.maxconn = clearmem / (STREAM_MAX_COST + 2 * global.tune.bufsize);
|
2015-01-15 15:45:22 -05:00
|
|
|
global.maxconn = round_2dig(global.maxconn);
|
2015-01-28 13:03:21 -05:00
|
|
|
#ifdef SYSTEM_MAXCONN
|
|
|
|
|
if (global.maxconn > DEFAULT_MAXCONN)
|
|
|
|
|
global.maxconn = DEFAULT_MAXCONN;
|
|
|
|
|
#endif /* SYSTEM_MAXCONN */
|
2015-01-15 15:45:22 -05:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2009-01-18 15:44:07 -05:00
|
|
|
if (!global.maxpipes) {
|
|
|
|
|
/* maxpipes not specified. Count how many frontends and backends
|
|
|
|
|
* may be using splicing, and bound that to maxconn.
|
|
|
|
|
*/
|
|
|
|
|
struct proxy *cur;
|
|
|
|
|
int nbfe = 0, nbbe = 0;
|
|
|
|
|
|
|
|
|
|
for (cur = proxy; cur; cur = cur->next) {
|
|
|
|
|
if (cur->options2 & (PR_O2_SPLIC_ANY)) {
|
|
|
|
|
if (cur->cap & PR_CAP_FE)
|
|
|
|
|
nbfe += cur->maxconn;
|
|
|
|
|
if (cur->cap & PR_CAP_BE)
|
2009-01-25 04:42:05 -05:00
|
|
|
nbbe += cur->fullconn ? cur->fullconn : global.maxconn;
|
2009-01-18 15:44:07 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
global.maxpipes = MAX(nbfe, nbbe);
|
|
|
|
|
if (global.maxpipes > global.maxconn)
|
|
|
|
|
global.maxpipes = global.maxconn;
|
2009-01-25 08:06:58 -05:00
|
|
|
global.maxpipes /= 4;
|
2009-01-18 15:44:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-09-07 08:26:33 -04:00
|
|
|
global.hardmaxconn = global.maxconn; /* keep this max value */
|
2006-06-25 20:48:02 -04:00
|
|
|
global.maxsock += global.maxconn * 2; /* each connection needs two sockets */
|
2009-01-18 14:39:42 -05:00
|
|
|
global.maxsock += global.maxpipes * 2; /* each pipe needs two FDs */
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2011-09-07 12:00:47 -04:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-03 11:16:49 -04:00
|
|
|
if (global.tune.maxpollevents <= 0)
|
|
|
|
|
global.tune.maxpollevents = MAX_POLL_EVENTS;
|
|
|
|
|
|
2009-03-21 15:43:57 -04:00
|
|
|
if (global.tune.recv_enough == 0)
|
|
|
|
|
global.tune.recv_enough = MIN_RECV_AT_ONCE_ENOUGH;
|
|
|
|
|
|
2015-09-28 07:53:23 -04:00
|
|
|
if (global.tune.maxrewrite < 0)
|
|
|
|
|
global.tune.maxrewrite = MAXREWRITE;
|
|
|
|
|
|
2009-08-17 01:23:33 -04:00
|
|
|
if (global.tune.maxrewrite >= global.tune.bufsize / 2)
|
|
|
|
|
global.tune.maxrewrite = global.tune.bufsize / 2;
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
if (arg_mode & (MODE_DEBUG | MODE_FOREGROUND)) {
|
|
|
|
|
/* command line debug mode inhibits configuration mode */
|
2013-02-12 04:53:52 -05:00
|
|
|
global.mode &= ~(MODE_DAEMON | MODE_SYSTEMD | MODE_QUIET);
|
2012-10-26 10:04:28 -04:00
|
|
|
global.mode |= (arg_mode & (MODE_DEBUG | MODE_FOREGROUND));
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
2012-10-26 10:04:28 -04:00
|
|
|
|
2013-02-12 04:53:52 -05:00
|
|
|
if (arg_mode & (MODE_DAEMON | MODE_SYSTEMD)) {
|
2012-10-26 10:04:28 -04:00
|
|
|
/* command line daemon mode inhibits foreground and debug modes mode */
|
|
|
|
|
global.mode &= ~(MODE_DEBUG | MODE_FOREGROUND);
|
2013-02-12 04:53:52 -05:00
|
|
|
global.mode |= (arg_mode & (MODE_DAEMON | MODE_SYSTEMD));
|
2012-10-26 10:04:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
global.mode |= (arg_mode & (MODE_QUIET | MODE_VERBOSE));
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2013-02-12 04:53:52 -05:00
|
|
|
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);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
2013-02-12 04:53:52 -05:00
|
|
|
if ((global.nbproc > 1) && !(global.mode & (MODE_DAEMON | MODE_SYSTEMD))) {
|
2006-06-25 20:48:02 -04:00
|
|
|
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;
|
|
|
|
|
|
2016-04-03 07:48:42 -04:00
|
|
|
swap_buffer = calloc(1, global.tune.bufsize);
|
|
|
|
|
get_http_auth_buff = calloc(1, global.tune.bufsize);
|
2015-07-24 13:31:59 -04:00
|
|
|
static_table_key = calloc(1, sizeof(*static_table_key));
|
2010-02-26 05:12:27 -05:00
|
|
|
|
2016-04-03 07:48:42 -04:00
|
|
|
fdinfo = calloc(1, sizeof(struct fdinfo) * (global.maxsock));
|
|
|
|
|
fdtab = calloc(1, sizeof(struct fdtab) * (global.maxsock));
|
2007-04-15 18:25:25 -04:00
|
|
|
/*
|
|
|
|
|
* Note: we could register external pollers here.
|
|
|
|
|
* Built-in pollers have been registered before main().
|
|
|
|
|
*/
|
2007-04-08 10:39:58 -04:00
|
|
|
|
2009-01-25 09:42:27 -05:00
|
|
|
if (!(global.tune.options & GTUNE_USE_KQUEUE))
|
2007-04-09 06:03:06 -04:00
|
|
|
disable_poller("kqueue");
|
|
|
|
|
|
2009-01-25 09:42:27 -05:00
|
|
|
if (!(global.tune.options & GTUNE_USE_EPOLL))
|
2007-04-08 10:39:58 -04:00
|
|
|
disable_poller("epoll");
|
|
|
|
|
|
2009-01-25 09:42:27 -05:00
|
|
|
if (!(global.tune.options & GTUNE_USE_POLL))
|
2007-04-08 10:39:58 -04:00
|
|
|
disable_poller("poll");
|
|
|
|
|
|
2009-01-25 09:42:27 -05:00
|
|
|
if (!(global.tune.options & GTUNE_USE_SELECT))
|
2007-04-08 10:39:58 -04:00
|
|
|
disable_poller("select");
|
|
|
|
|
|
|
|
|
|
/* Note: we could disable any poller by name here */
|
|
|
|
|
|
2016-03-07 06:46:38 -05:00
|
|
|
if (global.mode & (MODE_VERBOSE|MODE_DEBUG)) {
|
2007-04-09 13:29:56 -04:00
|
|
|
list_pollers(stderr);
|
2016-03-07 06:46:38 -05:00
|
|
|
fprintf(stderr, "\n");
|
|
|
|
|
list_filters(stderr);
|
|
|
|
|
}
|
2007-04-09 13:29:56 -04:00
|
|
|
|
2007-04-08 10:39:58 -04:00
|
|
|
if (!init_pollers()) {
|
2013-03-31 08:41:15 -04:00
|
|
|
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"
|
2013-05-14 14:56:28 -04:00
|
|
|
" global maxconn setting to accommodate the system's limitation. For reference,\n"
|
2013-03-31 08:41:15 -04:00
|
|
|
" 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);
|
2007-04-08 10:39:58 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
2007-04-09 13:29:56 -04:00
|
|
|
if (global.mode & (MODE_VERBOSE|MODE_DEBUG)) {
|
|
|
|
|
printf("Using %s() as the polling mechanism.\n", cur_poller.name);
|
2007-04-08 10:39:58 -04:00
|
|
|
}
|
|
|
|
|
|
2009-10-02 16:51:14 -04:00
|
|
|
if (!global.node)
|
|
|
|
|
global.node = strdup(hostname);
|
|
|
|
|
|
2015-01-23 06:08:30 -05:00
|
|
|
if (!hlua_post_init())
|
|
|
|
|
exit(1);
|
2015-05-12 11:23:58 -04:00
|
|
|
|
2015-04-13 17:40:55 -04:00
|
|
|
/* initialize structures for name resolution */
|
|
|
|
|
if (!dns_init_resolvers())
|
|
|
|
|
exit(1);
|
2016-05-13 17:52:55 -04:00
|
|
|
|
|
|
|
|
free(err_msg);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
2011-07-15 00:14:11 -04:00
|
|
|
static void deinit_acl_cond(struct acl_cond *cond)
|
2011-07-15 00:14:09 -04:00
|
|
|
{
|
|
|
|
|
struct acl_term_suite *suite, *suiteb;
|
|
|
|
|
struct acl_term *term, *termb;
|
|
|
|
|
|
2011-07-15 00:14:11 -04:00
|
|
|
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);
|
2011-07-15 00:14:09 -04:00
|
|
|
}
|
2011-07-15 00:14:11 -04:00
|
|
|
LIST_DEL(&suite->list);
|
|
|
|
|
free(suite);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(cond);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void deinit_tcp_rules(struct list *rules)
|
|
|
|
|
{
|
2015-08-04 13:35:46 -04:00
|
|
|
struct act_rule *trule, *truleb;
|
2011-07-15 00:14:11 -04:00
|
|
|
|
|
|
|
|
list_for_each_entry_safe(trule, truleb, rules, list) {
|
2011-07-15 00:14:09 -04:00
|
|
|
LIST_DEL(&trule->list);
|
2011-07-15 00:14:11 -04:00
|
|
|
deinit_acl_cond(trule->cond);
|
2011-07-15 00:14:09 -04:00
|
|
|
free(trule);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-27 15:37:17 -04:00
|
|
|
static void deinit_sample_arg(struct arg *p)
|
2011-07-15 00:14:11 -04:00
|
|
|
{
|
2012-04-20 08:03:29 -04:00
|
|
|
struct arg *p_back = p;
|
|
|
|
|
|
2011-07-15 00:14:11 -04:00
|
|
|
if (!p)
|
|
|
|
|
return;
|
|
|
|
|
|
2012-04-20 08:03:29 -04:00
|
|
|
while (p->type != ARGT_STOP) {
|
2012-06-01 04:38:29 -04:00
|
|
|
if (p->type == ARGT_STR || p->unresolved) {
|
2012-04-20 08:03:29 -04:00
|
|
|
free(p->data.str.str);
|
|
|
|
|
p->data.str.str = NULL;
|
2012-06-01 04:38:29 -04:00
|
|
|
p->unresolved = 0;
|
2012-04-20 06:29:52 -04:00
|
|
|
}
|
2015-01-19 13:00:58 -05:00
|
|
|
else if (p->type == ARGT_REG) {
|
|
|
|
|
if (p->data.reg) {
|
|
|
|
|
regex_free(p->data.reg);
|
|
|
|
|
free(p->data.reg);
|
|
|
|
|
p->data.reg = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-04-20 08:03:29 -04:00
|
|
|
p++;
|
2012-04-20 06:29:52 -04:00
|
|
|
}
|
2011-07-15 00:14:11 -04:00
|
|
|
|
2012-10-19 13:49:09 -04:00
|
|
|
if (p_back != empty_arg_list)
|
|
|
|
|
free(p_back);
|
2011-07-15 00:14:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
2012-04-27 15:37:17 -04:00
|
|
|
struct sample_conv_expr *conv_expr, *conv_exprb;
|
2011-07-15 00:14:11 -04:00
|
|
|
list_for_each_entry_safe(conv_expr, conv_exprb, &rule->expr->conv_exprs, list)
|
2012-04-27 15:37:17 -04:00
|
|
|
deinit_sample_arg(conv_expr->arg_p);
|
|
|
|
|
deinit_sample_arg(rule->expr->arg_p);
|
2011-07-15 00:14:11 -04:00
|
|
|
free(rule->expr);
|
|
|
|
|
}
|
|
|
|
|
free(rule);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
void deinit(void)
|
|
|
|
|
{
|
2007-05-13 18:39:29 -04:00
|
|
|
struct proxy *p = proxy, *p0;
|
2006-06-25 20:48:02 -04:00
|
|
|
struct cap_hdr *h,*h_next;
|
|
|
|
|
struct server *s,*s_next;
|
|
|
|
|
struct listener *l,*l_next;
|
2007-06-16 18:36:03 -04:00
|
|
|
struct acl_cond *cond, *condb;
|
|
|
|
|
struct hdr_exp *exp, *expb;
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
|
|
|
struct acl *acl, *aclb;
|
2008-05-31 07:53:23 -04:00
|
|
|
struct switching_rule *rule, *ruleb;
|
2012-04-05 15:09:48 -04:00
|
|
|
struct server_rule *srule, *sruleb;
|
2008-06-07 17:08:56 -04:00
|
|
|
struct redirect_rule *rdr, *rdrb;
|
2010-01-03 15:03:22 -05:00
|
|
|
struct wordlist *wl, *wlb;
|
2010-01-28 12:10:50 -05:00
|
|
|
struct cond_wordlist *cwl, *cwlb;
|
2008-05-31 07:53:23 -04:00
|
|
|
struct uri_auth *uap, *ua = NULL;
|
2011-10-12 11:50:54 -04:00
|
|
|
struct logsrv *log, *logb;
|
2012-02-08 10:37:49 -05:00
|
|
|
struct logformat_node *lf, *lfb;
|
2012-09-13 11:54:29 -04:00
|
|
|
struct bind_conf *bind_conf, *bind_back;
|
2007-06-16 18:36:03 -04:00
|
|
|
int i;
|
2008-05-31 07:53:23 -04:00
|
|
|
|
2010-08-27 11:56:48 -04:00
|
|
|
deinit_signals();
|
2006-06-25 20:48:02 -04:00
|
|
|
while (p) {
|
2012-10-04 02:01:43 -04:00
|
|
|
free(p->conf.file);
|
2008-08-03 06:19:50 -04:00
|
|
|
free(p->id);
|
|
|
|
|
free(p->check_req);
|
|
|
|
|
free(p->cookie_name);
|
|
|
|
|
free(p->cookie_domain);
|
|
|
|
|
free(p->url_param_name);
|
|
|
|
|
free(p->capture_name);
|
|
|
|
|
free(p->monitor_uri);
|
2011-07-15 00:14:08 -04:00
|
|
|
free(p->rdp_cookie_name);
|
2013-04-12 12:13:46 -04:00
|
|
|
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);
|
2013-10-02 05:10:11 -04:00
|
|
|
free(p->lbprm.map.srv);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2015-09-25 13:17:44 -04:00
|
|
|
if (p->conf.logformat_sd_string != default_rfc5424_sd_log_format)
|
|
|
|
|
free(p->conf.logformat_sd_string);
|
|
|
|
|
free(p->conf.lfsd_file);
|
|
|
|
|
|
2008-08-03 06:19:50 -04:00
|
|
|
for (i = 0; i < HTTP_ERR_SIZE; i++)
|
2009-09-27 07:23:20 -04:00
|
|
|
chunk_destroy(&p->errmsg[i]);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2010-01-28 12:10:50 -05:00
|
|
|
list_for_each_entry_safe(cwl, cwlb, &p->req_add, list) {
|
|
|
|
|
LIST_DEL(&cwl->list);
|
|
|
|
|
free(cwl->s);
|
|
|
|
|
free(cwl);
|
2010-01-03 15:03:22 -05:00
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2010-01-28 12:10:50 -05:00
|
|
|
list_for_each_entry_safe(cwl, cwlb, &p->rsp_add, list) {
|
|
|
|
|
LIST_DEL(&cwl->list);
|
|
|
|
|
free(cwl->s);
|
|
|
|
|
free(cwl);
|
2010-01-03 15:03:22 -05:00
|
|
|
}
|
2007-06-16 18:36:03 -04:00
|
|
|
|
2007-11-30 14:51:32 -05:00
|
|
|
list_for_each_entry_safe(cond, condb, &p->mon_fail_cond, list) {
|
|
|
|
|
LIST_DEL(&cond->list);
|
|
|
|
|
prune_acl_cond(cond);
|
|
|
|
|
free(cond);
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-16 18:36:03 -04:00
|
|
|
for (exp = p->req_exp; exp != NULL; ) {
|
2008-05-31 07:53:23 -04:00
|
|
|
if (exp->preg) {
|
2014-06-18 05:35:54 -04:00
|
|
|
regex_free(exp->preg);
|
|
|
|
|
free(exp->preg);
|
2008-05-31 07:53:23 -04:00
|
|
|
}
|
|
|
|
|
|
2015-05-26 06:18:29 -04:00
|
|
|
free((char *)exp->replace);
|
2007-06-16 18:36:03 -04:00
|
|
|
expb = exp;
|
|
|
|
|
exp = exp->next;
|
|
|
|
|
free(expb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (exp = p->rsp_exp; exp != NULL; ) {
|
2008-05-31 07:53:23 -04:00
|
|
|
if (exp->preg) {
|
2014-06-18 05:35:54 -04:00
|
|
|
regex_free(exp->preg);
|
|
|
|
|
free(exp->preg);
|
2008-05-31 07:53:23 -04:00
|
|
|
}
|
|
|
|
|
|
2015-05-26 06:18:29 -04:00
|
|
|
free((char *)exp->replace);
|
2007-06-16 18:36:03 -04:00
|
|
|
expb = exp;
|
|
|
|
|
exp = exp->next;
|
|
|
|
|
free(expb);
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-31 07:53:23 -04:00
|
|
|
/* build a list of unique uri_auths */
|
|
|
|
|
if (!ua)
|
|
|
|
|
ua = p->uri_auth;
|
|
|
|
|
else {
|
|
|
|
|
/* check if p->uri_auth is unique */
|
|
|
|
|
for (uap = ua; uap; uap=uap->next)
|
|
|
|
|
if (uap == p->uri_auth)
|
|
|
|
|
break;
|
|
|
|
|
|
2008-06-24 05:14:45 -04:00
|
|
|
if (!uap && p->uri_auth) {
|
2008-05-31 07:53:23 -04:00
|
|
|
/* add it, if it is */
|
|
|
|
|
p->uri_auth->next = ua;
|
|
|
|
|
ua = p->uri_auth;
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-06-16 18:36:03 -04:00
|
|
|
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
|
|
|
list_for_each_entry_safe(acl, aclb, &p->acl, list) {
|
|
|
|
|
LIST_DEL(&acl->list);
|
|
|
|
|
prune_acl(acl);
|
|
|
|
|
free(acl);
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-05 15:09:48 -04:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-31 07:53:23 -04:00
|
|
|
list_for_each_entry_safe(rule, ruleb, &p->switching_rules, list) {
|
|
|
|
|
LIST_DEL(&rule->list);
|
2014-04-22 19:21:56 -04:00
|
|
|
if (rule->cond) {
|
|
|
|
|
prune_acl_cond(rule->cond);
|
|
|
|
|
free(rule->cond);
|
|
|
|
|
}
|
2008-05-31 07:53:23 -04:00
|
|
|
free(rule);
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-07 17:08:56 -04:00
|
|
|
list_for_each_entry_safe(rdr, rdrb, &p->redirect_rules, list) {
|
|
|
|
|
LIST_DEL(&rdr->list);
|
2010-01-03 14:03:03 -05:00
|
|
|
if (rdr->cond) {
|
|
|
|
|
prune_acl_cond(rdr->cond);
|
|
|
|
|
free(rdr->cond);
|
|
|
|
|
}
|
2008-06-07 17:08:56 -04:00
|
|
|
free(rdr->rdr_str);
|
2013-11-29 06:15:45 -05:00
|
|
|
list_for_each_entry_safe(lf, lfb, &rdr->rdr_fmt, list) {
|
|
|
|
|
LIST_DEL(&lf->list);
|
|
|
|
|
free(lf);
|
|
|
|
|
}
|
2008-06-07 17:08:56 -04:00
|
|
|
free(rdr);
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-12 11:50:54 -04:00
|
|
|
list_for_each_entry_safe(log, logb, &p->logsrvs, list) {
|
|
|
|
|
LIST_DEL(&log->list);
|
|
|
|
|
free(log);
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-08 10:37:49 -05:00
|
|
|
list_for_each_entry_safe(lf, lfb, &p->logformat, list) {
|
|
|
|
|
LIST_DEL(&lf->list);
|
|
|
|
|
free(lf);
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-25 13:17:44 -04:00
|
|
|
list_for_each_entry_safe(lf, lfb, &p->logformat_sd, list) {
|
|
|
|
|
LIST_DEL(&lf->list);
|
|
|
|
|
free(lf);
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-15 00:14:09 -04:00
|
|
|
deinit_tcp_rules(&p->tcp_req.inspect_rules);
|
|
|
|
|
deinit_tcp_rules(&p->tcp_req.l4_rules);
|
|
|
|
|
|
2011-07-15 00:14:11 -04:00
|
|
|
deinit_stick_rules(&p->storersp_rules);
|
|
|
|
|
deinit_stick_rules(&p->sticking_rules);
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
h = p->req_cap;
|
|
|
|
|
while (h) {
|
|
|
|
|
h_next = h->next;
|
2008-08-03 06:19:50 -04:00
|
|
|
free(h->name);
|
2007-05-13 16:46:04 -04:00
|
|
|
pool_destroy2(h->pool);
|
2006-06-25 20:48:02 -04:00
|
|
|
free(h);
|
|
|
|
|
h = h_next;
|
|
|
|
|
}/* end while(h) */
|
|
|
|
|
|
|
|
|
|
h = p->rsp_cap;
|
|
|
|
|
while (h) {
|
|
|
|
|
h_next = h->next;
|
2008-08-03 06:19:50 -04:00
|
|
|
free(h->name);
|
2007-05-13 16:46:04 -04:00
|
|
|
pool_destroy2(h->pool);
|
2006-06-25 20:48:02 -04:00
|
|
|
free(h);
|
|
|
|
|
h = h_next;
|
|
|
|
|
}/* end while(h) */
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
s = p->srv;
|
|
|
|
|
while (s) {
|
|
|
|
|
s_next = s->next;
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
|
|
|
|
2012-09-28 09:01:02 -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
|
|
|
}
|
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
|
|
|
|
2011-10-31 06:53:20 -04:00
|
|
|
if (s->warmup) {
|
|
|
|
|
task_delete(s->warmup);
|
|
|
|
|
task_free(s->warmup);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-03 06:19:50 -04:00
|
|
|
free(s->id);
|
|
|
|
|
free(s->cookie);
|
2012-09-28 09:28:30 -04:00
|
|
|
free(s->check.bi);
|
|
|
|
|
free(s->check.bo);
|
2013-11-24 20:46:36 -05:00
|
|
|
free(s->agent.bi);
|
|
|
|
|
free(s->agent.bo);
|
2015-10-21 21:19:05 -04:00
|
|
|
free(s->agent.send_string);
|
2014-09-05 04:08:23 -04:00
|
|
|
free((char*)s->conf.file);
|
2015-07-29 07:02:40 -04:00
|
|
|
#ifdef USE_OPENSSL
|
|
|
|
|
if (s->use_ssl || s->check.use_ssl)
|
|
|
|
|
ssl_sock_free_srv_ctx(s);
|
|
|
|
|
#endif
|
2006-06-25 20:48:02 -04:00
|
|
|
free(s);
|
|
|
|
|
s = s_next;
|
|
|
|
|
}/* end while(s) */
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
|
|
|
|
2012-09-20 10:48:07 -04:00
|
|
|
list_for_each_entry_safe(l, l_next, &p->conf.listeners, by_fe) {
|
2010-09-03 04:38:17 -04:00
|
|
|
unbind_listener(l);
|
|
|
|
|
delete_listener(l);
|
2012-09-20 10:48:07 -04:00
|
|
|
LIST_DEL(&l->by_fe);
|
|
|
|
|
LIST_DEL(&l->by_bind);
|
2010-02-05 14:31:44 -05:00
|
|
|
free(l->name);
|
|
|
|
|
free(l->counters);
|
2006-06-25 20:48:02 -04:00
|
|
|
free(l);
|
2012-09-20 10:48:07 -04:00
|
|
|
}
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
|
|
|
|
2012-09-20 10:48:07 -04:00
|
|
|
/* Release unused SSL configs. */
|
2012-09-13 11:54:29 -04:00
|
|
|
list_for_each_entry_safe(bind_conf, bind_back, &p->conf.bind, by_fe) {
|
|
|
|
|
#ifdef USE_OPENSSL
|
2015-06-09 11:29:50 -04:00
|
|
|
ssl_sock_free_ca(bind_conf);
|
2012-09-13 11:54:29 -04:00
|
|
|
ssl_sock_free_all_ctx(bind_conf);
|
2012-10-05 06:00:26 -04:00
|
|
|
free(bind_conf->ca_file);
|
2015-06-09 11:29:50 -04:00
|
|
|
free(bind_conf->ca_sign_file);
|
|
|
|
|
free(bind_conf->ca_sign_pass);
|
2012-09-13 11:54:29 -04:00
|
|
|
free(bind_conf->ciphers);
|
2012-09-20 11:10:03 -04:00
|
|
|
free(bind_conf->ecdhe);
|
2012-10-05 06:00:26 -04:00
|
|
|
free(bind_conf->crl_file);
|
2012-09-07 10:58:00 -04:00
|
|
|
#endif /* USE_OPENSSL */
|
2012-09-13 11:54:29 -04:00
|
|
|
free(bind_conf->file);
|
|
|
|
|
free(bind_conf->arg);
|
|
|
|
|
LIST_DEL(&bind_conf->by_fe);
|
|
|
|
|
free(bind_conf);
|
|
|
|
|
}
|
2012-09-07 10:58:00 -04:00
|
|
|
|
MAJOR: filters: Add filters support
This patch adds the support of filters in HAProxy. The main idea is to have a
way to "easely" extend HAProxy by adding some "modules", called filters, that
will be able to change HAProxy behavior in a programmatic way.
To do so, many entry points has been added in code to let filters to hook up to
different steps of the processing. A filter must define a flt_ops sutrctures
(see include/types/filters.h for details). This structure contains all available
callbacks that a filter can define:
struct flt_ops {
/*
* Callbacks to manage the filter lifecycle
*/
int (*init) (struct proxy *p);
void (*deinit)(struct proxy *p);
int (*check) (struct proxy *p);
/*
* Stream callbacks
*/
void (*stream_start) (struct stream *s);
void (*stream_accept) (struct stream *s);
void (*session_establish)(struct stream *s);
void (*stream_stop) (struct stream *s);
/*
* HTTP callbacks
*/
int (*http_start) (struct stream *s, struct http_msg *msg);
int (*http_start_body) (struct stream *s, struct http_msg *msg);
int (*http_start_chunk) (struct stream *s, struct http_msg *msg);
int (*http_data) (struct stream *s, struct http_msg *msg);
int (*http_last_chunk) (struct stream *s, struct http_msg *msg);
int (*http_end_chunk) (struct stream *s, struct http_msg *msg);
int (*http_chunk_trailers)(struct stream *s, struct http_msg *msg);
int (*http_end_body) (struct stream *s, struct http_msg *msg);
void (*http_end) (struct stream *s, struct http_msg *msg);
void (*http_reset) (struct stream *s, struct http_msg *msg);
int (*http_pre_process) (struct stream *s, struct http_msg *msg);
int (*http_post_process) (struct stream *s, struct http_msg *msg);
void (*http_reply) (struct stream *s, short status,
const struct chunk *msg);
};
To declare and use a filter, in the configuration, the "filter" keyword must be
used in a listener/frontend section:
frontend test
...
filter <FILTER-NAME> [OPTIONS...]
The filter referenced by the <FILTER-NAME> must declare a configuration parser
on its own name to fill flt_ops and filter_conf field in the proxy's
structure. An exemple will be provided later to make it perfectly clear.
For now, filters cannot be used in backend section. But this is only a matter of
time. Documentation will also be added later. This is the first commit of a long
list about filters.
It is possible to have several filters on the same listener/frontend. These
filters are stored in an array of at most MAX_FILTERS elements (define in
include/types/filters.h). Again, this will be replaced later by a list of
filters.
The filter API has been highly refactored. Main changes are:
* Now, HA supports an infinite number of filters per proxy. To do so, filters
are stored in list.
* Because filters are stored in list, filters state has been moved from the
channel structure to the filter structure. This is cleaner because there is no
more info about filters in channel structure.
* It is possible to defined filters on backends only. For such filters,
stream_start/stream_stop callbacks are not called. Of course, it is possible
to mix frontend and backend filters.
* Now, TCP streams are also filtered. All callbacks without the 'http_' prefix
are called for all kind of streams. In addition, 2 new callbacks were added to
filter data exchanged through a TCP stream:
- tcp_data: it is called when new data are available or when old unprocessed
data are still waiting.
- tcp_forward_data: it is called when some data can be consumed.
* New callbacks attached to channel were added:
- channel_start_analyze: it is called when a filter is ready to process data
exchanged through a channel. 2 new analyzers (a frontend and a backend)
are attached to channels to call this callback. For a frontend filter, it
is called before any other analyzer. For a backend filter, it is called
when a backend is attached to a stream. So some processing cannot be
filtered in that case.
- channel_analyze: it is called before each analyzer attached to a channel,
expects analyzers responsible for data sending.
- channel_end_analyze: it is called when all other analyzers have finished
their processing. A new analyzers is attached to channels to call this
callback. For a TCP stream, this is always the last one called. For a HTTP
one, the callback is called when a request/response ends, so it is called
one time for each request/response.
* 'session_established' callback has been removed. Everything that is done in
this callback can be handled by 'channel_start_analyze' on the response
channel.
* 'http_pre_process' and 'http_post_process' callbacks have been replaced by
'channel_analyze'.
* 'http_start' callback has been replaced by 'http_headers'. This new one is
called just before headers sending and parsing of the body.
* 'http_end' callback has been replaced by 'channel_end_analyze'.
* It is possible to set a forwarder for TCP channels. It was already possible to
do it for HTTP ones.
* Forwarders can partially consumed forwardable data. For this reason a new
HTTP message state was added before HTTP_MSG_DONE : HTTP_MSG_ENDING.
Now all filters can define corresponding callbacks (http_forward_data
and tcp_forward_data). Each filter owns 2 offsets relative to buf->p, next and
forward, to track, respectively, input data already parsed but not forwarded yet
by the filter and parsed data considered as forwarded by the filter. A any time,
we have the warranty that a filter cannot parse or forward more input than
previous ones. And, of course, it cannot forward more input than it has
parsed. 2 macros has been added to retrieve these offets: FLT_NXT and FLT_FWD.
In addition, 2 functions has been added to change the 'next size' and the
'forward size' of a filter. When a filter parses input data, it can alter these
data, so the size of these data can vary. This action has an effet on all
previous filters that must be handled. To do so, the function
'filter_change_next_size' must be called, passing the size variation. In the
same spirit, if a filter alter forwarded data, it must call the function
'filter_change_forward_size'. 'filter_change_next_size' can be called in
'http_data' and 'tcp_data' callbacks and only these ones. And
'filter_change_forward_size' can be called in 'http_forward_data' and
'tcp_forward_data' callbacks and only these ones. The data changes are the
filter responsability, but with some limitation. It must not change already
parsed/forwarded data or data that previous filters have not parsed/forwarded
yet.
Because filters can be used on backends, when we the backend is set for a
stream, we add filters defined for this backend in the filter list of the
stream. But we must only do that when the backend and the frontend of the stream
are not the same. Else same filters are added a second time leading to undefined
behavior.
The HTTP compression code had to be moved.
So it simplifies http_response_forward_body function. To do so, the way the data
are forwarded has changed. Now, a filter (and only one) can forward data. In a
commit to come, this limitation will be removed to let all filters take part to
data forwarding. There are 2 new functions that filters should use to deal with
this feature:
* flt_set_http_data_forwarder: This function sets the filter (using its id)
that will forward data for the specified HTTP message. It is possible if it
was not already set by another filter _AND_ if no data was yet forwarded
(msg->msg_state <= HTTP_MSG_BODY). It returns -1 if an error occurs.
* flt_http_data_forwarder: This function returns the filter id that will
forward data for the specified HTTP message. If there is no forwarder set, it
returns -1.
When an HTTP data forwarder is set for the response, the HTTP compression is
disabled. Of course, this is not definitive.
2015-04-30 05:48:27 -04:00
|
|
|
flt_deinit(p);
|
|
|
|
|
|
2010-02-05 14:31:44 -05:00
|
|
|
free(p->desc);
|
|
|
|
|
free(p->fwdfor_hdr_name);
|
|
|
|
|
|
2011-01-06 11:51:27 -05:00
|
|
|
free_http_req_rules(&p->http_req_rules);
|
2014-06-16 14:05:59 -04:00
|
|
|
free_http_res_rules(&p->http_res_rules);
|
2011-07-25 10:33:49 -04:00
|
|
|
free(p->task);
|
2010-01-29 11:58:21 -05:00
|
|
|
|
2007-05-13 16:46:04 -04:00
|
|
|
pool_destroy2(p->req_cap_pool);
|
|
|
|
|
pool_destroy2(p->rsp_cap_pool);
|
2011-07-15 00:14:10 -04:00
|
|
|
pool_destroy2(p->table.pool);
|
2010-01-29 11:50:44 -05:00
|
|
|
|
2007-05-13 18:39:29 -04:00
|
|
|
p0 = p;
|
2006-06-25 20:48:02 -04:00
|
|
|
p = p->next;
|
2007-05-13 18:39:29 -04:00
|
|
|
free(p0);
|
2006-06-25 20:48:02 -04:00
|
|
|
}/* end while(p) */
|
2007-10-16 06:25:14 -04:00
|
|
|
|
2008-05-31 07:53:23 -04:00
|
|
|
while (ua) {
|
|
|
|
|
uap = ua;
|
|
|
|
|
ua = ua->next;
|
|
|
|
|
|
2008-08-03 06:19:50 -04:00
|
|
|
free(uap->uri_prefix);
|
|
|
|
|
free(uap->auth_realm);
|
2009-10-02 16:51:14 -04:00
|
|
|
free(uap->node);
|
|
|
|
|
free(uap->desc);
|
2008-05-31 07:53:23 -04:00
|
|
|
|
2010-01-29 13:29:32 -05:00
|
|
|
userlist_free(uap->userlist);
|
2011-01-06 11:51:27 -05:00
|
|
|
free_http_req_rules(&uap->http_req_rules);
|
2010-01-29 13:29:32 -05:00
|
|
|
|
2008-05-31 07:53:23 -04:00
|
|
|
free(uap);
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-29 11:50:44 -05:00
|
|
|
userlist_free(userlist);
|
|
|
|
|
|
2015-09-25 07:02:25 -04:00
|
|
|
cfg_unregister_sections();
|
|
|
|
|
|
|
|
|
|
free_trash_buffers();
|
|
|
|
|
chunk_destroy(&trash);
|
|
|
|
|
|
2007-10-16 06:25:14 -04:00
|
|
|
protocol_unbind_all();
|
|
|
|
|
|
2015-06-01 07:57:22 -04:00
|
|
|
#if defined(USE_DEVICEATLAS)
|
|
|
|
|
deinit_deviceatlas();
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-05-14 12:14:30 -04:00
|
|
|
#ifdef USE_51DEGREES
|
2015-06-29 10:43:25 -04:00
|
|
|
deinit_51degrees();
|
2015-05-29 07:54:25 -04:00
|
|
|
#endif
|
2015-05-14 12:14:30 -04:00
|
|
|
|
2016-11-04 05:55:08 -04:00
|
|
|
#ifdef USE_WURFL
|
|
|
|
|
ha_wurfl_deinit();
|
|
|
|
|
#endif
|
|
|
|
|
|
2010-12-29 11:05:48 -05:00
|
|
|
free(global.log_send_hostname); global.log_send_hostname = NULL;
|
2015-10-01 07:18:13 -04:00
|
|
|
chunk_destroy(&global.log_tag);
|
2008-08-03 06:19:50 -04:00
|
|
|
free(global.chroot); global.chroot = NULL;
|
|
|
|
|
free(global.pidfile); global.pidfile = NULL;
|
2009-10-02 16:51:14 -04:00
|
|
|
free(global.node); global.node = NULL;
|
|
|
|
|
free(global.desc); global.desc = NULL;
|
2013-06-26 04:49:51 -04:00
|
|
|
free(fdinfo); fdinfo = NULL;
|
2008-08-03 06:19:50 -04:00
|
|
|
free(fdtab); fdtab = NULL;
|
|
|
|
|
free(oldpids); oldpids = NULL;
|
2015-09-25 07:02:25 -04:00
|
|
|
free(static_table_key); static_table_key = NULL;
|
|
|
|
|
free(get_http_auth_buff); get_http_auth_buff = NULL;
|
|
|
|
|
free(swap_buffer); swap_buffer = NULL;
|
2011-08-01 14:57:55 -04:00
|
|
|
free(global_listener_queue_task); global_listener_queue_task = NULL;
|
2008-05-31 07:53:23 -04:00
|
|
|
|
2011-10-12 11:50:54 -04:00
|
|
|
list_for_each_entry_safe(log, logb, &global.logsrvs, list) {
|
|
|
|
|
LIST_DEL(&log->list);
|
|
|
|
|
free(log);
|
|
|
|
|
}
|
2010-01-03 15:12:30 -05:00
|
|
|
list_for_each_entry_safe(wl, wlb, &cfg_cfgfiles, list) {
|
2016-05-13 17:52:55 -04:00
|
|
|
free(wl->s);
|
2010-01-03 15:12:30 -05:00
|
|
|
LIST_DEL(&wl->list);
|
|
|
|
|
free(wl);
|
|
|
|
|
}
|
|
|
|
|
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
pool_destroy2(pool2_stream);
|
2015-04-03 08:10:06 -04:00
|
|
|
pool_destroy2(pool2_session);
|
2012-10-26 14:10:28 -04:00
|
|
|
pool_destroy2(pool2_connection);
|
2012-10-12 17:49:43 -04:00
|
|
|
pool_destroy2(pool2_buffer);
|
2007-05-13 15:36:56 -04:00
|
|
|
pool_destroy2(pool2_requri);
|
2007-05-13 13:43:47 -04:00
|
|
|
pool_destroy2(pool2_task);
|
2007-05-13 15:45:51 -04:00
|
|
|
pool_destroy2(pool2_capture);
|
2007-05-13 14:19:55 -04:00
|
|
|
pool_destroy2(pool2_pendconn);
|
2010-08-27 11:56:48 -04:00
|
|
|
pool_destroy2(pool2_sig_handlers);
|
2011-10-24 12:15:04 -04:00
|
|
|
pool_destroy2(pool2_hdr_idx);
|
2015-04-03 16:55:33 -04:00
|
|
|
pool_destroy2(pool2_http_txn);
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 17:53:44 -04:00
|
|
|
deinit_pollers();
|
2006-06-25 20:48:02 -04:00
|
|
|
} /* end deinit() */
|
|
|
|
|
|
2010-08-25 06:58:59 -04:00
|
|
|
/* 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)
|
2006-06-25 20:48:02 -04:00
|
|
|
{
|
|
|
|
|
int p;
|
2010-08-25 06:58:59 -04:00
|
|
|
int ret = 0;
|
2006-06-25 20:48:02 -04:00
|
|
|
for (p = 0; p < nb_oldpids; p++)
|
2010-08-25 06:58:59 -04:00
|
|
|
if (kill(oldpids[p], sig) == 0)
|
|
|
|
|
ret++;
|
|
|
|
|
return ret;
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
2011-07-25 10:33:49 -04:00
|
|
|
/* Runs the polling loop */
|
2007-04-08 10:39:58 -04:00
|
|
|
void run_poll_loop()
|
|
|
|
|
{
|
2008-07-06 18:09:58 -04:00
|
|
|
int next;
|
2007-04-08 10:39:58 -04:00
|
|
|
|
2008-06-23 08:00:57 -04:00
|
|
|
tv_update_date(0,1);
|
2007-04-08 10:39:58 -04:00
|
|
|
while (1) {
|
2014-12-15 07:26:01 -05:00
|
|
|
/* Process a few tasks */
|
|
|
|
|
process_runnable_tasks();
|
|
|
|
|
|
2009-05-10 03:01:21 -04:00
|
|
|
/* check if we caught some signals and process them */
|
|
|
|
|
signal_process_queue();
|
|
|
|
|
|
2008-06-29 16:40:23 -04:00
|
|
|
/* Check if we can expire some tasks */
|
2014-12-15 07:26:01 -05:00
|
|
|
next = wake_expired_tasks();
|
2007-04-08 10:39:58 -04:00
|
|
|
|
2010-08-31 09:39:26 -04:00
|
|
|
/* stop when there's nothing left to do */
|
|
|
|
|
if (jobs == 0)
|
2007-04-08 10:39:58 -04:00
|
|
|
break;
|
|
|
|
|
|
2015-04-13 14:44:19 -04:00
|
|
|
/* expire immediately if events are pending */
|
2015-09-25 11:39:23 -04:00
|
|
|
if (fd_cache_num || run_queue || signal_queue_len || !LIST_ISEMPTY(&applet_active_queue))
|
2015-04-13 14:44:19 -04:00
|
|
|
next = now_ms;
|
|
|
|
|
|
2008-06-29 16:40:23 -04:00
|
|
|
/* The poller will ensure it returns around <next> */
|
2008-07-06 18:09:58 -04:00
|
|
|
cur_poller.poll(&cur_poller, next);
|
2014-01-25 13:24:15 -05:00
|
|
|
fd_process_cached_events();
|
2015-04-19 03:59:31 -04:00
|
|
|
applet_run_active();
|
2007-04-08 10:39:58 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-01 14:57:55 -04:00
|
|
|
/* 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.
|
|
|
|
|
*/
|
2011-09-07 08:26:33 -04:00
|
|
|
dequeue_all_listeners(&global_listener_queue);
|
2011-08-01 14:57:55 -04:00
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
t->expire = next;
|
|
|
|
|
task_queue(t);
|
|
|
|
|
return t;
|
|
|
|
|
}
|
2007-04-08 10:39:58 -04:00
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
|
{
|
|
|
|
|
int err, retry;
|
|
|
|
|
struct rlimit limit;
|
2010-10-22 10:06:11 -04:00
|
|
|
char errmsg[100];
|
2012-09-05 02:02:48 -04:00
|
|
|
int pidfd = -1;
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2010-10-22 10:06:11 -04:00
|
|
|
init(argc, argv);
|
2010-08-27 11:56:48 -04:00
|
|
|
signal_register_fct(SIGQUIT, dump, SIGQUIT);
|
|
|
|
|
signal_register_fct(SIGUSR1, sig_soft_stop, SIGUSR1);
|
|
|
|
|
signal_register_fct(SIGHUP, sig_dump_state, SIGHUP);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2010-03-17 13:02:46 -04:00
|
|
|
/* 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.
|
2006-06-25 20:48:02 -04:00
|
|
|
*/
|
2010-08-27 11:56:48 -04:00
|
|
|
signal_register_fct(SIGPIPE, NULL, 0);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2011-02-16 05:10:36 -05:00
|
|
|
/* 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) {
|
2016-06-21 05:48:18 -04:00
|
|
|
/* try to set it to the max possible at least */
|
|
|
|
|
getrlimit(RLIMIT_NOFILE, &limit);
|
2016-06-21 05:51:59 -04:00
|
|
|
limit.rlim_cur = limit.rlim_max;
|
|
|
|
|
if (setrlimit(RLIMIT_NOFILE, &limit) != -1)
|
|
|
|
|
getrlimit(RLIMIT_NOFILE, &limit);
|
|
|
|
|
|
2016-06-21 05:48:18 -04:00
|
|
|
Warning("[%s.main()] Cannot raise FD limit to %d, limit is %d.\n", argv[0], global.rlimit_nofile, (int)limit.rlim_cur);
|
|
|
|
|
global.rlimit_nofile = limit.rlim_cur;
|
2011-02-16 05:10:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (global.rlimit_memmax) {
|
|
|
|
|
limit.rlim_cur = limit.rlim_max =
|
2015-12-14 06:46:07 -05:00
|
|
|
global.rlimit_memmax * 1048576ULL;
|
2011-02-16 05:10:36 -05:00
|
|
|
#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
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/* We will loop at most 100 times with 10 ms delay each time.
|
|
|
|
|
* That's at most 1 second. We only send a signal to old pids
|
|
|
|
|
* if we cannot grab at least one port.
|
|
|
|
|
*/
|
|
|
|
|
retry = MAX_START_RETRIES;
|
|
|
|
|
err = ERR_NONE;
|
|
|
|
|
while (retry >= 0) {
|
|
|
|
|
struct timeval w;
|
|
|
|
|
err = start_proxies(retry == 0 || nb_oldpids == 0);
|
2007-12-20 17:05:50 -05:00
|
|
|
/* exit the loop on no error or fatal error */
|
|
|
|
|
if ((err & (ERR_RETRYABLE|ERR_FATAL)) != ERR_RETRYABLE)
|
2006-06-25 20:48:02 -04:00
|
|
|
break;
|
2010-08-25 06:58:59 -04:00
|
|
|
if (nb_oldpids == 0 || retry == 0)
|
2006-06-25 20:48:02 -04:00
|
|
|
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.
|
|
|
|
|
*/
|
2010-08-25 06:58:59 -04:00
|
|
|
if (tell_old_pids(SIGTTOU) == 0) {
|
|
|
|
|
/* no need to wait if we can't contact old pids */
|
|
|
|
|
retry = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
/* give some time to old processes to stop listening */
|
|
|
|
|
w.tv_sec = 0;
|
|
|
|
|
w.tv_usec = 10*1000;
|
|
|
|
|
select(0, NULL, NULL, NULL, &w);
|
|
|
|
|
retry--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Note: start_proxies() sends an alert when it fails. */
|
2009-02-04 11:05:23 -05:00
|
|
|
if ((err & ~ERR_WARN) != ERR_NONE) {
|
2009-06-09 08:36:00 -04:00
|
|
|
if (retry != MAX_START_RETRIES && nb_oldpids) {
|
|
|
|
|
protocol_unbind_all(); /* cleanup everything we can */
|
2006-06-25 20:48:02 -04:00
|
|
|
tell_old_pids(SIGTTIN);
|
2009-06-09 08:36:00 -04:00
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (listeners == 0) {
|
2015-08-09 05:01:51 -04:00
|
|
|
Alert("[%s.main()] No enabled listener found (check for 'bind' directives) ! Exiting.\n", argv[0]);
|
2006-06-25 20:48:02 -04:00
|
|
|
/* Note: we don't have to send anything to the old pids because we
|
|
|
|
|
* never stopped them. */
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-22 10:06:11 -04:00
|
|
|
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);
|
|
|
|
|
|
2007-10-16 06:25:14 -04:00
|
|
|
Alert("[%s.main()] Some protocols failed to start their listeners! Exiting.\n", argv[0]);
|
|
|
|
|
protocol_unbind_all(); /* cleanup everything we can */
|
|
|
|
|
if (nb_oldpids)
|
|
|
|
|
tell_old_pids(SIGTTIN);
|
|
|
|
|
exit(1);
|
2010-10-22 10:06:11 -04:00
|
|
|
} else if (err & ERR_WARN) {
|
|
|
|
|
Alert("[%s.main()] %s.\n", argv[0], errmsg);
|
2007-10-16 06:25:14 -04:00
|
|
|
}
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/* prepare pause/play signals */
|
2010-08-27 11:56:48 -04:00
|
|
|
signal_register_fct(SIGTTOU, sig_pause, SIGTTOU);
|
|
|
|
|
signal_register_fct(SIGTTIN, sig_listen, SIGTTIN);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
/* MODE_QUIET can inhibit alerts and warnings below this line */
|
|
|
|
|
|
|
|
|
|
global.mode &= ~MODE_STARTING;
|
|
|
|
|
if ((global.mode & MODE_QUIET) && !(global.mode & MODE_VERBOSE)) {
|
|
|
|
|
/* detach from the tty */
|
|
|
|
|
fclose(stdin); fclose(stdout); fclose(stderr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* open log & pid files before the chroot */
|
2013-02-12 04:53:52 -05:00
|
|
|
if (global.mode & (MODE_DAEMON | MODE_SYSTEMD) && global.pidfile != NULL) {
|
2006-06-25 20:48:02 -04:00
|
|
|
unlink(global.pidfile);
|
|
|
|
|
pidfd = open(global.pidfile, O_CREAT | O_WRONLY | O_TRUNC, 0644);
|
|
|
|
|
if (pidfd < 0) {
|
|
|
|
|
Alert("[%s.main()] Cannot create pidfile %s\n", argv[0], global.pidfile);
|
|
|
|
|
if (nb_oldpids)
|
|
|
|
|
tell_old_pids(SIGTTIN);
|
2007-10-16 06:25:14 -04:00
|
|
|
protocol_unbind_all();
|
2006-06-25 20:48:02 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-03-24 12:24:39 -04:00
|
|
|
if ((global.last_checks & LSTCHK_NETADM) && global.uid) {
|
|
|
|
|
Alert("[%s.main()] Some configuration options require full privileges, so global.uid cannot be changed.\n"
|
2009-02-04 12:02:48 -05:00
|
|
|
"", argv[0]);
|
2007-10-16 06:25:14 -04:00
|
|
|
protocol_unbind_all();
|
2007-03-24 12:24:39 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-04 12:02:48 -05:00
|
|
|
/* If the user is not root, we'll still let him try the configuration
|
|
|
|
|
* but we inform him that unexpected behaviour may occur.
|
|
|
|
|
*/
|
|
|
|
|
if ((global.last_checks & LSTCHK_NETADM) && getuid())
|
|
|
|
|
Warning("[%s.main()] Some options which require full privileges"
|
|
|
|
|
" might not work well.\n"
|
|
|
|
|
"", argv[0]);
|
|
|
|
|
|
2007-10-15 12:57:08 -04:00
|
|
|
/* chroot if needed */
|
|
|
|
|
if (global.chroot != NULL) {
|
2012-04-29 08:11:38 -04:00
|
|
|
if (chroot(global.chroot) == -1 || chdir("/") == -1) {
|
2007-10-15 12:57:08 -04:00
|
|
|
Alert("[%s.main()] Cannot chroot(%s).\n", argv[0], global.chroot);
|
|
|
|
|
if (nb_oldpids)
|
|
|
|
|
tell_old_pids(SIGTTIN);
|
2007-10-16 06:25:14 -04:00
|
|
|
protocol_unbind_all();
|
2007-10-15 12:57:08 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
if (nb_oldpids)
|
2010-08-25 06:58:59 -04:00
|
|
|
nb_oldpids = tell_old_pids(oldpids_sig);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
/* Note that any error at this stage will be fatal because we will not
|
|
|
|
|
* be able to restart the old pids.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* setgid / setuid */
|
BUG/MEDIUM: remove supplementary groups when changing gid
Without it, haproxy will retain the group membership of root, which may
give more access than intended to the process. For example, haproxy would
still be in the wheel group on Fedora 18, as seen with :
# haproxy -f /etc/haproxy/haproxy.cfg
# ps a -o pid,user,group,command | grep hapr
3545 haproxy haproxy haproxy -f /etc/haproxy/haproxy.cfg
4356 root root grep --color=auto hapr
# grep Group /proc/3545/status
Groups: 0 1 2 3 4 6 10
# getent group wheel
wheel:x:10:root,misc
[WT: The issue has been investigated by independent security research team
and realized by itself not being able to allow security exploitation.
Additionally, dropping groups is not allowed to unprivileged users,
though this mode of deployment is quite common. Thus a warning is
emitted in this case to inform the user. The fix could be backported
into all supported versions as the issue has always been there. ]
2013-01-12 12:35:19 -05:00
|
|
|
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);
|
|
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (global.uid && setuid(global.uid) == -1) {
|
|
|
|
|
Alert("[%s.main()] Cannot set uid %d.\n", argv[0], global.uid);
|
2007-10-16 06:25:14 -04:00
|
|
|
protocol_unbind_all();
|
2006-06-25 20:48:02 -04:00
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* check ulimits */
|
|
|
|
|
limit.rlim_cur = limit.rlim_max = 0;
|
|
|
|
|
getrlimit(RLIMIT_NOFILE, &limit);
|
|
|
|
|
if (limit.rlim_cur < global.maxsock) {
|
|
|
|
|
Warning("[%s.main()] FD limit (%d) too low for maxconn=%d/maxsock=%d. Please raise 'ulimit-n' to %d or more to avoid any trouble.\n",
|
2009-04-03 08:49:12 -04:00
|
|
|
argv[0], (int)limit.rlim_cur, global.maxconn, global.maxsock, global.maxsock);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
2013-02-12 04:53:52 -05:00
|
|
|
if (global.mode & (MODE_DAEMON | MODE_SYSTEMD)) {
|
2009-02-04 16:05:05 -05:00
|
|
|
struct proxy *px;
|
2015-05-01 13:13:41 -04:00
|
|
|
struct peers *curpeers;
|
2006-06-25 20:48:02 -04:00
|
|
|
int ret = 0;
|
2013-02-12 04:53:52 -05:00
|
|
|
int *children = calloc(global.nbproc, sizeof(int));
|
2006-06-25 20:48:02 -04:00
|
|
|
int proc;
|
2016-10-25 11:20:24 -04:00
|
|
|
char *wrapper_fd;
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
/* the father launches the required number of processes */
|
|
|
|
|
for (proc = 0; proc < global.nbproc; proc++) {
|
|
|
|
|
ret = fork();
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
Alert("[%s.main()] Cannot fork.\n", argv[0]);
|
2007-10-16 06:25:14 -04:00
|
|
|
protocol_unbind_all();
|
2007-10-16 01:44:56 -04:00
|
|
|
exit(1); /* there has been an error */
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
else if (ret == 0) /* child breaks here */
|
|
|
|
|
break;
|
2013-02-12 04:53:52 -05:00
|
|
|
children[proc] = ret;
|
2012-09-05 02:02:48 -04:00
|
|
|
if (pidfd >= 0) {
|
|
|
|
|
char pidstr[100];
|
|
|
|
|
snprintf(pidstr, sizeof(pidstr), "%d\n", ret);
|
2013-12-13 09:14:55 -05:00
|
|
|
shut_your_big_mouth_gcc(write(pidfd, pidstr, strlen(pidstr)));
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
2007-11-04 17:35:08 -05:00
|
|
|
relative_pid++; /* each child will get a different one */
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
2012-11-16 10:12:27 -05:00
|
|
|
|
|
|
|
|
#ifdef USE_CPU_AFFINITY
|
|
|
|
|
if (proc < global.nbproc && /* child */
|
2015-04-20 05:36:57 -04:00
|
|
|
proc < LONGBITS && /* only the first 32/64 processes may be pinned */
|
2012-11-16 10:12:27 -05:00
|
|
|
global.cpu_map[proc]) /* only do this if the process has a CPU map */
|
2015-09-17 15:26:40 -04:00
|
|
|
#ifdef __FreeBSD__
|
|
|
|
|
cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(unsigned long), (void *)&global.cpu_map[proc]);
|
|
|
|
|
#else
|
2012-11-16 10:12:27 -05:00
|
|
|
sched_setaffinity(0, sizeof(unsigned long), (void *)&global.cpu_map[proc]);
|
2015-09-17 15:26:40 -04:00
|
|
|
#endif
|
2012-11-16 10:12:27 -05:00
|
|
|
#endif
|
2006-06-25 20:48:02 -04:00
|
|
|
/* close the pidfile both in children and father */
|
2012-09-05 02:02:48 -04:00
|
|
|
if (pidfd >= 0) {
|
|
|
|
|
//lseek(pidfd, 0, SEEK_SET); /* debug: emulate eglibc bug */
|
|
|
|
|
close(pidfd);
|
|
|
|
|
}
|
2010-08-25 06:49:05 -04:00
|
|
|
|
2016-10-25 11:20:24 -04:00
|
|
|
/* each child must notify the wrapper that it's ready by closing the requested fd */
|
|
|
|
|
wrapper_fd = getenv("HAPROXY_WRAPPER_FD");
|
|
|
|
|
if (wrapper_fd) {
|
|
|
|
|
int pipe_fd = atoi(wrapper_fd);
|
|
|
|
|
|
|
|
|
|
if (pipe_fd >= 0)
|
|
|
|
|
close(pipe_fd);
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-25 06:49:05 -04:00
|
|
|
/* We won't ever use this anymore */
|
|
|
|
|
free(oldpids); oldpids = NULL;
|
|
|
|
|
free(global.chroot); global.chroot = NULL;
|
|
|
|
|
free(global.pidfile); global.pidfile = NULL;
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2015-05-01 11:01:08 -04:00
|
|
|
if (proc == global.nbproc) {
|
|
|
|
|
if (global.mode & MODE_SYSTEMD) {
|
2016-01-14 12:10:30 -05:00
|
|
|
int i;
|
|
|
|
|
|
2015-05-01 11:01:08 -04:00
|
|
|
protocol_unbind_all();
|
2016-01-14 12:10:30 -05:00
|
|
|
for (i = 1; i < argc; i++) {
|
|
|
|
|
memset(argv[i], '\0', strlen(argv[i]));
|
|
|
|
|
}
|
|
|
|
|
/* it's OK because "-Ds -f x" is the shortest form going here */
|
|
|
|
|
memcpy(argv[0] + strlen(argv[0]), "-master", 8);
|
2015-05-01 11:01:08 -04:00
|
|
|
for (proc = 0; proc < global.nbproc; proc++)
|
|
|
|
|
while (waitpid(children[proc], NULL, 0) == -1 && errno == EINTR);
|
|
|
|
|
}
|
|
|
|
|
exit(0); /* parent must leave */
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-04 16:05:05 -05:00
|
|
|
/* we might have to unbind some proxies from some processes */
|
|
|
|
|
px = proxy;
|
|
|
|
|
while (px != NULL) {
|
|
|
|
|
if (px->bind_proc && px->state != PR_STSTOPPED) {
|
2013-01-18 05:29:29 -05:00
|
|
|
if (!(px->bind_proc & (1UL << proc)))
|
2009-02-04 16:05:05 -05:00
|
|
|
stop_proxy(px);
|
|
|
|
|
}
|
|
|
|
|
px = px->next;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-01 13:13:41 -04:00
|
|
|
/* we might have to unbind some peers sections from some processes */
|
|
|
|
|
for (curpeers = peers; curpeers; curpeers = curpeers->next) {
|
|
|
|
|
if (!curpeers->peers_fe)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (curpeers->peers_fe->bind_proc & (1UL << proc))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
stop_proxy(curpeers->peers_fe);
|
|
|
|
|
/* disable this peer section so that it kills itself */
|
2015-09-28 10:39:25 -04:00
|
|
|
signal_unregister_handler(curpeers->sighandler);
|
|
|
|
|
task_delete(curpeers->sync_task);
|
|
|
|
|
task_free(curpeers->sync_task);
|
|
|
|
|
curpeers->sync_task = NULL;
|
|
|
|
|
task_free(curpeers->peers_fe->task);
|
|
|
|
|
curpeers->peers_fe->task = NULL;
|
2015-05-01 13:13:41 -04:00
|
|
|
curpeers->peers_fe = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-28 18:57:16 -04:00
|
|
|
free(children);
|
|
|
|
|
children = NULL;
|
2006-06-25 20:48:02 -04:00
|
|
|
/* if we're NOT in QUIET mode, we should now close the 3 first FDs to ensure
|
|
|
|
|
* that we can detach from the TTY. We MUST NOT do it in other cases since
|
|
|
|
|
* it would have already be done, and 0-2 would have been affected to listening
|
|
|
|
|
* sockets
|
|
|
|
|
*/
|
2008-11-16 01:40:34 -05:00
|
|
|
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
|
2006-06-25 20:48:02 -04:00
|
|
|
/* detach from the tty */
|
|
|
|
|
fclose(stdin); fclose(stdout); fclose(stderr);
|
2008-11-16 01:40:34 -05:00
|
|
|
global.mode &= ~MODE_VERBOSE;
|
2006-06-25 20:48:02 -04:00
|
|
|
global.mode |= MODE_QUIET; /* ensure that we won't say anything from now */
|
|
|
|
|
}
|
|
|
|
|
pid = getpid(); /* update child's pid */
|
|
|
|
|
setsid();
|
2007-04-09 13:29:56 -04:00
|
|
|
fork_poller();
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
2007-10-16 06:25:14 -04:00
|
|
|
protocol_enable_all();
|
2007-04-08 10:39:58 -04:00
|
|
|
/*
|
|
|
|
|
* That's it : the central polling loop. Run until we stop.
|
|
|
|
|
*/
|
|
|
|
|
run_poll_loop();
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
/* Do some cleanup */
|
|
|
|
|
deinit();
|
|
|
|
|
|
|
|
|
|
exit(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Local variables:
|
|
|
|
|
* c-indent-level: 8
|
|
|
|
|
* c-basic-offset: 8
|
|
|
|
|
* End:
|
|
|
|
|
*/
|