BUG/MINOR: log: fix logging to both FD and IP

PiBa-NL reported an issue affecting logs when stdout is enabled at the
same time as an IP address. It does not affect FD and UNIX, but does
still affect multiple FDs. What happens is that the condition to detect
that the initialization was not made relies on the FD being -1, and in
this case the FD points to the *unique* FD used for AF_INET sockets, so
the configured socket used for outgoing logs over UDP gets overwritten
by the last configured FD. This is not appropriate, so instead we rely
on the sin_port part of the IPv4-mapped address to store the
initialization state for each FD.

This part deserves being significantly revamped, as IPv6 is still not
possible due to the way the FDs are managed, and inherited FDs are a
bit hackish.

Note that this patch relies on "MINOR: tools: preset the port of
fd-based "sockets" to zero" in order to operate properly.

No backport is needed.
This commit is contained in:
Willy Tarreau 2018-12-15 15:48:48 +01:00
parent 0205a4e0b5
commit 204e3f1fab
1 changed files with 17 additions and 9 deletions

View File

@ -1355,8 +1355,7 @@ void __send_log(struct proxy *p, int level, char *message, size_t size, char *sd
nblogger = 0;
list_for_each_entry(tmp, logsrvs, list) {
const struct logsrv *logsrv = tmp;
int *plogfd = logsrv->addr.ss_family == AF_UNIX ?
&logfdunix : &logfdinet;
int *plogfd;
char *pid_sep1 = "", *pid_sep2 = "";
char logheader_short[3];
int sent;
@ -1375,15 +1374,24 @@ void __send_log(struct proxy *p, int level, char *message, size_t size, char *sd
if (level > logsrv->level)
continue;
if (logsrv->addr.ss_family == AF_UNSPEC) {
/* the socket's address is a file descriptor */
plogfd = (int *)&((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
if (unlikely(!((struct sockaddr_in *)&logsrv->addr)->sin_port)) {
/* FD not yet initialized to non-blocking mode */
fcntl(*plogfd, F_SETFL, O_NONBLOCK);
((struct sockaddr_in *)&logsrv->addr)->sin_port = 1;
}
}
else if (logsrv->addr.ss_family == AF_UNIX)
plogfd = &logfdunix;
else
plogfd = &logfdinet;
if (unlikely(*plogfd < 0)) {
/* socket not successfully initialized yet */
if (logsrv->addr.ss_family == AF_UNSPEC) {
/* the socket's address is a file descriptor */
*plogfd = ((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
fcntl(*plogfd, F_SETFL, O_NONBLOCK);
}
else if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM,
(logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM,
(logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
static char once;
if (!once) {