log <address> <facility> [max level]
Adds a global syslog server. Up to two global servers can be defined. They
will receive logs for startups and exits, as well as all logs from proxies
- configured with "log global". <address> is an IPv4 address optionally
- followed by a colon and an UDP port. If no port is specified, 514 is used
- by default (the standard syslog port). <facility> must be one of the 24
- standard syslog facilities :
+ configured with "log global".
+
+ <address> can be one of:
+
+ - An IPv4 address optionally followed by a colon and an UDP port. If
+ no port is specified, 514 is used by default (the standard syslog
+ port).
+
+ - A filesystem path to a UNIX domain socket, keeping in mind
+ considerations for chroot (be sure the path is accessible inside
+ the chroot) and uid/gid (be sure the path is appropriately
+ writeable).
+
+ <facility> must be one of the 24 standard syslog facilities :
kern user mail daemon auth syslog lpr news
uucp cron auth2 ftp ntp audit alert cron2
*/
extern const char *invalid_char(const char *name);
+/*
+ * converts <str> to a struct sockaddr_un* which is locally allocated.
+ * The format is "/path", where "/path" is a path to a UNIX domain socket.
+ */
+struct sockaddr_un *str2sun(char *str);
+
/*
* converts <str> to a struct sockaddr_in* which is locally allocated.
* The format is "addr:port", where "addr" can be a dotted IPv4 address,
#include <netinet/in.h>
#include <common/config.h>
+#include <types/log.h>
#include <types/protocols.h>
#include <types/task.h>
char *pidfile;
int logfac1, logfac2;
int loglev1, loglev2;
- struct sockaddr_in logsrv1, logsrv2;
+ struct logsrv logsrv1, logsrv2;
struct {
int maxpollevents; /* max number of poll events at once */
} tune;
#ifndef _TYPES_LOG_H
#define _TYPES_LOG_H
+#include <sys/un.h>
+#include <netinet/in.h>
#include <common/config.h>
#define MAX_SYSLOG_LEN 1024
#define LW_REQHDR 1024 /* request header(s) */
#define LW_RSPHDR 2048 /* response header(s) */
+struct logsrv {
+ union {
+ struct sockaddr addr;
+ struct sockaddr_un un; /* AF_UNIX */
+ struct sockaddr_in in; /* AF_INET */
+ } u;
+};
+
+int logsrv_addrlen(const struct logsrv *logsrv);
#endif /* _TYPES_LOG_H */
#include <types/acl.h>
#include <types/buffers.h>
#include <types/httperr.h>
+#include <types/log.h>
#include <types/protocols.h>
#include <types/session.h>
#include <types/server.h>
struct sockaddr_in tproxy_addr; /* non-local address we want to bind to for connect() */
#endif
struct proxy *next;
- struct sockaddr_in logsrv1, logsrv2; /* 2 syslog servers */
+ struct logsrv logsrv1, logsrv2; /* 2 syslog servers */
signed char logfac1, logfac2; /* log facility for both servers. -1 = disabled */
int loglev1, loglev2; /* log level for each server, 7 by default */
int to_log; /* things to be logged (LW_*) */
global.pidfile = strdup(args[1]);
}
else if (!strcmp(args[0], "log")) { /* syslog server address */
- struct sockaddr_in *sa;
+ struct logsrv logsrv;
int facility, level;
if (*(args[1]) == 0 || *(args[2]) == 0) {
}
}
- sa = str2sa(args[1]);
- if (!sa->sin_port)
- sa->sin_port = htons(SYSLOG_PORT);
+ if (args[1][0] == '/') {
+ logsrv.u.addr.sa_family = AF_UNIX;
+ logsrv.u.un = *str2sun(args[1]);
+ } else {
+ logsrv.u.addr.sa_family = AF_INET;
+ logsrv.u.in = *str2sa(args[1]);
+ if (!logsrv.u.in.sin_port)
+ logsrv.u.in.sin_port = htons(SYSLOG_PORT);
+ }
if (global.logfac1 == -1) {
- global.logsrv1 = *sa;
+ global.logsrv1 = logsrv;
global.logfac1 = facility;
global.loglev1 = level;
}
else if (global.logfac2 == -1) {
- global.logsrv2 = *sa;
+ global.logsrv2 = logsrv;
global.logfac2 = facility;
global.loglev2 = level;
}
newsrv->prev_state = newsrv->state;
}
else if (!strcmp(args[0], "log")) { /* syslog server address */
- struct sockaddr_in *sa;
+ struct logsrv logsrv;
int facility;
if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) {
}
}
- sa = str2sa(args[1]);
- if (!sa->sin_port)
- sa->sin_port = htons(SYSLOG_PORT);
+ if (args[1][0] == '/') {
+ logsrv.u.addr.sa_family = AF_UNIX;
+ logsrv.u.un = *str2sun(args[1]);
+ } else {
+ logsrv.u.addr.sa_family = AF_INET;
+ logsrv.u.in = *str2sa(args[1]);
+ if (!logsrv.u.in.sin_port) {
+ logsrv.u.in.sin_port =
+ htons(SYSLOG_PORT);
+ }
+ }
if (curproxy->logfac1 == -1) {
- curproxy->logsrv1 = *sa;
+ curproxy->logsrv1 = logsrv;
curproxy->logfac1 = facility;
curproxy->loglev1 = level;
}
else if (curproxy->logfac2 == -1) {
- curproxy->logsrv2 = *sa;
+ curproxy->logsrv2 = logsrv;
curproxy->logfac2 = facility;
curproxy->loglev2 = level;
}
#include <syslog.h>
#include <time.h>
#include <unistd.h>
+#include <errno.h>
#include <sys/time.h>
#include <types/log.h>
#include <types/session.h>
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL (0)
+#endif /* !MSG_NOSIGNAL */
const char *log_facilities[NB_LOG_FACILITIES] = {
"kern", "user", "mail", "daemon",
return facility;
}
+/*
+ * Return the length of the address endpoint, suitable for use with sendto().
+ */
+int logsrv_addrlen(const struct logsrv *logsrv)
+{
+#ifdef __SOCKADDR_COMMON
+ switch (logsrv->u.addr.sa_family) {
+ case AF_UNIX:
+ return sizeof(logsrv->u.un);
+ case AF_INET:
+ return sizeof(logsrv->u.in);
+ default:
+ break;
+ }
+#else /* !__SOCKADDR_COMMON */
+ switch (logsrv->u.addr.sa_family) {
+ case AF_UNIX:
+ return logsrv->u.un.sun_len;
+ case AF_INET:
+ return logsrv->u.in.sin_len;
+ default:
+ break;
+ }
+#endif /* !__SOCKADDR_COMMON */
+ return -1;
+}
/*
* This function sends a syslog message to both log servers of a proxy,
*/
void send_log(struct proxy *p, int level, const char *message, ...)
{
- static int logfd = -1; /* syslog UDP socket */
+ static int logfdunix = -1; /* syslog to AF_UNIX socket */
+ static int logfdinet = -1; /* syslog to AF_INET socket */
static long tvsec = -1; /* to force the string to be initialized */
va_list argp;
static char logmsg[MAX_SYSLOG_LEN];
static char *dataptr = NULL;
int fac_level;
int hdr_len, data_len;
- struct sockaddr_in *sa[2];
+ struct logsrv *logsrvs[2];
int facilities[2], loglevel[2];
+ int nblogger;
int nbloggers = 0;
char *log_ptr;
- if (logfd < 0) {
- if ((logfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
- return;
- /* we don't want to receive anything on this socket */
- setsockopt(logfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
- /* need for AIX which does not know about MSG_DONTWAIT */
- if (!MSG_DONTWAIT)
- fcntl(logfd, F_SETFL, O_NONBLOCK);
- shutdown(logfd, SHUT_RD); /* does nothing under Linux, maybe needed for others */
- }
-
if (level < 0 || progname == NULL || message == NULL)
return;
if (p == NULL) {
if (global.logfac1 >= 0) {
- sa[nbloggers] = &global.logsrv1;
+ logsrvs[nbloggers] = &global.logsrv1;
facilities[nbloggers] = global.logfac1;
loglevel[nbloggers] = global.loglev1;
nbloggers++;
}
if (global.logfac2 >= 0) {
- sa[nbloggers] = &global.logsrv2;
+ logsrvs[nbloggers] = &global.logsrv2;
facilities[nbloggers] = global.logfac2;
loglevel[nbloggers] = global.loglev2;
nbloggers++;
}
} else {
if (p->logfac1 >= 0) {
- sa[nbloggers] = &p->logsrv1;
+ logsrvs[nbloggers] = &p->logsrv1;
facilities[nbloggers] = p->logfac1;
loglevel[nbloggers] = p->loglev1;
nbloggers++;
}
if (p->logfac2 >= 0) {
- sa[nbloggers] = &p->logsrv2;
+ logsrvs[nbloggers] = &p->logsrv2;
facilities[nbloggers] = p->logfac2;
loglevel[nbloggers] = p->loglev2;
nbloggers++;
}
}
- while (nbloggers-- > 0) {
+ /* Lazily set up syslog sockets for protocol families of configured
+ * syslog servers. */
+ for (nblogger = 0; nblogger < nbloggers; nblogger++) {
+ const struct logsrv *logsrv = logsrvs[nblogger];
+ int proto, *plogfd;
+ if (logsrv->u.addr.sa_family == AF_UNIX) {
+ proto = 0;
+ plogfd = &logfdunix;
+ } else {
+ /* sa_family == AF_INET */
+ proto = IPPROTO_UDP;
+ plogfd = &logfdinet;
+ }
+ if (*plogfd >= 0) {
+ /* socket already created. */
+ continue;
+ }
+ if ((*plogfd = socket(logsrv->u.addr.sa_family, SOCK_DGRAM,
+ proto)) < 0) {
+ Alert("socket for logger #%d failed: %s (errno=%d)\n",
+ nblogger + 1, strerror(errno), errno);
+ return;
+ }
+ /* we don't want to receive anything on this socket */
+ setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
+ /* does nothing under Linux, maybe needed for others */
+ shutdown(*plogfd, SHUT_RD);
+ }
+
+ /* Send log messages to syslog server. */
+ for (nblogger = 0; nblogger < nbloggers; nblogger++) {
+ const struct logsrv *logsrv = logsrvs[nblogger];
+ int *plogfd = logsrv->u.addr.sa_family == AF_UNIX ?
+ &logfdunix : &logfdinet;
+ int sent;
+
/* we can filter the level of the messages that are sent to each logger */
- if (level > loglevel[nbloggers])
+ if (level > loglevel[nblogger])
continue;
/* For each target, we may have a different facility.
* time, we only change the facility in the pre-computed header,
* and we change the pointer to the header accordingly.
*/
- fac_level = (facilities[nbloggers] << 3) + level;
+ fac_level = (facilities[nblogger] << 3) + level;
log_ptr = logmsg + 3; /* last digit of the log level */
do {
*log_ptr = '0' + fac_level % 10;
*log_ptr = '<';
/* the total syslog message now starts at logptr, for dataptr+data_len-logptr */
-
-#ifndef MSG_NOSIGNAL
- sendto(logfd, log_ptr, dataptr + data_len - log_ptr, MSG_DONTWAIT,
- (struct sockaddr *)sa[nbloggers], sizeof(**sa));
-#else
- sendto(logfd, log_ptr, dataptr + data_len - log_ptr, MSG_DONTWAIT | MSG_NOSIGNAL,
- (struct sockaddr *)sa[nbloggers], sizeof(**sa));
-#endif
+ sent = sendto(*plogfd, log_ptr, dataptr + data_len - log_ptr,
+ MSG_DONTWAIT | MSG_NOSIGNAL, &logsrv->u.addr, logsrv_addrlen(logsrv));
+ if (sent < 0) {
+ Alert("sendto logger #%d failed: %s (errno=%d)\n",
+ nblogger, strerror(errno), errno);
+ }
}
}
return (n) ? ultoa_r(n, buffer, size) : (alt ? alt : "");
}
+/*
+ * converts <str> to a struct sockaddr_un* which is locally allocated.
+ * The format is "/path", where "/path" is a path to a UNIX domain socket.
+ */
+struct sockaddr_un *str2sun(char *str)
+{
+ static struct sockaddr_un sun;
+ int strsz; /* length included null */
+
+ memset(&sun, 0, sizeof(sun));
+ str = strdup(str);
+ if (str == NULL)
+ goto out_nofree;
+
+ strsz = strlen(str) + 1;
+ if (strsz > sizeof(sun.sun_path)) {
+ Alert("Socket path '%s' too long (max %d)\n",
+ str, sizeof(sun.sun_path) - 1);
+ goto out_nofree;
+ }
+
+#ifndef __SOCKADDR_COMMON
+ sun.sun_len = sizeof(sun);
+#endif /* !__SOCKADDR_COMMON */
+ sun.sun_family = AF_UNIX;
+ memcpy(sun.sun_path, str, strsz);
+
+ free(str);
+ out_nofree:
+ return &sun;
+}
/*
* Returns non-zero if character <s> is a hex digit (0-9, a-f, A-F), else zero.
else
sa.sin_addr = *(struct in_addr *) *(he->h_addr_list);
}
+#ifndef __SOCKADDR_COMMON
+ sa.sin_len = sizeof(sa);
+#endif /* !__SOCKADDR_COMMON */
sa.sin_port = htons(port);
sa.sin_family = AF_INET;