[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;
+}
+