logger.c revision addab137cd8d318e4f543ca56018ee23d51aaca9
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering/***
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering This file is part of systemd.
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering Copyright 2010 Lennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering systemd is free software; you can redistribute it and/or modify it
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering under the terms of the GNU General Public License as published by
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering the Free Software Foundation; either version 2 of the License, or
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering (at your option) any later version.
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering systemd is distributed in the hope that it will be useful, but
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering General Public License for more details.
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering You should have received a copy of the GNU General Public License
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering***/
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include <sys/socket.h>
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include <sys/types.h>
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include <assert.h>
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include <time.h>
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include <string.h>
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include <stdio.h>
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include <errno.h>
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include <unistd.h>
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include <sys/poll.h>
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include <sys/epoll.h>
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include <sys/un.h>
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include <fcntl.h>
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include "util.h"
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include "log.h"
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include "list.h"
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include "sd-daemon.h"
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#include "tcpwrap.h"
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#define STREAMS_MAX 256
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#define SERVER_FD_MAX 16
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering#define TIMEOUT ((int) (10*MSEC_PER_SEC))
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poetteringtypedef struct Stream Stream;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poetteringtypedef struct Server {
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering int syslog_fd;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering int kmsg_fd;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering int epoll_fd;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering unsigned n_server_fd;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering LIST_HEAD(Stream, streams);
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering unsigned n_streams;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering} Server;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poetteringtypedef enum StreamTarget {
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering STREAM_SYSLOG,
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering STREAM_KMSG
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering} StreamTarget;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poetteringtypedef enum StreamState {
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering STREAM_TARGET,
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering STREAM_PRIORITY,
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering STREAM_PROCESS,
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering STREAM_PREFIX,
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering STREAM_RUNNING
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering} StreamState;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poetteringstruct Stream {
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering Server *server;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering StreamState state;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering int fd;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering StreamTarget target;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering int priority;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering char *process;
b040723ea412209e0edf54647fa5aa4287411507Jan Engelhardt pid_t pid;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering uid_t uid;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering gid_t gid;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering bool prefix;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering char buffer[LINE_MAX];
e0e009c067aa7237f9683c46e5845bbb11ec67c2Jan Engelhardt size_t length;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering LIST_FIELDS(Stream, stream);
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering};
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poetteringstatic int stream_log(Stream *s, char *p, usec_t ts) {
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering char header_priority[16], header_time[64], header_pid[16];
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering struct iovec iovec[5];
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering int priority;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
b040723ea412209e0edf54647fa5aa4287411507Jan Engelhardt assert(s);
e0e009c067aa7237f9683c46e5845bbb11ec67c2Jan Engelhardt assert(p);
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering priority = s->priority;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering if (s->prefix &&
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering p[0] == '<' &&
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering p[1] >= '0' && p[1] <= '7' &&
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering p[2] == '>') {
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering /* Detected priority prefix */
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering priority = LOG_MAKEPRI(LOG_FAC(priority), (p[1] - '0'));
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering p += 3;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering }
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering if (*p == 0)
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering return 0;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering /*
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering * The format glibc uses to talk to the syslog daemon is:
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering *
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering * <priority>time process[pid]: msg
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering *
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering * The format the kernel uses is:
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering *
e0e009c067aa7237f9683c46e5845bbb11ec67c2Jan Engelhardt * <priority>msg\n
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering *
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering * We extend the latter to include the process name and pid.
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering */
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering snprintf(header_priority, sizeof(header_priority), "<%i>",
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering s->target == STREAM_SYSLOG ? priority : LOG_PRI(priority));
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering char_array_0(header_priority);
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering if (s->target == STREAM_SYSLOG) {
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering time_t t;
494a66821815e8109afa136bd42818b85da38c09Jan Engelhardt struct tm *tm;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
6bb648a16ae4a682ad4784412af706d2e6a3e4daTom Gundersen t = (time_t) (ts / USEC_PER_SEC);
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering if (!(tm = localtime(&t)))
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering return -EINVAL;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering return -EINVAL;
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering }
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) s->pid);
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering char_array_0(header_pid);
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering zero(iovec);
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering IOVEC_SET_STRING(iovec[0], header_priority);
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering
99e0f83e0bcc54dbc2b3a4f3fdd15fcd3033b21eLennart Poettering if (s->target == STREAM_SYSLOG) {
struct msghdr msghdr;
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
} control;
struct ucred *ucred;
zero(control);
control.cmsghdr.cmsg_level = SOL_SOCKET;
control.cmsghdr.cmsg_type = SCM_CREDENTIALS;
control.cmsghdr.cmsg_len = CMSG_LEN(sizeof(struct ucred));
ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
ucred->pid = s->pid;
ucred->uid = s->uid;
ucred->gid = s->gid;
IOVEC_SET_STRING(iovec[1], header_time);
IOVEC_SET_STRING(iovec[2], s->process);
IOVEC_SET_STRING(iovec[3], header_pid);
IOVEC_SET_STRING(iovec[4], p);
zero(msghdr);
msghdr.msg_iov = iovec;
msghdr.msg_iovlen = ELEMENTSOF(iovec);
msghdr.msg_control = &control;
msghdr.msg_controllen = control.cmsghdr.cmsg_len;
if (sendmsg(s->server->syslog_fd, &msghdr, MSG_NOSIGNAL) < 0)
return -errno;
} else if (s->target == STREAM_KMSG) {
IOVEC_SET_STRING(iovec[1], s->process);
IOVEC_SET_STRING(iovec[2], header_pid);
IOVEC_SET_STRING(iovec[3], p);
IOVEC_SET_STRING(iovec[4], (char*) "\n");
if (writev(s->server->kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0)
return -errno;
} else
assert_not_reached("Unknown log target");
return 0;
}
static int stream_line(Stream *s, char *p, usec_t ts) {
int r;
assert(s);
assert(p);
p = strstrip(p);
switch (s->state) {
case STREAM_TARGET:
if (streq(p, "syslog"))
s->target = STREAM_SYSLOG;
else if (streq(p, "kmsg")) {
if (s->server->kmsg_fd >= 0 && s->uid == 0)
s->target = STREAM_KMSG;
else {
log_warning("/dev/kmsg logging not available.");
return -EPERM;
}
} else {
log_warning("Failed to parse log target line.");
return -EBADMSG;
}
s->state = STREAM_PRIORITY;
return 0;
case STREAM_PRIORITY:
if ((r = safe_atoi(p, &s->priority)) < 0) {
log_warning("Failed to parse log priority line: %m");
return r;
}
if (s->priority < 0) {
log_warning("Log priority negative: %m");
return -ERANGE;
}
s->state = STREAM_PROCESS;
return 0;
case STREAM_PROCESS:
if (!(s->process = strdup(p)))
return -ENOMEM;
s->state = STREAM_PREFIX;
return 0;
case STREAM_PREFIX:
if ((r = parse_boolean(p)) < 0)
return r;
s->prefix = r;
s->state = STREAM_RUNNING;
return 0;
case STREAM_RUNNING:
return stream_log(s, p, ts);
}
assert_not_reached("Unknown stream state");
}
static int stream_scan(Stream *s, usec_t ts) {
char *p;
size_t remaining;
int r = 0;
assert(s);
p = s->buffer;
remaining = s->length;
for (;;) {
char *newline;
if (!(newline = memchr(p, '\n', remaining)))
break;
*newline = 0;
if ((r = stream_line(s, p, ts)) >= 0) {
remaining -= newline-p+1;
p = newline+1;
}
}
if (p > s->buffer) {
memmove(s->buffer, p, remaining);
s->length = remaining;
}
return r;
}
static int stream_process(Stream *s, usec_t ts) {
ssize_t l;
int r;
assert(s);
if ((l = read(s->fd, s->buffer+s->length, LINE_MAX-s->length)) < 0) {
if (errno == EAGAIN)
return 0;
log_warning("Failed to read from stream: %m");
return -1;
}
if (l == 0)
return 0;
s->length += l;
r = stream_scan(s, ts);
if (r < 0)
return r;
return 1;
}
static void stream_free(Stream *s) {
assert(s);
if (s->server) {
assert(s->server->n_streams > 0);
s->server->n_streams--;
LIST_REMOVE(Stream, stream, s->server->streams, s);
}
if (s->fd >= 0) {
if (s->server)
epoll_ctl(s->server->epoll_fd, EPOLL_CTL_DEL, s->fd, NULL);
close_nointr_nofail(s->fd);
}
free(s->process);
free(s);
}
static int stream_new(Server *s, int server_fd) {
Stream *stream;
int fd;
struct ucred ucred;
socklen_t len = sizeof(ucred);
struct epoll_event ev;
int r;
assert(s);
if ((fd = accept4(server_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC)) < 0)
return -errno;
if (s->n_streams >= STREAMS_MAX) {
log_warning("Too many connections, refusing connection.");
close_nointr_nofail(fd);
return 0;
}
if (!socket_tcpwrap(fd, "systemd-logger")) {
close_nointr_nofail(fd);
return 0;
}
if (!(stream = new0(Stream, 1))) {
close_nointr_nofail(fd);
return -ENOMEM;
}
stream->fd = fd;
if (getsockopt(stream->fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) {
r = -errno;
goto fail;
}
if (shutdown(fd, SHUT_WR) < 0) {
r = -errno;
goto fail;
}
zero(ev);
ev.data.ptr = stream;
ev.events = EPOLLIN;
if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
r = -errno;
goto fail;
}
stream->pid = ucred.pid;
stream->uid = ucred.uid;
stream->gid = ucred.gid;
stream->server = s;
LIST_PREPEND(Stream, stream, s->streams, stream);
s->n_streams ++;
return 0;
fail:
stream_free(stream);
return r;
}
static void server_done(Server *s) {
unsigned i;
assert(s);
while (s->streams)
stream_free(s->streams);
for (i = 0; i < s->n_server_fd; i++)
close_nointr_nofail(SD_LISTEN_FDS_START+i);
if (s->syslog_fd >= 0)
close_nointr_nofail(s->syslog_fd);
if (s->epoll_fd >= 0)
close_nointr_nofail(s->epoll_fd);
if (s->kmsg_fd >= 0)
close_nointr_nofail(s->kmsg_fd);
}
static int server_init(Server *s, unsigned n_sockets) {
int r;
unsigned i;
union {
struct sockaddr sa;
struct sockaddr_un un;
} sa;
assert(s);
assert(n_sockets > 0);
zero(*s);
s->n_server_fd = n_sockets;
s->syslog_fd = -1;
s->kmsg_fd = -1;
if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) {
r = -errno;
log_error("Failed to create epoll object: %m");
goto fail;
}
for (i = 0; i < n_sockets; i++) {
struct epoll_event ev;
int fd;
fd = SD_LISTEN_FDS_START+i;
if ((r = sd_is_socket(fd, AF_UNSPEC, SOCK_STREAM, 1)) < 0) {
log_error("Failed to determine file descriptor type: %s", strerror(-r));
goto fail;
}
if (!r) {
log_error("Wrong file descriptor type.");
r = -EINVAL;
goto fail;
}
/* We use ev.data.ptr instead of ev.data.fd here,
* since on 64bit archs fd is 32bit while a pointer is
* 64bit. To make sure we can easily distuingish fd
* values and pointer values we want to make sure to
* write the full field unconditionally. */
zero(ev);
ev.events = EPOLLIN;
ev.data.ptr = INT_TO_PTR(fd);
if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
r = -errno;
log_error("Failed to add server fd to epoll object: %m");
goto fail;
}
}
if ((s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
r = -errno;
log_error("Failed to create log fd: %m");
goto fail;
}
zero(sa);
sa.un.sun_family = AF_UNIX;
strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
if (connect(s->syslog_fd, &sa.sa, sizeof(sa)) < 0) {
r = -errno;
log_error("Failed to connect log socket to /dev/log: %m");
goto fail;
}
/* /dev/kmsg logging is strictly optional */
if ((s->kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0)
log_warning("Failed to open /dev/kmsg for logging, disabling kernel log buffer support: %m");
return 0;
fail:
server_done(s);
return r;
}
static int process_event(Server *s, struct epoll_event *ev) {
int r;
assert(s);
/* Yes, this is a bit ugly, we assume that that valid pointers
* are > SD_LISTEN_FDS_START+SERVER_FD_MAX. Which is certainly
* true on Linux (and probably most other OSes, too, since the
* first 4k usually are part of a seperate null pointer
* dereference page. */
if (PTR_TO_INT(ev->data.ptr) >= SD_LISTEN_FDS_START &&
PTR_TO_INT(ev->data.ptr) < SD_LISTEN_FDS_START+(int)s->n_server_fd) {
if (ev->events != EPOLLIN) {
log_info("Got invalid event from epoll. (1)");
return -EIO;
}
if ((r = stream_new(s, PTR_TO_INT(ev->data.ptr))) < 0) {
log_info("Failed to accept new connection: %s", strerror(-r));
return r;
}
} else {
usec_t ts;
Stream *stream = ev->data.ptr;
ts = now(CLOCK_REALTIME);
if (!(ev->events & EPOLLIN)) {
log_info("Got invalid event from epoll. (2)");
stream_free(stream);
return 0;
}
if ((r = stream_process(stream, ts)) <= 0) {
if (r < 0)
log_info("Got error on stream: %s", strerror(-r));
stream_free(stream);
return 0;
}
}
return 0;
}
int main(int argc, char *argv[]) {
Server server;
int r = 3, n;
if (getppid() != 1) {
log_error("This program should be invoked by init only.");
return 1;
}
if (argc > 1) {
log_error("This program does not take arguments.");
return 1;
}
log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
log_parse_environment();
log_open();
if ((n = sd_listen_fds(true)) < 0) {
log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
return 1;
}
if (n <= 0 || n > SERVER_FD_MAX) {
log_error("No or too many file descriptors passed.");
return 2;
}
if (server_init(&server, (unsigned) n) < 0)
return 3;
log_debug("systemd-logger running as pid %lu", (unsigned long) getpid());
sd_notify(false,
"READY=1\n"
"STATUS=Processing requests...");
for (;;) {
struct epoll_event event;
int k;
if ((k = epoll_wait(server.epoll_fd,
&event, 1,
server.n_streams <= 0 ? TIMEOUT : -1)) < 0) {
if (errno == EINTR)
continue;
log_error("epoll_wait() failed: %m");
goto fail;
}
if (k <= 0)
break;
if (process_event(&server, &event) < 0)
goto fail;
}
r = 0;
log_debug("systemd-logger stopped as pid %lu", (unsigned long) getpid());
fail:
sd_notify(false,
"STATUS=Shutting down...");
server_done(&server);
return r;
}