X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flogger.c;h=104d7c385b757d5e63e484577f06d3ff16425874;hp=95d58fc1b3d875cca408f159202621e86be91fdc;hb=14f3c8252b4dd73bff778b5af9f872e929bd566c;hpb=6309077509a8221b1b298ba9dd05143908f9939c diff --git a/src/logger.c b/src/logger.c index 95d58fc1b..104d7c385 100644 --- a/src/logger.c +++ b/src/logger.c @@ -38,10 +38,9 @@ #include "sd-daemon.h" #include "tcpwrap.h" -#define STREAM_BUFFER 2048 -#define STREAMS_MAX 256 +#define STREAMS_MAX 4096 #define SERVER_FD_MAX 16 -#define TIMEOUT ((int) (10*MSEC_PER_SEC)) +#define TIMEOUT ((int) (5*60*MSEC_PER_SEC)) typedef struct Stream Stream; @@ -52,6 +51,8 @@ typedef struct Server { unsigned n_server_fd; + bool syslog_is_stream; + LIST_HEAD(Stream, streams); unsigned n_streams; } Server; @@ -83,9 +84,10 @@ struct Stream { uid_t uid; gid_t gid; - bool prefix; + bool prefix:1; + bool tee_console:1; - char buffer[STREAM_BUFFER]; + char buffer[LINE_MAX]; size_t length; LIST_FIELDS(Stream, stream); @@ -173,14 +175,48 @@ static int stream_log(Stream *s, char *p, usec_t ts) { 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); @@ -193,6 +229,20 @@ static int stream_log(Stream *s, char *p, usec_t ts) { } else assert_not_reached("Unknown log target"); + if (s->tee_console) { + int console; + + if ((console = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC)) >= 0) { + IOVEC_SET_STRING(iovec[0], s->process); + IOVEC_SET_STRING(iovec[1], header_pid); + IOVEC_SET_STRING(iovec[2], p); + IOVEC_SET_STRING(iovec[3], (char*) "\n"); + + writev(console, iovec, 4); + } + + } + return 0; } @@ -207,9 +257,9 @@ static int stream_line(Stream *s, char *p, usec_t ts) { switch (s->state) { case STREAM_TARGET: - if (streq(p, "syslog")) + if (streq(p, "syslog") || streq(p, "syslog+console")) s->target = STREAM_SYSLOG; - else if (streq(p, "kmsg")) { + else if (streq(p, "kmsg") || streq(p, "kmsg+console")) { if (s->server->kmsg_fd >= 0 && s->uid == 0) s->target = STREAM_KMSG; @@ -221,6 +271,10 @@ static int stream_line(Stream *s, char *p, usec_t ts) { log_warning("Failed to parse log target line."); return -EBADMSG; } + + if (endswith(p, "+console")) + s->tee_console = true; + s->state = STREAM_PRIORITY; return 0; @@ -297,13 +351,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: %m"); - return -1; + return -errno; } @@ -466,7 +520,7 @@ static int server_init(Server *s, unsigned n_sockets) { /* 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 + * 64bit. To make sure we can easily distinguish fd * values and pointer values we want to make sure to * write the full field unconditionally. */ @@ -480,22 +534,35 @@ static int server_init(Server *s, unsigned n_sockets) { } } - 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) { + 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: %m"); + 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_warning("Failed to open /dev/kmsg for logging, disabling kernel log buffer support: %m"); @@ -515,7 +582,7 @@ 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_INT(ev->data.ptr) >= SD_LISTEN_FDS_START && @@ -558,16 +625,16 @@ 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 1; + return EXIT_FAILURE; } if (argc > 1) { log_error("This program does not take arguments."); - return 1; + return EXIT_FAILURE; } log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); @@ -576,16 +643,16 @@ int main(int argc, char *argv[]) { 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()); @@ -615,9 +682,9 @@ int main(int argc, char *argv[]) { goto fail; } - r = 0; + r = EXIT_SUCCESS; - log_info("systemd-logger stopped as pid %lu", (unsigned long) getpid()); + log_debug("systemd-logger stopped as pid %lu", (unsigned long) getpid()); fail: sd_notify(false,