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[DECIMAL_STR_MAX(priority) + 3], header_time[64],
128 header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t) + 1];
132 char *ident_buf = NULL;
135 assert(priority >= 0);
136 assert(priority <= 999);
139 if (LOG_PRI(priority) > s->max_level_syslog)
142 /* First: priority field */
143 xsprintf(header_priority, "<%i>", 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 xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid);
165 IOVEC_SET_STRING(iovec[n++], identifier);
167 IOVEC_SET_STRING(iovec[n++], header_pid);
168 } else if (identifier) {
169 IOVEC_SET_STRING(iovec[n++], identifier);
170 IOVEC_SET_STRING(iovec[n++], ": ");
173 /* Fourth: message */
174 IOVEC_SET_STRING(iovec[n++], message);
176 forward_syslog_iovec(s, iovec, n, ucred, tv);
181 int syslog_fixup_facility(int priority) {
183 if ((priority & LOG_FACMASK) == 0)
184 return (priority & LOG_PRIMASK) | LOG_USER;
189 size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) {
200 p += strspn(p, WHITESPACE);
201 l = strcspn(p, WHITESPACE);
216 t = strndup(p+k+1, l-k-2);
235 e += strspn(p + e, WHITESPACE);
240 static void syslog_skip_date(char **buf) {
248 LETTER, LETTER, LETTER,
250 SPACE_OR_NUMBER, NUMBER,
252 SPACE_OR_NUMBER, NUMBER,
254 SPACE_OR_NUMBER, NUMBER,
256 SPACE_OR_NUMBER, NUMBER,
268 for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
273 switch (sequence[i]) {
280 case SPACE_OR_NUMBER:
287 if (*p < '0' || *p > '9')
293 if (!(*p >= 'A' && *p <= 'Z') &&
294 !(*p >= 'a' && *p <= 'z'))
310 void server_process_syslog_message(
313 const struct ucred *ucred,
314 const struct timeval *tv,
318 char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
319 syslog_facility[sizeof("SYSLOG_FACILITY") + DECIMAL_STR_MAX(int)];
320 const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
321 struct iovec iovec[N_IOVEC_META_FIELDS + 6];
323 int priority = LOG_USER | LOG_INFO;
324 _cleanup_free_ char *identifier = NULL, *pid = NULL;
331 syslog_parse_priority(&buf, &priority, true);
333 if (s->forward_to_syslog)
334 forward_syslog_raw(s, priority, orig, ucred, tv);
336 syslog_skip_date((char**) &buf);
337 syslog_parse_identifier(&buf, &identifier, &pid);
339 if (s->forward_to_kmsg)
340 server_forward_kmsg(s, priority, identifier, buf, ucred);
342 if (s->forward_to_console)
343 server_forward_console(s, priority, identifier, buf, ucred);
345 if (s->forward_to_wall)
346 server_forward_wall(s, priority, identifier, buf, ucred);
348 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
350 sprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
351 IOVEC_SET_STRING(iovec[n++], syslog_priority);
353 if (priority & LOG_FACMASK) {
354 sprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
355 IOVEC_SET_STRING(iovec[n++], syslog_facility);
359 syslog_identifier = strjoina("SYSLOG_IDENTIFIER=", identifier);
360 if (syslog_identifier)
361 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
365 syslog_pid = strjoina("SYSLOG_PID=", pid);
367 IOVEC_SET_STRING(iovec[n++], syslog_pid);
370 message = strjoina("MESSAGE=", buf);
372 IOVEC_SET_STRING(iovec[n++], message);
374 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
377 int server_open_syslog_socket(Server *s) {
378 static const int one = 1;
383 if (s->syslog_fd < 0) {
384 static const union sockaddr_union sa = {
385 .un.sun_family = AF_UNIX,
386 .un.sun_path = "/run/systemd/journal/dev-log",
389 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
390 if (s->syslog_fd < 0)
391 return log_error_errno(errno, "socket() failed: %m");
393 unlink(sa.un.sun_path);
395 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
397 return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
399 chmod(sa.un.sun_path, 0666);
401 fd_nonblock(s->syslog_fd, 1);
403 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
405 return log_error_errno(errno, "SO_PASSCRED failed: %m");
408 if (mac_selinux_use()) {
409 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
411 log_warning_errno(errno, "SO_PASSSEC failed: %m");
415 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
417 return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
419 r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, server_process_datagram, s);
421 return log_error_errno(r, "Failed to add syslog server fd to event loop: %m");
426 void server_maybe_warn_forward_syslog_missed(Server *s) {
430 if (s->n_forward_syslog_missed <= 0)
433 n = now(CLOCK_MONOTONIC);
434 if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
437 server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
439 s->n_forward_syslog_missed = 0;
440 s->last_warn_forward_syslog_missed = n;