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 "socket-util.h"
28 #include "journald-syslog.h"
29 #include "journald-kmsg.h"
30 #include "journald-console.h"
32 static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, struct ucred *ucred, struct timeval *tv) {
36 struct cmsghdr cmsghdr;
37 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
39 union sockaddr_union sa;
46 msghdr.msg_iov = (struct iovec*) iovec;
47 msghdr.msg_iovlen = n_iovec;
50 sa.un.sun_family = AF_UNIX;
51 strncpy(sa.un.sun_path, "/run/systemd/journal/syslog", sizeof(sa.un.sun_path));
52 msghdr.msg_name = &sa;
53 msghdr.msg_namelen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path);
57 msghdr.msg_control = &control;
58 msghdr.msg_controllen = sizeof(control);
60 cmsg = CMSG_FIRSTHDR(&msghdr);
61 cmsg->cmsg_level = SOL_SOCKET;
62 cmsg->cmsg_type = SCM_CREDENTIALS;
63 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
64 memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
65 msghdr.msg_controllen = cmsg->cmsg_len;
68 /* Forward the syslog message we received via /dev/log to
69 * /run/systemd/syslog. Unfortunately we currently can't set
70 * the SO_TIMESTAMP auxiliary data, and hence we don't. */
72 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
75 /* The socket is full? I guess the syslog implementation is
76 * too slow, and we shouldn't wait for that... */
80 if (ucred && errno == ESRCH) {
83 /* Hmm, presumably the sender process vanished
84 * by now, so let's fix it as good as we
89 memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
91 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
99 log_debug("Failed to forward syslog message: %m");
102 static void forward_syslog_raw(Server *s, int priority, const char *buffer, struct ucred *ucred, struct timeval *tv) {
108 if (LOG_PRI(priority) > s->max_level_syslog)
111 IOVEC_SET_STRING(iovec, buffer);
112 forward_syslog_iovec(s, &iovec, 1, ucred, tv);
115 void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred, struct timeval *tv) {
116 struct iovec iovec[5];
117 char header_priority[6], header_time[64], header_pid[16];
121 char *ident_buf = NULL;
124 assert(priority >= 0);
125 assert(priority <= 999);
128 if (LOG_PRI(priority) > s->max_level_syslog)
131 /* First: priority field */
132 snprintf(header_priority, sizeof(header_priority), "<%i>", priority);
133 char_array_0(header_priority);
134 IOVEC_SET_STRING(iovec[n++], header_priority);
136 /* Second: timestamp */
137 t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
141 if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
143 IOVEC_SET_STRING(iovec[n++], header_time);
145 /* Third: identifier and PID */
148 get_process_comm(ucred->pid, &ident_buf);
149 identifier = ident_buf;
152 snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid);
153 char_array_0(header_pid);
156 IOVEC_SET_STRING(iovec[n++], identifier);
158 IOVEC_SET_STRING(iovec[n++], header_pid);
159 } else if (identifier) {
160 IOVEC_SET_STRING(iovec[n++], identifier);
161 IOVEC_SET_STRING(iovec[n++], ": ");
164 /* Fourth: message */
165 IOVEC_SET_STRING(iovec[n++], message);
167 forward_syslog_iovec(s, iovec, n, ucred, tv);
172 int syslog_fixup_facility(int priority) {
174 if ((priority & LOG_FACMASK) == 0)
175 return (priority & LOG_PRIMASK) | LOG_USER;
180 void syslog_parse_identifier(const char **buf, char **identifier, char **pid) {
191 p += strspn(p, WHITESPACE);
192 l = strcspn(p, WHITESPACE);
207 t = strndup(p+k+1, l-k-2);
227 *buf += strspn(*buf, WHITESPACE);
230 void syslog_parse_priority(char **p, int *priority) {
231 int a = 0, b = 0, c = 0;
241 if (!strchr(*p, '>'))
244 if ((*p)[2] == '>') {
245 c = undecchar((*p)[1]);
247 } else if ((*p)[3] == '>') {
248 b = undecchar((*p)[1]);
249 c = undecchar((*p)[2]);
251 } else if ((*p)[4] == '>') {
252 a = undecchar((*p)[1]);
253 b = undecchar((*p)[2]);
254 c = undecchar((*p)[3]);
259 if (a < 0 || b < 0 || c < 0)
262 *priority = a*100+b*10+c;
266 static void syslog_skip_date(char **buf) {
274 LETTER, LETTER, LETTER,
276 SPACE_OR_NUMBER, NUMBER,
278 SPACE_OR_NUMBER, NUMBER,
280 SPACE_OR_NUMBER, NUMBER,
282 SPACE_OR_NUMBER, NUMBER,
294 for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
299 switch (sequence[i]) {
306 case SPACE_OR_NUMBER:
313 if (*p < '0' || *p > '9')
319 if (!(*p >= 'A' && *p <= 'Z') &&
320 !(*p >= 'a' && *p <= 'z'))
336 void server_process_syslog_message(
344 char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
345 struct iovec iovec[N_IOVEC_META_FIELDS + 6];
347 int priority = LOG_USER | LOG_INFO;
348 char *identifier = NULL, *pid = NULL;
355 syslog_parse_priority((char**) &buf, &priority);
357 if (s->forward_to_syslog)
358 forward_syslog_raw(s, priority, orig, ucred, tv);
360 syslog_skip_date((char**) &buf);
361 syslog_parse_identifier(&buf, &identifier, &pid);
363 if (s->forward_to_kmsg)
364 server_forward_kmsg(s, priority, identifier, buf, ucred);
366 if (s->forward_to_console)
367 server_forward_console(s, priority, identifier, buf, ucred);
369 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
371 if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
372 IOVEC_SET_STRING(iovec[n++], syslog_priority);
374 if (priority & LOG_FACMASK)
375 if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
376 IOVEC_SET_STRING(iovec[n++], syslog_facility);
379 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
380 if (syslog_identifier)
381 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
385 syslog_pid = strappend("SYSLOG_PID=", pid);
387 IOVEC_SET_STRING(iovec[n++], syslog_pid);
390 message = strappend("MESSAGE=", buf);
392 IOVEC_SET_STRING(iovec[n++], message);
394 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority);
399 free(syslog_priority);
400 free(syslog_facility);
401 free(syslog_identifier);
405 int server_open_syslog_socket(Server *s) {
406 union sockaddr_union sa;
408 struct epoll_event ev;
412 if (s->syslog_fd < 0) {
414 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
415 if (s->syslog_fd < 0) {
416 log_error("socket() failed: %m");
421 sa.un.sun_family = AF_UNIX;
422 strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
424 unlink(sa.un.sun_path);
426 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
428 log_error("bind() failed: %m");
432 chmod(sa.un.sun_path, 0666);
434 fd_nonblock(s->syslog_fd, 1);
437 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
439 log_error("SO_PASSCRED failed: %m");
445 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
447 log_warning("SO_PASSSEC failed: %m");
451 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
453 log_error("SO_TIMESTAMP failed: %m");
459 ev.data.fd = s->syslog_fd;
460 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->syslog_fd, &ev) < 0) {
461 log_error("Failed to add syslog server fd to epoll object: %m");