chiark / gitweb /
pam: set XDG_SEAT and XDG_VTNR when logging in
[elogind.git] / src / logger.c
index 5a28c2197fd3c0d9a36f604e453e5afe8e6b31e6..81196dbe00cd0e0734989d22149439bd5e040d14 100644 (file)
 #include "list.h"
 #include "sd-daemon.h"
 #include "tcpwrap.h"
+#include "def.h"
 
-#define STREAMS_MAX 256
+#define STREAMS_MAX 4096
 #define SERVER_FD_MAX 16
-#define TIMEOUT ((int) (10*MSEC_PER_SEC))
+#define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
 
 typedef struct Stream Stream;
 
@@ -51,6 +52,8 @@ typedef struct Server {
 
         unsigned n_server_fd;
 
+        bool syslog_is_stream;
+
         LIST_HEAD(Stream, streams);
         unsigned n_streams;
 } Server;
@@ -82,7 +85,8 @@ struct Stream {
         uid_t uid;
         gid_t gid;
 
-        bool prefix;
+        bool prefix:1;
+        bool tee_console:1;
 
         char buffer[LINE_MAX];
         size_t length;
@@ -101,20 +105,16 @@ static int stream_log(Stream *s, char *p, usec_t ts) {
 
         priority = s->priority;
 
-        if (s->prefix &&
-            p[0] == '<' &&
-            p[1] >= '0' && p[1] <= '7' &&
-            p[2] == '>') {
-
-                /* Detected priority prefix */
-                priority = LOG_MAKEPRI(LOG_FAC(priority), (p[1] - '0'));
-
-                p += 3;
-        }
+        if (s->prefix)
+                parse_syslog_priority(&p, &priority);
 
         if (*p == 0)
                 return 0;
 
+        /* Patch in LOG_USER facility if necessary */
+        if ((priority & LOG_FACMASK) == 0)
+                priority = LOG_USER | LOG_PRI(priority);
+
         /*
          * The format glibc uses to talk to the syslog daemon is:
          *
@@ -127,8 +127,7 @@ static int stream_log(Stream *s, char *p, usec_t ts) {
          *  We extend the latter to include the process name and pid.
          */
 
-        snprintf(header_priority, sizeof(header_priority), "<%i>",
-                 s->target == STREAM_SYSLOG ? priority : LOG_PRI(priority));
+        snprintf(header_priority, sizeof(header_priority), "<%i>", priority);
         char_array_0(header_priority);
 
         if (s->target == STREAM_SYSLOG) {
@@ -172,14 +171,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);
@@ -192,6 +225,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;
 }
 
@@ -206,9 +253,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;
@@ -220,6 +267,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;
 
@@ -302,7 +353,7 @@ static int stream_process(Stream *s, usec_t ts) {
                         return 0;
 
                 log_warning("Failed to read from stream: %m");
-                return -1;
+                return -errno;
         }
 
 
@@ -465,7 +516,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. */
 
@@ -479,22 +530,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");
@@ -514,7 +578,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 &&
@@ -557,16 +621,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);
@@ -575,16 +639,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());
 
@@ -598,7 +662,7 @@ int main(int argc, char *argv[]) {
 
                 if ((k = epoll_wait(server.epoll_fd,
                                     &event, 1,
-                                    server.n_streams <= 0 ? TIMEOUT : -1)) < 0) {
+                                    server.n_streams <= 0 ? TIMEOUT_MSEC : -1)) < 0) {
 
                         if (errno == EINTR)
                                 continue;
@@ -614,7 +678,7 @@ int main(int argc, char *argv[]) {
                         goto fail;
         }
 
-        r = 0;
+        r = EXIT_SUCCESS;
 
         log_debug("systemd-logger stopped as pid %lu", (unsigned long) getpid());