[MAJOR] added generic protocol support

A new generic protocol mechanism has been added. It provides
an easy method to implement new protocols with different
listeners (eg: unix sockets).

The listeners are automatically started at the right moment
and enabled after the possible fork().
diff --git a/Makefile b/Makefile
index ba9a011..f7474d3 100644
--- a/Makefile
+++ b/Makefile
@@ -226,7 +226,7 @@
 
 all: haproxy
 
-OBJS = src/haproxy.o src/sessionhash.o src/base64.o \
+OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocols.o \
        src/uri_auth.o src/standard.o src/buffers.o src/log.o src/task.o \
        src/time.o src/fd.o src/regex.o src/cfgparse.o src/server.o \
        src/checks.o src/queue.o src/client.o src/proxy.o \
diff --git a/Makefile.bsd b/Makefile.bsd
index f4ff4f2..31a894d 100644
--- a/Makefile.bsd
+++ b/Makefile.bsd
@@ -98,7 +98,7 @@
 CFLAGS  = -Wall $(COPTS) $(DEBUG)
 LDFLAGS = -g
 
-OBJS = src/haproxy.o src/sessionhash.o src/base64.o \
+OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocols.o \
        src/uri_auth.o src/standard.o src/buffers.o src/log.o src/task.o \
        src/time.o src/fd.o src/regex.o src/cfgparse.o src/server.o \
        src/checks.o src/queue.o src/client.o src/proxy.o \
diff --git a/Makefile.osx b/Makefile.osx
index 1c24e77..5d6d875 100644
--- a/Makefile.osx
+++ b/Makefile.osx
@@ -95,7 +95,7 @@
 CFLAGS  = -Wall $(COPTS) $(DEBUG) -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch ppc -arch i386
 LDFLAGS = -g -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch ppc -arch i386
 
-OBJS = src/haproxy.o src/sessionhash.o src/base64.o \
+OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocols.o \
        src/uri_auth.o src/standard.o src/buffers.o src/log.o src/task.o \
        src/time.o src/fd.o src/regex.o src/cfgparse.o src/server.o \
        src/checks.o src/queue.o src/client.o src/proxy.o \
diff --git a/include/proto/protocols.h b/include/proto/protocols.h
new file mode 100644
index 0000000..aec2438
--- /dev/null
+++ b/include/proto/protocols.h
@@ -0,0 +1,61 @@
+/*
+  include/proto/protocols.h
+  This file declares generic protocol primitives.
+
+  Copyright (C) 2000-2007 Willy Tarreau - w@1wt.eu
+  
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation, version 2.1
+  exclusively.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef _PROTO_PROTOCOLS_H
+#define _PROTO_PROTOCOLS_H
+
+#include <types/protocols.h>
+
+/* Registers the protocol <proto> */
+void protocol_register(struct protocol *proto);
+
+/* Unregisters the protocol <proto>. Note that all listeners must have
+ * previously been unbound.
+ */
+void protocol_unregister(struct protocol *proto);
+
+/* binds all listeneres of all registered protocols. Returns a composition
+ * of ERR_NONE, ERR_RETRYABLE, ERR_FATAL.
+ */
+int protocol_bind_all(void);
+
+/* unbinds all listeners of all registered protocols. They are also closed.
+ * This must be performed before calling exit() in order to get a chance to
+ * remove file-system based sockets and pipes.
+ * Returns a composition of ERR_NONE, ERR_RETRYABLE, ERR_FATAL.
+ */
+int protocol_unbind_all(void);
+
+/* enables all listeners of all registered protocols. This is intended to be
+ * used after a fork() to enable reading on all file descriptors. Returns a
+ * composition of ERR_NONE, ERR_RETRYABLE, ERR_FATAL.
+ */
+int protocol_enable_all(void);
+
+
+#endif /* _PROTO_PROTOCOLS_H */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ */
diff --git a/include/types/fd.h b/include/types/fd.h
index 3eacd7a..488887a 100644
--- a/include/types/fd.h
+++ b/include/types/fd.h
@@ -30,6 +30,7 @@
 #include <common/config.h>
 #include <types/task.h>
 #include <types/buffers.h>
+#include <types/protocols.h>
 
 /* different possible states for the fd */
 #define FD_STCLOSE	0
@@ -66,6 +67,7 @@
 	unsigned char ev;                    /* event seen in return of poll() : FD_POLL_* */
 	struct sockaddr *peeraddr;           /* pointer to peer's network address, or NULL if unset */
 	socklen_t peerlen;                   /* peer's address length, or 0 if unset */
+	struct listener *listener;           /* the listener which created this fd, or NULL if unset */
 };
 
 /*
diff --git a/include/types/protocols.h b/include/types/protocols.h
new file mode 100644
index 0000000..12636d8
--- /dev/null
+++ b/include/types/protocols.h
@@ -0,0 +1,93 @@
+/*
+  include/types/protocols.h
+  This file defines the structures used by generic network protocols.
+
+  Copyright (C) 2000-2007 Willy Tarreau - w@1wt.eu
+  
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation, version 2.1
+  exclusively.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef _TYPES_PROTOCOLS_H
+#define _TYPES_PROTOCOLS_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <common/config.h>
+#include <common/mini-clist.h>
+
+/* max length of a protcol name, including trailing zero */
+#define PROTO_NAME_LEN 16
+
+/* return codes for bind_all() */
+#define ERR_NONE	0	/* no error */
+#define ERR_RETRYABLE	1	/* retryable error, may be cumulated */
+#define ERR_FATAL	2	/* fatal error, may be cumulated */
+
+/* listener state */
+#define LI_NEW		0	/* not initialized yet */
+#define LI_LISTEN	1	/* started, listening but not enabled */
+#define LI_READY	2	/* started, listening and enabled */
+#define LI_FULL		3	/* reached its connection limit */
+
+/* The listener will be directly referenced by the fdtab[] which holds its
+ * socket. The listener provides the protocol-specific accept() function to
+ * the fdtab.
+ */
+struct listener {
+	int fd;				/* the listen socket */
+	int state;			/* state: NEW, READY, FULL */
+	struct sockaddr_storage addr;	/* the address we listen to */
+	struct protocol *proto;		/* protocol this listener belongs to */
+	int nbconn;			/* current number of connections on this listener */
+	int maxconn;			/* maximum connections allowed on this listener */
+	struct listener *next;		/* next address for the same proxy, or NULL */
+	struct list proto_list;         /* list in the protocol header */
+	int (*accept)(int fd);		/* accept() function passed to fdtab[] */
+	void (*handler)(struct task *t, struct timeval *next); /* protocol handler */
+	struct timeval *timeout;	/* pointer to client-side timeout */
+	void *private;			/* any private data which may be used by accept() */
+};
+
+/* This structure contains all information needed to easily handle a protocol.
+ * Its primary goal is to ease listeners maintenance. Specifically, the
+ * bind_all() primitive must be used before any fork(), and the enable_all()
+ * primitive must be called after the fork() to enable all fds. Last, the
+ * unbind_all() primitive closes all listeners.
+ */
+struct protocol {
+	char name[PROTO_NAME_LEN];			/* protocol name, zero-terminated */
+	int sock_domain;				/* socket domain, as passed to socket()   */
+	int sock_type;					/* socket type, as passed to socket()     */
+	int sock_prot;					/* socket protocol, as passed to socket() */
+	sa_family_t sock_family;			/* socket family, for sockaddr */
+	int (*read)(int fd);				/* generic read function */
+	int (*write)(int fd);				/* generic write function */
+	int (*bind_all)(struct protocol *proto);	/* bind all unbound listeners */
+	int (*unbind_all)(struct protocol *proto);	/* unbind all bound listeners */
+	int (*enable_all)(struct protocol *proto);	/* enable all bound listeners */
+	struct list listeners;				/* list of listeners using this protocol */
+	int nb_listeners;				/* number of listeners */
+	struct list list;				/* list of registered protocols */
+};
+
+#endif /* _TYPES_PROTOCOLS_H */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ */
diff --git a/include/types/proxy.h b/include/types/proxy.h
index e4cf4ce..aae63b8 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -37,6 +37,7 @@
 #include <types/acl.h>
 #include <types/buffers.h>
 #include <types/httperr.h>
+#include <types/protocols.h>
 #include <types/session.h>
 #include <types/server.h>
 
@@ -63,18 +64,6 @@
 #define PR_CAP_RS      0x0004
 #define PR_CAP_LISTEN  (PR_CAP_FE|PR_CAP_BE|PR_CAP_RS)
 
-/* return codes for start_proxies */
-#define ERR_NONE	0	/* no error */
-#define ERR_RETRYABLE	1	/* retryable error, may be cumulated */
-#define ERR_FATAL	2	/* fatal error, may be cumulated */
-
-
-struct listener {
-	int fd;				/* the listen socket */
-	struct sockaddr_storage addr;	/* the address we listen to */
-	struct listener *next;		/* next address or NULL */
-};
-
 struct proxy {
 	struct listener *listen;		/* the listen addresses and sockets */
 	struct in_addr mon_net, mon_mask;	/* don't forward connections from this net (network order) FIXME: should support IPv6 */
diff --git a/src/haproxy.c b/src/haproxy.c
index 8cce7eb..c13c392 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -85,6 +85,7 @@
 #include <proto/client.h>
 #include <proto/fd.h>
 #include <proto/log.h>
+#include <proto/protocols.h>
 #include <proto/proto_http.h>
 #include <proto/proxy.h>
 #include <proto/queue.h>
@@ -720,7 +721,9 @@
 		p = p->next;
 		free(p0);
 	}/* end while(p) */
-    
+
+	protocol_unbind_all();
+
 	if (global.chroot)    free(global.chroot);
 	global.chroot = NULL;
 
@@ -838,6 +841,14 @@
 		exit(1);
 	}
 
+	if (protocol_bind_all() != ERR_NONE) {
+		Alert("[%s.main()] Some protocols failed to start their listeners! Exiting.\n", argv[0]);
+		protocol_unbind_all(); /* cleanup everything we can */
+		if (nb_oldpids)
+			tell_old_pids(SIGTTIN);
+		exit(1);
+	}
+
 	/* prepare pause/play signals */
 	signal(SIGTTOU, sig_pause);
 	signal(SIGTTIN, sig_listen);
@@ -865,6 +876,7 @@
 			Alert("[%s.main()] 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");
@@ -904,6 +916,7 @@
 			      "  Make sure you have enough permissions and that the module is loadable.\n"
 			      "  Alternatively, you may disable the 'tcpsplice' options in the configuration.\n"
 			      "", argv[0], global.gid);
+			protocol_unbind_all();
 			exit(1);
 		}
 	}
@@ -919,6 +932,7 @@
 			      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);
 		}
 	}
@@ -927,6 +941,7 @@
 	if ((global.last_checks & LSTCHK_NETADM) && global.uid) {
 		Alert("[%s.main()] Some configuration options require full privileges, so global.uid cannot be changed.\n"
 		      "", argv[0], global.gid);
+		protocol_unbind_all();
 		exit(1);
 	}
 
@@ -936,6 +951,7 @@
 			Alert("[%s.main()] Cannot chroot(%s).\n", argv[0], global.chroot);
 			if (nb_oldpids)
 				tell_old_pids(SIGTTIN);
+			protocol_unbind_all();
 			exit(1);
 		}
 		chdir("/");
@@ -951,11 +967,13 @@
 	/* setgid / setuid */
 	if (global.gid && setgid(global.gid) == -1) {
 		Alert("[%s.main()] Cannot set gid %d.\n", argv[0], global.gid);
+		protocol_unbind_all();
 		exit(1);
 	}
 
 	if (global.uid && setuid(global.uid) == -1) {
 		Alert("[%s.main()] Cannot set uid %d.\n", argv[0], global.uid);
+		protocol_unbind_all();
 		exit(1);
 	}
 
@@ -976,6 +994,7 @@
 			ret = fork();
 			if (ret < 0) {
 				Alert("[%s.main()] Cannot fork.\n", argv[0]);
+				protocol_unbind_all();
 				exit(1); /* there has been an error */
 			}
 			else if (ret == 0) /* child breaks here */
@@ -1010,6 +1029,7 @@
 		fork_poller();
 	}
 
+	protocol_enable_all();
 	/*
 	 * That's it : the central polling loop. Run until we stop.
 	 */
diff --git a/src/protocols.c b/src/protocols.c
new file mode 100644
index 0000000..49204b8
--- /dev/null
+++ b/src/protocols.c
@@ -0,0 +1,90 @@
+/*
+ * Protocol registration functions.
+ *
+ * Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <common/config.h>
+#include <common/mini-clist.h>
+#include <common/standard.h>
+
+#include <types/protocols.h>
+
+/* List head of all registered protocols */
+static struct list protocols = LIST_HEAD_INIT(protocols);
+
+/* Registers the protocol <proto> */
+void protocol_register(struct protocol *proto)
+{
+	LIST_ADDQ(&protocols, &proto->list);
+}
+
+/* Unregisters the protocol <proto>. Note that all listeners must have
+ * previously been unbound.
+ */
+void protocol_unregister(struct protocol *proto)
+{
+	LIST_DEL(&proto->list);
+	LIST_INIT(&proto->list);
+}
+
+/* binds all listeneres of all registered protocols. Returns a composition
+ * of ERR_NONE, ERR_RETRYABLE, ERR_FATAL.
+ */
+int protocol_bind_all(void)
+{
+	struct protocol *proto;
+	int err;
+
+	err = 0;
+	list_for_each_entry(proto, &protocols, list) {
+		if (proto->bind_all)
+			err |= proto->bind_all(proto);
+	}
+	return err;
+}
+
+/* unbinds all listeners of all registered protocols. They are also closed.
+ * This must be performed before calling exit() in order to get a chance to
+ * remove file-system based sockets and pipes.
+ * Returns a composition of ERR_NONE, ERR_RETRYABLE, ERR_FATAL.
+ */
+int protocol_unbind_all(void)
+{
+	struct protocol *proto;
+	int err;
+
+	err = 0;
+	list_for_each_entry(proto, &protocols, list) {
+		if (proto->unbind_all)
+			err |= proto->unbind_all(proto);
+	}
+	return err;
+}
+
+/* enables all listeners of all registered protocols. This is intended to be
+ * used after a fork() to enable reading on all file descriptors. Returns a
+ * composition of ERR_NONE, ERR_RETRYABLE, ERR_FATAL.
+ */
+int protocol_enable_all(void)
+{
+	struct protocol *proto;
+	int err;
+
+	err = 0;
+	list_for_each_entry(proto, &protocols, list) {
+		if (proto->enable_all)
+			err |= proto->enable_all(proto);
+	}
+	return err;
+}
+