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) {
91 /* Hmm, presumably the sender process vanished
92 * by now, so let's fix it as good as we
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("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 void syslog_parse_priority(const char **p, int *priority, bool with_facility) {
242 int a = 0, b = 0, c = 0;
252 if (!strchr(*p, '>'))
255 if ((*p)[2] == '>') {
256 c = undecchar((*p)[1]);
258 } else if ((*p)[3] == '>') {
259 b = undecchar((*p)[1]);
260 c = undecchar((*p)[2]);
262 } else if ((*p)[4] == '>') {
263 a = undecchar((*p)[1]);
264 b = undecchar((*p)[2]);
265 c = undecchar((*p)[3]);
270 if (a < 0 || b < 0 || c < 0 ||
271 (!with_facility && (a || b || c > 7)))
275 *priority = a*100 + b*10 + c;
277 *priority = (*priority & LOG_FACMASK) | c;
281 static void syslog_skip_date(char **buf) {
289 LETTER, LETTER, LETTER,
291 SPACE_OR_NUMBER, NUMBER,
293 SPACE_OR_NUMBER, NUMBER,
295 SPACE_OR_NUMBER, NUMBER,
297 SPACE_OR_NUMBER, NUMBER,
309 for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
314 switch (sequence[i]) {
321 case SPACE_OR_NUMBER:
328 if (*p < '0' || *p > '9')
334 if (!(*p >= 'A' && *p <= 'Z') &&
335 !(*p >= 'a' && *p <= 'z'))
351 void server_process_syslog_message(
354 const struct ucred *ucred,
355 const struct timeval *tv,
359 char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
360 syslog_facility[sizeof("SYSLOG_FACILITY") + DECIMAL_STR_MAX(int)];
361 const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
362 struct iovec iovec[N_IOVEC_META_FIELDS + 6];
364 int priority = LOG_USER | LOG_INFO;
365 _cleanup_free_ char *identifier = NULL, *pid = NULL;
372 syslog_parse_priority(&buf, &priority, true);
374 if (s->forward_to_syslog)
375 forward_syslog_raw(s, priority, orig, ucred, tv);
377 syslog_skip_date((char**) &buf);
378 syslog_parse_identifier(&buf, &identifier, &pid);
380 if (s->forward_to_kmsg)
381 server_forward_kmsg(s, priority, identifier, buf, ucred);
383 if (s->forward_to_console)
384 server_forward_console(s, priority, identifier, buf, ucred);
386 if (s->forward_to_wall)
387 server_forward_wall(s, priority, identifier, buf, ucred);
389 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
391 sprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
392 IOVEC_SET_STRING(iovec[n++], syslog_priority);
394 if (priority & LOG_FACMASK) {
395 sprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
396 IOVEC_SET_STRING(iovec[n++], syslog_facility);
400 syslog_identifier = strappenda("SYSLOG_IDENTIFIER=", identifier);
401 if (syslog_identifier)
402 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
406 syslog_pid = strappenda("SYSLOG_PID=", pid);
408 IOVEC_SET_STRING(iovec[n++], syslog_pid);
411 message = strappenda("MESSAGE=", buf);
413 IOVEC_SET_STRING(iovec[n++], message);
415 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
418 int server_open_syslog_socket(Server *s) {
419 static const int one = 1;
424 if (s->syslog_fd < 0) {
425 static const union sockaddr_union sa = {
426 .un.sun_family = AF_UNIX,
427 .un.sun_path = "/run/systemd/journal/dev-log",
430 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
431 if (s->syslog_fd < 0) {
432 log_error("socket() failed: %m");
436 unlink(sa.un.sun_path);
438 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
440 log_error("bind(%s) failed: %m", sa.un.sun_path);
444 chmod(sa.un.sun_path, 0666);
446 fd_nonblock(s->syslog_fd, 1);
448 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
450 log_error("SO_PASSCRED failed: %m");
455 if (mac_selinux_use()) {
456 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
458 log_warning("SO_PASSSEC failed: %m");
462 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
464 log_error("SO_TIMESTAMP failed: %m");
468 r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, process_datagram, s);
470 log_error_errno(r, "Failed to add syslog server fd to event loop: %m");
477 void server_maybe_warn_forward_syslog_missed(Server *s) {
481 if (s->n_forward_syslog_missed <= 0)
484 n = now(CLOCK_MONOTONIC);
485 if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
488 server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
490 s->n_forward_syslog_missed = 0;
491 s->last_warn_forward_syslog_missed = n;