diff --git a/Makefile b/Makefile index ba9a01156..f7474d37e 100644 --- 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 \ diff --git a/Makefile.bsd b/Makefile.bsd index f4ff4f2b6..31a894d1f 100644 --- a/Makefile.bsd +++ b/Makefile.bsd @@ -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 \ diff --git a/Makefile.osx b/Makefile.osx index 1c24e777e..5d6d875f6 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -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 index 000000000..aec243804 --- /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 + +/* Registers the protocol */ +void protocol_register(struct protocol *proto); + +/* Unregisters the protocol . 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 3eacd7a32..488887ac0 100644 --- a/include/types/fd.h +++ b/include/types/fd.h @@ -30,6 +30,7 @@ #include #include #include +#include /* 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 index 000000000..12636d83a --- /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 +#include + +#include +#include + +/* 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 e4cf4ce66..aae63b86c 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -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 8cce7ebf7..c13c392d6 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -85,6 +85,7 @@ #include #include #include +#include #include #include #include @@ -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 index 000000000..49204b891 --- /dev/null +++ b/src/protocols.c @@ -0,0 +1,90 @@ +/* + * Protocol registration functions. + * + * Copyright 2000-2007 Willy Tarreau + * + * 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 +#include + +#include +#include +#include + +#include + +/* List head of all registered protocols */ +static struct list protocols = LIST_HEAD_INIT(protocols); + +/* Registers the protocol */ +void protocol_register(struct protocol *proto) +{ + LIST_ADDQ(&protocols, &proto->list); +} + +/* Unregisters the protocol . 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; +} +