]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MAJOR] added generic protocol support
authorWilly Tarreau <w@1wt.eu>
Tue, 16 Oct 2007 10:25:14 +0000 (12:25 +0200)
committerWilly Tarreau <w@1wt.eu>
Thu, 18 Oct 2007 12:11:12 +0000 (14:11 +0200)
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().

Makefile
Makefile.bsd
Makefile.osx
include/proto/protocols.h [new file with mode: 0644]
include/types/fd.h
include/types/protocols.h [new file with mode: 0644]
include/types/proxy.h
src/haproxy.c
src/protocols.c [new file with mode: 0644]

index ba9a01156c3f6c4659a795a9de113d7a82b812c2..f7474d37eafb8ba0e83dee98ce080630073dba5e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -226,7 +226,7 @@ LDFLAGS = -g
 
 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 \
index f4ff4f2b6d5e3b0e9b46adfe79a4b52ae36d301e..31a894d1f28d8773a4cd39e6c0938eb274fa42b0 100644 (file)
@@ -98,7 +98,7 @@ LIBS    = $(LIBS.$(TARGET)) $(LIBS.$(REGEX)) $(ADDLIB)
 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 \
index 1c24e777e29030b79ff44835c5e1509bccdaedcc..5d6d875f6a030ead56ff814215a0e154a2b68621 100644 (file)
@@ -95,7 +95,7 @@ LIBS    = $(LIBS.$(TARGET)) $(LIBS.$(REGEX)) $(ADDLIB)
 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 (file)
index 0000000..aec2438
--- /dev/null
@@ -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:
+ */
index 3eacd7a32e0d285d248d2b1a1dc50f4296f667d9..488887ac094b2348f28231d19c5abb7b797aa49d 100644 (file)
@@ -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 @@ struct fdtab {
        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 (file)
index 0000000..12636d8
--- /dev/null
@@ -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:
+ */
index e4cf4ce664a4a3dc7dbb3ea8e30b8813452949dd..aae63b86cbd5937141abe89521e8c99700101760 100644 (file)
@@ -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>
 
 #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 */
index 8cce7ebf71da76cbcad433dafc144dfb8fa7ff73..c13c392d6c942ee79b6ea00ed5ae70c2ca3738df 100644 (file)
@@ -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 @@ void deinit(void)
                p = p->next;
                free(p0);
        }/* end while(p) */
-    
+
+       protocol_unbind_all();
+
        if (global.chroot)    free(global.chroot);
        global.chroot = NULL;
 
@@ -838,6 +841,14 @@ int main(int argc, char **argv)
                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 @@ int main(int argc, char **argv)
                        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 @@ int main(int argc, char **argv)
                              "  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 @@ int main(int argc, char **argv)
                              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 @@ int main(int argc, char **argv)
        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 @@ int main(int argc, char **argv)
                        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 @@ int main(int argc, char **argv)
        /* 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 @@ int main(int argc, char **argv)
                        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 @@ int main(int argc, char **argv)
                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 (file)
index 0000000..49204b8
--- /dev/null
@@ -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;
+}
+