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"
34 /* Warn once every 30s if we missed syslog message */
35 #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
37 static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, struct ucred *ucred, struct timeval *tv) {
39 union sockaddr_union sa = {
40 .un.sun_family = AF_UNIX,
41 .un.sun_path = "/run/systemd/journal/syslog",
43 struct msghdr msghdr = {
44 .msg_iov = (struct iovec *) iovec,
45 .msg_iovlen = n_iovec,
47 .msg_namelen = offsetof(union sockaddr_union, un.sun_path)
48 + sizeof("/run/systemd/journal/syslog") - 1,
52 struct cmsghdr cmsghdr;
53 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
62 msghdr.msg_control = &control;
63 msghdr.msg_controllen = sizeof(control);
65 cmsg = CMSG_FIRSTHDR(&msghdr);
66 cmsg->cmsg_level = SOL_SOCKET;
67 cmsg->cmsg_type = SCM_CREDENTIALS;
68 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
69 memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
70 msghdr.msg_controllen = cmsg->cmsg_len;
73 /* Forward the syslog message we received via /dev/log to
74 * /run/systemd/syslog. Unfortunately we currently can't set
75 * the SO_TIMESTAMP auxiliary data, and hence we don't. */
77 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
80 /* The socket is full? I guess the syslog implementation is
81 * too slow, and we shouldn't wait for that... */
82 if (errno == EAGAIN) {
83 s->n_forward_syslog_missed++;
87 if (ucred && errno == ESRCH) {
90 /* Hmm, presumably the sender process vanished
91 * by now, so let's fix it as good as we
96 memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
98 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
101 if (errno == EAGAIN) {
102 s->n_forward_syslog_missed++;
108 log_debug("Failed to forward syslog message: %m");
111 static void forward_syslog_raw(Server *s, int priority, const char *buffer, struct ucred *ucred, struct timeval *tv) {
117 if (LOG_PRI(priority) > s->max_level_syslog)
120 IOVEC_SET_STRING(iovec, buffer);
121 forward_syslog_iovec(s, &iovec, 1, ucred, tv);
124 void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred, struct timeval *tv) {
125 struct iovec iovec[5];
126 char header_priority[6], header_time[64], header_pid[16];
130 char *ident_buf = NULL;
133 assert(priority >= 0);
134 assert(priority <= 999);
137 if (LOG_PRI(priority) > s->max_level_syslog)
140 /* First: priority field */
141 snprintf(header_priority, sizeof(header_priority), "<%i>", priority);
142 char_array_0(header_priority);
143 IOVEC_SET_STRING(iovec[n++], header_priority);
145 /* Second: timestamp */
146 t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
150 if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
152 IOVEC_SET_STRING(iovec[n++], header_time);
154 /* Third: identifier and PID */
157 get_process_comm(ucred->pid, &ident_buf);
158 identifier = ident_buf;
161 snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid);
162 char_array_0(header_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 void syslog_parse_priority(char **p, int *priority, bool with_facility) {
241 int a = 0, b = 0, c = 0;
251 if (!strchr(*p, '>'))
254 if ((*p)[2] == '>') {
255 c = undecchar((*p)[1]);
257 } else if ((*p)[3] == '>') {
258 b = undecchar((*p)[1]);
259 c = undecchar((*p)[2]);
261 } else if ((*p)[4] == '>') {
262 a = undecchar((*p)[1]);
263 b = undecchar((*p)[2]);
264 c = undecchar((*p)[3]);
269 if (a < 0 || b < 0 || c < 0 ||
270 (!with_facility && (a || b || c > 7)))
274 *priority = a*100 + b*10 + c;
276 *priority = (*priority & LOG_FACMASK) | c;
280 static void syslog_skip_date(char **buf) {
288 LETTER, LETTER, LETTER,
290 SPACE_OR_NUMBER, NUMBER,
292 SPACE_OR_NUMBER, NUMBER,
294 SPACE_OR_NUMBER, NUMBER,
296 SPACE_OR_NUMBER, NUMBER,
308 for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
313 switch (sequence[i]) {
320 case SPACE_OR_NUMBER:
327 if (*p < '0' || *p > '9')
333 if (!(*p >= 'A' && *p <= 'Z') &&
334 !(*p >= 'a' && *p <= 'z'))
350 void server_process_syslog_message(
358 char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
359 struct iovec iovec[N_IOVEC_META_FIELDS + 6];
361 int priority = LOG_USER | LOG_INFO;
362 char *identifier = NULL, *pid = NULL;
369 syslog_parse_priority((char**) &buf, &priority, true);
371 if (s->forward_to_syslog)
372 forward_syslog_raw(s, priority, orig, ucred, tv);
374 syslog_skip_date((char**) &buf);
375 syslog_parse_identifier(&buf, &identifier, &pid);
377 if (s->forward_to_kmsg)
378 server_forward_kmsg(s, priority, identifier, buf, ucred);
380 if (s->forward_to_console)
381 server_forward_console(s, priority, identifier, buf, ucred);
383 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
385 if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
386 IOVEC_SET_STRING(iovec[n++], syslog_priority);
388 if (priority & LOG_FACMASK)
389 if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
390 IOVEC_SET_STRING(iovec[n++], syslog_facility);
393 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
394 if (syslog_identifier)
395 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
399 syslog_pid = strappend("SYSLOG_PID=", pid);
401 IOVEC_SET_STRING(iovec[n++], syslog_pid);
404 message = strappend("MESSAGE=", buf);
406 IOVEC_SET_STRING(iovec[n++], message);
408 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
413 free(syslog_priority);
414 free(syslog_facility);
415 free(syslog_identifier);
419 int server_open_syslog_socket(Server *s) {
421 struct epoll_event ev;
425 if (s->syslog_fd < 0) {
426 union sockaddr_union sa = {
427 .un.sun_family = AF_UNIX,
428 .un.sun_path = "/dev/log",
431 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
432 if (s->syslog_fd < 0) {
433 log_error("socket() failed: %m");
437 unlink(sa.un.sun_path);
439 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
441 log_error("bind() failed: %m");
445 chmod(sa.un.sun_path, 0666);
447 fd_nonblock(s->syslog_fd, 1);
450 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
452 log_error("SO_PASSCRED failed: %m");
459 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
461 log_warning("SO_PASSSEC failed: %m");
466 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
468 log_error("SO_TIMESTAMP failed: %m");
474 ev.data.fd = s->syslog_fd;
475 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->syslog_fd, &ev) < 0) {
476 log_error("Failed to add syslog server fd to epoll object: %m");
483 void server_maybe_warn_forward_syslog_missed(Server *s) {
487 if (s->n_forward_syslog_missed <= 0)
490 n = now(CLOCK_MONOTONIC);
491 if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
494 server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
496 s->n_forward_syslog_missed = 0;
497 s->last_warn_forward_syslog_missed = n;