[ANNOUNCE] haproxy 1.4.13

2011-03-09 Thread Willy Tarreau
Hi all,

since Hank's report about the crash on files containing an empty
pattern, I have released 1.4.13 with the fix for this regression
introduced in 1.4.11 when trying to fix handling of empty lines.

In the commit, I stated that I could only reproduce it with files
composed only of empty lines but this is wrong. In fact if patterns
are not loaded in a tree (eg: regex, prefixes, ...) then the issue
can happen if the file ends with an empty line, so this is quite a
serious regression because many pattern lists may be manually edited
and contain an empty line at the end. The nice point is that it
crashes upon startup so if your process is running, there is nothing
to worry about. Of course, my tests only stressed IP addresses which
are loaded in a tree...

So please avoid 1.4.11-1.4.12 and use only 1.4.13 :

site index : http://haproxy.1wt.eu/
sources: http://haproxy.1wt.eu/download/1.4/src/
changelog  : http://haproxy.1wt.eu/download/1.4/src/CHANGELOG
binaries   : http://haproxy.1wt.eu/download/1.4/bin/

Thanks and sorry for the mess,
Willy




Apache Compatible HAProxy Log Formatting?

2011-03-09 Thread Joel Krauska
Has anyone worked out a way to get HAProxy to output logging in an 
Apache Combined Log Format?


Most log analysis tools out there already speak Apache log format and I 
was hoping to avoid writing a parser for what is essentially mostly the 
same information.


Anyone got a work around for this situation?

Best,

Joel



Re: Apache Compatible HAProxy Log Formatting?

2011-03-09 Thread Hervé COMMOWICK
On Wed, 09 Mar 2011 00:40:12 -0800
Joel Krauska jkrau...@gmail.com wrote:

 Has anyone worked out a way to get HAProxy to output logging in an 
 Apache Combined Log Format?
 
 Most log analysis tools out there already speak Apache log format and
 I was hoping to avoid writing a parser for what is essentially mostly
 the same information.
 
 Anyone got a work around for this situation?
 
 Best,
 
 Joel
 

Hi Joel,

you can try option httplog clf instead of option httplog

Regards,

Hervé.

-- 
Your Network supports your *BUSINESS !*
Appliances de *contrôle d'activité* et d'*optimisation* du réseau
EXCELIANCE - Rule your Network ! - www.exceliance.fr
ZAC des Metz - 3 Rue du petit robinson
78350 Jouy en Josas
Tél: +33 1 30 67 60 74 - Fax: +33 1 75 43 40 70




mode tcp with mode http/shoutcast radio issue

2011-03-09 Thread Florescu, Dan Alexandru
Hello,

I'm trying to use:
frontend front
mode http

and
backend web
mode http

backend radio
mode tcp


But will error with Unable to use proxy with wrong mode, required: http, has: 
tcp.
Ok, this is understandable, so I changed to:
frontend front
mode tcp

and
backend web
mode http

backend radio
mode tcp

This works. Problem is that I cannot use acl's  like the following:
hdr(User-Agent) -i WinampMPEG/5.60
hdr(Host) -i 80.86.106.35

I guess this is because it receives the tcp stream and doen't analyze headers 
anymore. So I tried to match at destination ip using
dst 80.86.106.35.
This also fails because I am using a proxy, and the connection is in fact being 
addressed to the proxy ip, not the external host that I am trying to contact.
If I use dst $proxy_ip, it matches. But it matchess _ALL_ connections, and I 
want to separate http ones from radio streaming.

Also, trying to use mode http for the backend radio will give me 502 Bad 
Gateway errors when trying to connect to Shoutcast radio servers. mode tcp is 
the only way I could make it work.
Also found a patch for haproxy+icey but I'm not very keen on recompiling.

Any ideas?



















The information contained herein is intended for its addressee(s) only and it 
is privileged or otherwise confidential. Any unauthorized distribution, 
amendment or disclosure hereof is strictly forbidden by the law. Please find 
complete and translated versions at http://www.rompetrol.com/disclaimer.html


Re: Script greasmonkey to navigate in Haproxy docs ...

2011-03-09 Thread Tommaso Lanza
Ignoring the fact that some else 
(http://www.mail-archive.com/haproxy@formilux.org/msg02764.html) had a similar 
idea, I pushed a public Git repo of the docs in Markdown format.
I've only used a few simple regexes, which I lazily dumped in the repo as well, 
as well as a healthy dose of manual labor. There are still some glitches here 
and there, but it's a good start overall.

Unfortunately Github strips the anchors I've inserted to navigate the docs, but 
any other markdown converter should do it nicely (I'm using Wolf Rentzsch 
Markdown Live on osx).

Help yourselves: https://github.com/tmslnz/HAProxy_Markdown


~~~
  tommasolanza.com
  +44 772 1692021
  @tmslnz




Re: Apache Compatible HAProxy Log Formatting?

2011-03-09 Thread Willy Tarreau
On Wed, Mar 09, 2011 at 01:10:12AM -0800, Joel Krauska wrote:
 On 3/9/11 1:03 AM, Graeme Donaldson wrote:
 2011/3/9 Hervé COMMOWICKhcommow...@exceliance.fr:
 http://haproxy.1wt.eu/download/1.4/doc/configuration.txt
 
 option httplog [ clf ]
   Enable logging of HTTP request, session state and timers
   May be used in sections :   defaults | frontend | listen | backend
  yes   |yes   |   yes  |   yes
   Arguments :
 clf   if the clf argument is added, then the output format
   will be the CLF format instead of HAProxy's default HTTP format. You
   can use this when you need to feed HAProxy's logs through a specific
   log analyser which only support the CLF format and which is not
   extensible.
 
 
 Be careful, HAproxy's CLF is Apache's common log format, not the
 combined log format that Joel originally asked for. The latter also
 logs the User-Agent and Referer headers.
 
 Regards,
 Graeme.
 
 
 Ah yes.
 
 That's 95% though. I'll take it.
 
 Combined would be a great addition.

Hey, simply add header captures and you'll get it at a different position
though.

 Even better would be apache style custom format syntax.

No, it serves a different purpose. Some info that apache has are not in
haproxy and many of the info haproxy logs cannot be expressed with the
custom format.

Anyway, we've planned to do a big rework of the logs subsystem to make
them much more extensible.

Regards,
Willy




[PATCH 04/26] Reap children

2011-03-09 Thread Simon Horman
In the master/worker the workers will be child processes of the master
and should be reaped after they exit.
---
 src/haproxy.c |   11 +++
 1 files changed, 11 insertions(+), 0 deletions(-)

diff --git a/src/haproxy.c b/src/haproxy.c
index 3fe8889..99d8c0d 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -48,6 +48,7 @@
 #include sys/prctl.h
 #include sys/capability.h
 #endif
+#include sys/wait.h
 
 #ifdef DEBUG_FULL
 #include assert.h
@@ -287,6 +288,15 @@ void sig_listen(struct sig_handler *sh)
 }
 
 /*
+ * upon SIGCHLD reap child
+ */
+void sig_reaper(struct sig_handler *sh)
+{
+   int status;
+   while(waitpid(-1, status, WNOHANG)  0);
+}
+
+/*
  * this function dumps every server's state when the process receives SIGHUP.
  */
 void sig_dump_state(struct sig_handler *sh)
@@ -1045,6 +1055,7 @@ void run(int argc, char **argv)
signal_register_fct(SIGQUIT, dump, SIGQUIT);
signal_register_fct(SIGUSR1, sig_soft_stop, SIGUSR1);
signal_register_fct(SIGHUP, sig_dump_state, SIGHUP);
+   signal_register_fct(SIGCHLD, sig_reaper, SIGCHLD);
 
/* Always catch SIGPIPE even on platforms which define MSG_NOSIGNAL.
 * Some recent FreeBSD setups report broken pipes, and MSG_NOSIGNAL
-- 
1.7.2.3




[PATCH 02/26] Add a socket cache

2011-03-09 Thread Simon Horman
When a socket is closed, typically because a proxy is deleted,
don't close the file descriptor. Rather keep a cache which can
be looked-up and used when a subsequent proxy is started.

The aim is to avoid unbinding and binding to ports and thus
interrupting services unnecessarily. This will be used
in conjunction with a facility to allow haproxy to reinitialise
itself.
---
 include/proto/fd.h |5 ++
 include/types/fd.h |   28 ++
 include/types/global.h |1 +
 src/fd.c   |  133 
 src/haproxy.c  |4 ++
 src/proto_tcp.c|   10 +++-
 src/proto_uxst.c   |   12 -
 7 files changed, 191 insertions(+), 2 deletions(-)

diff --git a/include/proto/fd.h b/include/proto/fd.h
index 1cba33b..3ca4a97 100644
--- a/include/proto/fd.h
+++ b/include/proto/fd.h
@@ -87,6 +87,11 @@ static inline void fd_insert(int fd)
maxfd = fd + 1;
 }
 
+void socket_cache_make_all_available(void);
+int socket_cache_get(const struct listener *listener);
+void socket_cache_gc(void);
+int socket_cache_add(int fd, struct listener *listener);
+
 
 #endif /* _PROTO_FD_H */
 
diff --git a/include/types/fd.h b/include/types/fd.h
index f698863..eb253a5 100644
--- a/include/types/fd.h
+++ b/include/types/fd.h
@@ -136,6 +136,34 @@ extern int maxfd;   /* # of the 
highest fd + 1 */
 extern int totalconn;   /* total # of terminated sessions */
 extern int actconn; /* # of active sessions */
 
+/* The socket cache allows bound sockets to be looked up
+ * by the code that would bind them
+ */
+struct socket_cache {
+   struct socket_cache *next;  /* next entry in the cache, or NULL */
+   int fd; /* the listen socket */
+   int state;  /* state: NEW, ASSIGNED */
+   char *interface;/* interface name or NULL */
+
+   /* Body */
+   int sock_type;  /* As passed to socket() */
+   int sock_prot;  /* As passed to socket() */
+   socklen_t sock_addrlen; /* socket address length, used by 
bind() */
+   struct sockaddr_storage addr;   /* the address we listen to */
+   int options;/* socket options : LI_O_* */
+   int maxconn;/* maximum connections allowed on this 
listener */
+   unsigned int backlog;   /* if set, listen backlog */
+   union { /* protocol-dependant access 
restrictions */
+   struct {/* UNIX socket permissions */
+   uid_t uid;  /* -1 to leave unchanged */
+   gid_t gid;  /* -1 to leave unchanged */
+   mode_t mode;/* 0 to leave unchanged */
+   int level;  /* access level (ACCESS_LVL_*) */
+   } ux;
+   } perm;
+   int maxseg; /* for TCP, advertised MSS */
+};
+
 #endif /* _TYPES_FD_H */
 
 /*
diff --git a/include/types/global.h b/include/types/global.h
index b6c60dd..535b833 100644
--- a/include/types/global.h
+++ b/include/types/global.h
@@ -121,6 +121,7 @@ extern const int zero;
 extern const int one;
 extern const struct linger nolinger;
 extern int stopping;   /* non zero means stopping in progress */
+extern int is_master;
 extern char hostname[MAX_HOSTNAME_LEN];
 extern char localpeer[MAX_HOSTNAME_LEN];
 
diff --git a/src/fd.c b/src/fd.c
index 80bddd6..c2f16bb 100644
--- a/src/fd.c
+++ b/src/fd.c
@@ -18,6 +18,8 @@
 #include common/compat.h
 #include common/config.h
 
+#include types/protocols.h
+
 #include proto/fd.h
 #include proto/port_range.h
 
@@ -31,6 +33,7 @@ struct poller pollers[MAX_POLLERS];
 struct poller cur_poller;
 int nbpollers = 0;
 
+static struct socket_cache *socket_cache = NULL;
 
 /* Deletes an FD from the fdsets, and recomputes the maxfd limit.
  * The file descriptor is also closed.
@@ -174,6 +177,136 @@ int fork_poller()
return 1;
 }
 
+enum {
+   SC_AVAILABLE,
+   SC_INVALID,
+   SC_INUSE
+};
+
+void socket_cache_make_all_available(void)
+{
+   struct socket_cache *e;
+
+   for (e = socket_cache; e; e = e-next)
+   e-state = SC_AVAILABLE;
+}
+
+void socket_cache_gc(void)
+{
+   struct socket_cache *e, *next, *prev = NULL;
+
+   for (e = socket_cache; e; e = next) {
+   next = e-next;
+   if (e-state == SC_INUSE) {
+   prev = e;
+   continue;
+   }
+   if (prev)
+   prev-next = e-next;
+   else
+   socket_cache = e-next;
+   if (e-state != SC_INVALID)
+   fd_delete(e-fd);
+   free(e);
+   }
+}
+
+static void socket_cache_body_assign(struct socket_cache *e,
+const struct listener *listener)

[PATCH 06/26] Add vAlert and vWarning

2011-03-09 Thread Simon Horman
---
 include/proto/log.h |   12 
 src/log.c   |   35 ---
 2 files changed, 40 insertions(+), 7 deletions(-)

diff --git a/include/proto/log.h b/include/proto/log.h
index f2ecf0b..45bec88 100644
--- a/include/proto/log.h
+++ b/include/proto/log.h
@@ -25,6 +25,7 @@
 
 #include stdio.h
 #include syslog.h
+#include stdarg.h
 
 #include common/config.h
 #include common/memory.h
@@ -38,12 +39,23 @@ extern struct pool_head *pool2_requri;
  * Displays the message on stderr with the date and pid. Overrides the quiet
  * mode during startup.
  */
+void vAlert(const char *fmt, va_list argp);
+
+/*
+ * Displays the message on stderr with the date and pid. Overrides the quiet
+ * mode during startup.
+ */
 void Alert(const char *fmt, ...)
__attribute__ ((format(printf, 1, 2)));
 
 /*
  * Displays the message on stderr with the date and pid.
  */
+void vWarning(const char *fmt, va_list argp);
+
+/*
+ * Displays the message on stderr with the date and pid.
+ */
 void Warning(const char *fmt, ...)
__attribute__ ((format(printf, 1, 2)));
 
diff --git a/src/log.c b/src/log.c
index 119536c..4cd3b55 100644
--- a/src/log.c
+++ b/src/log.c
@@ -91,6 +91,17 @@ static void __Alert(int send_log, const char *fmt, va_list 
argp)
  * with a priority of LOG_ERR.
  * Overrides the quiet mode during startup.
  */
+void vAlert(const char *fmt, va_list argp)
+{
+   __Alert(1, fmt, argp);
+}
+
+/*
+ * Displays the message on stderr with the date and pid.
+ * Also logs the same message using the prevailing logger, if any,
+ * with a priority of LOG_ERR.
+ * Overrides the quiet mode during startup.
+ */
 void Alert(const char *fmt, ...)
 {
va_list argp;
@@ -116,27 +127,37 @@ static void Alert_no_send_log(const char *fmt, ...)
 /*
  * Displays the message on stderr with the date and pid.
  */
-void Warning(const char *fmt, ...)
+void vWarning(const char *fmt, va_list argp)
 {
-   va_list argp;
+   va_list argp2;
struct tm tm;
 
if (!(global.mode  MODE_QUIET) || (global.mode  MODE_VERBOSE)) {
-   va_start(argp, fmt);
-   vsend_log(NULL, LOG_WARNING, fmt, argp);
-   va_end(argp);
+   va_copy(argp2, argp);
+   vsend_log(NULL, LOG_WARNING, fmt, argp2);
+   va_end(argp2);
 
-   va_start(argp, fmt);
get_localtime(date.tv_sec, tm);
fprintf(stderr, [WARNING] %03d/%02d%02d%02d (%d) : ,
tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec, 
(int)getpid());
vfprintf(stderr, fmt, argp);
fflush(stderr);
-   va_end(argp);
}
 }
 
 /*
+ * Displays the message on stderr with the date and pid.
+ */
+void Warning(const char *fmt, ...)
+{
+   va_list argp;
+
+   va_start(argp, fmt);
+   vWarning(fmt, argp);
+   va_end(argp);
+}
+
+/*
  * Displays the message on out only if quiet mode is not set.
  */
 void qfprintf(FILE *out, const char *fmt, ...)
-- 
1.7.2.3




[PATCH 07/26] Add is_master flag

2011-03-09 Thread Simon Horman
This is in preparation for adding Master/Worker mode
---
 src/haproxy.c |3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/src/haproxy.c b/src/haproxy.c
index 99d8c0d..1f02902 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -136,6 +136,9 @@ struct global global = {
 /*/
 
 int stopping;  /* non zero means stopping in progress */
+int is_master = 0; /* non zero means that master/worker mode
+* has been activated and the current process
+* is the master */
 int jobs = 0;   /* number of active jobs (conns, listeners, active tasks, ...) 
*/
 
 /* Here we store informations about the pids of the processes we may pause
-- 
1.7.2.3




[PATCH 09/26] Clean up logger on deinit()

2011-03-09 Thread Simon Horman
This will allow new logging configuration to take effect
once changes to reinitialise haproxy are introduced.
---
 include/proto/log.h |5 +
 src/haproxy.c   |1 +
 src/log.c   |   17 ++---
 3 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/include/proto/log.h b/include/proto/log.h
index 45bec88..ecd51b1 100644
--- a/include/proto/log.h
+++ b/include/proto/log.h
@@ -66,6 +66,11 @@ void qfprintf(FILE *out, const char *fmt, ...)
__attribute__ ((format(printf, 2, 3)));
 
 /*
+ * Close and clean up logs
+ */
+void close_log(void);
+
+/*
  * This function sends a syslog message to both log servers of a proxy,
  * or to global log servers if the proxy is NULL.
  * It also tries not to waste too much time computing the message header.
diff --git a/src/haproxy.c b/src/haproxy.c
index 1f02902..a0bc8f5 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -1055,6 +1055,7 @@ void run(int argc, char **argv)
socket_cache_make_all_available();
 
init(argc, argv);
+   close_log(); /* It will automatically be reopened as needed */
signal_register_fct(SIGQUIT, dump, SIGQUIT);
signal_register_fct(SIGUSR1, sig_soft_stop, SIGUSR1);
signal_register_fct(SIGHUP, sig_dump_state, SIGHUP);
diff --git a/src/log.c b/src/log.c
index 4cd3b55..5da1260 100644
--- a/src/log.c
+++ b/src/log.c
@@ -217,6 +217,20 @@ static inline int logsrv_addrlen(const struct logsrv 
*logsrv)
return -1;
 }
 
+static int logfdunix = -1; /* syslog to AF_UNIX socket */
+static int logfdinet = -1; /* syslog to AF_INET socket */
+static long tvsec = -1;/* to force the string to be initialized */
+
+void close_log(void)
+{
+   if (logfdunix = 0)
+   close(logfdunix);
+   logfdunix = -1;
+   if (logfdinet = 0)
+   close(logfdinet);
+   logfdinet = -1;
+}
+
 /*
  * This function sends a syslog message to both log servers of a proxy,
  * or to global log servers if the proxy is NULL.
@@ -226,9 +240,6 @@ static inline int logsrv_addrlen(const struct logsrv 
*logsrv)
 static void vsend_log(struct proxy *p, int level, const char *message,
  va_list argp)
 {
-   static int logfdunix = -1;  /* syslog to AF_UNIX socket */
-   static int logfdinet = -1;  /* syslog to AF_INET socket */
-   static long tvsec = -1; /* to force the string to be initialized */
static char logmsg[MAX_SYSLOG_LEN];
static char *dataptr = NULL;
int fac_level;
-- 
1.7.2.3




[PATCH 12/26] Free global.chroot earlier

2011-03-09 Thread Simon Horman
---
 src/haproxy.c |2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/src/haproxy.c b/src/haproxy.c
index dc2739b..8c9053f 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -1131,6 +1131,7 @@ void run(int argc, char **argv)
}
chdir(/);
}
+   free(global.chroot);  global.chroot = NULL;
 
setid_early(argv[0]);
 
@@ -1280,7 +1281,6 @@ void run(int argc, char **argv)
 
/* We won't ever use this anymore */
free(oldpids);oldpids = NULL;
-   free(global.chroot);  global.chroot = NULL;
free(global.pidfile); global.pidfile = NULL;
 
/* we might have to unbind some proxies from some processes */
-- 
1.7.2.3




[PATCH 14/26] Allow pollers to be initialised multiple times

2011-03-09 Thread Simon Horman
---
 src/ev_epoll.c  |2 +-
 src/ev_kqueue.c |1 -
 src/ev_poll.c   |1 -
 src/ev_select.c |1 -
 src/ev_sepoll.c |4 +++-
 5 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/src/ev_epoll.c b/src/ev_epoll.c
index b976868..f55a382 100644
--- a/src/ev_epoll.c
+++ b/src/ev_epoll.c
@@ -336,13 +336,13 @@ REGPRM1 static void _do_term(struct poller *p)
epoll_fd = -1;
}
 
+   nbchanges = 0;
chg_ptr = NULL;
chg_list = NULL;
fd_evts = NULL;
epoll_events = NULL;
 
p-private = NULL;
-   p-pref = 0;
 }
 
 /*
diff --git a/src/ev_kqueue.c b/src/ev_kqueue.c
index e167984..61a5422 100644
--- a/src/ev_kqueue.c
+++ b/src/ev_kqueue.c
@@ -210,7 +210,6 @@ REGPRM1 static void _do_term(struct poller *p)
}
 
p-private = NULL;
-   p-pref = 0;
 }
 
 /*
diff --git a/src/ev_poll.c b/src/ev_poll.c
index f5d011e..48e6247 100644
--- a/src/ev_poll.c
+++ b/src/ev_poll.c
@@ -212,7 +212,6 @@ REGPRM1 static void _do_term(struct poller *p)
free(fd_evts[DIR_RD]);
free(poll_events);
p-private = NULL;
-   p-pref = 0;
 }
 
 /*
diff --git a/src/ev_select.c b/src/ev_select.c
index 5a87282..6a0562d 100644
--- a/src/ev_select.c
+++ b/src/ev_select.c
@@ -208,7 +208,6 @@ REGPRM1 static void _do_term(struct poller *p)
free(tmp_evts[DIR_WR]);
free(tmp_evts[DIR_RD]);
p-private = NULL;
-   p-pref = 0;
 }
 
 /*
diff --git a/src/ev_sepoll.c b/src/ev_sepoll.c
index 248f1f4..9bddaad 100644
--- a/src/ev_sepoll.c
+++ b/src/ev_sepoll.c
@@ -587,11 +587,13 @@ REGPRM1 static void _do_term(struct poller *p)
epoll_fd = -1;
}
 
+   nbspec = 0;
+   absmaxevents = 0;
+   fd_created = 0;
spec_list = NULL;
epoll_events = NULL;
 
p-private = NULL;
-   p-pref = 0;
 }
 
 /*
-- 
1.7.2.3




[PATCH 08/26] Conditionally ignore errors when resolving users and groups

2011-03-09 Thread Simon Horman
If is_master is set then the configuration is being
read as part a reinitialisation. And as such it seems
reasonable to ignore errors when resolving users and groups -
if they are unchanged from the original configuration then
they will already be set, and otherwise they may not be settable
as the process may already be non-privileged.

Resolution failure may commonly occur if chroot is in effect
and for example /etc/passwd and /etc/group are not present in
the chroot.
---
 src/cfgparse.c |   39 +++
 1 files changed, 23 insertions(+), 16 deletions(-)

diff --git a/src/cfgparse.c b/src/cfgparse.c
index aae2efe..e7ce4c3 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -466,6 +466,17 @@ static int warnif_cond_requires_req(const struct acl_cond 
*cond, const char *fil
return ERR_WARN;
 }
 
+static int Warning_if_master(const char *fmt, ...)
+{
+   va_list argp;
+   void (*f)(const char *, va_list) = is_master ? vWarning : vAlert;
+
+   va_start(argp, fmt);
+   f(fmt, argp);
+   va_end(argp);
+
+   return is_master ? 0 : (ERR_ALERT | ERR_FATAL);
+}
 
 /*
  * parse a line in a global section. Returns the error code, 0 if OK, or
@@ -655,8 +666,7 @@ int cfg_parse_global(const char *file, int linenum, char 
**args, int kwm)
global.uid = (int)ha_user-pw_uid;
}
else {
-   Alert(parsing [%s:%d] : cannot find user id for '%s' 
(%d:%s)\n, file, linenum, args[1], errno, strerror(errno));
-   err_code |= ERR_ALERT | ERR_FATAL;
+   err_code |= Warning_if_master(parsing [%s:%d] : cannot 
find user id for '%s' (%d:%s)\n, file, linenum, args[1], errno, 
strerror(errno));
}
}
else if (!strcmp(args[0], group)) {
@@ -672,8 +682,7 @@ int cfg_parse_global(const char *file, int linenum, char 
**args, int kwm)
global.gid = (int)ha_group-gr_gid;
}
else {
-   Alert(parsing [%s:%d] : cannot find group id for '%s' 
(%d:%s)\n, file, linenum, args[1], errno, strerror(errno));
-   err_code |= ERR_ALERT | ERR_FATAL;
+   err_code |= Warning_if_master(parsing [%s:%d] : cannot 
find group id for '%s' (%d:%s)\n, file, linenum, args[1], errno, 
strerror(errno));
}
}
/* end of user/group name handling*/
@@ -1871,13 +1880,12 @@ int cfg_parse_listen(const char *file, int linenum, 
char **args, int kwm)
}
user = getpwnam(args[cur_arg + 1]);
if (!user) {
-   Alert(parsing [%s:%d] : '%s' : '%s' 
unknown user.\n,
+   err_code |= Warning_if_master(parsing 
[%s:%d] : '%s' : '%s' unknown user.\n,
file, linenum, args[0], 
args[cur_arg + 1 ]);
-   err_code |= ERR_ALERT | ERR_FATAL;
-   goto out;
-   }
-
-   curproxy-listen-perm.ux.uid = user-pw_uid;
+   if (!is_master)
+   goto out;
+   } else
+   curproxy-listen-perm.ux.uid = 
user-pw_uid;
cur_arg += 2;
continue;
 }
@@ -1893,13 +1901,12 @@ int cfg_parse_listen(const char *file, int linenum, 
char **args, int kwm)
}
group = getgrnam(args[cur_arg + 1]);
if (!group) {
-   Alert(parsing [%s:%d] : '%s' : '%s' 
unknown group.\n,
+   err_code |= Warning_if_master(parsing 
[%s:%d] : '%s' : '%s' unknown group.\n,
file, linenum, args[0], 
args[cur_arg + 1 ]);
-   err_code |= ERR_ALERT | ERR_FATAL;
-   goto out;
-   }
-
-   curproxy-listen-perm.ux.gid = group-gr_gid;
+   if (!is_master)
+   goto out;
+   } else
+   curproxy-listen-perm.ux.gid = 
group-gr_gid;
cur_arg += 2;
continue;
 }
-- 
1.7.2.3




[PATCH 11/26] Move chroot much earlier

2011-03-09 Thread Simon Horman
This allows chroot to occur before setsid_early().

In the case where a master process is re-initialised
* It will not chroot itself, though if it is already chrooted that will
  remain in effect.
* If it is already chrooted then the path to the configuration file
  read during reinitialisation will be relative to the chroot.
---
 src/haproxy.c |  116 
 1 files changed, 58 insertions(+), 58 deletions(-)

diff --git a/src/haproxy.c b/src/haproxy.c
index 4ee7161..dc2739b 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -1074,6 +1074,64 @@ void run(int argc, char **argv)
 */
signal_register_fct(SIGPIPE, NULL, 0);
 
+   /* open log  pid files before the chroot */
+   if (global.mode  MODE_DAEMON  global.pidfile != NULL) {
+   int pidfd;
+   unlink(global.pidfile);
+   pidfd = open(global.pidfile, O_CREAT | O_WRONLY | O_TRUNC, 
0644);
+   if (pidfd  0) {
+   Alert([%s.run()] Cannot create pidfile %s\n, argv[0], 
global.pidfile);
+   if (nb_oldpids)
+   tell_old_pids(SIGTTIN);
+   protocol_unbind_all();
+   exit(1);
+   }
+   pidfile = fdopen(pidfd, w);
+   }
+
+#ifdef CONFIG_HAP_CTTPROXY
+   if (global.last_checks  LSTCHK_CTTPROXY) {
+   int ret;
+
+   ret = check_cttproxy_version();
+   if (ret  0) {
+   Alert([%s.run()] Cannot enable cttproxy.\n%s,
+ argv[0],
+ (ret == -1) ?   Incorrect module version.\n
+ :   Make sure you have enough permissions and 
that the module is loaded.\n);
+   protocol_unbind_all();
+   exit(1);
+   }
+   }
+#endif
+
+   if ((global.last_checks  LSTCHK_NETADM)  global.uid) {
+   Alert([%s.run()] Some configuration options require full 
privileges, so global.uid cannot be changed.\n
+ , argv[0]);
+   protocol_unbind_all();
+   exit(1);
+   }
+
+   /* If the user is not root, we'll still let him try the configuration
+* but we inform him that unexpected behaviour may occur.
+*/
+   if ((global.last_checks  LSTCHK_NETADM)  getuid())
+   Warning([%s.run()] Some options which require full privileges
+might not work well.\n
+   , argv[0]);
+
+   /* chroot if needed */
+   if (!is_master  global.chroot != NULL) {
+   if (chroot(global.chroot) == -1) {
+   Alert([%s.run()] Cannot chroot(%s).\n, argv[0], 
global.chroot);
+   if (nb_oldpids)
+   tell_old_pids(SIGTTIN);
+   protocol_unbind_all();
+   exit(1);
+   }
+   chdir(/);
+   }
+
setid_early(argv[0]);
 
/* ulimits */
@@ -1176,64 +1234,6 @@ void run(int argc, char **argv)
fclose(stdin); fclose(stdout); fclose(stderr);
}
 
-   /* open log  pid files before the chroot */
-   if (global.mode  MODE_DAEMON  global.pidfile != NULL) {
-   int pidfd;
-   unlink(global.pidfile);
-   pidfd = open(global.pidfile, O_CREAT | O_WRONLY | O_TRUNC, 
0644);
-   if (pidfd  0) {
-   Alert([%s.run()] Cannot create pidfile %s\n, argv[0], 
global.pidfile);
-   if (nb_oldpids)
-   tell_old_pids(SIGTTIN);
-   protocol_unbind_all();
-   exit(1);
-   }
-   pidfile = fdopen(pidfd, w);
-   }
-
-#ifdef CONFIG_HAP_CTTPROXY
-   if (global.last_checks  LSTCHK_CTTPROXY) {
-   int ret;
-
-   ret = check_cttproxy_version();
-   if (ret  0) {
-   Alert([%s.run()] Cannot enable cttproxy.\n%s,
- argv[0],
- (ret == -1) ?   Incorrect module version.\n
- :   Make sure you have enough permissions and 
that the module is loaded.\n);
-   protocol_unbind_all();
-   exit(1);
-   }
-   }
-#endif
-
-   if ((global.last_checks  LSTCHK_NETADM)  global.uid) {
-   Alert([%s.run()] Some configuration options require full 
privileges, so global.uid cannot be changed.\n
- , argv[0]);
-   protocol_unbind_all();
-   exit(1);
-   }
-
-   /* If the user is not root, we'll still let him try the configuration
-* but we inform him that unexpected behaviour may occur.
-*/
-   if ((global.last_checks  LSTCHK_NETADM)  getuid())
- 

[PATCH 16/26] Restart master process on SIGUSR2

2011-03-09 Thread Simon Horman
If worker/master mode is active and the master process receives
a SIGUSR2 then it will restart haproxy by re-reading the configuration file,
killing existing workers and starting new workers.

SIGUSR2 is ignored if worker/master mode is not active.
SIGUSR2 is ignored by worker processes.
---
 include/types/global.h |1 +
 src/haproxy.c  |   30 --
 2 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/include/types/global.h b/include/types/global.h
index fecbeae..0739ca3 100644
--- a/include/types/global.h
+++ b/include/types/global.h
@@ -122,6 +122,7 @@ extern const int zero;
 extern const int one;
 extern const struct linger nolinger;
 extern int stopping;   /* non zero means stopping in progress */
+extern int restarting; /* non zero means restart in progress */
 extern int is_master;
 extern char hostname[MAX_HOSTNAME_LEN];
 extern char localpeer[MAX_HOSTNAME_LEN];
diff --git a/src/haproxy.c b/src/haproxy.c
index 3c20f92..076a7f4 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -137,6 +137,7 @@ struct global global_default = {
 /*/
 
 int stopping;  /* non zero means stopping in progress */
+int restarting;/* non zero means restart in progress */
 int is_master = 0; /* non zero means that master/worker mode
 * has been activated and the current process
 * is the master */
@@ -276,6 +277,20 @@ void sig_soft_stop(struct sig_handler *sh)
 }
 
 /*
+ * upon SIGUSR2, restart master. Ignored by workers
+ */
+void sig_restart(struct sig_handler *sh)
+{
+   if (!is_master)
+   return;
+
+   restarting = 1;
+   soft_stop();
+   signal_unregister_handler(sh);
+   pool_gc2();
+}
+
+/*
  * upon SIGTTOU, we pause everything
  */
 void sig_pause(struct sig_handler *sh)
@@ -391,7 +406,7 @@ void init(int argc, char **argv)
 * Initialize the previously static variables.
 */
 
-   totalconn = actconn = maxfd = listeners = stopping = 0;
+   totalconn = actconn = maxfd = listeners = stopping = restarting = 0;
 
 
 #ifdef HAPROXY_MEMMAX
@@ -1406,7 +1421,18 @@ void run(int argc, char **argv)
 
 int main(int argc, char **argv)
 {
-   run(argc, argv);
+   while (1) {
+   run(argc, argv);
+   if (!restarting)
+   break;
+   }
+
+   if (is_master)
+   /* The master is gracefully shutting down,
+* ask the clients to gracefully shutdown too.
+*/
+   tell_old_pids(SIGUSR1);
+
exit(0);
 }
 
-- 
1.7.2.3




[PATCH 22/26] Write pid file if master/worker mode is active

2011-03-09 Thread Simon Horman
If master/worker mode is active then there will be multiple processes
present and as such it is useful to know the relevant pids.

This also splits run() up into two parts which will aid later
refactoring.
---
 src/haproxy.c |   43 +++
 1 files changed, 27 insertions(+), 16 deletions(-)

diff --git a/src/haproxy.c b/src/haproxy.c
index 998c4ff..caa2a29 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -404,6 +404,8 @@ void init(int argc, char **argv)
error_snapshot_id = 0;
jobs = 0;
 
+   socket_cache_make_all_available();
+
/* 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.
@@ -1100,12 +1102,12 @@ static void setid_late(const char *name)
 #endif
 }
 
-void run(int argc, char **argv)
+static FILE *prepare(int argc, char **argv)
 {
int err, retry;
int mode = global.mode  (MODE_DAEMON|MODE_MASTER_WORKER);
struct rlimit limit;
-   static FILE *pidfile = NULL;
+   FILE *pidfile = NULL;
char errmsg[100];
 
socket_cache_make_all_available();
@@ -1120,7 +1122,7 @@ void run(int argc, char **argv)
/* Signalling a reset can be handled here */
if (oldpids_sig == SIGUSR2) {
tell_old_pids(SIGUSR2);
-   return;
+   exit(0);
}
 
signal_register_fct(SIGQUIT, dump, SIGQUIT);
@@ -1138,7 +1140,8 @@ void run(int argc, char **argv)
signal_register_fct(SIGPIPE, NULL, 0);
 
/* open log  pid files before the chroot */
-   if (!pidfile  global.mode  MODE_DAEMON  global.pidfile != NULL) {
+   if (!is_master  global.mode  (MODE_DAEMON|MODE_MASTER_WORKER) 
+   global.pidfile != NULL) {
int pidfd;
unlink(global.pidfile);
pidfd = open(global.pidfile, O_CREAT | O_WRONLY | O_TRUNC, 
0644);
@@ -1319,6 +1322,12 @@ void run(int argc, char **argv)
 
socket_cache_gc();
 
+   return pidfile;
+}
+
+static void run(int argc, char **argv, FILE *pidfile)
+{
+
if (global.mode  (MODE_DAEMON|MODE_MASTER_WORKER)) {
struct proxy *px;
int ret = 0;
@@ -1348,13 +1357,11 @@ void run(int argc, char **argv)
send_log(NULL, LOG_INFO, Master started\n);
}
 
-   if (pidfile != NULL) {
-   rewind(pidfile);
-   ftruncate(fileno(pidfile), 0);
-   if (global.mode  MODE_MASTER_WORKER) {
-   fprintf(pidfile, %d\n, pid);
-   fflush(pidfile);
-   }
+   rewind(pidfile);
+   ftruncate(fileno(pidfile), 0);
+   if (global.mode  MODE_MASTER_WORKER) {
+   fprintf(pidfile, %d\n, pid);
+   fflush(pidfile);
}
 
/* Store PIDs of worker processes in oldpid so
@@ -1388,10 +1395,8 @@ void run(int argc, char **argv)
 Worker #%d started\n, proc);
break;
}
-   if (pidfile != NULL) {
-   fprintf(pidfile, %d\n, ret);
-   fflush(pidfile);
-   }
+   fprintf(pidfile, %d\n, ret);
+   fflush(pidfile);
oldpids[proc] = ret;
relative_pid++; /* each child will get a different one 
*/
}
@@ -1452,8 +1457,14 @@ void run(int argc, char **argv)
 
 int main(int argc, char **argv)
 {
+   FILE *pidfile = NULL;
+
while (1) {
-   run(argc, argv);
+   FILE *newpidfile = prepare(argc, argv);
+   if (!is_master)
+   pidfile = newpidfile;
+
+   run(argc, argv, pidfile);
if (!restarting)
break;
}
-- 
1.7.2.3




[PATCH 21/26] Exit workers if the master disappears

2011-03-09 Thread Simon Horman
This should allow the master to be restarted, for instance using ruinit,
if it exits for some reason.
---
 src/haproxy.c |8 
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/src/haproxy.c b/src/haproxy.c
index 175d3e8..998c4ff 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -983,6 +983,8 @@ static int tell_old_pids(int sig)
 void run_poll_loop()
 {
int next;
+   pid_t ppid = ((global.mode  MODE_MASTER_WORKER)  !is_master) ?
+   getppid() : -1;
 
tv_update_date(0,1);
while (1) {
@@ -1004,6 +1006,12 @@ void run_poll_loop()
if (jobs == 0)
break;
 
+   if (ppid  0  kill(ppid, 0)) {
+   send_log(NULL, LOG_INFO,
+Parent disappeared, exiting.\n);
+   break;
+   }
+
if (is_master) {
sleep(1);
continue;
-- 
1.7.2.3




[PATCH 17/26] Exit master proxies as soon as possible

2011-03-09 Thread Simon Horman
---
 src/proxy.c |7 ---
 1 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/proxy.c b/src/proxy.c
index c4f452c..ff5f635 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -575,9 +575,10 @@ void soft_stop(void)
tv_update_date(0,1); /* else, the old time before select will be used */
while (p) {
if (p-state != PR_STSTOPPED) {
-   Warning(Stopping %s %s in %d ms.\n, 
proxy_cap_str(p-cap), p-id, p-grace);
-   send_log(p, LOG_WARNING, Stopping %s %s in %d ms.\n, 
proxy_cap_str(p-cap), p-id, p-grace);
-   p-stop_time = tick_add(now_ms, p-grace);
+   int grace = is_master ? 0 : p-grace;
+   Warning(Stopping %s %s in %d ms.\n, 
proxy_cap_str(p-cap), p-id, grace);
+   send_log(p, LOG_WARNING, Stopping %s %s in %d ms.\n, 
proxy_cap_str(p-cap), p-id, grace);
+   p-stop_time = tick_add(now_ms, grace);
}
if (p-table.size  p-table.sync_task)
 task_wakeup(p-table.sync_task, TASK_WOKEN_MSG);
-- 
1.7.2.3




[PATCH 20/26] Document master/worker mode

2011-03-09 Thread Simon Horman
Note: I have not updated the French documentation
---
 doc/configuration.txt |   48 ++--
 doc/haproxy-en.txt|   47 ++-
 doc/haproxy.1 |   20 ++--
 3 files changed, 94 insertions(+), 21 deletions(-)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index 7a5ace2..f9c9bd8 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -432,6 +432,7 @@ The following keywords are supported in the global 
section :
  * Process management and security
- chroot
- daemon
+   - master_worker
- gid
- group
- log
@@ -486,10 +487,25 @@ daemon
   operation. It is equivalent to the command line -D argument. It can be
   disabled by the command line -db argument.
 
+master_worker
+  Split haproxy into two types of processes. A master process which
+  binds to sockets and worker sockets which run proxies. When running
+  in this mode haproxy will restart rather than exiting when
+  haproxy -st is used. On restart configuration files will be reread.
+
+  On linux the master process will run with CAP_NET_BIND_SERVCE and
+  CAP_SYS_RESOURCE so that even if is running as non-root it will be able
+  to bind to privilaged ports and update ulimits.
+
 gid number
   Changes the process' group ID to number. It is recommended that the group
   ID is dedicated to HAProxy or to a small set of similar daemons. HAProxy must
   be started with a user belonging to this group, or with superuser privileges.
+
+  If running in master_worker mode changes to the gid are ignored if
+  haproxy is already runing as non-root, either because it was started as
+  non-root or it switched to another user when originally started.
+
   See also group and uid.
 
 group group name
@@ -542,17 +558,21 @@ log-tag string
   running on the same host.
 
 nbproc number
-  Creates number processes when going daemon. This requires the daemon
-  mode. By default, only one process is created, which is the recommended mode
-  of operation. For systems limited to small sets of file descriptors per
-  process, it may be needed to fork multiple daemons. USING MULTIPLE PROCESSES
-  IS HARDER TO DEBUG AND IS REALLY DISCOURAGED. See also daemon.
+  Creates number processes when going daemon. This requires daemon or
+  master_worker mode. By default, only one process is created, which is
+  the recommended mode of operation. For systems limited to small sets of
+  file descriptors per process, it may be needed to fork multiple daemons.
+  USING MULTIPLE PROCESSES IS HARDER TO DEBUG AND IS REALLY DISCOURAGED.
+  See also daemon.
 
 pidfile pidfile
   Writes pids of all daemons into file pidfile. This option is equivalent to
   the -p command line argument. The file must be accessible to the user
   starting the process. See also daemon.
 
+  If both daemon and master_worker mode is set then the pid
+  of the master process will be the first entry in pid file.
+
 stats socket path [{uid | user} uid] [{gid | group} gid] [mode mode]
   [level level]
 
@@ -595,7 +615,13 @@ uid number
   Changes the process' user ID to number. It is recommended that the user ID
   is dedicated to HAProxy or to a small set of similar daemons. HAProxy must
   be started with superuser privileges in order to be able to switch to another
-  one. See also gid and user.
+  one.
+
+  If running in master_worker mode changes to the uid are ignored if
+  haproxy is already runing as non-root, either because it was started as
+  non-root or it switched to another user when originally started.
+
+  See also gid and user.
 
 ulimit-n number
   Sets the maximum number of per-process file-descriptors to number. By
@@ -616,10 +642,17 @@ unix-bind [ prefix prefix ] [ mode mode ] [ user 
user ] [ uid uid ]
   both are specified, the bind statement has priority, meaning that the
   unix-bind settings may be seen as process-wide default settings.
 
+  If running in master_worker mode and in a chroot any failure to resolve
+  the user or group on restart will be ignored and the previous settings
+  will prevail.
+
 user user name
   Similar to uid but uses the UID of user name user name from /etc/passwd.
   See also uid and group.
 
+  If running in master_worker mode and in a chroot any failure to resolve
+  the user will be ignored and the previous settings will prevail.
+
 node name
   Only letters, digits, hyphen and underscore are allowed, like in DNS names.
 
@@ -791,6 +824,9 @@ group groupname [users user,user,(...)]
   attach users to this group by using a comma separated list of names
   proceeded by users keyword.
 
+  If running in master_worker mode and in a chroot any failure to resolve
+  the group will be ignored and the previous settings will prevail.
+
 user username [password|insecure-password password]
 [groups group,group,(...)]
   Adds user username to the current userlist. Both secure (encrypted) and
diff --git a/doc/haproxy-en.txt 

[PATCH 15/26] Add master/worker model

2011-03-09 Thread Simon Horman
A master process is detached from the terminal and then spawns worker
processes. The master does nothing but waits for a signal on which it will
kill its workers.

A later patch will allow for restarting the workers.  To facilitate this
the master keeps all bound sockets open and by using the socket_cache these
can be re-used. This will enable haproxy to be restarted without refusing or
resetting connections to existing sockets.
---
 examples/examples.cfg  |1 +
 examples/haproxy.vim   |2 +-
 include/types/global.h |1 +
 src/cfgparse.c |3 +
 src/haproxy.c  |  112 ---
 src/log.c  |1 +
 src/protocols.c|5 ++-
 7 files changed, 106 insertions(+), 19 deletions(-)

diff --git a/examples/examples.cfg b/examples/examples.cfg
index 3499e7b..739f8a8 100644
--- a/examples/examples.cfg
+++ b/examples/examples.cfg
@@ -8,6 +8,7 @@ global
 #  chroot  /tmp
 #  nbproc  2
 #  daemon
+#  master_worker
 #  debug
 #  quiet
 
diff --git a/examples/haproxy.vim b/examples/haproxy.vim
index 728a1c5..44efdaf 100644
--- a/examples/haproxy.vim
+++ b/examples/haproxy.vim
@@ -41,7 +41,7 @@ syn match   hapIp2   
/,\(\d\{1,3}\.\d\{1,3}\.\d\{1,3}\.\d\{1,3}\)\?:\d\{1,5}
 
  Parameters
 syn keyword hapParam chroot cliexp clitimeout contimeout
-syn keyword hapParam daemon debug disabled
+syn keyword hapParam daemon debug disabled master_worker
 syn keyword hapParam enabled
 syn keyword hapParam fullconn
 syn keyword hapParam gid grace group
diff --git a/include/types/global.h b/include/types/global.h
index 535b833..fecbeae 100644
--- a/include/types/global.h
+++ b/include/types/global.h
@@ -38,6 +38,7 @@
 #defineMODE_VERBOSE0x10
 #defineMODE_STARTING   0x20
 #defineMODE_FOREGROUND 0x40
+#defineMODE_MASTER_WORKER  0x80
 
 /* list of last checks to perform, depending on config options */
 #define LSTCHK_CAP_BIND0x0001  /* check that we can bind to 
any port */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index e7ce4c3..dec3ef6 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -499,6 +499,9 @@ int cfg_parse_global(const char *file, int linenum, char 
**args, int kwm)
else if (!strcmp(args[0], daemon)) {
global.mode |= MODE_DAEMON;
}
+   else if (!strcmp(args[0], master_worker)) {
+   global.mode |= MODE_MASTER_WORKER;
+   }
else if (!strcmp(args[0], debug)) {
global.mode |= MODE_DEBUG;
}
diff --git a/src/haproxy.c b/src/haproxy.c
index 8a50f8c..3c20f92 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -232,6 +232,7 @@ void usage(char *name)
-c check mode : only check config files and exit\n
-n sets the maximum total # of connections (%d)\n
-m limits the usable amount of memory (in MB)\n
+   -M master/worker mode\n
-N sets the default, per-proxy maximum # of 
connections (%d)\n
-L set local peer name (default to hostname)\n
-p writes pids of all children to this file\n
@@ -476,6 +477,8 @@ void init(int argc, char **argv)
arg_mode |= MODE_CHECK;
else if (*flag == 'D')
arg_mode |= MODE_DAEMON;
+   else if (*flag == 'M')
+   arg_mode |= MODE_MASTER_WORKER;
else if (*flag == 'q')
arg_mode |= MODE_QUIET;
else if (*flag == 's'  (flag[1] == 'f' || flag[1] == 
't')) {
@@ -529,7 +532,8 @@ void init(int argc, char **argv)
 
global.mode = MODE_STARTING | /* during startup, we want most of the 
alerts */
(arg_mode  (MODE_DAEMON | MODE_FOREGROUND | MODE_VERBOSE
-| MODE_QUIET | MODE_CHECK | MODE_DEBUG));
+| MODE_QUIET | MODE_CHECK | MODE_DEBUG
+| MODE_MASTER_WORKER));
 
if (LIST_ISEMPTY(cfg_cfgfiles))
usage(old_argv);
@@ -632,16 +636,18 @@ void init(int argc, char **argv)
global.mode = ~(MODE_DAEMON | MODE_QUIET);
}
global.mode |= (arg_mode  (MODE_DAEMON | MODE_FOREGROUND | MODE_QUIET |
-   MODE_VERBOSE | MODE_DEBUG ));
+   MODE_VERBOSE | MODE_DEBUG |
+   MODE_MASTER_WORKER));
 
if ((global.mode  MODE_DEBUG)  (global.mode  (MODE_DAEMON | 
MODE_QUIET))) {
Warning(debug mode incompatible with quiet and daemon. 
Keeping debug only.\n);
global.mode = ~(MODE_DAEMON | MODE_QUIET);
}
 
-   if ((global.nbproc  1)  !(global.mode  MODE_DAEMON)) {
+   if ((global.nbproc  1) 
+   

[PATCH 19/26] Add restart (-sr)

2011-03-09 Thread Simon Horman
This works similarly to -sf and -st except that rather
than terminating processes and then starting a new haproxy
instance is requests a restart and then exits.
---
 src/haproxy.c |   17 ++---
 1 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/src/haproxy.c b/src/haproxy.c
index 96c39ab..175d3e8 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -507,12 +507,16 @@ void init(int argc, char **argv)
arg_mode |= MODE_MASTER_WORKER;
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') */
+   else if (*flag == 's'  (flag[1] == 'f' || flag[1] == 
'r' || flag[1] == 't')) {
+   /* list of pids to finish ('f'),
+* reset ('r') or terminate ('t')
+*/
 
if (flag[1] == 'f')
oldpids_sig = SIGUSR1; /* finish then 
exit */
-   else
+   else if (flag[1] == 'r')
+   oldpids_sig = SIGUSR2; /* restart */
+   else if (flag[1] == 't')
oldpids_sig = SIGTERM; /* terminate 
immediately */
argv++; argc--;
 
@@ -1104,6 +1108,13 @@ void run(int argc, char **argv)
global.mode = (global.mode 
~(MODE_DAEMON|MODE_MASTER_WORKER)) | mode;
close_log(); /* It will automatically be reopened as needed */
+
+   /* Signalling a reset can be handled here */
+   if (oldpids_sig == SIGUSR2) {
+   tell_old_pids(SIGUSR2);
+   return;
+   }
+
signal_register_fct(SIGQUIT, dump, SIGQUIT);
signal_register_fct(SIGUSR1, sig_soft_stop, SIGUSR1);
if (global.mode  MODE_MASTER_WORKER)
-- 
1.7.2.3




[PATCH 24/26] Write pidfile after spawning all workers

2011-03-09 Thread Simon Horman
This is to allow the pidfile to be more easily rewritten
if the pid a worker changes
---
 src/haproxy.c |   19 ++-
 1 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/src/haproxy.c b/src/haproxy.c
index 35ee06e..c33c072 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -1358,13 +1358,6 @@ static void create_processes(int argc, char **argv, FILE 
*pidfile)
send_log(NULL, LOG_INFO, Master started\n);
}
 
-   rewind(pidfile);
-   ftruncate(fileno(pidfile), 0);
-   if (global.mode  MODE_MASTER_WORKER) {
-   fprintf(pidfile, %d\n, pid);
-   fflush(pidfile);
-   }
-
/* Store PIDs of worker processes in oldpid so
 * they can be signaled later */
nb_oldpids = global.nbproc;
@@ -1396,8 +1389,6 @@ static void create_processes(int argc, char **argv, FILE 
*pidfile)
 Worker #%d started\n, proc);
break;
}
-   fprintf(pidfile, %d\n, ret);
-   fflush(pidfile);
oldpids[proc] = ret;
relative_pid++; /* each child will get a different one */
}
@@ -1432,6 +1423,16 @@ static void create_processes(int argc, char **argv, FILE 
*pidfile)
global.mode |= MODE_QUIET; /* ensure that we won't say anything 
from now */
}
 
+   if (pidfile  (is_master || !(global.mode  MODE_MASTER_WORKER))) {
+   rewind(pidfile);
+   ftruncate(fileno(pidfile), 0);
+   if (is_master)
+   fprintf(pidfile, %d\n, pid);
+   for (proc = 0; proc  nb_oldpids; proc++)
+   fprintf(pidfile, %d\n, oldpids[proc]);
+   fflush(pidfile);
+   }
+
fork_poller();
 }
 
-- 
1.7.2.3




[PATCH 23/26] Break out process creation

2011-03-09 Thread Simon Horman
Break out the portion of run() that forks processes
into create_processes() and move the remainder of run()
back into main().

This increases readability by
1) reducing the indentation of almost all of the code in create_processes()
2) Removing the arbitrary split between run() and main().
   It was originally envisaged that run() would be the inner-loop of
   main(), but the split never worked that way.
---
 src/haproxy.c |  214 -
 1 files changed, 105 insertions(+), 109 deletions(-)

diff --git a/src/haproxy.c b/src/haproxy.c
index caa2a29..35ee06e 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -1325,134 +1325,114 @@ static FILE *prepare(int argc, char **argv)
return pidfile;
 }
 
-static void run(int argc, char **argv, FILE *pidfile)
+static void create_processes(int argc, char **argv, FILE *pidfile)
 {
+   struct proxy *px;
+   int ret = 0;
+   int proc;
 
-   if (global.mode  (MODE_DAEMON|MODE_MASTER_WORKER)) {
-   struct proxy *px;
-   int ret = 0;
-   int proc;
-
-   /* Create master if it doesn't already exist,
-* is needed and is to be detached
-*/
-   if (!is_master  global.mode  MODE_MASTER_WORKER) {
-   if (global.mode  MODE_DAEMON) {
-   ret = fork();
-   if (ret  0) {
-   Alert([%s.run()] Cannot fork.\n,
- argv[0]);
-   protocol_unbind_all();
-   exit(1); /* there has been an error */
-   }
-   if (ret  0)
-   exit(0); /* Exit original process */
-
-   /* Child */
-   setsid();
-   close_log();
-   pid = getpid();
-   }
-   if (!(global.mode  MODE_QUIET))
-   send_log(NULL, LOG_INFO, Master started\n);
-   }
-
-   rewind(pidfile);
-   ftruncate(fileno(pidfile), 0);
-   if (global.mode  MODE_MASTER_WORKER) {
-   fprintf(pidfile, %d\n, pid);
-   fflush(pidfile);
-   }
-
-   /* Store PIDs of worker processes in oldpid so
-* they can be signaled later */
-   nb_oldpids = global.nbproc;
-   free(oldpids);
-   oldpids = malloc(nb_oldpids * sizeof(*oldpids));
-   if (!oldpids) {
-   send_log(NULL, LOG_ERR, Cannot allocate memory 
-for oldpids.\n);
-   protocol_unbind_all();
-   exit(1); /* there has been an error */
-   }
+   if (!(global.mode  (MODE_DAEMON|MODE_MASTER_WORKER)))
+   return;
 
-   /* the father launches the required number of processes */
-   for (proc = 0; proc  global.nbproc; proc++) {
+   /* Create master if it doesn't already exist,
+* is needed and is to be detached
+*/
+   if (!is_master  global.mode  MODE_MASTER_WORKER) {
+   if (global.mode  MODE_DAEMON) {
ret = fork();
if (ret  0) {
-   send_log(NULL, LOG_ERR, Cannot fork.\n);
+   Alert([%s.run()] Cannot fork.\n,
+ argv[0]);
protocol_unbind_all();
exit(1); /* there has been an error */
}
-   else if (ret == 0) { /* child breaks here */
-   if (!(global.mode  MODE_MASTER_WORKER))
-   setsid();
-   is_master = 0;
-   close_log();
-   pid = getpid();
-   if (!(global.mode  MODE_QUIET))
-   send_log(NULL, LOG_INFO,
-Worker #%d started\n, proc);
-   break;
-   }
-   fprintf(pidfile, %d\n, ret);
-   fflush(pidfile);
-   oldpids[proc] = ret;
-   relative_pid++; /* each child will get a different one 
*/
+   if (ret  0)
+   exit(0); /* Exit original process */
+
+   /* Child */
+   setsid();
+   close_log();
+   pid = getpid();
}
-   /* close 

Re: [PATCH 00/26] Restart of haproxy without dropping connections

2011-03-09 Thread Simon Horman
On Thu, Mar 10, 2011 at 01:32:42PM +0900, Simon Horman wrote:
 Hi,
 
 this patch series attempts to allow haproxy to be restarted - perhaps
 reconfigured would be a better term - without refusing or dropping
 connections.
 
 The model that I am using is that of a master process,
 which really does nothing but hold bound file descriptors,
 and workers - these should be very similar to what haproxy
 processes have looked like up to now.
 
 This feature can be enabled using the -M command line option
 of the master_worker configuration file option. It may be
 used in conjunction with daemon.
 
 In order to effect a restart a SIGUSR2 should be sent to the master
 process. The master process is guaranteed to be the first pid listed
 in the pid file. It will also be the parent process, and can thus
 be found using ps. It should also be noted in the logs.
 
 SIGUSR2 is ignored if master/worker mode is not enabled.
 SIGUSR2 is ignored by workers.

The patches are available in git
git://github.com/horms/haproxy.git master



How to delete a node in EB Tree

2011-03-09 Thread xiaoxin
Hello:
   I am try to delete the proxy because some task. and reload the new 
proxy. and then use check_config_validity.
   but I get the problem that 
   I use  the fallowing code in delete_proxy() (custom function)

 /* p = proxy */
   786 if (!p-uuid){
  787 eb32_delete(p-conf.id);
  788 }
 
   I clear all the proxy, but when i use
 
  next_pxid = get_next_id(used_proxy_id, next_pxid);

  I get segment fault. 
  I thought the whole tree should be empty, but not. why? 
  Am I using the right eb tree api?
  

Thanks! 





Re: [PATCH 00/26] Restart of haproxy without dropping connections

2011-03-09 Thread Willy Tarreau
Hi Simon,

On Thu, Mar 10, 2011 at 01:32:42PM +0900, Simon Horman wrote:
 Hi,
 
 this patch series attempts to allow haproxy to be restarted - perhaps
 reconfigured would be a better term - without refusing or dropping
 connections.

Wow, I'm impressed, you managed to do that really quickly !

You sent that at the right moment, because I have almost settled down
on the internal changes needed to work on server-side keep-alive, so
I should issue -dev4 once I'm fine with those changes. Then we'll be
able to start by integrating your changes.

In the mean time, I have a few questions which come to mind :
  - does the socket cache consider all of the bind parameters ?
(eg: mss, interface, transparent, ...)

  - what happens if the new config file uses some conflicting bind
entries ? Eg: old config used to listen on 192.168.1.1:80 and
the new one uses 0.0.0.0:80 ? Or even :80 for the old one (IPv4)
and :::80 for the new one (IPv6) ?

  - does the master send a signal to all children asking them to unbind
(as we did with -sf) ?

  - do the debug modes (-d/-db) disable the master_worker mode ?

In fact I'm interested in any corner cases we should be aware of so
that we can clearly document them and indicate how to handle them
(eg: fall back to -st if it's not possible to rebind, etc...)

Thanks !
Willy




Re: How to deal with proxying shoutcast / ICY protocol

2011-03-09 Thread Willy Tarreau
Hi David,

On Thu, Mar 10, 2011 at 03:36:11PM +1300, David Young wrote:
 I thought that if I configured haproxy in mode tcp instead of mode
 http, that it'd behave more like a simple loadbalancer, and just pass
 the request onto my squid backend verbatim, but that doesn't seem to
 have worked either.
 
 An example URL I'm having trouble with is http://66.225.205.47/;stream.mp3
 
 Even in mode tcp, if I request that URL through by browser from
 haproxy, I get a 502 error, whereas if I requested it directly from the
 backend squid instance which services my request anyway, I get the
 expected headers and stream.

If you get a 502 while you're in mode tcp, then you're not in mode tcp,
but still in http. Haproxy cannot emit HTTP error codes in TCP mode.

Looking at your config, I understand what happens : your defaults section
is http, which means that all subsequent sections will have mode http by
default. Your frontend is explicitly TCP but not your backend, which is
still HTTP. So once the connection is forwarded to the backend, it's still
parsed and processed in HTTP mode.

One thing to keep in mind : if you set a mode in a defaults section, do not
use a different one in subsequent sections, or sooner or later you'll get
trapped. Better redefined a new defaults section to make things clearer. I
like to use two sections defaults http and defaults tcp for this.

Regards,
Willy




Re: How to delete a node in EB Tree

2011-03-09 Thread Willy Tarreau
Hello,

On Thu, Mar 10, 2011 at 01:59:30PM +0800, xiaoxin wrote:
 Hello:
I am try to delete the proxy because some task. and reload the new 
 proxy. and then use check_config_validity.
but I get the problem that 
I use  the fallowing code in delete_proxy() (custom function)
 
  /* p = proxy */
786 if (!p-uuid){
   787 eb32_delete(p-conf.id);
   788 }
  
I clear all the proxy, but when i use
  
   next_pxid = get_next_id(used_proxy_id, next_pxid);
 
   I get segment fault. 
   I thought the whole tree should be empty, but not. why? 

I don't know what your whole function does, but I suspect the following is
happening :

  - your proxy has a valid p-uuid 
  - line 787 does not delete it from the tree
  - after that you delete and free all the proxy contents
  - later on, get_next_id() walks the ID tree and at one point it
encounters the node that you freed without deleting it and
since the memory is not allocated anymore, the process crashes.


   Am I using the right eb tree api?

Yes, in order to delete a node from a tree, ebXX_delete(node) is the
way to do it. You did it right, reason why I think the scenario above
happened.

Try to comment out lines 786 and 788 in order to unconditionally remove
the id from the tree. I'm sure it will not crash anymore.

Regards,
Willy




Re: [PATCH 00/26] Restart of haproxy without dropping connections

2011-03-09 Thread Simon Horman
Hi Willy,

On Thu, Mar 10, 2011 at 07:29:11AM +0100, Willy Tarreau wrote:
 Hi Simon,
 
 On Thu, Mar 10, 2011 at 01:32:42PM +0900, Simon Horman wrote:
  Hi,
  
  this patch series attempts to allow haproxy to be restarted - perhaps
  reconfigured would be a better term - without refusing or dropping
  connections.
 
 Wow, I'm impressed, you managed to do that really quickly !

Thanks. Its taken quite a bit of effort to get this far.

 You sent that at the right moment, because I have almost settled down
 on the internal changes needed to work on server-side keep-alive, so
 I should issue -dev4 once I'm fine with those changes. Then we'll be
 able to start by integrating your changes.

Great.

 In the mean time, I have a few questions which come to mind :
   - does the socket cache consider all of the bind parameters ?
 (eg: mss, interface, transparent, ...)

Yes, it attempts to take them into account, though in a very naive way.
If they all match then the socket cache entry can be re-used.
Otherwise the entry is invalidated.

Thanks for earlier advice on this else I would have probably
written code that didn't take these parameters into account at all.

   - what happens if the new config file uses some conflicting bind
 entries ? Eg: old config used to listen on 192.168.1.1:80 and
 the new one uses 0.0.0.0:80 ? Or even :80 for the old one (IPv4)
 and :::80 for the new one (IPv6) ?

I think there will be a problem. Lets talk about what would be a sensible
thing to do.

   - does the master send a signal to all children asking them to unbind
 (as we did with -sf) ?

Yes. It sends them a SIGUSR1.

It does not send them a SIGTTOU. Because although that works fine
I am not entirely sure how to sanely unwind the master at that point.

   - do the debug modes (-d/-db) disable the master_worker mode ?

No. I can make that so if you like.

 In fact I'm interested in any corner cases we should be aware of so
 that we can clearly document them and indicate how to handle them
 (eg: fall back to -st if it's not possible to rebind, etc...)

I don't know of any off hand. But one of my reasons for posting
the code was to get more eyes on it so we can find such cases.

Re-initialising the configuration was somewhat non-trivial, and I am
sure that I have missed a few things.

 Thanks !
 Willy
 
 



Re: [PATCH 00/26] Restart of haproxy without dropping connections

2011-03-09 Thread Willy Tarreau
On Thu, Mar 10, 2011 at 03:52:58PM +0900, Simon Horman wrote:
  Wow, I'm impressed, you managed to do that really quickly !
 
 Thanks. Its taken quite a bit of effort to get this far.

I have no doubt about that :-)

  In the mean time, I have a few questions which come to mind :
- does the socket cache consider all of the bind parameters ?
  (eg: mss, interface, transparent, ...)
 
 Yes, it attempts to take them into account, though in a very naive way.
 If they all match then the socket cache entry can be re-used.
 Otherwise the entry is invalidated.

OK.

- what happens if the new config file uses some conflicting bind
  entries ? Eg: old config used to listen on 192.168.1.1:80 and
  the new one uses 0.0.0.0:80 ? Or even :80 for the old one (IPv4)
  and :::80 for the new one (IPv6) ?
 
 I think there will be a problem. Lets talk about what would be a sensible
 thing to do.

In my opinion we should define how to fail a restart. Right now with
-sf/-st, the new process is able to detect the failure to bind and
exits with an error which can be detected by management scripts and
reported to the admin which must immediately take action.

I think that a failure to restart smoothly means that the admin will
have to take the decision to restart harder (-sf/-st). Sometimes the
scripts will do that by themselves as an automatic fallback. So that
make me believe that all we want is the master process to refuse to
change anything in case an anomaly is detected, and to indicate its
refusal.  We cannot make it send a signal to the calling process
because it does not know its pid, and even if it knew it it would
probably not have enough permissions to send it anyway. But we should
find something the master process can act on that the calling one can
detect (eg: among the horrible ideas, if it's not chrooted, it can
change its pid and update the pidfile, but that's ugly).

- does the master send a signal to all children asking them to unbind
  (as we did with -sf) ?
 
 Yes. It sends them a SIGUSR1.
 
 It does not send them a SIGTTOU. Because although that works fine
 I am not entirely sure how to sanely unwind the master at that point.

I think it's OK that way because the master still owns the sockets, so
even in case of a late failure, it can restart new processes anyway.

- do the debug modes (-d/-db) disable the master_worker mode ?
 
 No. I can make that so if you like.

Yes that's the idea. Many users abuse -d/-db (including me) and it's
important that they don't have to touch their config for this.

  In fact I'm interested in any corner cases we should be aware of so
  that we can clearly document them and indicate how to handle them
  (eg: fall back to -st if it's not possible to rebind, etc...)
 
 I don't know of any off hand. But one of my reasons for posting
 the code was to get more eyes on it so we can find such cases.

Fine. Any failure to restart with the expected config typically is what
I'd call a corner case. That's why I'd like that we find a way to safely
fail so that proper action can be taken externally.

 Re-initialising the configuration was somewhat non-trivial, and I am
 sure that I have missed a few things.

I'm certain that some parts were a real nightmare ! I'll take time to
review all your patches because yes, it's possible that you fell in some
traps or had to take some decisions that might have other impacts. That's
also why I'd like to get your code quickly merged, it's the best way to
find what might have been overlooked.

Best regards,
Willy