handle loss of syslog socket connection

when traditional syslogd implementations are restarted, the old server
socket ceases to exist and a new unix socket with the same pathname is
created. when this happens, the default destination address associated
with the client socket via connect is no longer valid, and attempts to
send produce errors. this happens despite the socket being datagram
type, and is in contrast to the behavior that would be seen with an IP
datagram (UDP) socket.

in order to avoid a situation where the application is unable to send
further syslog messages without calling closelog, this patch makes
syslog attempt to reconnect the socket when send returns an error
indicating a lost connection.

additionally, initial failure to connect the socket no longer results
in the socket being closed. this ensures that an application which
calls openlog to reserve the socket file descriptor will not run into
a situation where transient connection failure (e.g. due to syslogd
restart) prevents fd reservation. however, applications which may be
unable to connect the socket later (e.g. due to chroot, restricted
permissions, seccomp, etc.) will still fail to log if the syslog
socket cannot be connected at openlog time or if it has to be
reconnected later.
This commit is contained in:
Rich Felker 2015-07-09 18:36:02 +00:00
parent 11894f6d3a
commit 0f9c2666ac

View File

@ -48,12 +48,8 @@ void closelog(void)
static void __openlog() static void __openlog()
{ {
int fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); log_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
if (fd < 0) return; if (log_fd >= 0) connect(log_fd, (void *)&log_addr, sizeof log_addr);
if (connect(fd, (void *)&log_addr, sizeof log_addr) < 0)
close(fd);
else
log_fd = fd;
} }
void openlog(const char *ident, int opt, int facility) void openlog(const char *ident, int opt, int facility)
@ -78,6 +74,11 @@ void openlog(const char *ident, int opt, int facility)
pthread_setcancelstate(cs, 0); pthread_setcancelstate(cs, 0);
} }
static int is_lost_conn(int e)
{
return e==ECONNREFUSED || e==ECONNRESET || e==ENOTCONN || e==EPIPE;
}
static void _vsyslog(int priority, const char *message, va_list ap) static void _vsyslog(int priority, const char *message, va_list ap)
{ {
char timebuf[16]; char timebuf[16];
@ -107,7 +108,10 @@ static void _vsyslog(int priority, const char *message, va_list ap)
if (l2 >= sizeof buf - l) l = sizeof buf - 1; if (l2 >= sizeof buf - l) l = sizeof buf - 1;
else l += l2; else l += l2;
if (buf[l-1] != '\n') buf[l++] = '\n'; if (buf[l-1] != '\n') buf[l++] = '\n';
if (send(log_fd, buf, l, 0) < 0 && (log_opt & LOG_CONS)) { if (send(log_fd, buf, l, 0) < 0 && (!is_lost_conn(errno)
|| connect(log_fd, (void *)&log_addr, sizeof log_addr) < 0
|| send(log_fd, buf, l, 0) < 0)
&& (log_opt & LOG_CONS)) {
fd = open("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); fd = open("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
if (fd >= 0) { if (fd >= 0) {
dprintf(fd, "%.*s", l-hlen, buf+hlen); dprintf(fd, "%.*s", l-hlen, buf+hlen);