[MEDIUM] Enhance message errors management on binds

This commit is contained in:
Emeric Brun 2010-10-22 16:06:11 +02:00 committed by Willy Tarreau
parent 71c814efcb
commit cf20bf1c1c
8 changed files with 76 additions and 41 deletions

View File

@ -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);

View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -55,7 +55,8 @@
#include <import/ip_tproxy.h>
#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 <const> 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;
}

View File

@ -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 <const> 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 <tempname> 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;
}

View File

@ -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;
}

View File

@ -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 <verbose> is set or if they are fatal */
if (verbose || (lerr & (ERR_FATAL | ERR_ABORT))) {