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 "journald-server.h"
29 #include "journald-syslog.h"
30 #include "journald-kmsg.h"
31 #include "journald-console.h"
33 /* Warn once every 30s if we missed syslog message */
34 #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
36 static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, struct ucred *ucred, struct timeval *tv) {
38 union sockaddr_union sa = {
39 .un.sun_family = AF_UNIX,
40 .un.sun_path = "/run/systemd/journal/syslog",
42 struct msghdr msghdr = {
43 .msg_iov = (struct iovec *) iovec,
44 .msg_iovlen = n_iovec,
46 .msg_namelen = offsetof(union sockaddr_union, un.sun_path)
47 + sizeof("/run/systemd/journal/syslog") - 1,
51 struct cmsghdr cmsghdr;
52 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
61 msghdr.msg_control = &control;
62 msghdr.msg_controllen = sizeof(control);
64 cmsg = CMSG_FIRSTHDR(&msghdr);
65 cmsg->cmsg_level = SOL_SOCKET;
66 cmsg->cmsg_type = SCM_CREDENTIALS;
67 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
68 memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
69 msghdr.msg_controllen = cmsg->cmsg_len;
72 /* Forward the syslog message we received via /dev/log to
73 * /run/systemd/syslog. Unfortunately we currently can't set
74 * the SO_TIMESTAMP auxiliary data, and hence we don't. */
76 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
79 /* The socket is full? I guess the syslog implementation is
80 * too slow, and we shouldn't wait for that... */
81 if (errno == EAGAIN) {
82 s->n_forward_syslog_missed++;
86 if (ucred && errno == ESRCH) {
89 /* Hmm, presumably the sender process vanished
90 * by now, so let's fix it as good as we
95 memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
97 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
100 if (errno == EAGAIN) {
101 s->n_forward_syslog_missed++;
107 log_debug("Failed to forward syslog message: %m");
110 static void forward_syslog_raw(Server *s, int priority, const char *buffer, struct ucred *ucred, struct timeval *tv) {
116 if (LOG_PRI(priority) > s->max_level_syslog)
119 IOVEC_SET_STRING(iovec, buffer);
120 forward_syslog_iovec(s, &iovec, 1, ucred, tv);
123 void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred, struct timeval *tv) {
124 struct iovec iovec[5];
125 char header_priority[6], header_time[64], header_pid[16];
129 char *ident_buf = NULL;
132 assert(priority >= 0);
133 assert(priority <= 999);
136 if (LOG_PRI(priority) > s->max_level_syslog)
139 /* First: priority field */
140 snprintf(header_priority, sizeof(header_priority), "<%i>", priority);
141 char_array_0(header_priority);
142 IOVEC_SET_STRING(iovec[n++], header_priority);
144 /* Second: timestamp */
145 t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
149 if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
151 IOVEC_SET_STRING(iovec[n++], header_time);
153 /* Third: identifier and PID */
156 get_process_comm(ucred->pid, &ident_buf);
157 identifier = ident_buf;
160 snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid);
161 char_array_0(header_pid);
164 IOVEC_SET_STRING(iovec[n++], identifier);
166 IOVEC_SET_STRING(iovec[n++], header_pid);
167 } else if (identifier) {
168 IOVEC_SET_STRING(iovec[n++], identifier);
169 IOVEC_SET_STRING(iovec[n++], ": ");
172 /* Fourth: message */
173 IOVEC_SET_STRING(iovec[n++], message);
175 forward_syslog_iovec(s, iovec, n, ucred, tv);
180 int syslog_fixup_facility(int priority) {
182 if ((priority & LOG_FACMASK) == 0)
183 return (priority & LOG_PRIMASK) | LOG_USER;
188 size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) {
199 p += strspn(p, WHITESPACE);
200 l = strcspn(p, WHITESPACE);
215 t = strndup(p+k+1, l-k-2);
234 e += strspn(p + e, WHITESPACE);
239 void syslog_parse_priority(char **p, int *priority, bool with_facility) {
240 int a = 0, b = 0, c = 0;
250 if (!strchr(*p, '>'))
253 if ((*p)[2] == '>') {
254 c = undecchar((*p)[1]);
256 } else if ((*p)[3] == '>') {
257 b = undecchar((*p)[1]);
258 c = undecchar((*p)[2]);
260 } else if ((*p)[4] == '>') {
261 a = undecchar((*p)[1]);
262 b = undecchar((*p)[2]);
263 c = undecchar((*p)[3]);
268 if (a < 0 || b < 0 || c < 0 ||
269 (!with_facility && (a || b || c > 7)))
273 *priority = a*100 + b*10 + c;
275 *priority = (*priority & LOG_FACMASK) | c;
279 static void syslog_skip_date(char **buf) {
287 LETTER, LETTER, LETTER,
289 SPACE_OR_NUMBER, NUMBER,
291 SPACE_OR_NUMBER, NUMBER,
293 SPACE_OR_NUMBER, NUMBER,
295 SPACE_OR_NUMBER, NUMBER,
307 for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
312 switch (sequence[i]) {
319 case SPACE_OR_NUMBER:
326 if (*p < '0' || *p > '9')
332 if (!(*p >= 'A' && *p <= 'Z') &&
333 !(*p >= 'a' && *p <= 'z'))
349 void server_process_syslog_message(
357 char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
358 struct iovec iovec[N_IOVEC_META_FIELDS + 6];
360 int priority = LOG_USER | LOG_INFO;
361 char *identifier = NULL, *pid = NULL;
368 syslog_parse_priority((char**) &buf, &priority, true);
370 if (s->forward_to_syslog)
371 forward_syslog_raw(s, priority, orig, ucred, tv);
373 syslog_skip_date((char**) &buf);
374 syslog_parse_identifier(&buf, &identifier, &pid);
376 if (s->forward_to_kmsg)
377 server_forward_kmsg(s, priority, identifier, buf, ucred);
379 if (s->forward_to_console)
380 server_forward_console(s, priority, identifier, buf, ucred);
382 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
384 if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
385 IOVEC_SET_STRING(iovec[n++], syslog_priority);
387 if (priority & LOG_FACMASK)
388 if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
389 IOVEC_SET_STRING(iovec[n++], syslog_facility);
392 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
393 if (syslog_identifier)
394 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
398 syslog_pid = strappend("SYSLOG_PID=", pid);
400 IOVEC_SET_STRING(iovec[n++], syslog_pid);
403 message = strappend("MESSAGE=", buf);
405 IOVEC_SET_STRING(iovec[n++], message);
407 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
412 free(syslog_priority);
413 free(syslog_facility);
414 free(syslog_identifier);
418 int server_open_syslog_socket(Server *s) {
420 struct epoll_event ev;
424 if (s->syslog_fd < 0) {
425 union sockaddr_union sa = {
426 .un.sun_family = AF_UNIX,
427 .un.sun_path = "/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() failed: %m");
444 chmod(sa.un.sun_path, 0666);
446 fd_nonblock(s->syslog_fd, 1);
449 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
451 log_error("SO_PASSCRED failed: %m");
457 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
459 log_warning("SO_PASSSEC failed: %m");
463 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
465 log_error("SO_TIMESTAMP failed: %m");
471 ev.data.fd = s->syslog_fd;
472 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->syslog_fd, &ev) < 0) {
473 log_error("Failed to add syslog server fd to epoll object: %m");
480 void server_maybe_warn_forward_syslog_missed(Server *s) {
484 if (s->n_forward_syslog_missed <= 0)
487 n = now(CLOCK_MONOTONIC);
488 if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
491 server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
493 s->n_forward_syslog_missed = 0;
494 s->last_warn_forward_syslog_missed = n;