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) {
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 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) {
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)
271 *priority = a*100+b*10+c;
275 static void syslog_skip_date(char **buf) {
283 LETTER, LETTER, LETTER,
285 SPACE_OR_NUMBER, NUMBER,
287 SPACE_OR_NUMBER, NUMBER,
289 SPACE_OR_NUMBER, NUMBER,
291 SPACE_OR_NUMBER, NUMBER,
303 for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
308 switch (sequence[i]) {
315 case SPACE_OR_NUMBER:
322 if (*p < '0' || *p > '9')
328 if (!(*p >= 'A' && *p <= 'Z') &&
329 !(*p >= 'a' && *p <= 'z'))
345 void server_process_syslog_message(
353 char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
354 struct iovec iovec[N_IOVEC_META_FIELDS + 6];
356 int priority = LOG_USER | LOG_INFO;
357 char *identifier = NULL, *pid = NULL;
364 syslog_parse_priority((char**) &buf, &priority);
366 if (s->forward_to_syslog)
367 forward_syslog_raw(s, priority, orig, ucred, tv);
369 syslog_skip_date((char**) &buf);
370 syslog_parse_identifier(&buf, &identifier, &pid);
372 if (s->forward_to_kmsg)
373 server_forward_kmsg(s, priority, identifier, buf, ucred);
375 if (s->forward_to_console)
376 server_forward_console(s, priority, identifier, buf, ucred);
378 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
380 if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
381 IOVEC_SET_STRING(iovec[n++], syslog_priority);
383 if (priority & LOG_FACMASK)
384 if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
385 IOVEC_SET_STRING(iovec[n++], syslog_facility);
388 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
389 if (syslog_identifier)
390 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
394 syslog_pid = strappend("SYSLOG_PID=", pid);
396 IOVEC_SET_STRING(iovec[n++], syslog_pid);
399 message = strappend("MESSAGE=", buf);
401 IOVEC_SET_STRING(iovec[n++], message);
403 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority);
408 free(syslog_priority);
409 free(syslog_facility);
410 free(syslog_identifier);
414 int server_open_syslog_socket(Server *s) {
415 union sockaddr_union sa;
417 struct epoll_event ev;
421 if (s->syslog_fd < 0) {
423 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
424 if (s->syslog_fd < 0) {
425 log_error("socket() failed: %m");
430 sa.un.sun_family = AF_UNIX;
431 strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
433 unlink(sa.un.sun_path);
435 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
437 log_error("bind() failed: %m");
441 chmod(sa.un.sun_path, 0666);
443 fd_nonblock(s->syslog_fd, 1);
446 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
448 log_error("SO_PASSCRED failed: %m");
454 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
456 log_warning("SO_PASSSEC failed: %m");
460 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
462 log_error("SO_TIMESTAMP failed: %m");
468 ev.data.fd = s->syslog_fd;
469 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->syslog_fd, &ev) < 0) {
470 log_error("Failed to add syslog server fd to epoll object: %m");
477 void server_maybe_warn_forward_syslog_missed(Server *s) {
481 if (s->n_forward_syslog_missed <= 0)
484 n = now(CLOCK_MONOTONIC);
485 if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
488 server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
490 s->n_forward_syslog_missed = 0;
491 s->last_warn_forward_syslog_missed = n;