LCOV - code coverage report
Current view: top level - journal - journald-syslog.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 30 254 11.8 %
Date: 2019-08-22 15:41:25 Functions: 1 9 11.1 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <stddef.h>
       4             : #include <sys/epoll.h>
       5             : #include <unistd.h>
       6             : 
       7             : #include "sd-messages.h"
       8             : 
       9             : #include "alloc-util.h"
      10             : #include "fd-util.h"
      11             : #include "format-util.h"
      12             : #include "io-util.h"
      13             : #include "journald-console.h"
      14             : #include "journald-kmsg.h"
      15             : #include "journald-server.h"
      16             : #include "journald-syslog.h"
      17             : #include "journald-wall.h"
      18             : #include "process-util.h"
      19             : #include "selinux-util.h"
      20             : #include "socket-util.h"
      21             : #include "stdio-util.h"
      22             : #include "string-util.h"
      23             : #include "syslog-util.h"
      24             : 
      25             : /* Warn once every 30s if we missed syslog message */
      26             : #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
      27             : 
      28           0 : static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, const struct ucred *ucred, const struct timeval *tv) {
      29             : 
      30             :         static const union sockaddr_union sa = {
      31             :                 .un.sun_family = AF_UNIX,
      32             :                 .un.sun_path = "/run/systemd/journal/syslog",
      33             :         };
      34           0 :         struct msghdr msghdr = {
      35             :                 .msg_iov = (struct iovec *) iovec,
      36             :                 .msg_iovlen = n_iovec,
      37             :                 .msg_name = (struct sockaddr*) &sa.sa,
      38           0 :                 .msg_namelen = SOCKADDR_UN_LEN(sa.un),
      39             :         };
      40             :         struct cmsghdr *cmsg;
      41             :         union {
      42             :                 struct cmsghdr cmsghdr;
      43             :                 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
      44             :         } control;
      45             : 
      46           0 :         assert(s);
      47           0 :         assert(iovec);
      48           0 :         assert(n_iovec > 0);
      49             : 
      50           0 :         if (ucred) {
      51           0 :                 zero(control);
      52           0 :                 msghdr.msg_control = &control;
      53           0 :                 msghdr.msg_controllen = sizeof(control);
      54             : 
      55           0 :                 cmsg = CMSG_FIRSTHDR(&msghdr);
      56           0 :                 cmsg->cmsg_level = SOL_SOCKET;
      57           0 :                 cmsg->cmsg_type = SCM_CREDENTIALS;
      58           0 :                 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
      59           0 :                 memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
      60           0 :                 msghdr.msg_controllen = cmsg->cmsg_len;
      61             :         }
      62             : 
      63             :         /* Forward the syslog message we received via /dev/log to
      64             :          * /run/systemd/syslog. Unfortunately we currently can't set
      65             :          * the SO_TIMESTAMP auxiliary data, and hence we don't. */
      66             : 
      67           0 :         if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
      68           0 :                 return;
      69             : 
      70             :         /* The socket is full? I guess the syslog implementation is
      71             :          * too slow, and we shouldn't wait for that... */
      72           0 :         if (errno == EAGAIN) {
      73           0 :                 s->n_forward_syslog_missed++;
      74           0 :                 return;
      75             :         }
      76             : 
      77           0 :         if (ucred && IN_SET(errno, ESRCH, EPERM)) {
      78             :                 struct ucred u;
      79             : 
      80             :                 /* Hmm, presumably the sender process vanished
      81             :                  * by now, or we don't have CAP_SYS_AMDIN, so
      82             :                  * let's fix it as good as we can, and retry */
      83             : 
      84           0 :                 u = *ucred;
      85           0 :                 u.pid = getpid_cached();
      86           0 :                 memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
      87             : 
      88           0 :                 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
      89           0 :                         return;
      90             : 
      91           0 :                 if (errno == EAGAIN) {
      92           0 :                         s->n_forward_syslog_missed++;
      93           0 :                         return;
      94             :                 }
      95             :         }
      96             : 
      97           0 :         if (errno != ENOENT)
      98           0 :                 log_debug_errno(errno, "Failed to forward syslog message: %m");
      99             : }
     100             : 
     101           0 : static void forward_syslog_raw(Server *s, int priority, const char *buffer, size_t buffer_len, const struct ucred *ucred, const struct timeval *tv) {
     102             :         struct iovec iovec;
     103             : 
     104           0 :         assert(s);
     105           0 :         assert(buffer);
     106             : 
     107           0 :         if (LOG_PRI(priority) > s->max_level_syslog)
     108           0 :                 return;
     109             : 
     110           0 :         iovec = IOVEC_MAKE((char *) buffer, buffer_len);
     111           0 :         forward_syslog_iovec(s, &iovec, 1, ucred, tv);
     112             : }
     113             : 
     114           0 : void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv) {
     115             :         struct iovec iovec[5];
     116             :         char header_priority[DECIMAL_STR_MAX(priority) + 3], header_time[64],
     117             :              header_pid[STRLEN("[]: ") + DECIMAL_STR_MAX(pid_t) + 1];
     118           0 :         int n = 0;
     119             :         time_t t;
     120             :         struct tm tm;
     121           0 :         _cleanup_free_ char *ident_buf = NULL;
     122             : 
     123           0 :         assert(s);
     124           0 :         assert(priority >= 0);
     125           0 :         assert(priority <= 999);
     126           0 :         assert(message);
     127             : 
     128           0 :         if (LOG_PRI(priority) > s->max_level_syslog)
     129           0 :                 return;
     130             : 
     131             :         /* First: priority field */
     132           0 :         xsprintf(header_priority, "<%i>", priority);
     133           0 :         iovec[n++] = IOVEC_MAKE_STRING(header_priority);
     134             : 
     135             :         /* Second: timestamp */
     136           0 :         t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
     137           0 :         if (!localtime_r(&t, &tm))
     138           0 :                 return;
     139           0 :         if (strftime(header_time, sizeof(header_time), "%h %e %T ", &tm) <= 0)
     140           0 :                 return;
     141           0 :         iovec[n++] = IOVEC_MAKE_STRING(header_time);
     142             : 
     143             :         /* Third: identifier and PID */
     144           0 :         if (ucred) {
     145           0 :                 if (!identifier) {
     146           0 :                         get_process_comm(ucred->pid, &ident_buf);
     147           0 :                         identifier = ident_buf;
     148             :                 }
     149             : 
     150           0 :                 xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid);
     151             : 
     152           0 :                 if (identifier)
     153           0 :                         iovec[n++] = IOVEC_MAKE_STRING(identifier);
     154             : 
     155           0 :                 iovec[n++] = IOVEC_MAKE_STRING(header_pid);
     156           0 :         } else if (identifier) {
     157           0 :                 iovec[n++] = IOVEC_MAKE_STRING(identifier);
     158           0 :                 iovec[n++] = IOVEC_MAKE_STRING(": ");
     159             :         }
     160             : 
     161             :         /* Fourth: message */
     162           0 :         iovec[n++] = IOVEC_MAKE_STRING(message);
     163             : 
     164           0 :         forward_syslog_iovec(s, iovec, n, ucred, tv);
     165             : }
     166             : 
     167           0 : int syslog_fixup_facility(int priority) {
     168             : 
     169           0 :         if ((priority & LOG_FACMASK) == 0)
     170           0 :                 return (priority & LOG_PRIMASK) | LOG_USER;
     171             : 
     172           0 :         return priority;
     173             : }
     174             : 
     175          14 : size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) {
     176             :         const char *p;
     177             :         char *t;
     178             :         size_t l, e;
     179             : 
     180          14 :         assert(buf);
     181          14 :         assert(identifier);
     182          14 :         assert(pid);
     183             : 
     184          14 :         p = *buf;
     185             : 
     186          14 :         p += strspn(p, WHITESPACE);
     187          14 :         l = strcspn(p, WHITESPACE);
     188             : 
     189          14 :         if (l <= 0 ||
     190          12 :             p[l-1] != ':')
     191           5 :                 return 0;
     192             : 
     193           9 :         e = l;
     194           9 :         l--;
     195             : 
     196           9 :         if (l > 0 && p[l-1] == ']') {
     197           1 :                 size_t k = l-1;
     198             : 
     199             :                 for (;;) {
     200             : 
     201           5 :                         if (p[k] == '[') {
     202           1 :                                 t = strndup(p+k+1, l-k-2);
     203           1 :                                 if (t)
     204           1 :                                         *pid = t;
     205             : 
     206           1 :                                 l = k;
     207           1 :                                 break;
     208             :                         }
     209             : 
     210           4 :                         if (k == 0)
     211           0 :                                 break;
     212             : 
     213           4 :                         k--;
     214             :                 }
     215             :         }
     216             : 
     217           9 :         t = strndup(p, l);
     218           9 :         if (t)
     219           9 :                 *identifier = t;
     220             : 
     221             :         /* Single space is used as separator */
     222           9 :         if (p[e] != '\0' && strchr(WHITESPACE, p[e]))
     223           5 :                 e++;
     224             : 
     225           9 :         l = (p - *buf) + e;
     226           9 :         *buf = p + e;
     227           9 :         return l;
     228             : }
     229             : 
     230           0 : static int syslog_skip_timestamp(const char **buf) {
     231             :         enum {
     232             :                 LETTER,
     233             :                 SPACE,
     234             :                 NUMBER,
     235             :                 SPACE_OR_NUMBER,
     236             :                 COLON
     237           0 :         } sequence[] = {
     238             :                 LETTER, LETTER, LETTER,
     239             :                 SPACE,
     240             :                 SPACE_OR_NUMBER, NUMBER,
     241             :                 SPACE,
     242             :                 SPACE_OR_NUMBER, NUMBER,
     243             :                 COLON,
     244             :                 SPACE_OR_NUMBER, NUMBER,
     245             :                 COLON,
     246             :                 SPACE_OR_NUMBER, NUMBER,
     247             :                 SPACE
     248             :         };
     249             : 
     250             :         const char *p, *t;
     251             :         unsigned i;
     252             : 
     253           0 :         assert(buf);
     254           0 :         assert(*buf);
     255             : 
     256           0 :         for (i = 0, p = *buf; i < ELEMENTSOF(sequence); i++, p++) {
     257           0 :                 if (!*p)
     258           0 :                         return 0;
     259             : 
     260           0 :                 switch (sequence[i]) {
     261             : 
     262           0 :                 case SPACE:
     263           0 :                         if (*p != ' ')
     264           0 :                                 return 0;
     265           0 :                         break;
     266             : 
     267           0 :                 case SPACE_OR_NUMBER:
     268           0 :                         if (*p == ' ')
     269           0 :                                 break;
     270             : 
     271             :                         _fallthrough_;
     272             :                 case NUMBER:
     273           0 :                         if (*p < '0' || *p > '9')
     274           0 :                                 return 0;
     275             : 
     276           0 :                         break;
     277             : 
     278           0 :                 case LETTER:
     279           0 :                         if (!(*p >= 'A' && *p <= 'Z') &&
     280           0 :                             !(*p >= 'a' && *p <= 'z'))
     281           0 :                                 return 0;
     282             : 
     283           0 :                         break;
     284             : 
     285           0 :                 case COLON:
     286           0 :                         if (*p != ':')
     287           0 :                                 return 0;
     288           0 :                         break;
     289             : 
     290             :                 }
     291           0 :         }
     292             : 
     293           0 :         t = *buf;
     294           0 :         *buf = p;
     295           0 :         return p - t;
     296             : }
     297             : 
     298           0 : void server_process_syslog_message(
     299             :                 Server *s,
     300             :                 const char *buf,
     301             :                 size_t raw_len,
     302             :                 const struct ucred *ucred,
     303             :                 const struct timeval *tv,
     304             :                 const char *label,
     305             :                 size_t label_len) {
     306             : 
     307             :         char *t, syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
     308             :                  syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
     309             :         const char *msg, *syslog_ts, *a;
     310           0 :         _cleanup_free_ char *identifier = NULL, *pid = NULL,
     311           0 :                 *dummy = NULL, *msg_msg = NULL, *msg_raw = NULL;
     312           0 :         int priority = LOG_USER | LOG_INFO, r;
     313           0 :         ClientContext *context = NULL;
     314             :         struct iovec *iovec;
     315           0 :         size_t n = 0, m, i, leading_ws, syslog_ts_len;
     316             :         bool store_raw;
     317             : 
     318           0 :         assert(s);
     319           0 :         assert(buf);
     320             :         /* The message cannot be empty. */
     321           0 :         assert(raw_len > 0);
     322             :         /* The buffer NUL-terminated and can be used a string. raw_len is the length
     323             :          * without the terminating NUL byte, the buffer is actually one bigger. */
     324           0 :         assert(buf[raw_len] == '\0');
     325             : 
     326           0 :         if (ucred && pid_is_valid(ucred->pid)) {
     327           0 :                 r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
     328           0 :                 if (r < 0)
     329           0 :                         log_warning_errno(r, "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m", ucred->pid);
     330             :         }
     331             : 
     332             :         /* We are creating a copy of the message because we want to forward the original message
     333             :            verbatim to the legacy syslog implementation */
     334           0 :         for (i = raw_len; i > 0; i--)
     335           0 :                 if (!strchr(WHITESPACE, buf[i-1]))
     336           0 :                         break;
     337             : 
     338           0 :         leading_ws = strspn(buf, WHITESPACE);
     339             : 
     340           0 :         if (i == 0)
     341             :                 /* The message contains only whitespaces */
     342           0 :                 msg = buf + raw_len;
     343           0 :         else if (i == raw_len)
     344             :                 /* Nice! No need to strip anything on the end, let's optimize this a bit */
     345           0 :                 msg = buf + leading_ws;
     346             :         else {
     347           0 :                 msg = dummy = new(char, i - leading_ws + 1);
     348           0 :                 if (!dummy) {
     349           0 :                         log_oom();
     350           0 :                         return;
     351             :                 }
     352             : 
     353           0 :                 memcpy(dummy, buf + leading_ws, i - leading_ws);
     354           0 :                 dummy[i - leading_ws] = 0;
     355             :         }
     356             : 
     357             :         /* We will add the SYSLOG_RAW= field when we stripped anything
     358             :          * _or_ if the input message contained NUL bytes. */
     359           0 :         store_raw = msg != buf || strlen(msg) != raw_len;
     360             : 
     361           0 :         syslog_parse_priority(&msg, &priority, true);
     362             : 
     363           0 :         if (!client_context_test_priority(context, priority))
     364           0 :                 return;
     365             : 
     366           0 :         syslog_ts = msg;
     367           0 :         syslog_ts_len = syslog_skip_timestamp(&msg);
     368           0 :         if (syslog_ts_len == 0)
     369             :                 /* We failed to parse the full timestamp, store the raw message too */
     370           0 :                 store_raw = true;
     371             : 
     372           0 :         syslog_parse_identifier(&msg, &identifier, &pid);
     373             : 
     374           0 :         if (s->forward_to_syslog)
     375           0 :                 forward_syslog_raw(s, priority, buf, raw_len, ucred, tv);
     376             : 
     377           0 :         if (s->forward_to_kmsg)
     378           0 :                 server_forward_kmsg(s, priority, identifier, msg, ucred);
     379             : 
     380           0 :         if (s->forward_to_console)
     381           0 :                 server_forward_console(s, priority, identifier, msg, ucred);
     382             : 
     383           0 :         if (s->forward_to_wall)
     384           0 :                 server_forward_wall(s, priority, identifier, msg, ucred);
     385             : 
     386           0 :         m = N_IOVEC_META_FIELDS + 8 + client_context_extra_fields_n_iovec(context);
     387           0 :         iovec = newa(struct iovec, m);
     388             : 
     389           0 :         iovec[n++] = IOVEC_MAKE_STRING("_TRANSPORT=syslog");
     390             : 
     391           0 :         xsprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
     392           0 :         iovec[n++] = IOVEC_MAKE_STRING(syslog_priority);
     393             : 
     394           0 :         if (priority & LOG_FACMASK) {
     395           0 :                 xsprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
     396           0 :                 iovec[n++] = IOVEC_MAKE_STRING(syslog_facility);
     397             :         }
     398             : 
     399           0 :         if (identifier) {
     400           0 :                 a = strjoina("SYSLOG_IDENTIFIER=", identifier);
     401           0 :                 iovec[n++] = IOVEC_MAKE_STRING(a);
     402             :         }
     403             : 
     404           0 :         if (pid) {
     405           0 :                 a = strjoina("SYSLOG_PID=", pid);
     406           0 :                 iovec[n++] = IOVEC_MAKE_STRING(a);
     407             :         }
     408             : 
     409           0 :         if (syslog_ts_len > 0) {
     410           0 :                 const size_t hlen = STRLEN("SYSLOG_TIMESTAMP=");
     411             : 
     412           0 :                 t = newa(char, hlen + syslog_ts_len);
     413           0 :                 memcpy(t, "SYSLOG_TIMESTAMP=", hlen);
     414           0 :                 memcpy(t + hlen, syslog_ts, syslog_ts_len);
     415             : 
     416           0 :                 iovec[n++] = IOVEC_MAKE(t, hlen + syslog_ts_len);
     417             :         }
     418             : 
     419           0 :         msg_msg = strjoin("MESSAGE=", msg);
     420           0 :         if (!msg_msg) {
     421           0 :                 log_oom();
     422           0 :                 return;
     423             :         }
     424           0 :         iovec[n++] = IOVEC_MAKE_STRING(msg_msg);
     425             : 
     426           0 :         if (store_raw) {
     427           0 :                 const size_t hlen = STRLEN("SYSLOG_RAW=");
     428             : 
     429           0 :                 msg_raw = new(char, hlen + raw_len);
     430           0 :                 if (!msg_raw) {
     431           0 :                         log_oom();
     432           0 :                         return;
     433             :                 }
     434             : 
     435           0 :                 memcpy(msg_raw, "SYSLOG_RAW=", hlen);
     436           0 :                 memcpy(msg_raw + hlen, buf, raw_len);
     437             : 
     438           0 :                 iovec[n++] = IOVEC_MAKE(msg_raw, hlen + raw_len);
     439             :         }
     440             : 
     441           0 :         server_dispatch_message(s, iovec, n, m, context, tv, priority, 0);
     442             : }
     443             : 
     444           0 : int server_open_syslog_socket(Server *s) {
     445             : 
     446             :         static const union sockaddr_union sa = {
     447             :                 .un.sun_family = AF_UNIX,
     448             :                 .un.sun_path = "/run/systemd/journal/dev-log",
     449             :         };
     450             :         int r;
     451             : 
     452           0 :         assert(s);
     453             : 
     454           0 :         if (s->syslog_fd < 0) {
     455           0 :                 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
     456           0 :                 if (s->syslog_fd < 0)
     457           0 :                         return log_error_errno(errno, "socket() failed: %m");
     458             : 
     459           0 :                 (void) sockaddr_un_unlink(&sa.un);
     460             : 
     461           0 :                 r = bind(s->syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
     462           0 :                 if (r < 0)
     463           0 :                         return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
     464             : 
     465           0 :                 (void) chmod(sa.un.sun_path, 0666);
     466             :         } else
     467           0 :                 (void) fd_nonblock(s->syslog_fd, true);
     468             : 
     469           0 :         r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, true);
     470           0 :         if (r < 0)
     471           0 :                 return log_error_errno(r, "SO_PASSCRED failed: %m");
     472             : 
     473             : #if HAVE_SELINUX
     474           0 :         if (mac_selinux_use()) {
     475           0 :                 r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, true);
     476           0 :                 if (r < 0)
     477           0 :                         log_warning_errno(r, "SO_PASSSEC failed: %m");
     478             :         }
     479             : #endif
     480             : 
     481           0 :         r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, true);
     482           0 :         if (r < 0)
     483           0 :                 return log_error_errno(r, "SO_TIMESTAMP failed: %m");
     484             : 
     485           0 :         r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, server_process_datagram, s);
     486           0 :         if (r < 0)
     487           0 :                 return log_error_errno(r, "Failed to add syslog server fd to event loop: %m");
     488             : 
     489           0 :         r = sd_event_source_set_priority(s->syslog_event_source, SD_EVENT_PRIORITY_NORMAL+5);
     490           0 :         if (r < 0)
     491           0 :                 return log_error_errno(r, "Failed to adjust syslog event source priority: %m");
     492             : 
     493           0 :         return 0;
     494             : }
     495             : 
     496           0 : void server_maybe_warn_forward_syslog_missed(Server *s) {
     497             :         usec_t n;
     498             : 
     499           0 :         assert(s);
     500             : 
     501           0 :         if (s->n_forward_syslog_missed <= 0)
     502           0 :                 return;
     503             : 
     504           0 :         n = now(CLOCK_MONOTONIC);
     505           0 :         if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
     506           0 :                 return;
     507             : 
     508           0 :         server_driver_message(s, 0,
     509             :                               "MESSAGE_ID=" SD_MESSAGE_FORWARD_SYSLOG_MISSED_STR,
     510             :                               LOG_MESSAGE("Forwarding to syslog missed %u messages.",
     511             :                                           s->n_forward_syslog_missed),
     512             :                               NULL);
     513             : 
     514           0 :         s->n_forward_syslog_missed = 0;
     515           0 :         s->last_warn_forward_syslog_missed = n;
     516             : }

Generated by: LCOV version 1.14