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"
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) {
40 struct cmsghdr cmsghdr;
41 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
43 union sockaddr_union sa;
50 msghdr.msg_iov = (struct iovec*) iovec;
51 msghdr.msg_iovlen = n_iovec;
54 sa.un.sun_family = AF_UNIX;
55 strncpy(sa.un.sun_path, "/run/systemd/journal/syslog", sizeof(sa.un.sun_path));
56 msghdr.msg_name = &sa;
57 msghdr.msg_namelen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path);
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 void 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);
235 *buf += strspn(*buf, WHITESPACE);
238 void syslog_parse_priority(char **p, int *priority) {
239 int a = 0, b = 0, c = 0;
249 if (!strchr(*p, '>'))
252 if ((*p)[2] == '>') {
253 c = undecchar((*p)[1]);
255 } else if ((*p)[3] == '>') {
256 b = undecchar((*p)[1]);
257 c = undecchar((*p)[2]);
259 } else if ((*p)[4] == '>') {
260 a = undecchar((*p)[1]);
261 b = undecchar((*p)[2]);
262 c = undecchar((*p)[3]);
267 if (a < 0 || b < 0 || c < 0)
270 *priority = a*100+b*10+c;
274 static void syslog_skip_date(char **buf) {
282 LETTER, LETTER, LETTER,
284 SPACE_OR_NUMBER, NUMBER,
286 SPACE_OR_NUMBER, NUMBER,
288 SPACE_OR_NUMBER, NUMBER,
290 SPACE_OR_NUMBER, NUMBER,
302 for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
307 switch (sequence[i]) {
314 case SPACE_OR_NUMBER:
321 if (*p < '0' || *p > '9')
327 if (!(*p >= 'A' && *p <= 'Z') &&
328 !(*p >= 'a' && *p <= 'z'))
344 void server_process_syslog_message(
352 char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
353 struct iovec iovec[N_IOVEC_META_FIELDS + 6];
355 int priority = LOG_USER | LOG_INFO;
356 char *identifier = NULL, *pid = NULL;
363 syslog_parse_priority((char**) &buf, &priority);
365 if (s->forward_to_syslog)
366 forward_syslog_raw(s, priority, orig, ucred, tv);
368 syslog_skip_date((char**) &buf);
369 syslog_parse_identifier(&buf, &identifier, &pid);
371 if (s->forward_to_kmsg)
372 server_forward_kmsg(s, priority, identifier, buf, ucred);
374 if (s->forward_to_console)
375 server_forward_console(s, priority, identifier, buf, ucred);
377 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
379 if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
380 IOVEC_SET_STRING(iovec[n++], syslog_priority);
382 if (priority & LOG_FACMASK)
383 if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
384 IOVEC_SET_STRING(iovec[n++], syslog_facility);
387 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
388 if (syslog_identifier)
389 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
393 syslog_pid = strappend("SYSLOG_PID=", pid);
395 IOVEC_SET_STRING(iovec[n++], syslog_pid);
398 message = strappend("MESSAGE=", buf);
400 IOVEC_SET_STRING(iovec[n++], message);
402 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority);
407 free(syslog_priority);
408 free(syslog_facility);
409 free(syslog_identifier);
413 int server_open_syslog_socket(Server *s) {
414 union sockaddr_union sa;
416 struct epoll_event ev;
420 if (s->syslog_fd < 0) {
422 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
423 if (s->syslog_fd < 0) {
424 log_error("socket() failed: %m");
429 sa.un.sun_family = AF_UNIX;
430 strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
432 unlink(sa.un.sun_path);
434 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
436 log_error("bind() failed: %m");
440 chmod(sa.un.sun_path, 0666);
442 fd_nonblock(s->syslog_fd, 1);
445 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
447 log_error("SO_PASSCRED failed: %m");
453 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
455 log_warning("SO_PASSSEC failed: %m");
459 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
461 log_error("SO_TIMESTAMP failed: %m");
467 ev.data.fd = s->syslog_fd;
468 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->syslog_fd, &ev) < 0) {
469 log_error("Failed to add syslog server fd to epoll object: %m");
476 void server_maybe_warn_forward_syslog_missed(Server *s) {
480 if (s->n_forward_syslog_missed <= 0)
483 n = now(CLOCK_MONOTONIC);
484 if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
487 server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
489 s->n_forward_syslog_missed = 0;
490 s->last_warn_forward_syslog_missed = n;