chiark / gitweb /
journald: move /dev/log socket to /run
[elogind.git] / src / journal / journald-syslog.c
index 91664762e64a2706ead10ffdbb9343fc329bd286..b826e23c016b1be84e6ca6c1277c41496d70a6dd 100644 (file)
 ***/
 
 #include <unistd.h>
+#include <stddef.h>
 #include <sys/epoll.h>
 
+#include "systemd/sd-messages.h"
 #include "socket-util.h"
-#include "journald.h"
+#include "selinux-util.h"
+#include "journald-server.h"
 #include "journald-syslog.h"
 #include "journald-kmsg.h"
+#include "journald-console.h"
+#include "journald-wall.h"
+
+/* Warn once every 30s if we missed syslog message */
+#define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
 
 static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, struct ucred *ucred, struct timeval *tv) {
-        struct msghdr msghdr;
+
+        union sockaddr_union sa = {
+                .un.sun_family = AF_UNIX,
+                .un.sun_path = "/run/systemd/journal/syslog",
+        };
+        struct msghdr msghdr = {
+                .msg_iov = (struct iovec *) iovec,
+                .msg_iovlen = n_iovec,
+                .msg_name = &sa,
+                .msg_namelen = offsetof(union sockaddr_union, un.sun_path)
+                               + strlen("/run/systemd/journal/syslog"),
+        };
         struct cmsghdr *cmsg;
         union {
                 struct cmsghdr cmsghdr;
                 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
         } control;
-        union sockaddr_union sa;
 
         assert(s);
         assert(iovec);
         assert(n_iovec > 0);
 
-        zero(msghdr);
-        msghdr.msg_iov = (struct iovec*) iovec;
-        msghdr.msg_iovlen = n_iovec;
-
-        zero(sa);
-        sa.un.sun_family = AF_UNIX;
-        strncpy(sa.un.sun_path, "/run/systemd/journal/syslog", sizeof(sa.un.sun_path));
-        msghdr.msg_name = &sa;
-        msghdr.msg_namelen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path);
-
         if (ucred) {
                 zero(control);
                 msghdr.msg_control = &control;
@@ -72,8 +80,10 @@ static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned
 
         /* The socket is full? I guess the syslog implementation is
          * too slow, and we shouldn't wait for that... */
-        if (errno == EAGAIN)
+        if (errno == EAGAIN) {
+                s->n_forward_syslog_missed++;
                 return;
+        }
 
         if (ucred && errno == ESRCH) {
                 struct ucred u;
@@ -89,8 +99,10 @@ static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned
                 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
                         return;
 
-                if (errno == EAGAIN)
+                if (errno == EAGAIN) {
+                        s->n_forward_syslog_missed++;
                         return;
+                }
         }
 
         if (errno != ENOENT)
@@ -147,7 +159,7 @@ void server_forward_syslog(Server *s, int priority, const char *identifier, cons
                         identifier = ident_buf;
                 }
 
-                snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid);
+                snprintf(header_pid, sizeof(header_pid), "["PID_FMT"]: ", ucred->pid);
                 char_array_0(header_pid);
 
                 if (identifier)
@@ -175,7 +187,7 @@ int syslog_fixup_facility(int priority) {
         return priority;
 }
 
-void syslog_read_identifier(const char **buf, char **identifier, char **pid) {
+size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) {
         const char *p;
         char *t;
         size_t l, e;
@@ -191,7 +203,7 @@ void syslog_read_identifier(const char **buf, char **identifier, char **pid) {
 
         if (l <= 0 ||
             p[l-1] != ':')
-                return;
+                return 0;
 
         e = l;
         l--;
@@ -221,8 +233,119 @@ void syslog_read_identifier(const char **buf, char **identifier, char **pid) {
         if (t)
                 *identifier = t;
 
+        e += strspn(p + e, WHITESPACE);
         *buf = p + e;
-        *buf += strspn(*buf, WHITESPACE);
+        return e;
+}
+
+void syslog_parse_priority(const char **p, int *priority, bool with_facility) {
+        int a = 0, b = 0, c = 0;
+        int k;
+
+        assert(p);
+        assert(*p);
+        assert(priority);
+
+        if ((*p)[0] != '<')
+                return;
+
+        if (!strchr(*p, '>'))
+                return;
+
+        if ((*p)[2] == '>') {
+                c = undecchar((*p)[1]);
+                k = 3;
+        } else if ((*p)[3] == '>') {
+                b = undecchar((*p)[1]);
+                c = undecchar((*p)[2]);
+                k = 4;
+        } else if ((*p)[4] == '>') {
+                a = undecchar((*p)[1]);
+                b = undecchar((*p)[2]);
+                c = undecchar((*p)[3]);
+                k = 5;
+        } else
+                return;
+
+        if (a < 0 || b < 0 || c < 0 ||
+            (!with_facility && (a || b || c > 7)))
+                return;
+
+        if (with_facility)
+                *priority = a*100 + b*10 + c;
+        else
+                *priority = (*priority & LOG_FACMASK) | c;
+        *p += k;
+}
+
+static void syslog_skip_date(char **buf) {
+        enum {
+                LETTER,
+                SPACE,
+                NUMBER,
+                SPACE_OR_NUMBER,
+                COLON
+        } sequence[] = {
+                LETTER, LETTER, LETTER,
+                SPACE,
+                SPACE_OR_NUMBER, NUMBER,
+                SPACE,
+                SPACE_OR_NUMBER, NUMBER,
+                COLON,
+                SPACE_OR_NUMBER, NUMBER,
+                COLON,
+                SPACE_OR_NUMBER, NUMBER,
+                SPACE
+        };
+
+        char *p;
+        unsigned i;
+
+        assert(buf);
+        assert(*buf);
+
+        p = *buf;
+
+        for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
+
+                if (!*p)
+                        return;
+
+                switch (sequence[i]) {
+
+                case SPACE:
+                        if (*p != ' ')
+                                return;
+                        break;
+
+                case SPACE_OR_NUMBER:
+                        if (*p == ' ')
+                                break;
+
+                        /* fall through */
+
+                case NUMBER:
+                        if (*p < '0' || *p > '9')
+                                return;
+
+                        break;
+
+                case LETTER:
+                        if (!(*p >= 'A' && *p <= 'Z') &&
+                            !(*p >= 'a' && *p <= 'z'))
+                                return;
+
+                        break;
+
+                case COLON:
+                        if (*p != ':')
+                                return;
+                        break;
+
+                }
+        }
+
+        *buf = p;
 }
 
 void server_process_syslog_message(
@@ -244,13 +367,13 @@ void server_process_syslog_message(
         assert(buf);
 
         orig = buf;
-        syslog_parse_priority((char**) &buf, &priority);
+        syslog_parse_priority(&buf, &priority, true);
 
         if (s->forward_to_syslog)
                 forward_syslog_raw(s, priority, orig, ucred, tv);
 
         syslog_skip_date((char**) &buf);
-        syslog_read_identifier(&buf, &identifier, &pid);
+        syslog_parse_identifier(&buf, &identifier, &pid);
 
         if (s->forward_to_kmsg)
                 server_forward_kmsg(s, priority, identifier, buf, ucred);
@@ -258,6 +381,9 @@ void server_process_syslog_message(
         if (s->forward_to_console)
                 server_forward_console(s, priority, identifier, buf, ucred);
 
+        if (s->forward_to_wall)
+                server_forward_wall(s, priority, identifier, buf, ucred);
+
         IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
 
         if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
@@ -283,7 +409,7 @@ void server_process_syslog_message(
         if (message)
                 IOVEC_SET_STRING(iovec[n++], message);
 
-        server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority);
+        server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
 
         free(message);
         free(identifier);
@@ -295,13 +421,15 @@ void server_process_syslog_message(
 }
 
 int server_open_syslog_socket(Server *s) {
-        union sockaddr_union sa;
         int one, r;
-        struct epoll_event ev;
 
         assert(s);
 
         if (s->syslog_fd < 0) {
+                union sockaddr_union sa = {
+                        .un.sun_family = AF_UNIX,
+                        .un.sun_path = "/run/systemd/journal/dev-log",
+                };
 
                 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
                 if (s->syslog_fd < 0) {
@@ -309,10 +437,6 @@ int server_open_syslog_socket(Server *s) {
                         return -errno;
                 }
 
-                zero(sa);
-                sa.un.sun_family = AF_UNIX;
-                strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
-
                 unlink(sa.un.sun_path);
 
                 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
@@ -333,10 +457,12 @@ int server_open_syslog_socket(Server *s) {
         }
 
 #ifdef HAVE_SELINUX
-        one = 1;
-        r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
-        if (r < 0)
-                log_warning("SO_PASSSEC failed: %m");
+        if (use_selinux()) {
+                one = 1;
+                r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
+                if (r < 0)
+                        log_warning("SO_PASSSEC failed: %m");
+        }
 #endif
 
         one = 1;
@@ -346,13 +472,28 @@ int server_open_syslog_socket(Server *s) {
                 return -errno;
         }
 
-        zero(ev);
-        ev.events = EPOLLIN;
-        ev.data.fd = s->syslog_fd;
-        if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->syslog_fd, &ev) < 0) {
-                log_error("Failed to add syslog server fd to epoll object: %m");
-                return -errno;
+        r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, process_datagram, s);
+        if (r < 0) {
+                log_error("Failed to add syslog server fd to event loop: %s", strerror(-r));
+                return r;
         }
 
         return 0;
 }
+
+void server_maybe_warn_forward_syslog_missed(Server *s) {
+        usec_t n;
+        assert(s);
+
+        if (s->n_forward_syslog_missed <= 0)
+                return;
+
+        n = now(CLOCK_MONOTONIC);
+        if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
+                return;
+
+        server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
+
+        s->n_forward_syslog_missed = 0;
+        s->last_warn_forward_syslog_missed = n;
+}