1
0
mirror of http://git.haproxy.org/git/haproxy.git/ synced 2025-01-10 16:00:08 +00:00

* released 1.2.1 (1.1.28)

* added the '-V' command line option to verbosely report errors even though
  the -q or 'quiet' options are specified. This is useful with '-c'.
* added a Red Hat init script and a .spec from Simon Matter <simon.matter@invoca.ch>
* added 'rspdeny' and 'rspideny' to block certain responses to avoid sensible
  information leak from servers.
* more examples added into the configuration
This commit is contained in:
willy tarreau 2005-12-18 00:57:06 +01:00
parent 97f58576eb
commit 982249e9e7
8 changed files with 583 additions and 86 deletions

View File

@ -1,13 +1,19 @@
ChangeLog :
===========
2004/06/06 : 1.2.1 (1.1.28)
- added the '-V' command line option to verbosely report errors even though
the -q or 'quiet' options are specified. This is useful with '-c'.
- added a Red Hat init script and a .spec from Simon Matter <simon.matter@invoca.ch>
2004/06/05 : 1.2.1 (1.1.28)
- added the "logasap" option which produces a log without waiting for
the data to be transferred from the server to the client.
- added the "httpclose" option which removes any "connection:" header
and adds "Connection: close" in both direction.
2004/06/05 :
- added the "logasap" option which produces a log without waiting for the data
to be transferred from the server to the client.
- added the "httpclose" option which removes any "connection:" header and adds
"Connection: close" in both direction.
- added the 'checkcache' option which blocks cacheable responses containing
dangerous headers, such as 'set-cookie'.
- added 'rspdeny' and 'rspideny' to block certain responses to avoid sensible
information leak from servers.
2004/04/18 :
- send an EMERG log when no server is available for a given proxy

View File

@ -1,9 +1,9 @@
H A - P r o x y
---------------
version 1.1.27
version 1.2.1
willy tarreau
2003/10/27
2004/06/06
============
| Abstract |
@ -35,6 +35,10 @@ There are only a few command line options :
-N <high limit for the per-proxy number of simultaneous connections>
-d starts in foregreound with debugging mode enabled
-D starts in daemon mode
-q disable messages on output
-V displays messages on output even when -q or 'quiet' are specified.
-c only checks config file and exits with code 0 if no error was found, or
exits with code 1 if a syntax error was found.
-p <pidfile> asks the process to write down each of its children's
pids to this file in daemon mode.
-s shows statistics (only if compiled in)
@ -219,7 +223,7 @@ reasons.
1.5) Increasing the overall processing power
--------------------------------------------
On multi-processor systems, it may seem to be a shame to use only one processor,
eventhough the load needed to saturate a recent processor are far above common
eventhough the load needed to saturate a recent processor is far above common
usage. Anyway, for very specific needs, the proxy can start several processes
between which the operating system will spread the incoming connections. The
number of processes is controlled by the 'nbproc' parameter in the 'global'
@ -379,7 +383,7 @@ Example :
The 'maxconn' parameter allows a proxy to refuse connections above a certain
amount of simultaneous ones. When the limit is reached, it simply stops
listening, but the system may still be accepting them because of the back log
queue. These connections will be processed further when other ones have freed
queue. These connections will be processed later when other ones have freed
some slots. This provides a serialization effect which helps very fragile
servers resist to high loads. Se further for system limitations.
@ -733,6 +737,11 @@ The servers status will be dumped into the logs at the 'notice' level, as well
as on <stderr> if not closed. For this reason, it's always a good idea to have
one local log server at the 'notice' level.
Since version 1.1.28 and 1.2.1, if an instance loses all its servers, an
emergency mesasge will be sent in the logs to inform the administator that an
immediate action must be taken.
Examples :
----------
# same setup as in paragraph 3) with TCP monitoring
@ -917,6 +926,15 @@ the proxy will wait until the session ends to generate an enhanced log
containing more information such as session duration and its state during the
disconnection.
Example :
---------
listen relais-tcp 0.0.0.0:8000
mode tcp
option tcplog
log 192.168.2.200 local3
>>> haproxy[18989]: 127.0.0.1:34550 [15/Oct/2003:15:24:28] relais-tcp Srv1 0/5007 0 --
Another option, 'httplog', provides more detailed information about HTTP
contents, such as the request and some cookies. In the event where an external
component would establish frequent connections to check the service, logs may be
@ -932,6 +950,34 @@ Example :
option dontlognull
log 192.168.2.200 local3
>>> haproxy[674]: 127.0.0.1:33319 [15/Oct/2003:08:31:57] relais-http Srv1 9/7/147/723 200 243 - - ---- "HEAD / HTTP/1.0"
The problem when logging at end of connection is that you have no clue about
what is happening during very long sessions. To workaround this problem, a
new option 'logasap' has been introduced in 1.1.28/1.2.1. When specified, the
proxy will log as soon as possible, just before data transfer begins. This means
that in case of TCP, it will still log the connection status to the server, and
in case of HTTP, it will log just after processing the server headers. In this
case, the number of bytes reported is the number of header bytes sent to the
client.
In order to avoid confusion with normal logs, the total time field and the
number of bytes are prefixed with a '+' sign which mean that real numbers are
certainly bigger.
Example :
---------
listen http_proxy 0.0.0.0:80
mode http
option httplog
option dontlognull
option logasap
log 192.168.2.200 local3
>>> haproxy[674]: 127.0.0.1:33320 [15/Oct/2003:08:32:17] relais-http Srv1 9/7/14/+30 200 +243 - - ---- "GET /image.iso HTTP/1.0"
4.2.3) Timing events
--------------------
Timers provide a great help in trouble shooting network problems. All values
@ -956,8 +1002,10 @@ reported under the form 'Tq/Tc/Tr/Tt' :
means that the last the response header (empty line) was never seen.
- Tt: total session duration time, between the moment the proxy accepted it
and the moment both ends were closed. From this one, we can deduce Td,
the data transmission time, by substracting other timers when valid :
and the moment both ends were closed. The exception is when the 'logasap'
option is specified. In this case, it only equals (Tq+Tc+Tr), and is
prefixed with a '+' sign. From this field, we can deduce Td, the data
transmission time, by substracting other timers when valid :
Td = Tt - (Tq + Tc + Tr)
@ -1005,7 +1053,9 @@ TCP and HTTP logs provide a session completion indicator. It's a 4-characters
C : the TCP session was aborted by the client.
S : the TCP session was aborted by the server, or the server refused it.
P : the session was abordted prematurely by the proxy, either because of
an internal error, or because a DENY filter was matched.
an internal error, because a DENY filter was matched, or because of
a security check which detected a dangerous error in server
response.
c : the client time-out expired first.
s : the server time-out expired first.
- : normal session completion.
@ -1014,7 +1064,7 @@ TCP and HTTP logs provide a session completion indicator. It's a 4-characters
R : waiting for complete REQUEST from the client
C : waiting for CONNECTION to establish on the server
H : waiting for complete HEADERS from the server
H : processing server HEADERS
D : the session was in the DATA phase
L : the proxy was still transmitting LAST data to the client while the
server had already finished.
@ -1073,6 +1123,18 @@ client. Each of these field is replaced with '-' when no cookie was seen.
=> long request (6.5s) entered by hand through 'telnet'. The server replied
in 147 ms, and the session ended normally ('----')
- haproxy[674]: 127.0.0.1:33320 [15/Oct/2003:08:32:17] relais-http Srv1 9/7/14/+30 200 +243 - - ---- "GET /image.iso HTTP/1.0"
=> request for a long data transfer. The 'logasap' option was specified, so
the log was produced just before transfering data. The server replied in
14 ms, 243 bytes of headers were sent to the client, and total time from
accept to first data byte is 30 ms.
- haproxy[674]: 127.0.0.1:33320 [15/Oct/2003:08:32:17] relais-http Srv1 9/7/14/30 502 243 - - PH-- "GET /cgi-bin/bug.cgi? HTTP/1.0"
=> the proxy blocked a server response either because of an 'rspdeny' or
'rspideny' filter, or because it blocked sensible information which risked
being cached. In this case, the response is replaced with a '502 bad
gateway'.
- haproxy[18113]: 127.0.0.1:34548 [15/Oct/2003:15:18:55] relais-http <NOSRV> -1/-1/-1/8490 -1 0 - - CR-- ""
=> the client never completed its request and aborted itself ('C---') after
8.5s, while the proxy was waiting for the request headers ('-R--').
@ -1122,6 +1184,8 @@ The syntax is :
rspirep <search> <replace> same, but ignoring the case
rspdel <search> to delete the response
rspidel <search> same, but ignoring the case
rspdeny <search> replaces a response with a HTTP 502 if a header matches <search>
rspideny <search> same, but ignoring the case
<search> is a POSIX regular expression (regex) which supports grouping through
@ -1160,6 +1224,9 @@ Notes :
value is easy to modify in the code if needed (#define). If it is too short
on occasional uses, it is possible to gain some space by removing some
useless headers before adding new ones.
- a denied request will generate an "HTTP 403 forbidden" response, while a
denied response will generate an "HTTP 502 Bad gateway" response.
Examples :
----------
@ -1195,20 +1262,21 @@ Examples :
reqideny ^[^:\ ]*\
# force connection:close, thus disabling HTTP keep-alive
reqidel ^Connection:
rspidel ^Connection:
reqadd Connection:\ close
rspadd Connection:\ close
option httpclose
# change the server name
rspidel ^Server:\
rspadd Server:\ Formilux/0.1.8
Last, the 'forwardfor' option creates an HTTP 'X-Forwarded-For' header which
Also, the 'forwardfor' option creates an HTTP 'X-Forwarded-For' header which
contains the client's IP address. This is useful to let the final web server
know what the client address was (eg for statistics on domains).
Last, the 'httpclose' option removes any 'Connection' header both ways, and
adds a 'Connection: close' header in each direction. This makes it easier to
disable HTTP keep-alive than the previous 4-rules block..
Example :
---------
listen http_proxy 0.0.0.0:80
@ -1217,10 +1285,10 @@ Example :
option httplog
option dontlognull
option forwardfor
option httpclose
4.4) Load balancing with persistence
------------------------------------
Combining cookie insertion with internal load balancing allows to transparently
bring persistence to applications. The principle is quite simple :
- assign a cookie value to each server
@ -1239,9 +1307,35 @@ Example :
server 192.168.1.1:80 cookie server01 check
server 192.168.1.2:80 cookie server02 check
4.5) Customizing errors
-----------------------
4.5) Protection against information leak from the servers
---------------------------------------------------------
In versions 1.1.28/1.2.1, a new option 'checkcache' was created. It carefully
checks 'Cache-control', 'Pragma' and 'Set-cookie' headers in server response
to check if there's a risk of caching a cookie on a client-side proxy. When this
option is enabled, the only responses which can be delivered to the client are :
- all those without 'Set-Cookie' header ;
- all those with a return code other than 200, 203, 206, 300, 301, 410,
provided that the server has not set a 'Cache-control: public' header ;
- all those that come from a POST request, provided that the server has not
set a 'Cache-Control: public' header ;
- those with a 'Pragma: no-cache' header
- those with a 'Cache-control: private' header
- those with a 'Cache-control: no-store' header
- those with a 'Cache-control: max-age=0' header
- those with a 'Cache-control: s-maxage=0' header
- those with a 'Cache-control: no-cache' header
- those with a 'Cache-control: no-cache="set-cookie"' header
- those with a 'Cache-control: no-cache="set-cookie,' header
(allowing other fields after set-cookie)
If a response doesn't respect these requirements, then it will be blocked just
as if it was from an 'rspdeny' filter, with an "HTTP 502 bad gateway". The
session state shows "PH--" meaning that the proxy blocked the response during
headers processing. Additionnaly, an alert will be sent in the logs so that
admins are told that there's something to be done.
4.6) Customizing errors
-----------------------
Some situations can make haproxy return an HTTP error code to the client :
- invalid or too long request => HTTP 400
- request not completely sent in time => HTTP 408
@ -1275,9 +1369,8 @@ Example :
errorloc 503 http://192.168.114.58/error50x.html
errorloc 504 http://192.168.114.58/error50x.html
4.6) Modifying default values
4.7) Modifying default values
-----------------------------
Version 1.1.22 introduced the notion of default values, which eliminates the
pain of often repeating common parameters between many instances, such as
logs, timeouts, modes, etc...
@ -1290,7 +1383,7 @@ used for this section. The 'defaults' section uses the same syntax as the
everything on its command line, so that fake instance names can be specified
there for better clarity.
In version 1.1.23, only those parameters can be preset in the 'default'
In version 1.1.28/1.2.1, only those parameters can be preset in the 'default'
section :
- log (the first and second one)
- mode { tcp, http, health }
@ -1298,8 +1391,8 @@ section :
- disabled (to disable every further instances)
- enabled (to enable every further instances, this is the default)
- contimeout, clitimeout, srvtimeout, grace, retries, maxconn
- option { redispatch, transparent, keepalive, forwardfor, httplog,
dontlognull, persist, httpchk }
- option { redispatch, transparent, keepalive, forwardfor, logasap, httpclose,
checkcache, httplog, tcplog, dontlognull, persist, httpchk }
- redispatch, redisp, transparent, source { addr:port }
- cookie, capture
- errorloc

View File

@ -1,9 +1,9 @@
H A - P r o x y
---------------
version 1.1.27
version 1.2.1
willy tarreau
2003/10/27
2004/06/06
================
| Introduction |
@ -37,6 +37,12 @@ Les options de lancement sont peu nombreuses :
-N <nombre maximal de connexions simultanées par proxy>
-d active le mode debug
-D passe en daemon
-q désactive l'affichage de messages sur la sortie standard.
-V affiche les messages sur la sortie standard, même si -q ou 'quiet' sont
spécifiés.
-c vérifie le fichier de configuration puis quitte avec un code de retour 0
si aucune erreur n'a été trouvée, ou 1 si une erreur de syntaxe a été
détectée.
-p <fichier> indique au processus père qu'il doit écrire les PIDs de ses
fils dans ce fichier en mode démon.
-s affiche les statistiques (si option compilée)
@ -754,6 +760,10 @@ les logs en niveau "notice", ainsi que sur la sortie d'erreurs si elle est
active. C'est une bonne raison pour avoir au moins un serveur de logs local en
niveau notice.
Depuis la version 1.1.18 (et 1.2.1), un message d'urgence est envoyé dans les
logs en niveau 'emerg' si tous les serveurs d'une même instance sont tombés,
afin de notifier l'administrateur qu'il faut prendre une action immédiate.
Exemples :
----------
# conf du paragraphe 3) avec surveillance TCP
@ -937,6 +947,15 @@ la connexion ne sera journalis
sur son état lors de la déconnexion, ainsi que le temps de connexion et la
durée totale de la session.
Exemple :
---------
listen relais-tcp 0.0.0.0:8000
mode tcp
option tcplog
log 192.168.2.200 local3
>>> haproxy[18989]: 127.0.0.1:34550 [15/Oct/2003:15:24:28] relais-tcp Srv1 0/5007 0 --
Une autre option, 'httplog', fournit plus de détails sur le protocole HTTP,
notamment la requête et l'état des cookies. Dans les cas où un mécanisme de
surveillance effectuant des connexions et déconnexions fréquentes, polluerait
@ -952,6 +971,35 @@ Exemple :
option dontlognull
log 192.168.2.200 local3
>>> haproxy[674]: 127.0.0.1:33319 [15/Oct/2003:08:31:57] relais-http Srv1 9/7/147/723 200 243 - - ---- "HEAD / HTTP/1.0"
Le problème de loguer uniquement en fin de session, c'est qu'il est impossible
de savoir ce qui se passe durant de gros transferts ou des sessions longues.
Pour pallier à ce problème, une nouvelle option 'logasap' a été introduite dans
la version 1.1.28 (1.2.1). Lorsqu'elle est activée, le proxy loguera le plus tôt
possible, c'est à dire juste avant que ne débutent les transferts de données.
Cela signifie, dans le cas du TCP, qu'il loguera toujours le résultat de la
connexion vers le serveur, et dans le cas HTTP, qu'il loguera en fin de
traitement des entêtes de la réponse du serveur, auquel cas le nombre d'octets
représentera la taille des entêtes retournés au client.
Afin d'éviter toute confusion avec les logs normaux, le temps total de transfert
et le nombre d'octets transférés sont préfixés d'un signe '+' rappeleant que les
valeurs réelles sont certainement plus élevées.
Exemple :
---------
listen http_proxy 0.0.0.0:80
mode http
option httplog
option dontlognull
option logasap
log 192.168.2.200 local3
>>> haproxy[674]: 127.0.0.1:33320 [15/Oct/2003:08:32:17] relais-http Srv1 9/7/14/+30 200 +243 - - ---- "GET /image.iso HTTP/1.0"
4.2.3) Chronométrage des événements
-----------------------------------
Pour déceler des problèmes réseau, les mesures du temps écoulé entre certains
@ -980,8 +1028,11 @@ la forme Tq/Tc/Tr/Tt :
- Tt: durée de vie totale de la session, entre le moment où la demande de
connexion du client a été acquittée et le moment où la connexion a été
refermée aux deux extrémités (client et serveur). On peut donc en déduire
Td, le temps de transfert des données, en excluant les autres temps :
refermée aux deux extrémités (client et serveur). La signification change
un peu si l'option 'logasap' est présente. Dans ce cas, le temps correspond
uniquement à (Tq + Tc + Tr), et se trouve préfixé d'un signe '+'. On peut
donc déduire Td, le temps de transfert des données, en excluant les autres
temps :
Td = Tt - (Tq + Tc + Tr)
@ -1032,7 +1083,9 @@ C'est un champ de 4 caract
C : fermeture de la session TCP de la part du client
S : fermeture de la session TCP de la part du serveur, ou refus de connexion
P : terminaison prématurée des sessions par le proxy, pour cas d'erreur
interne ou de configuration (ex: filtre d'URL)
interne, de configuration (ex: filtre d'URL), ou parce qu'un
contrôle de sécurité a détecté une anomalie dans la réponse du
serveur.
c : expiration du délai d'attente côté client : clitimeout
s : expiration du délai d'attente côté serveur: srvtimeout et contimeout
- : terminaison normale.
@ -1042,7 +1095,7 @@ C'est un champ de 4 caract
R : terminaison en attendant la réception totale de la requête du client
C : terminaison en attendant la connexion vers le serveur
H : terminaison en attendant la réception totale des entêtes du serveur
H : terminaison en traitant les entêtes du serveur
D : terminaison durant le transfert des données du serveur vers le client
L : terminaison durant le transfert des dernières données du proxy vers
le client, alors que le serveur a déjà fini.
@ -1107,6 +1160,18 @@ client ou le serveur.
=> requête longue (6.5s) saisie à la main avec un client telnet. Le serveur a
répondu en 147 ms et la session s'est terminée normalement ('----')
- haproxy[674]: 127.0.0.1:33320 [15/Oct/2003:08:32:17] relais-http Srv1 9/7/14/+30 200 +243 - - ---- "GET /image.iso HTTP/1.0"
=> requête pour un long transfert. L'option 'logasap' était spécifiée donc le
log a été généré juste avant le transfert de données. Le serveur a répondu
en 14 ms, 243 octets d'entêtes ont été transférés au client, et le temps
total entre l'accept() et le premier octet de donnée est de 30 ms.
- haproxy[674]: 127.0.0.1:33320 [15/Oct/2003:08:32:17] relais-http Srv1 9/7/14/30 502 243 - - PH-- "GET /cgi-bin/bug.cgi? HTTP/1.0"
=> le proxy a bloqué une réponse du serveur soit à cause d'un filtre 'rspdeny'
ou 'rspideny', soit parce qu'il a détecté un risque de fuite sensible
d'informations risquant d'être cachées. Dans ce cas, la réponse est
remplacée par '502 bad gateway'.
- haproxy[18113]: 127.0.0.1:34548 [15/Oct/2003:15:18:55] relais-http <NOSRV> -1/-1/-1/8490 -1 0 - - CR-- ""
=> Le client n'a pas envoyé sa requête et a refermé la connexion lui-même
('C---') au bout de 8.5s, alors que le relais attendait l'entête ('-R--').
@ -1155,6 +1220,9 @@ La syntaxe est :
rspirep <search> <replace> idem sans distinction majuscules/minuscules
rspdel <search> pour supprimer un en-tête dans la réponse
rspidel <search> idem sans distinction majuscules/minuscules
rspdeny <search> remplace la réponse par un HTTP 502 si un
entête valide <search>
rspideny <search> idem sans distinction majuscules/minuscules
<search> est une expression régulière compatible POSIX regexp supportant le
@ -1195,6 +1263,8 @@ Remarques :
limite était à 256 auparavant). Cette valeur est modifiable dans le code.
Pour un usage temporaire, on peut gagner de la place en supprimant quelques
entêtes inutiles avant les ajouts.
- une requête bloquée produira une réponse "HTTP 403 forbidden" tandis qu'une
réponse bloquée produira une réponse "HTTP 502 Bad gateway".
Exemples :
----------
@ -1230,20 +1300,22 @@ Exemples :
reqideny ^[^:\ ]*\
# force connection:close, thus disabling HTTP keep-alive
reqidel ^Connection:
rspidel ^Connection:
reqadd Connection:\ close
rspadd Connection:\ close
option httpclos
# change the server name
rspidel ^Server:\
rspadd Server:\ Formilux/0.1.8
Enfin, l'option 'forwardfor' ajoute l'adresse IP du client dans un champ
De plus, l'option 'forwardfor' ajoute l'adresse IP du client dans un champ
'X-Forwarded-For' de la requête, ce qui permet à un serveur web final de
connaître l'adresse IP du client initial.
Enfin, l'option 'httpclose' apparue dans la version 1.1.28/1.2.1 supprime tout
entête de type 'Connection:' et ajoute 'Connection: close' dans les deux sens.
Ceci simplifie la désactivation du keep-alive HTTP par rapport à l'ancienne
méthode impliquant 4 règles.
Exemple :
---------
listen http_proxy 0.0.0.0:80
@ -1252,10 +1324,10 @@ Exemple :
option httplog
option dontlognull
option forwardfor
option httpclose
4.4) Répartition avec persistence
---------------------------------
La combinaison de l'insertion de cookie avec la répartition de charge interne
permet d'assurer une persistence dans les sessions HTTP d'une manière
pratiquement transparente pour les applications. Le principe est simple :
@ -1274,9 +1346,37 @@ Exemple :
server 192.168.1.1:80 cookie server01 check
server 192.168.1.2:80 cookie server02 check
4.5) Personalisation des erreurs
--------------------------------
4.5) Protection contre les fuites d'informations du serveur
-----------------------------------------------------------
Dans les versions 1.1.28 et 1.2.1, une nouvelle option 'checkcache' a été créée.
Elle sert à inspecter minutieusement les entêtes 'Cache-control', 'Pragma', et
'Set-cookie' dans les réponses serveur pour déterminer s'il y a un risque de
cacher un cookie sur un proxy côté client. Quand cette option est activée, les
seules réponses qui peuvent être retournées au client sont :
- toutes celles qui n'ont pas d'entête 'Set-cookie' ;
- toutes celles qui ont un code de retour autre que 200, 203, 206, 300, 301,
410, sauf si le server a positionné un entête 'Cache-control: public' ;
- celles qui font suite à une requête POST, sauf si le serveur a positionné
un entête 'Cache-control: public' ;
- celles qui ont un entête 'Pragma: no-cache' ;
- celles qui ont un entête 'Cache-control: private' ;
- celles qui ont un entête 'Cache-control: no-store' ;
- celles qui ont un entête 'Cache-control: max-age=0' ;
- celles qui ont un entête 'Cache-control: s-maxage=0' ;
- celles qui ont un entête 'Cache-control: no-cache' ;
- celles qui ont un entête 'Cache-control: no-cache="set-cookie"' ;
- celles qui ont un entête 'Cache-control: no-cache="set-cookie,'
(autorisant d'autres champs après set-cookie).
Si une réponse ne respecte pas ces pré-requis, alors elle sera bloquée de la
même manière que s'il s'agissait d'un filtre 'rspdeny', avec en retour un
message "HTTP 502 bad gateway". L'état de session montre "PH--" ce qui veut
dire que c'est le proxy qui a bloqué la réponse durant le traitement des
entêtes. De plus, un message d'alerte sera envoyé dans les logs de sorte que
l'administrateur sache qu'il y a une action correctrice à entreprendre.
4.6) Personalisation des erreurs
--------------------------------
Certaines situations conduisent à retourner une erreur HTTP au client :
- requête invalide ou trop longue => code HTTP 400
- requête mettant trop de temps à venir => code HTTP 408
@ -1309,9 +1409,8 @@ Exemple :
errorloc 503 http://192.168.114.58/error50x.html
errorloc 504 http://192.168.114.58/error50x.html
4.6) Changement des valeurs par défaut
4.7) Changement des valeurs par défaut
--------------------------------------
Dans la version 1.1.22 est apparue la notion de valeurs par défaut, ce qui évite
de répéter des paramètres communs à toutes les instances, tels que les timeouts,
adresses de log, modes de fonctionnement, etc.
@ -1326,16 +1425,16 @@ La section 'defaults' utilise la m
paramètres près qui ne sont pas supportés. Le mot clé 'defaults' peut accepter
un commentaire en guise paramètre.
Dans la version 1.1.23, seuls les paramètres suivants peuvent être positionnés
dans une section 'defaults' :
Dans la version 1.1.28/1.2.1, seuls les paramètres suivants peuvent être
positionnés dans une section 'defaults' :
- log (le premier et le second)
- mode { tcp, http, health }
- balance { roundrobin }
- disabled (pour désactiver toutes les instances qui suivent)
- enabled (pour faire l'opération inverse, mais c'est le cas par défaut)
- contimeout, clitimeout, srvtimeout, grace, retries, maxconn
- option { redispatch, transparent, keepalive, forwardfor, httplog,
dontlognull, persist, httpchk }
- option { redispatch, transparent, keepalive, forwardfor, logasap, httpclose,
checkcache, httplog, tcplog, dontlognull, persist, httpchk }
- redispatch, redisp, transparent, source { addr:port }
- cookie, capture
- errorloc

View File

@ -1,13 +1,13 @@
# this config needs haproxy-1.1.23
# this config needs haproxy-1.1.28 or haproxy-1.2.1
global
log 127.0.0.1 local0
log 127.0.0.1 local1 notice
#log loghost local0 info
maxconn 4096
chroot /tmp
uid 11
gid 2
chroot /usr/share/haproxy
uid 99
gid 99
daemon
#debug
#quiet
@ -40,10 +40,7 @@ listen appli2-insert 0.0.0.0:10002
server inst2 192.168.114.56:81 cookie server02 check inter 2000 fall 3
capture cookie vgnvisitor= len 32
reqidel ^Connection: # disable keep-alive
reqadd Connection:\ close
rspidel ^Connection:
rspadd Connection:\ close
option httpclose # disable keep-alive
rspidel ^Set-cookie:\ IP= # do not let this cookie tell our internal IP address
listen appli3-relais 0.0.0.0:10003
@ -66,10 +63,9 @@ listen appli5-backup 0.0.0.0:10005
capture cookie ASPSESSION len 32
srvtimeout 20000
reqidel ^Connection: # disable keep-alive
reqadd Connection:\ close
rspidel ^Connection:
rspadd Connection:\ close
option httpclose # disable keep-alive
option checkcache # block response if set-cookie & cacheable
rspidel ^Set-cookie:\ IP= # do not let this cookie tell our internal IP address
errorloc 502 http://192.168.114.58/error502.html

114
examples/haproxy.init Normal file
View File

@ -0,0 +1,114 @@
#!/bin/sh
#
# chkconfig: - 85 15
# description: HA-Proxy is a TCP/HTTP reverse proxy which is particularly suited \
# for high availability environments.
# processname: haproxy
# config: /etc/haproxy/haproxy.cfg
# pidfile: /var/run/haproxy.pid
# Script Author: Simon Matter <simon.matter@invoca.ch>
# Version: 2004060600
# Source function library.
if [ -f /etc/init.d/functions ]; then
. /etc/init.d/functions
elif [ -f /etc/rc.d/init.d/functions ] ; then
. /etc/rc.d/init.d/functions
else
exit 0
fi
# Source networking configuration.
. /etc/sysconfig/network
# Check that networking is up.
[ ${NETWORKING} = "no" ] && exit 0
# This is our service name
BASENAME=`basename $0`
if [ -L $0 ]; then
BASENAME=`find $0 -name $BASENAME -printf %l`
BASENAME=`basename $BASENAME`
fi
[ -f /etc/$BASENAME/$BASENAME.cfg ] || exit 1
RETVAL=0
start() {
/usr/sbin/$BASENAME -c -q -f /etc/$BASENAME/$BASENAME.cfg
if [ $? -ne 0 ]; then
echo "Errors found in configuration file, check it with '$BASENAME check'."
return 1
fi
echo -n "Starting $BASENAME: "
daemon /usr/sbin/$BASENAME -D -f /etc/$BASENAME/$BASENAME.cfg -p /var/run/$BASENAME.pid
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && touch /var/lock/subsys/$BASENAME
return $RETVAL
}
stop() {
echo -n "Shutting down $BASENAME: "
killproc $BASENAME -USR1
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$BASENAME
[ $RETVAL -eq 0 ] && rm -f /var/run/$BASENAME.pid
return $RETVAL
}
restart() {
/usr/sbin/$BASENAME -c -q -f /etc/$BASENAME/$BASENAME.cfg
if [ $? -ne 0 ]; then
echo "Errors found in configuration file, check it with '$BASENAME check'."
return 1
fi
stop
start
}
check() {
/usr/sbin/$BASENAME -c -q -V -f /etc/$BASENAME/$BASENAME.cfg
}
rhstatus() {
status $BASENAME
}
condrestart() {
[ -e /var/lock/subsys/$BASENAME ] && restart || :
}
# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
reload)
restart
;;
condrestart)
condrestart
;;
status)
rhstatus
;;
check)
check
;;
*)
echo $"Usage: $BASENAME {start|stop|restart|reload|condrestart|status|check}"
RETVAL=1
esac
exit $RETVAL

92
examples/haproxy.spec Normal file
View File

@ -0,0 +1,92 @@
Summary: HA-Proxy is a TCP/HTTP reverse proxy for high availability environments
Name: haproxy
Version: 1.2.1
Release: 1
License: GPL
Group: System Environment/Daemons
URL: http://w.ods.org/tools/%{name}/
Packager: Simon Matter <simon.matter@invoca.ch>
Vendor: Invoca Systems
Distribution: Invoca Linux Server
Source0: http://w.ods.org/tools/%{name}/%{name}-%{version}.tar.gz
Source1: %{name}.cfg
Source2: %{name}.init
BuildRoot: %{_tmppath}/%{name}-%{version}-root
BuildRequires: pcre-devel
Prereq: /sbin/chkconfig
%description
HA-Proxy is a TCP/HTTP reverse proxy which is particularly suited for high
availability environments. Indeed, it can:
- route HTTP requests depending on statically assigned cookies
- spread the load among several servers while assuring server persistence
through the use of HTTP cookies
- switch to backup servers in the event a main one fails
- accept connections to special ports dedicated to service monitoring
- stop accepting connections without breaking existing ones
- add/modify/delete HTTP headers both ways
- block requests matching a particular pattern
It needs very little resource. Its event-driven architecture allows it to easily
handle thousands of simultaneous connections on hundreds of instances without
risking the system's stability.
%prep
%setup -q
%build
%{__make} REGEX=pcre DEBUG=""
%install
[ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot}
%{__install} -d %{buildroot}%{_sbindir}
%{__install} -d %{buildroot}%{_sysconfdir}/rc.d/init.d
%{__install} -d %{buildroot}%{_sysconfdir}/logrotate.d
%{__install} -d %{buildroot}%{_sysconfdir}/%{name}
%{__install} -d %{buildroot}%{_datadir}/%{name}
%{__install} -s %{name} %{buildroot}%{_sbindir}/
%{__install} -c -m 644 %{SOURCE1} %{buildroot}%{_sysconfdir}/%{name}/
%{__install} -c -m 755 %{SOURCE2} %{buildroot}%{_sysconfdir}/rc.d/init.d/%{name}
%clean
[ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot}
%post
/sbin/chkconfig --add %{name}
%preun
if [ $1 = 0 ]; then
/sbin/service %{name} stop >/dev/null 2>&1 || :
/sbin/chkconfig --del %{name}
fi
%postun
if [ "$1" -ge "1" ]; then
/sbin/service %{name} condrestart >/dev/null 2>&1 || :
fi
%files
%defattr(-,root,root)
%doc CHANGELOG TODO examples
%attr(0755,root,root) %{_sbindir}/%{name}
%dir %{_sysconfdir}/%{name}
%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/%{name}/%{name}.cfg
%attr(0755,root,root) %config %{_sysconfdir}/rc.d/init.d/%{name}
%dir %{_datadir}/%{name}
%changelog
* Sun Jun 6 2004 Willy Tarreau <willy@w.ods.org>
- updated to 1.1.28
- added config check support to the init script
* Tue Oct 28 2003 Simon Matter <simon.matter@invoca.ch>
- updated to 1.1.27
- added pid support to the init script
* Wed Oct 22 2003 Simon Matter <simon.matter@invoca.ch>
- updated to 1.1.26
* Thu Oct 16 2003 Simon Matter <simon.matter@invoca.ch>
- initial build

View File

@ -7,7 +7,7 @@ option bin reserved_option /usr/sbin/haproxy
option cmdline reserved_option '$bin -f ${opt_config} -p ${pidfile} -D -q'
function do_help {
echo "Usage: ${0##*/} <status|start|stop|help>"
echo "Usage: ${0##*/} <status|start|stop|help|conf>"
echo "List of config.rc options (name, type, default value, current value) :"
echo
echo " - config ; def=/etc/haproxy/haproxy.cfg ; cur=$opt_confdir"
@ -15,6 +15,11 @@ function do_help {
exit 1
}
# reads the configuration file and checks its syntax.
function do_conf {
$bin -c -V -q -f ${opt_config}
}
# assign default values to options and variables before parsing the cfg file
function fct_begin_section {
pidfile="/var/run/haproxy${2:+-$2}.pid"

146
haproxy.c
View File

@ -8,7 +8,10 @@
* 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.
* 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
@ -54,7 +57,7 @@
#endif
#define HAPROXY_VERSION "1.2.1"
#define HAPROXY_DATE "2004/06/05"
#define HAPROXY_DATE "2004/06/06"
/* this is for libc5 for example */
#ifndef TCP_NODELAY
@ -300,6 +303,7 @@ int strlcpy2(char *dst, const char *src, int size) {
#define MODE_DAEMON 8
#define MODE_QUIET 16
#define MODE_CHECK 32
#define MODE_VERBOSE 64
/* server flags */
#define SRV_RUNNING 1 /* the server is UP */
@ -684,13 +688,14 @@ void display_version() {
void usage(char *name) {
display_version();
fprintf(stderr,
"Usage : %s -f <cfgfile> [ -vd"
"Usage : %s -f <cfgfile> [ -vdV"
#if STATTIME > 0
"sl"
#endif
"D ] [ -n <maxconn> ] [ -N <maxpconn> ] [ -p <pidfile> ]\n"
" -v displays version\n"
" -d enters debug mode\n"
" -V enters verbose mode (disables quiet mode)\n"
#if STATTIME > 0
" -s enables statistics output\n"
" -l enables long statistics format\n"
@ -714,7 +719,7 @@ void Alert(char *fmt, ...) {
struct timeval tv;
struct tm *tm;
if (!(global.mode & MODE_QUIET)) {
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
va_start(argp, fmt);
gettimeofday(&tv, NULL);
@ -736,7 +741,7 @@ void Warning(char *fmt, ...) {
struct timeval tv;
struct tm *tm;
if (!(global.mode & MODE_QUIET)) {
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
va_start(argp, fmt);
gettimeofday(&tv, NULL);
@ -755,7 +760,7 @@ void Warning(char *fmt, ...) {
void qfprintf(FILE *out, char *fmt, ...) {
va_list argp;
if (!(global.mode & MODE_QUIET)) {
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
va_start(argp, fmt);
vfprintf(out, fmt, argp);
fflush(out);
@ -2100,9 +2105,6 @@ int event_accept(int fd) {
s->req = s->rep = NULL; /* will be allocated later */
s->flags = 0;
if (p->options & PR_O_CHK_CACHE)
s->flags |= SN_CACHEABLE | SN_CACHE_COOK;
s->res_cr = s->res_cw = s->res_sr = s->res_sw = RES_SILENT;
s->cli_fd = cfd;
s->srv_fd = -1;
@ -2165,7 +2167,7 @@ int event_accept(int fd) {
}
}
if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
struct sockaddr_in sockname;
int namelen;
int len;
@ -2525,8 +2527,10 @@ int process_cli(struct session *t) {
if (t->proxy->options & PR_O_HTTP_CLOSE)
buffer_replace2(req, req->h, req->h, "Connection: close\r\n", 19);
if (!memcmp(req->data, "POST ", 5))
t->flags |= SN_POST; /* this is a POST request */
if (!memcmp(req->data, "POST ", 5)) {
/* this is a POST request, which is not cacheable by default */
t->flags |= SN_POST;
}
t->cli_state = CL_STDATA;
req->rlim = req->data + BUFSIZE; /* no more rewrite needed */
@ -2608,7 +2612,7 @@ int process_cli(struct session *t) {
delete_header = 0;
if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
int len, max;
len = sprintf(trash, "%08x:%s.clihdr[%04x:%04x]: ", t->uniq_id, t->proxy->id, (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
max = ptr - req->h;
@ -3085,7 +3089,7 @@ int process_cli(struct session *t) {
return 0;
}
else { /* CL_STCLOSE: nothing to do */
if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
int len;
len = sprintf(trash, "%08x:%s.clicls[%04x:%04x]\n", t->uniq_id, t->proxy->id, (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
write(1, trash, len);
@ -3288,6 +3292,21 @@ int process_srv(struct session *t) {
}
}
/* next, we'll block if an 'rspideny' or 'rspdeny' filter matched */
if (t->flags & SN_SVDENY) {
tv_eternity(&t->srexpire);
tv_eternity(&t->swexpire);
fd_delete(t->srv_fd);
t->srv_state = SV_STCLOSE;
t->logs.status = 502;
client_return(t, t->proxy->errmsg.len502, t->proxy->errmsg.msg502);
if (!(t->flags & SN_ERR_MASK))
t->flags |= SN_ERR_PRXCOND;
if (!(t->flags & SN_FINST_MASK))
t->flags |= SN_FINST_H;
return 1;
}
/* we'll have something else to do here : add new headers ... */
if ((t->srv) && !(t->flags & SN_DIRECT) && (t->proxy->options & PR_O_COOK_INS) &&
@ -3366,14 +3385,38 @@ int process_srv(struct session *t) {
*/
if (t->logs.logwait & LW_RESP) {
if (t->logs.status == -1) {
t->logs.logwait &= ~LW_RESP;
t->logs.status = atoi(rep->h + 9);
switch (t->logs.status) {
case 200:
case 203:
case 206:
case 300:
case 301:
case 410:
/* RFC2616 @13.4:
* "A response received with a status code of
* 200, 203, 206, 300, 301 or 410 MAY be stored
* by a cache (...) unless a cache-control
* directive prohibits caching."
*
* RFC2616 @9.5: POST method :
* "Responses to this method are not cacheable,
* unless the response includes appropriate
* Cache-Control or Expires header fields."
*/
if ((!t->flags & SN_POST) && (t->proxy->options & PR_O_CHK_CACHE))
t->flags |= SN_CACHEABLE | SN_CACHE_COOK;
break;
default:
break;
}
}
delete_header = 0;
if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
int len, max;
len = sprintf(trash, "%08x:%s.srvhdr[%04x:%04x]: ", t->uniq_id, t->proxy->id, (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
max = ptr - rep->h;
@ -3434,21 +3477,27 @@ int process_srv(struct session *t) {
t->flags &= ~SN_CACHEABLE & ~SN_CACHE_COOK;
else if (strncasecmp(rep->h, "Cache-control: ", 15) == 0) {
if (strncasecmp(rep->h + 15, "no-cache", 8) == 0) {
if (rep->h + 23 == ptr || rep->h[23] == ';')
if (rep->h + 23 == ptr || rep->h[23] == ',')
t->flags &= ~SN_CACHEABLE & ~SN_CACHE_COOK;
else {
if (strncasecmp(rep->h + 23, "=\"set-cookie", 12) == 0
&& (rep->h[35] == '"' || rep->h[35] == ';'))
&& (rep->h[35] == '"' || rep->h[35] == ','))
t->flags &= ~SN_CACHE_COOK;
}
} else if ((strncasecmp(rep->h + 15, "private", 7) == 0 &&
(rep->h + 22 == ptr || rep->h[22] == ';'))
(rep->h + 22 == ptr || rep->h[22] == ','))
|| (strncasecmp(rep->h + 15, "no-store", 8) == 0 &&
(rep->h + 23 == ptr || rep->h[23] == ';'))) {
(rep->h + 23 == ptr || rep->h[23] == ','))) {
t->flags &= ~SN_CACHEABLE & ~SN_CACHE_COOK;
} else if (strncasecmp(rep->h + 15, "max-age=0", 9) == 0 &&
(rep->h + 24 == ptr || rep->h[24] == ';')) {
(rep->h + 24 == ptr || rep->h[24] == ',')) {
t->flags &= ~SN_CACHEABLE & ~SN_CACHE_COOK;
} else if (strncasecmp(rep->h + 15, "s-maxage=0", 10) == 0 &&
(rep->h + 25 == ptr || rep->h[25] == ',')) {
t->flags &= ~SN_CACHEABLE & ~SN_CACHE_COOK;
} else if (strncasecmp(rep->h + 15, "public", 6) == 0 &&
(rep->h + 21 == ptr || rep->h[21] == ',')) {
t->flags |= SN_CACHEABLE | SN_CACHE_COOK;
}
}
}
@ -3869,7 +3918,7 @@ int process_srv(struct session *t) {
return 0;
}
else { /* SV_STCLOSE : nothing to do */
if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
int len;
len = sprintf(trash, "%08x:%s.srvcls[%04x:%04x]\n", t->uniq_id, t->proxy->id, (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
write(1, trash, len);
@ -3916,7 +3965,7 @@ int process_session(struct task *t) {
s->proxy->nbconn--;
actconn--;
if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
int len;
len = sprintf(trash, "%08x:%s.closed[%04x:%04x]\n", s->uniq_id, s->proxy->id, (unsigned short)s->cli_fd, (unsigned short)s->srv_fd);
write(1, trash, len);
@ -5436,6 +5485,26 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
chain_regex(&curproxy->rsp_exp, preg, ACT_REMOVE, strdup(args[2]));
}
else if (!strcmp(args[0], "rspdeny")) { /* block response header from a regex */
regex_t *preg;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (*(args[1]) == 0) {
Alert("parsing [%s:%d] : '%s' expects <search> as an argument.\n", file, linenum, args[0]);
return -1;
}
preg = calloc(1, sizeof(regex_t));
if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
Alert("parsing [%s:%d] : bad regular expression '%s'.\n", file, linenum, args[1]);
return -1;
}
chain_regex(&curproxy->rsp_exp, preg, ACT_DENY, strdup(args[2]));
}
else if (!strcmp(args[0], "rspirep")) { /* replace response header from a regex ignoring case */
regex_t *preg;
if (curproxy == &defproxy) {
@ -5477,6 +5546,26 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
chain_regex(&curproxy->rsp_exp, preg, ACT_REMOVE, strdup(args[2]));
}
else if (!strcmp(args[0], "rspideny")) { /* block response header from a regex ignoring case */
regex_t *preg;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (*(args[1]) == 0) {
Alert("parsing [%s:%d] : '%s' expects <search> as an argument.\n", file, linenum, args[0]);
return -1;
}
preg = calloc(1, sizeof(regex_t));
if (regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) {
Alert("parsing [%s:%d] : bad regular expression '%s'.\n", file, linenum, args[1]);
return -1;
}
chain_regex(&curproxy->rsp_exp, preg, ACT_DENY, strdup(args[2]));
}
else if (!strcmp(args[0], "rspadd")) { /* add response header */
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
@ -5830,7 +5919,7 @@ void init(int argc, char **argv) {
if (1<<INTBITS != sizeof(int)*8) {
fprintf(stderr,
"Error: wrong architecture. Recompile so that sizeof(int)=%d\n",
sizeof(int)*8);
(int)(sizeof(int)*8));
exit(1);
}
@ -5851,6 +5940,8 @@ void init(int argc, char **argv) {
display_version();
exit(0);
}
else if (*flag == 'V')
arg_mode |= MODE_VERBOSE;
else if (*flag == 'd')
arg_mode |= MODE_DEBUG;
else if (*flag == 'c')
@ -5884,7 +5975,7 @@ void init(int argc, char **argv) {
argv++; argc--;
}
global.mode = (arg_mode & (MODE_DAEMON | MODE_QUIET | MODE_DEBUG));
global.mode = (arg_mode & (MODE_DAEMON | MODE_VERBOSE | MODE_QUIET | MODE_CHECK | MODE_DEBUG));
if (!cfg_cfgfile)
usage(old_argv);
@ -5896,7 +5987,7 @@ void init(int argc, char **argv) {
exit(1);
}
if (arg_mode & MODE_CHECK) {
if (global.mode & MODE_CHECK) {
qfprintf(stdout, "Configuration file is valid : %s\n", cfg_cfgfile);
exit(0);
}
@ -5919,7 +6010,8 @@ void init(int argc, char **argv) {
/* command line debug mode inhibits configuration mode */
global.mode &= ~(MODE_DAEMON | MODE_QUIET);
}
global.mode |= (arg_mode & (MODE_DAEMON | MODE_QUIET | MODE_DEBUG | MODE_STATS | MODE_LOG));
global.mode |= (arg_mode & (MODE_DAEMON | MODE_QUIET | MODE_VERBOSE
| MODE_DEBUG | MODE_STATS | MODE_LOG));
if ((global.mode & MODE_DEBUG) && (global.mode & (MODE_DAEMON | MODE_QUIET))) {
Warning("<debug> mode incompatible with <quiet> and <daemon>. Keeping <debug> only.\n");