X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fjournal%2Fjournald-syslog.c;h=dc66ba8c8fa77ed580d21785582008ab87d9b548;hp=91664762e64a2706ead10ffdbb9343fc329bd286;hb=872c8faaf2009422a91d227ae0b5c6f04c9d2c69;hpb=35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9 diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c index 91664762e..dc66ba8c8 100644 --- a/src/journal/journald-syslog.c +++ b/src/journal/journald-syslog.c @@ -20,36 +20,43 @@ ***/ #include +#include #include +#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" + +/* 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) + + sizeof("/run/systemd/journal/syslog") - 1, + }; 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 +79,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 +98,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) @@ -175,7 +186,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 +202,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 +232,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(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 +366,13 @@ void server_process_syslog_message( assert(buf); orig = buf; - syslog_parse_priority((char**) &buf, &priority); + syslog_parse_priority((char**) &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); @@ -283,7 +405,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 +417,16 @@ 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 = "/dev/log", + }; s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (s->syslog_fd < 0) { @@ -309,10 +434,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 +454,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; @@ -356,3 +479,20 @@ int server_open_syslog_socket(Server *s) { 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; +}