X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=logger.c;h=af3293bd7e1812f6b46464e736bd5a8f5deb6b11;hp=c7c4cb83d2158d7c455142b431662cc6525f3a54;hb=47be870bd83fb3719dffc3ee9348a409ab762a14;hpb=b52429d4e0de52b89c7d043164aa1ac720e9179f diff --git a/logger.c b/logger.c index c7c4cb83d..af3293bd7 100644 --- a/logger.c +++ b/logger.c @@ -1,5 +1,24 @@ /*-*- Mode: C; c-basic-offset: 8 -*-*/ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + #include #include #include @@ -11,6 +30,7 @@ #include #include #include +#include #include "util.h" #include "log.h" @@ -25,7 +45,8 @@ typedef struct Stream Stream; typedef struct Server { - int log_fd; + int syslog_fd; + int kmsg_fd; int epoll_fd; unsigned n_server_fd; @@ -35,25 +56,33 @@ typedef struct Server { } Server; typedef enum StreamState { + STREAM_LOG_TARGET, STREAM_PRIORITY, STREAM_PROCESS, STREAM_RUNNING } StreamState; +typedef enum LogTarget { + LOG_TARGET_SYSLOG, + LOG_TARGET_KMSG +} LogTarget; + struct Stream { Server *server; StreamState state; int fd; + + LogTarget target; int priority; char *process; + pid_t pid; + uid_t uid; char buffer[STREAM_BUFFER]; size_t length; - pid_t pid; - LIST_FIELDS(Stream, stream); }; @@ -66,8 +95,6 @@ struct Stream { static int stream_log(Stream *s, char *p, usec_t timestamp) { char header_priority[16], header_time[64], header_pid[16]; - time_t t; - struct tm *tm; struct msghdr msghdr; struct iovec iovec[5]; @@ -78,37 +105,62 @@ static int stream_log(Stream *s, char *p, usec_t timestamp) { return 0; /* - * The format glibc uses is: + * The format glibc uses to talk to the syslog daemon is: + * + * time process[pid]: msg * - * time process[pid]: msg + * The format the kernel uses is: + * + * msg\n + * + * We extend the latter to include the process name and pid. */ - snprintf(header_priority, sizeof(header_priority), "<%i>", s->priority); + snprintf(header_priority, sizeof(header_priority), "<%i>", + s->target == LOG_TARGET_SYSLOG ? s->priority : LOG_PRI(s->priority)); char_array_0(header_priority); - t = (time_t) (timestamp / USEC_PER_SEC); - if (!(tm = localtime(&t))) - return -EINVAL; + if (s->target == LOG_TARGET_SYSLOG) { + time_t t; + struct tm *tm; - if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0) - return -EINVAL; + t = (time_t) (timestamp / USEC_PER_SEC); + if (!(tm = localtime(&t))) + return -EINVAL; + + if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0) + return -EINVAL; + } snprintf(header_pid, sizeof(header_pid), "[%llu]: ", (unsigned long long) s->pid); char_array_0(header_pid); zero(iovec); IOVEC_SET_STRING(iovec[0], header_priority); - 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); + if (s->target == LOG_TARGET_SYSLOG) { + 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); - if (sendmsg(s->server->log_fd, &msghdr, MSG_NOSIGNAL) < 0) - return -errno; + zero(msghdr); + msghdr.msg_iov = iovec; + msghdr.msg_iovlen = ELEMENTSOF(iovec); + + if (sendmsg(s->server->syslog_fd, &msghdr, MSG_NOSIGNAL) < 0) + return -errno; + + } else if (s->target == LOG_TARGET_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; } @@ -123,12 +175,34 @@ static int stream_line(Stream *s, char *p, usec_t timestamp) { switch (s->state) { + case STREAM_LOG_TARGET: + if (streq(p, "syslog")) + s->target = LOG_TARGET_SYSLOG; + else if (streq(p, "kmsg")) { + + if (s->server->kmsg_fd >= 0 && s->uid == 0) + s->target = LOG_TARGET_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) + if ((r = safe_atoi(p, &s->priority)) < 0) { + log_warning("Failed to parse log priority line: %s", strerror(errno)); return r; + } - if (s->priority < 0) + if (s->priority < 0) { + log_warning("Log priority negative: %s", strerror(errno)); return -ERANGE; + } s->state = STREAM_PROCESS; return 0; @@ -264,13 +338,14 @@ static int stream_new(Server *s, int server_fd) { zero(ev); ev.data.ptr = stream; - ev.events = POLLIN; + 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->server = s; LIST_PREPEND(Stream, stream, s->streams, stream); @@ -336,11 +411,14 @@ static void server_done(Server *s) { for (i = 0; i < s->n_server_fd; i++) assert_se(close_nointr(SERVER_FD_START+i) == 0); - if (s->log_fd >= 0) - assert_se(close_nointr(s->log_fd) == 0); + if (s->syslog_fd >= 0) + assert_se(close_nointr(s->syslog_fd) == 0); if (s->epoll_fd >= 0) assert_se(close_nointr(s->epoll_fd) == 0); + + if (s->kmsg_fd >= 0) + assert_se(close_nointr(s->kmsg_fd) == 0); } static int server_init(Server *s, unsigned n_sockets) { @@ -357,7 +435,8 @@ static int server_init(Server *s, unsigned n_sockets) { zero(*s); s->n_server_fd = n_sockets; - s->log_fd = -1; + s->syslog_fd = -1; + s->kmsg_fd = -1; if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) { r = -errno; @@ -369,7 +448,7 @@ static int server_init(Server *s, unsigned n_sockets) { struct epoll_event ev; zero(ev); - ev.events = POLLIN; + ev.events = EPOLLIN; ev.data.ptr = UINT_TO_PTR(SERVER_FD_START+i); if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, SERVER_FD_START+i, &ev) < 0) { r = -errno; @@ -378,7 +457,7 @@ static int server_init(Server *s, unsigned n_sockets) { } } - if ((s->log_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) { + if ((s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) { r = -errno; log_error("Failed to create log fd: %s", strerror(errno)); goto fail; @@ -388,12 +467,16 @@ static int server_init(Server *s, unsigned n_sockets) { sa.un.sun_family = AF_UNIX; strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path)); - if (connect(s->log_fd, &sa.sa, sizeof(sa)) < 0) { + if (connect(s->syslog_fd, &sa.sa, sizeof(sa)) < 0) { r = -errno; log_error("Failed to connect log socket to /dev/log: %s", strerror(errno)); goto fail; } + /* /dev/kmsg logging is strictly optional */ + if ((s->kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) + log_debug("Failed to open /dev/kmsg for logging, disabling kernel log buffer support: %s", strerror(errno)); + return 0; fail: @@ -415,7 +498,7 @@ static int process_event(Server *s, struct epoll_event *ev) { if (PTR_TO_UINT(ev->data.ptr) >= SERVER_FD_START && PTR_TO_UINT(ev->data.ptr) < SERVER_FD_START+s->n_server_fd) { - if (ev->events != POLLIN) { + if (ev->events != EPOLLIN) { log_info("Got invalid event from epoll. (1)"); return -EIO; } @@ -431,7 +514,7 @@ static int process_event(Server *s, struct epoll_event *ev) { timestamp = now(CLOCK_REALTIME); - if (!(ev->events & POLLIN)) { + if (!(ev->events & EPOLLIN)) { log_info("Got invalid event from epoll. (3)"); stream_free(stream); return 0; @@ -463,12 +546,11 @@ int main(int argc, char *argv[]) { if (server_init(&server, n) < 0) return 2; - for (;;) { struct epoll_event event; - int n; + int k; - if ((n = epoll_wait(server.epoll_fd, + if ((k = epoll_wait(server.epoll_fd, &event, 1, server.n_streams <= 0 ? TIMEOUT : -1)) < 0) { @@ -479,7 +561,7 @@ int main(int argc, char *argv[]) { goto fail; } - if (n <= 0) + if (k <= 0) break; if ((r = process_event(&server, &event)) < 0)