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, struct ucred *ucred, struct timeval *tv) {
40 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,
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, struct ucred *ucred, 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, struct ucred *ucred, 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), "[%lu]: ", (unsigned long) 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(
359 char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
360 struct iovec iovec[N_IOVEC_META_FIELDS + 6];
362 int priority = LOG_USER | LOG_INFO;
363 char *identifier = NULL, *pid = NULL;
370 syslog_parse_priority(&buf, &priority, true);
372 if (s->forward_to_syslog)
373 forward_syslog_raw(s, priority, orig, ucred, tv);
375 syslog_skip_date((char**) &buf);
376 syslog_parse_identifier(&buf, &identifier, &pid);
378 if (s->forward_to_kmsg)
379 server_forward_kmsg(s, priority, identifier, buf, ucred);
381 if (s->forward_to_console)
382 server_forward_console(s, priority, identifier, buf, ucred);
384 if (s->forward_to_wall)
385 server_forward_wall(s, priority, identifier, buf, ucred);
387 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
389 if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
390 IOVEC_SET_STRING(iovec[n++], syslog_priority);
392 if (priority & LOG_FACMASK)
393 if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
394 IOVEC_SET_STRING(iovec[n++], syslog_facility);
397 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
398 if (syslog_identifier)
399 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
403 syslog_pid = strappend("SYSLOG_PID=", pid);
405 IOVEC_SET_STRING(iovec[n++], syslog_pid);
408 message = strappend("MESSAGE=", buf);
410 IOVEC_SET_STRING(iovec[n++], message);
412 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
417 free(syslog_priority);
418 free(syslog_facility);
419 free(syslog_identifier);
423 int server_open_syslog_socket(Server *s) {
428 if (s->syslog_fd < 0) {
429 union sockaddr_union sa = {
430 .un.sun_family = AF_UNIX,
431 .un.sun_path = "/dev/log",
434 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
435 if (s->syslog_fd < 0) {
436 log_error("socket() failed: %m");
440 unlink(sa.un.sun_path);
442 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
444 log_error("bind() failed: %m");
448 chmod(sa.un.sun_path, 0666);
450 fd_nonblock(s->syslog_fd, 1);
453 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
455 log_error("SO_PASSCRED failed: %m");
462 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
464 log_warning("SO_PASSSEC failed: %m");
469 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
471 log_error("SO_TIMESTAMP failed: %m");
475 r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, process_datagram, s);
477 log_error("Failed to add syslog server fd to event loop: %s", strerror(-r));
484 void server_maybe_warn_forward_syslog_missed(Server *s) {
488 if (s->n_forward_syslog_missed <= 0)
491 n = now(CLOCK_MONOTONIC);
492 if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
495 server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
497 s->n_forward_syslog_missed = 0;
498 s->last_warn_forward_syslog_missed = n;