X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flogger.c;h=482ec41244502298a1b12b877367a00b37a77ece;hp=48eee6cd12958bcc5617b2cf69abc0480a7fe6ea;hb=2d011a7923a35d8aef49059c2e9010bf1d6a4bac;hpb=8c47c7325fa1ab72febf807f8831ff24c75fbf45 diff --git a/src/logger.c b/src/logger.c index 48eee6cd1..482ec4124 100644 --- a/src/logger.c +++ b/src/logger.c @@ -1,4 +1,4 @@ -/*-*- Mode: C; c-basic-offset: 8 -*-*/ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ /*** This file is part of systemd. @@ -36,8 +36,8 @@ #include "log.h" #include "list.h" #include "sd-daemon.h" +#include "tcpwrap.h" -#define STREAM_BUFFER 2048 #define STREAMS_MAX 256 #define SERVER_FD_MAX 16 #define TIMEOUT ((int) (10*MSEC_PER_SEC)) @@ -51,6 +51,8 @@ typedef struct Server { unsigned n_server_fd; + bool syslog_is_stream; + LIST_HEAD(Stream, streams); unsigned n_streams; } Server; @@ -80,10 +82,11 @@ struct Stream { char *process; pid_t pid; uid_t uid; + gid_t gid; bool prefix; - char buffer[STREAM_BUFFER]; + char buffer[LINE_MAX]; size_t length; LIST_FIELDS(Stream, stream); @@ -142,7 +145,7 @@ static int stream_log(Stream *s, char *p, usec_t ts) { return -EINVAL; } - snprintf(header_pid, sizeof(header_pid), "[%llu]: ", (unsigned long long) s->pid); + snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) s->pid); char_array_0(header_pid); zero(iovec); @@ -150,18 +153,69 @@ static int stream_log(Stream *s, char *p, usec_t ts) { 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); + /* When using syslog via SOCK_STREAM separate the messages by NUL chars */ + if (s->server->syslog_is_stream) + iovec[4].iov_len++; + 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; + for (;;) { + ssize_t n; + + if ((n = sendmsg(s->server->syslog_fd, &msghdr, MSG_NOSIGNAL)) < 0) { + + if (errno == ESRCH) { + pid_t our_pid; + + /* Hmm, maybe the process this + * line originates from is + * dead? Then let's patch in + * our own pid and retry, + * since we have nothing + * better */ + + our_pid = getpid(); + + if (ucred->pid != our_pid) { + ucred->pid = our_pid; + continue; + } + } + + return -errno; + } + + if (!s->server->syslog_is_stream || + (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec))) + break; + + IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n); + } } else if (s->target == STREAM_KMSG) { IOVEC_SET_STRING(iovec[1], s->process); @@ -207,12 +261,12 @@ static int stream_line(Stream *s, char *p, usec_t ts) { case STREAM_PRIORITY: if ((r = safe_atoi(p, &s->priority)) < 0) { - log_warning("Failed to parse log priority line: %s", strerror(errno)); + log_warning("Failed to parse log priority line: %m"); return r; } if (s->priority < 0) { - log_warning("Log priority negative: %s", strerror(errno)); + log_warning("Log priority negative: %m"); return -ERANGE; } @@ -278,13 +332,13 @@ static int stream_process(Stream *s, usec_t ts) { int r; assert(s); - if ((l = read(s->fd, s->buffer+s->length, STREAM_BUFFER-s->length)) < 0) { + 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: %s", strerror(errno)); - return -1; + log_warning("Failed to read from stream: %m"); + return -errno; } @@ -340,6 +394,11 @@ static int stream_new(Server *s, int server_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; @@ -367,6 +426,7 @@ static int stream_new(Server *s, int server_fd) { stream->pid = ucred.pid; stream->uid = ucred.uid; + stream->gid = ucred.gid; stream->server = s; LIST_PREPEND(Stream, stream, s->streams, stream); @@ -418,7 +478,7 @@ static int server_init(Server *s, unsigned n_sockets) { if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) { r = -errno; - log_error("Failed to create epoll object: %s", strerror(errno)); + log_error("Failed to create epoll object: %m"); goto fail; } @@ -439,35 +499,54 @@ static int server_init(Server *s, unsigned n_sockets) { 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 = UINT_TO_PTR(fd); + 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: %s", strerror(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: %s", strerror(errno)); - 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) { + if ((s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) { r = -errno; - log_error("Failed to connect log socket to /dev/log: %s", strerror(errno)); + log_error("Failed to create log fd: %m"); goto fail; } + if (connect(s->syslog_fd, &sa.sa, sizeof(sa)) < 0) { + close_nointr_nofail(s->syslog_fd); + + if ((s->syslog_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0)) < 0) { + r = -errno; + log_error("Failed to create log fd: %m"); + goto fail; + } + + 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; + } + + s->syslog_is_stream = true; + } else + s->syslog_is_stream = false; + /* /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)); + log_warning("Failed to open /dev/kmsg for logging, disabling kernel log buffer support: %m"); return 0; @@ -484,18 +563,18 @@ static int process_event(Server *s, struct epoll_event *ev) { /* 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 + * first 4k usually are part of a separate null pointer * dereference page. */ - if (PTR_TO_UINT(ev->data.ptr) >= SD_LISTEN_FDS_START && - PTR_TO_UINT(ev->data.ptr) < SD_LISTEN_FDS_START+s->n_server_fd) { + 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_UINT(ev->data.ptr))) < 0) { + if ((r = stream_new(s, PTR_TO_INT(ev->data.ptr))) < 0) { log_info("Failed to accept new connection: %s", strerror(-r)); return r; } @@ -527,25 +606,36 @@ static int process_event(Server *s, struct epoll_event *ev) { int main(int argc, char *argv[]) { Server server; - int r = 3, n; + int r = EXIT_FAILURE, n; + + if (getppid() != 1) { + log_error("This program should be invoked by init only."); + return EXIT_FAILURE; + } + + if (argc > 1) { + log_error("This program does not take arguments."); + return EXIT_FAILURE; + } log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); log_parse_environment(); - - log_info("systemd-logger running as pid %llu", (unsigned long long) getpid()); + log_open(); if ((n = sd_listen_fds(true)) < 0) { log_error("Failed to read listening file descriptors from environment: %s", strerror(-r)); - return 1; + return EXIT_FAILURE; } if (n <= 0 || n > SERVER_FD_MAX) { log_error("No or too many file descriptors passed."); - return 2; + return EXIT_FAILURE; } if (server_init(&server, (unsigned) n) < 0) - return 3; + return EXIT_FAILURE; + + log_debug("systemd-logger running as pid %lu", (unsigned long) getpid()); sd_notify(false, "READY=1\n" @@ -562,17 +652,20 @@ int main(int argc, char *argv[]) { if (errno == EINTR) continue; - log_error("epoll_wait() failed: %s", strerror(errno)); + log_error("epoll_wait() failed: %m"); goto fail; } if (k <= 0) break; - if ((k = process_event(&server, &event)) < 0) + if (process_event(&server, &event) < 0) goto fail; } - r = 0; + + r = EXIT_SUCCESS; + + log_debug("systemd-logger stopped as pid %lu", (unsigned long) getpid()); fail: sd_notify(false, @@ -580,7 +673,5 @@ fail: server_done(&server); - log_info("systemd-logger stopped as pid %llu", (unsigned long long) getpid()); - return r; }