1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/epoll.h>
26 #include "systemd/sd-messages.h"
27 #include "socket-util.h"
28 #include "selinux-util.h"
29 #include "journald-server.h"
30 #include "journald-syslog.h"
31 #include "journald-kmsg.h"
32 #include "journald-console.h"
33 #include "journald-wall.h"
35 /* Warn once every 30s if we missed syslog message */
36 #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
38 static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, const struct ucred *ucred, const struct timeval *tv) {
40 static const union sockaddr_union sa = {
41 .un.sun_family = AF_UNIX,
42 .un.sun_path = "/run/systemd/journal/syslog",
44 struct msghdr msghdr = {
45 .msg_iov = (struct iovec *) iovec,
46 .msg_iovlen = n_iovec,
47 .msg_name = (struct sockaddr*) &sa.sa,
48 .msg_namelen = offsetof(union sockaddr_union, un.sun_path)
49 + strlen("/run/systemd/journal/syslog"),
53 struct cmsghdr cmsghdr;
54 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
63 msghdr.msg_control = &control;
64 msghdr.msg_controllen = sizeof(control);
66 cmsg = CMSG_FIRSTHDR(&msghdr);
67 cmsg->cmsg_level = SOL_SOCKET;
68 cmsg->cmsg_type = SCM_CREDENTIALS;
69 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
70 memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
71 msghdr.msg_controllen = cmsg->cmsg_len;
74 /* Forward the syslog message we received via /dev/log to
75 * /run/systemd/syslog. Unfortunately we currently can't set
76 * the SO_TIMESTAMP auxiliary data, and hence we don't. */
78 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
81 /* The socket is full? I guess the syslog implementation is
82 * too slow, and we shouldn't wait for that... */
83 if (errno == EAGAIN) {
84 s->n_forward_syslog_missed++;
88 if (ucred && (errno == ESRCH || errno == EPERM)) {
91 /* Hmm, presumably the sender process vanished
92 * by now, or we don't have CAP_SYS_AMDIN, so
93 * let's fix it as good as we can, and retry */
97 memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
99 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
102 if (errno == EAGAIN) {
103 s->n_forward_syslog_missed++;
109 log_debug_errno(errno, "Failed to forward syslog message: %m");
112 static void forward_syslog_raw(Server *s, int priority, const char *buffer, const struct ucred *ucred, const struct timeval *tv) {
118 if (LOG_PRI(priority) > s->max_level_syslog)
121 IOVEC_SET_STRING(iovec, buffer);
122 forward_syslog_iovec(s, &iovec, 1, ucred, tv);
125 void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv) {
126 struct iovec iovec[5];
127 char header_priority[6], header_time[64], header_pid[16];
131 char *ident_buf = NULL;
134 assert(priority >= 0);
135 assert(priority <= 999);
138 if (LOG_PRI(priority) > s->max_level_syslog)
141 /* First: priority field */
142 snprintf(header_priority, sizeof(header_priority), "<%i>", priority);
143 char_array_0(header_priority);
144 IOVEC_SET_STRING(iovec[n++], header_priority);
146 /* Second: timestamp */
147 t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
151 if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
153 IOVEC_SET_STRING(iovec[n++], header_time);
155 /* Third: identifier and PID */
158 get_process_comm(ucred->pid, &ident_buf);
159 identifier = ident_buf;
162 snprintf(header_pid, sizeof(header_pid), "["PID_FMT"]: ", ucred->pid);
163 char_array_0(header_pid);
166 IOVEC_SET_STRING(iovec[n++], identifier);
168 IOVEC_SET_STRING(iovec[n++], header_pid);
169 } else if (identifier) {
170 IOVEC_SET_STRING(iovec[n++], identifier);
171 IOVEC_SET_STRING(iovec[n++], ": ");
174 /* Fourth: message */
175 IOVEC_SET_STRING(iovec[n++], message);
177 forward_syslog_iovec(s, iovec, n, ucred, tv);
182 int syslog_fixup_facility(int priority) {
184 if ((priority & LOG_FACMASK) == 0)
185 return (priority & LOG_PRIMASK) | LOG_USER;
190 size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) {
201 p += strspn(p, WHITESPACE);
202 l = strcspn(p, WHITESPACE);
217 t = strndup(p+k+1, l-k-2);
236 e += strspn(p + e, WHITESPACE);
241 static void syslog_skip_date(char **buf) {
249 LETTER, LETTER, LETTER,
251 SPACE_OR_NUMBER, NUMBER,
253 SPACE_OR_NUMBER, NUMBER,
255 SPACE_OR_NUMBER, NUMBER,
257 SPACE_OR_NUMBER, NUMBER,
269 for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
274 switch (sequence[i]) {
281 case SPACE_OR_NUMBER:
288 if (*p < '0' || *p > '9')
294 if (!(*p >= 'A' && *p <= 'Z') &&
295 !(*p >= 'a' && *p <= 'z'))
311 void server_process_syslog_message(
314 const struct ucred *ucred,
315 const struct timeval *tv,
319 char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
320 syslog_facility[sizeof("SYSLOG_FACILITY") + DECIMAL_STR_MAX(int)];
321 const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
322 struct iovec iovec[N_IOVEC_META_FIELDS + 6];
324 int priority = LOG_USER | LOG_INFO;
325 _cleanup_free_ char *identifier = NULL, *pid = NULL;
332 syslog_parse_priority(&buf, &priority, true);
334 if (s->forward_to_syslog)
335 forward_syslog_raw(s, priority, orig, ucred, tv);
337 syslog_skip_date((char**) &buf);
338 syslog_parse_identifier(&buf, &identifier, &pid);
340 if (s->forward_to_kmsg)
341 server_forward_kmsg(s, priority, identifier, buf, ucred);
343 if (s->forward_to_console)
344 server_forward_console(s, priority, identifier, buf, ucred);
346 if (s->forward_to_wall)
347 server_forward_wall(s, priority, identifier, buf, ucred);
349 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
351 sprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
352 IOVEC_SET_STRING(iovec[n++], syslog_priority);
354 if (priority & LOG_FACMASK) {
355 sprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
356 IOVEC_SET_STRING(iovec[n++], syslog_facility);
360 syslog_identifier = strappenda("SYSLOG_IDENTIFIER=", identifier);
361 if (syslog_identifier)
362 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
366 syslog_pid = strappenda("SYSLOG_PID=", pid);
368 IOVEC_SET_STRING(iovec[n++], syslog_pid);
371 message = strappenda("MESSAGE=", buf);
373 IOVEC_SET_STRING(iovec[n++], message);
375 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
378 int server_open_syslog_socket(Server *s) {
379 static const int one = 1;
384 if (s->syslog_fd < 0) {
385 static const union sockaddr_union sa = {
386 .un.sun_family = AF_UNIX,
387 .un.sun_path = "/run/systemd/journal/dev-log",
390 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
391 if (s->syslog_fd < 0)
392 return log_error_errno(errno, "socket() failed: %m");
394 unlink(sa.un.sun_path);
396 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
398 return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
400 chmod(sa.un.sun_path, 0666);
402 fd_nonblock(s->syslog_fd, 1);
404 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
406 return log_error_errno(errno, "SO_PASSCRED failed: %m");
409 if (mac_selinux_use()) {
410 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
412 log_warning_errno(errno, "SO_PASSSEC failed: %m");
416 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
418 return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
420 r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, server_process_datagram, s);
422 return log_error_errno(r, "Failed to add syslog server fd to event loop: %m");
427 void server_maybe_warn_forward_syslog_missed(Server *s) {
431 if (s->n_forward_syslog_missed <= 0)
434 n = now(CLOCK_MONOTONIC);
435 if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
438 server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
440 s->n_forward_syslog_missed = 0;
441 s->last_warn_forward_syslog_missed = n;