From: Emeric Brun Date: Fri, 22 Oct 2010 14:06:11 +0000 (+0200) Subject: [MEDIUM] Enhance message errors management on binds X-Git-Tag: v1.5-dev8~411 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cf20bf1c1c2318bab62c4811795572fe3e143ebb;p=thirdparty%2Fhaproxy.git [MEDIUM] Enhance message errors management on binds --- diff --git a/include/proto/proto_tcp.h b/include/proto/proto_tcp.h index 1b46d37ad1..c81be9bdcc 100644 --- a/include/proto/proto_tcp.h +++ b/include/proto/proto_tcp.h @@ -30,7 +30,6 @@ int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct sockaddr_in *remote); void tcpv4_add_listener(struct listener *listener); void tcpv6_add_listener(struct listener *listener); -int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen); int tcpv4_connect_server(struct stream_interface *si, struct proxy *be, struct server *srv, struct sockaddr *srv_addr, struct sockaddr *from_addr); diff --git a/include/proto/protocols.h b/include/proto/protocols.h index cd54ec8ee0..d0364c047d 100644 --- a/include/proto/protocols.h +++ b/include/proto/protocols.h @@ -82,7 +82,7 @@ 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); +int protocol_bind_all(char *errmsg, int errlen); /* 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 diff --git a/include/types/protocols.h b/include/types/protocols.h index 64e1c5541f..3dcb2e7400 100644 --- a/include/types/protocols.h +++ b/include/types/protocols.h @@ -135,7 +135,8 @@ struct protocol { int (*accept)(int fd); /* generic accept function */ 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 (*bind)(struct listener *l, char *errmsg, int errlen); /* bind a listener */ + int (*bind_all)(struct protocol *proto, char *errmsg, int errlen); /* bind all unbound listeners */ int (*unbind_all)(struct protocol *proto); /* unbind all bound listeners */ int (*enable_all)(struct protocol *proto); /* enable all bound listeners */ int (*disable_all)(struct protocol *proto); /* disable all bound listeners */ diff --git a/src/haproxy.c b/src/haproxy.c index c8f0a38f17..4859d092d9 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -939,8 +939,9 @@ int main(int argc, char **argv) int err, retry; struct rlimit limit; FILE *pidfile = NULL; - init(argc, argv); + char errmsg[100]; + init(argc, argv); signal_register_fct(SIGQUIT, dump, SIGQUIT); signal_register_fct(SIGUSR1, sig_soft_stop, SIGUSR1); signal_register_fct(SIGHUP, sig_dump_state, SIGHUP); @@ -998,12 +999,18 @@ int main(int argc, char **argv) exit(1); } - if ((protocol_bind_all() & ~ERR_WARN) != ERR_NONE) { + err = protocol_bind_all(errmsg, sizeof(errmsg)); + if ((err & ~ERR_WARN) != ERR_NONE) { + if ((err & ERR_ALERT) || (err & ERR_WARN)) + Alert("[%s.main()] %s.\n", argv[0], errmsg); + 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); + } else if (err & ERR_WARN) { + Alert("[%s.main()] %s.\n", argv[0], errmsg); } /* prepare pause/play signals */ diff --git a/src/proto_tcp.c b/src/proto_tcp.c index 7aa6d407df..1d8b25727a 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -55,7 +55,8 @@ #include #endif -static int tcp_bind_listeners(struct protocol *proto); +static int tcp_bind_listeners(struct protocol *proto, char *errmsg, int errlen); +static int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen); /* Note: must not be declared as its list will be overwritten */ static struct protocol proto_tcpv4 = { @@ -69,6 +70,7 @@ static struct protocol proto_tcpv4 = { .accept = &stream_sock_accept, .read = &stream_sock_read, .write = &stream_sock_write, + .bind = tcp_bind_listener, .bind_all = tcp_bind_listeners, .unbind_all = unbind_all_listeners, .enable_all = enable_all_listeners, @@ -88,6 +90,7 @@ static struct protocol proto_tcpv6 = { .accept = &stream_sock_accept, .read = &stream_sock_read, .write = &stream_sock_write, + .bind = tcp_bind_listener, .bind_all = tcp_bind_listeners, .unbind_all = unbind_all_listeners, .enable_all = enable_all_listeners, @@ -571,14 +574,14 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen) * loose them across the fork(). A call to enable_all_listeners() is needed * to complete initialization. The return value is composed from ERR_*. */ -static int tcp_bind_listeners(struct protocol *proto) +static int tcp_bind_listeners(struct protocol *proto, char *errmsg, int errlen) { struct listener *listener; int err = ERR_NONE; list_for_each_entry(listener, &proto->listeners, proto_list) { - err |= tcp_bind_listener(listener, NULL, 0); - if ((err & ERR_CODE) == ERR_ABORT) + err |= tcp_bind_listener(listener, errmsg, errlen); + if (err & ERR_ABORT) break; } diff --git a/src/proto_uxst.c b/src/proto_uxst.c index 98c9555520..511753395f 100644 --- a/src/proto_uxst.c +++ b/src/proto_uxst.c @@ -47,7 +47,8 @@ #define MAXPATHLEN 128 #endif -static int uxst_bind_listeners(struct protocol *proto); +static int uxst_bind_listener(struct listener *listener, char *errmsg, int errlen); +static int uxst_bind_listeners(struct protocol *proto, char *errmsg, int errlen); static int uxst_unbind_listeners(struct protocol *proto); /* Note: must not be declared as its list will be overwritten */ @@ -62,6 +63,7 @@ static struct protocol proto_unix = { .accept = &stream_sock_accept, .read = &stream_sock_read, .write = &stream_sock_write, + .bind = uxst_bind_listener, .bind_all = uxst_bind_listeners, .unbind_all = uxst_unbind_listeners, .enable_all = enable_all_listeners, @@ -82,7 +84,7 @@ static struct protocol proto_unix = { * OS, it's still useful where it works. * It returns the assigned file descriptor, or -1 in the event of an error. */ -static int create_uxst_socket(const char *path, uid_t uid, gid_t gid, mode_t mode) +static int create_uxst_socket(const char *path, uid_t uid, gid_t gid, mode_t mode, char *errmsg, int errlen) { char tempname[MAXPATHLEN]; char backname[MAXPATHLEN]; @@ -92,36 +94,42 @@ static int create_uxst_socket(const char *path, uid_t uid, gid_t gid, mode_t mod /* 1. create socket names */ if (!path[0]) { - Alert("Invalid empty name for a UNIX socket. Aborting.\n"); + if (errmsg && errlen) + snprintf(errmsg, errlen, "Invalid empty name for a UNIX socket"); goto err_return; } ret = snprintf(tempname, MAXPATHLEN, "%s.%d.tmp", path, pid); if (ret < 0 || ret >= MAXPATHLEN) { - Alert("name too long for UNIX socket (%s). Aborting.\n", path); + if (errmsg && errlen) + snprintf(errmsg, errlen, "name too long for UNIX socket (%s)", path); goto err_return; } ret = snprintf(backname, MAXPATHLEN, "%s.%d.bak", path, pid); if (ret < 0 || ret >= MAXPATHLEN) { - Alert("name too long for UNIX socket (%s). Aborting.\n", path); + if (errmsg && errlen) + snprintf(errmsg, errlen, "name too long for UNIX socket (%s)", path); goto err_return; } /* 2. clean existing orphaned entries */ if (unlink(tempname) < 0 && errno != ENOENT) { - Alert("error when trying to unlink previous UNIX socket (%s). Aborting.\n", path); + if (errmsg && errlen) + snprintf(errmsg, errlen, "error when trying to unlink previous UNIX socket (%s)", path); goto err_return; } if (unlink(backname) < 0 && errno != ENOENT) { - Alert("error when trying to unlink previous UNIX socket (%s). Aborting.\n", path); + if (errmsg && errlen) + snprintf(errmsg, errlen, "error when trying to unlink previous UNIX socket (%s)", path); goto err_return; } /* 3. backup existing socket */ if (link(path, backname) < 0 && errno != ENOENT) { - Alert("error when trying to preserve previous UNIX socket (%s). Aborting.\n", path); + if (errmsg && errlen) + snprintf(errmsg, errlen, "error when trying to preserve previous UNIX socket (%s)", path); goto err_return; } @@ -132,34 +140,40 @@ static int create_uxst_socket(const char *path, uid_t uid, gid_t gid, mode_t mod sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) { - Alert("cannot create socket for UNIX listener (%s). Aborting.\n", path); + if (errmsg && errlen) + snprintf(errmsg, errlen, "cannot create socket for UNIX listener (%s)", path); goto err_unlink_back; } if (sock >= global.maxsock) { - Alert("socket(): not enough free sockets for UNIX listener (%s). Raise -n argument. Aborting.\n", path); + if (errmsg && errlen) + snprintf(errmsg, errlen, "socket(): not enough free sockets for UNIX listener (%s). Raise -n argument", path); goto err_unlink_temp; } if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { - Alert("cannot make UNIX socket non-blocking. Aborting.\n"); + if (errmsg && errlen) + snprintf(errmsg, errlen, "cannot make UNIX socket non-blocking"); goto err_unlink_temp; } if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { /* note that bind() creates the socket on the file system */ - Alert("cannot bind socket for UNIX listener (%s). Aborting.\n", path); + if (errmsg && errlen) + snprintf(errmsg, errlen, "cannot bind socket for UNIX listener (%s)", path); goto err_unlink_temp; } if (((uid != -1 || gid != -1) && (chown(tempname, uid, gid) == -1)) || (mode != 0 && chmod(tempname, mode) == -1)) { - Alert("cannot change UNIX socket ownership (%s). Aborting.\n", path); + if (errmsg && errlen) + snprintf(errmsg, errlen, "cannot change UNIX socket ownership (%s)", path); goto err_unlink_temp; } if (listen(sock, 0) < 0) { - Alert("cannot listen to socket for UNIX listener (%s). Aborting.\n", path); + if (errmsg && errlen) + snprintf(errmsg, errlen, "cannot listen to socket for UNIX listener (%s)", path); goto err_unlink_temp; } @@ -169,7 +183,8 @@ static int create_uxst_socket(const char *path, uid_t uid, gid_t gid, mode_t mod * backname. */ if (rename(tempname, path) < 0) { - Alert("cannot switch final and temporary sockets for UNIX listener (%s). Aborting.\n", path); + if (errmsg && errlen) + snprintf(errmsg, errlen, "cannot switch final and temporary sockets for UNIX listener (%s)", path); goto err_rename; } @@ -234,20 +249,24 @@ static void destroy_uxst_socket(const char *path) * the state from ASSIGNED to LISTEN. The socket is NOT enabled for polling. * The return value is composed from ERR_NONE, ERR_RETRYABLE and ERR_FATAL. */ -static int uxst_bind_listener(struct listener *listener) +static int uxst_bind_listener(struct listener *listener, char *errmsg, int errlen) { int fd; + /* ensure we never return garbage */ + if (errmsg && errlen) + *errmsg = 0; + if (listener->state != LI_ASSIGNED) return ERR_NONE; /* already bound */ fd = create_uxst_socket(((struct sockaddr_un *)&listener->addr)->sun_path, listener->perm.ux.uid, listener->perm.ux.gid, - listener->perm.ux.mode); - if (fd == -1) - return ERR_FATAL; - + listener->perm.ux.mode, errmsg, errlen); + if (fd == -1) { + return ERR_FATAL | ERR_ALERT; + } /* the socket is now listening */ listener->fd = fd; listener->state = LI_LISTEN; @@ -307,15 +326,15 @@ void uxst_add_listener(struct listener *listener) * * The return value is composed from ERR_NONE, ERR_RETRYABLE and ERR_FATAL. */ -static int uxst_bind_listeners(struct protocol *proto) +static int uxst_bind_listeners(struct protocol *proto, char *errmsg, int errlen) { struct listener *listener; int err = ERR_NONE; list_for_each_entry(listener, &proto->listeners, proto_list) { - err |= uxst_bind_listener(listener); - if (err != ERR_NONE) - continue; + err |= uxst_bind_listener(listener, errmsg, errlen); + if (err & ERR_ABORT) + break; } return err; } diff --git a/src/protocols.c b/src/protocols.c index a32272d732..5eddb2c593 100644 --- a/src/protocols.c +++ b/src/protocols.c @@ -144,15 +144,18 @@ void protocol_unregister(struct protocol *proto) /* binds all listeners of all registered protocols. Returns a composition * of ERR_NONE, ERR_RETRYABLE, ERR_FATAL. */ -int protocol_bind_all(void) +int protocol_bind_all(char *errmsg, int errlen) { struct protocol *proto; int err; err = 0; list_for_each_entry(proto, &protocols, list) { - if (proto->bind_all) - err |= proto->bind_all(proto); + if (proto->bind_all) { + err |= proto->bind_all(proto, errmsg, errlen); + if ( err & ERR_ABORT ) + break; + } } return err; } @@ -160,7 +163,7 @@ 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. + * Returns a composition of ERR_NONE, ERR_RETRYABLE, ERR_FATAL, ERR_ABORT. */ int protocol_unbind_all(void) { @@ -169,8 +172,9 @@ int protocol_unbind_all(void) err = 0; list_for_each_entry(proto, &protocols, list) { - if (proto->unbind_all) + if (proto->unbind_all) { err |= proto->unbind_all(proto); + } } return err; } @@ -186,8 +190,9 @@ int protocol_enable_all(void) err = 0; list_for_each_entry(proto, &protocols, list) { - if (proto->enable_all) + if (proto->enable_all) { err |= proto->enable_all(proto); + } } return err; } @@ -203,8 +208,9 @@ int protocol_disable_all(void) err = 0; list_for_each_entry(proto, &protocols, list) { - if (proto->disable_all) + if (proto->disable_all) { err |= proto->disable_all(proto); + } } return err; } diff --git a/src/proxy.c b/src/proxy.c index f25216c0ea..99594980ac 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -427,7 +427,7 @@ int start_proxies(int verbose) if (listener->state != LI_ASSIGNED) continue; /* already started */ - lerr = tcp_bind_listener(listener, msg, sizeof(msg)); + lerr = listener->proto->bind(listener, msg, sizeof(msg)); /* errors are reported if is set or if they are fatal */ if (verbose || (lerr & (ERR_FATAL | ERR_ABORT))) {