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/>.
23 #include <sys/epoll.h>
25 #include "socket-util.h"
27 #include "journald-syslog.h"
28 #include "journald-kmsg.h"
30 static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, struct ucred *ucred, struct timeval *tv) {
34 struct cmsghdr cmsghdr;
35 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
37 union sockaddr_union sa;
44 msghdr.msg_iov = (struct iovec*) iovec;
45 msghdr.msg_iovlen = n_iovec;
48 sa.un.sun_family = AF_UNIX;
49 strncpy(sa.un.sun_path, "/run/systemd/journal/syslog", sizeof(sa.un.sun_path));
50 msghdr.msg_name = &sa;
51 msghdr.msg_namelen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path);
55 msghdr.msg_control = &control;
56 msghdr.msg_controllen = sizeof(control);
58 cmsg = CMSG_FIRSTHDR(&msghdr);
59 cmsg->cmsg_level = SOL_SOCKET;
60 cmsg->cmsg_type = SCM_CREDENTIALS;
61 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
62 memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
63 msghdr.msg_controllen = cmsg->cmsg_len;
66 /* Forward the syslog message we received via /dev/log to
67 * /run/systemd/syslog. Unfortunately we currently can't set
68 * the SO_TIMESTAMP auxiliary data, and hence we don't. */
70 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
73 /* The socket is full? I guess the syslog implementation is
74 * too slow, and we shouldn't wait for that... */
78 if (ucred && errno == ESRCH) {
81 /* Hmm, presumably the sender process vanished
82 * by now, so let's fix it as good as we
87 memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
89 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
97 log_debug("Failed to forward syslog message: %m");
100 static void forward_syslog_raw(Server *s, int priority, const char *buffer, struct ucred *ucred, struct timeval *tv) {
106 if (LOG_PRI(priority) > s->max_level_syslog)
109 IOVEC_SET_STRING(iovec, buffer);
110 forward_syslog_iovec(s, &iovec, 1, ucred, tv);
113 void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred, struct timeval *tv) {
114 struct iovec iovec[5];
115 char header_priority[6], header_time[64], header_pid[16];
119 char *ident_buf = NULL;
122 assert(priority >= 0);
123 assert(priority <= 999);
126 if (LOG_PRI(priority) > s->max_level_syslog)
129 /* First: priority field */
130 snprintf(header_priority, sizeof(header_priority), "<%i>", priority);
131 char_array_0(header_priority);
132 IOVEC_SET_STRING(iovec[n++], header_priority);
134 /* Second: timestamp */
135 t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
139 if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
141 IOVEC_SET_STRING(iovec[n++], header_time);
143 /* Third: identifier and PID */
146 get_process_comm(ucred->pid, &ident_buf);
147 identifier = ident_buf;
150 snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid);
151 char_array_0(header_pid);
154 IOVEC_SET_STRING(iovec[n++], identifier);
156 IOVEC_SET_STRING(iovec[n++], header_pid);
157 } else if (identifier) {
158 IOVEC_SET_STRING(iovec[n++], identifier);
159 IOVEC_SET_STRING(iovec[n++], ": ");
162 /* Fourth: message */
163 IOVEC_SET_STRING(iovec[n++], message);
165 forward_syslog_iovec(s, iovec, n, ucred, tv);
170 int syslog_fixup_facility(int priority) {
172 if ((priority & LOG_FACMASK) == 0)
173 return (priority & LOG_PRIMASK) | LOG_USER;
178 void syslog_parse_identifier(const char **buf, char **identifier, char **pid) {
189 p += strspn(p, WHITESPACE);
190 l = strcspn(p, WHITESPACE);
205 t = strndup(p+k+1, l-k-2);
225 *buf += strspn(*buf, WHITESPACE);
228 void syslog_parse_priority(char **p, int *priority) {
229 int a = 0, b = 0, c = 0;
239 if (!strchr(*p, '>'))
242 if ((*p)[2] == '>') {
243 c = undecchar((*p)[1]);
245 } else if ((*p)[3] == '>') {
246 b = undecchar((*p)[1]);
247 c = undecchar((*p)[2]);
249 } else if ((*p)[4] == '>') {
250 a = undecchar((*p)[1]);
251 b = undecchar((*p)[2]);
252 c = undecchar((*p)[3]);
257 if (a < 0 || b < 0 || c < 0)
260 *priority = a*100+b*10+c;
264 static void syslog_skip_date(char **buf) {
272 LETTER, LETTER, LETTER,
274 SPACE_OR_NUMBER, NUMBER,
276 SPACE_OR_NUMBER, NUMBER,
278 SPACE_OR_NUMBER, NUMBER,
280 SPACE_OR_NUMBER, NUMBER,
292 for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
297 switch (sequence[i]) {
304 case SPACE_OR_NUMBER:
311 if (*p < '0' || *p > '9')
317 if (!(*p >= 'A' && *p <= 'Z') &&
318 !(*p >= 'a' && *p <= 'z'))
334 void server_process_syslog_message(
342 char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
343 struct iovec iovec[N_IOVEC_META_FIELDS + 6];
345 int priority = LOG_USER | LOG_INFO;
346 char *identifier = NULL, *pid = NULL;
353 syslog_parse_priority((char**) &buf, &priority);
355 if (s->forward_to_syslog)
356 forward_syslog_raw(s, priority, orig, ucred, tv);
358 syslog_skip_date((char**) &buf);
359 syslog_parse_identifier(&buf, &identifier, &pid);
361 if (s->forward_to_kmsg)
362 server_forward_kmsg(s, priority, identifier, buf, ucred);
364 if (s->forward_to_console)
365 server_forward_console(s, priority, identifier, buf, ucred);
367 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
369 if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
370 IOVEC_SET_STRING(iovec[n++], syslog_priority);
372 if (priority & LOG_FACMASK)
373 if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
374 IOVEC_SET_STRING(iovec[n++], syslog_facility);
377 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
378 if (syslog_identifier)
379 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
383 syslog_pid = strappend("SYSLOG_PID=", pid);
385 IOVEC_SET_STRING(iovec[n++], syslog_pid);
388 message = strappend("MESSAGE=", buf);
390 IOVEC_SET_STRING(iovec[n++], message);
392 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority);
397 free(syslog_priority);
398 free(syslog_facility);
399 free(syslog_identifier);
403 int server_open_syslog_socket(Server *s) {
404 union sockaddr_union sa;
406 struct epoll_event ev;
410 if (s->syslog_fd < 0) {
412 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
413 if (s->syslog_fd < 0) {
414 log_error("socket() failed: %m");
419 sa.un.sun_family = AF_UNIX;
420 strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
422 unlink(sa.un.sun_path);
424 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
426 log_error("bind() failed: %m");
430 chmod(sa.un.sun_path, 0666);
432 fd_nonblock(s->syslog_fd, 1);
435 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
437 log_error("SO_PASSCRED failed: %m");
443 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
445 log_warning("SO_PASSSEC failed: %m");
449 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
451 log_error("SO_TIMESTAMP failed: %m");
457 ev.data.fd = s->syslog_fd;
458 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->syslog_fd, &ev) < 0) {
459 log_error("Failed to add syslog server fd to epoll object: %m");