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>
26 #include "sd-messages.h"
28 #include "formats-util.h"
29 #include "journald-console.h"
30 #include "journald-kmsg.h"
31 #include "journald-server.h"
32 #include "journald-wall.h"
33 #include "process-util.h"
34 #include "selinux-util.h"
35 #include "socket-util.h"
36 #include "string-util.h"
37 #include "journald-syslog.h"
39 /* Warn once every 30s if we missed syslog message */
40 #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
42 static void forward_syslog_iovec(Server
*s
, const struct iovec
*iovec
, unsigned n_iovec
, const struct ucred
*ucred
, const struct timeval
*tv
) {
44 static const union sockaddr_union sa
= {
45 .un
.sun_family
= AF_UNIX
,
46 .un
.sun_path
= "/run/systemd/journal/syslog",
48 struct msghdr msghdr
= {
49 .msg_iov
= (struct iovec
*) iovec
,
50 .msg_iovlen
= n_iovec
,
51 .msg_name
= (struct sockaddr
*) &sa
.sa
,
52 .msg_namelen
= offsetof(union sockaddr_union
, un
.sun_path
)
53 + strlen("/run/systemd/journal/syslog"),
57 struct cmsghdr cmsghdr
;
58 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
))];
67 msghdr
.msg_control
= &control
;
68 msghdr
.msg_controllen
= sizeof(control
);
70 cmsg
= CMSG_FIRSTHDR(&msghdr
);
71 cmsg
->cmsg_level
= SOL_SOCKET
;
72 cmsg
->cmsg_type
= SCM_CREDENTIALS
;
73 cmsg
->cmsg_len
= CMSG_LEN(sizeof(struct ucred
));
74 memcpy(CMSG_DATA(cmsg
), ucred
, sizeof(struct ucred
));
75 msghdr
.msg_controllen
= cmsg
->cmsg_len
;
78 /* Forward the syslog message we received via /dev/log to
79 * /run/systemd/syslog. Unfortunately we currently can't set
80 * the SO_TIMESTAMP auxiliary data, and hence we don't. */
82 if (sendmsg(s
->syslog_fd
, &msghdr
, MSG_NOSIGNAL
) >= 0)
85 /* The socket is full? I guess the syslog implementation is
86 * too slow, and we shouldn't wait for that... */
87 if (errno
== EAGAIN
) {
88 s
->n_forward_syslog_missed
++;
92 if (ucred
&& (errno
== ESRCH
|| errno
== EPERM
)) {
95 /* Hmm, presumably the sender process vanished
96 * by now, or we don't have CAP_SYS_AMDIN, so
97 * let's fix it as good as we can, and retry */
101 memcpy(CMSG_DATA(cmsg
), &u
, sizeof(struct ucred
));
103 if (sendmsg(s
->syslog_fd
, &msghdr
, MSG_NOSIGNAL
) >= 0)
106 if (errno
== EAGAIN
) {
107 s
->n_forward_syslog_missed
++;
113 log_debug_errno(errno
, "Failed to forward syslog message: %m");
116 static void forward_syslog_raw(Server
*s
, int priority
, const char *buffer
, const struct ucred
*ucred
, const struct timeval
*tv
) {
122 if (LOG_PRI(priority
) > s
->max_level_syslog
)
125 IOVEC_SET_STRING(iovec
, buffer
);
126 forward_syslog_iovec(s
, &iovec
, 1, ucred
, tv
);
129 void server_forward_syslog(Server
*s
, int priority
, const char *identifier
, const char *message
, const struct ucred
*ucred
, const struct timeval
*tv
) {
130 struct iovec iovec
[5];
131 char header_priority
[DECIMAL_STR_MAX(priority
) + 3], header_time
[64],
132 header_pid
[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t
) + 1];
136 char *ident_buf
= NULL
;
139 assert(priority
>= 0);
140 assert(priority
<= 999);
143 if (LOG_PRI(priority
) > s
->max_level_syslog
)
146 /* First: priority field */
147 xsprintf(header_priority
, "<%i>", priority
);
148 IOVEC_SET_STRING(iovec
[n
++], header_priority
);
150 /* Second: timestamp */
151 t
= tv
? tv
->tv_sec
: ((time_t) (now(CLOCK_REALTIME
) / USEC_PER_SEC
));
155 if (strftime(header_time
, sizeof(header_time
), "%h %e %T ", tm
) <= 0)
157 IOVEC_SET_STRING(iovec
[n
++], header_time
);
159 /* Third: identifier and PID */
162 get_process_comm(ucred
->pid
, &ident_buf
);
163 identifier
= ident_buf
;
166 xsprintf(header_pid
, "["PID_FMT
"]: ", ucred
->pid
);
169 IOVEC_SET_STRING(iovec
[n
++], identifier
);
171 IOVEC_SET_STRING(iovec
[n
++], header_pid
);
172 } else if (identifier
) {
173 IOVEC_SET_STRING(iovec
[n
++], identifier
);
174 IOVEC_SET_STRING(iovec
[n
++], ": ");
177 /* Fourth: message */
178 IOVEC_SET_STRING(iovec
[n
++], message
);
180 forward_syslog_iovec(s
, iovec
, n
, ucred
, tv
);
185 int syslog_fixup_facility(int priority
) {
187 if ((priority
& LOG_FACMASK
) == 0)
188 return (priority
& LOG_PRIMASK
) | LOG_USER
;
193 size_t syslog_parse_identifier(const char **buf
, char **identifier
, char **pid
) {
204 p
+= strspn(p
, WHITESPACE
);
205 l
= strcspn(p
, WHITESPACE
);
220 t
= strndup(p
+k
+1, l
-k
-2);
239 if (strchr(WHITESPACE
, p
[e
]))
245 static void syslog_skip_date(char **buf
) {
253 LETTER
, LETTER
, LETTER
,
255 SPACE_OR_NUMBER
, NUMBER
,
257 SPACE_OR_NUMBER
, NUMBER
,
259 SPACE_OR_NUMBER
, NUMBER
,
261 SPACE_OR_NUMBER
, NUMBER
,
273 for (i
= 0; i
< ELEMENTSOF(sequence
); i
++, p
++) {
278 switch (sequence
[i
]) {
285 case SPACE_OR_NUMBER
:
292 if (*p
< '0' || *p
> '9')
298 if (!(*p
>= 'A' && *p
<= 'Z') &&
299 !(*p
>= 'a' && *p
<= 'z'))
315 void server_process_syslog_message(
318 const struct ucred
*ucred
,
319 const struct timeval
*tv
,
323 char syslog_priority
[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
324 syslog_facility
[sizeof("SYSLOG_FACILITY") + DECIMAL_STR_MAX(int)];
325 const char *message
= NULL
, *syslog_identifier
= NULL
, *syslog_pid
= NULL
;
326 struct iovec iovec
[N_IOVEC_META_FIELDS
+ 6];
328 int priority
= LOG_USER
| LOG_INFO
;
329 _cleanup_free_
char *identifier
= NULL
, *pid
= NULL
;
336 syslog_parse_priority(&buf
, &priority
, true);
338 if (s
->forward_to_syslog
)
339 forward_syslog_raw(s
, priority
, orig
, ucred
, tv
);
341 syslog_skip_date((char**) &buf
);
342 syslog_parse_identifier(&buf
, &identifier
, &pid
);
344 if (s
->forward_to_kmsg
)
345 server_forward_kmsg(s
, priority
, identifier
, buf
, ucred
);
347 if (s
->forward_to_console
)
348 server_forward_console(s
, priority
, identifier
, buf
, ucred
);
350 if (s
->forward_to_wall
)
351 server_forward_wall(s
, priority
, identifier
, buf
, ucred
);
353 IOVEC_SET_STRING(iovec
[n
++], "_TRANSPORT=syslog");
355 sprintf(syslog_priority
, "PRIORITY=%i", priority
& LOG_PRIMASK
);
356 IOVEC_SET_STRING(iovec
[n
++], syslog_priority
);
358 if (priority
& LOG_FACMASK
) {
359 sprintf(syslog_facility
, "SYSLOG_FACILITY=%i", LOG_FAC(priority
));
360 IOVEC_SET_STRING(iovec
[n
++], syslog_facility
);
364 syslog_identifier
= strjoina("SYSLOG_IDENTIFIER=", identifier
);
365 if (syslog_identifier
)
366 IOVEC_SET_STRING(iovec
[n
++], syslog_identifier
);
370 syslog_pid
= strjoina("SYSLOG_PID=", pid
);
372 IOVEC_SET_STRING(iovec
[n
++], syslog_pid
);
375 message
= strjoina("MESSAGE=", buf
);
377 IOVEC_SET_STRING(iovec
[n
++], message
);
379 server_dispatch_message(s
, iovec
, n
, ELEMENTSOF(iovec
), ucred
, tv
, label
, label_len
, NULL
, priority
, 0);
382 int server_open_syslog_socket(Server
*s
) {
383 static const int one
= 1;
388 if (s
->syslog_fd
< 0) {
389 static const union sockaddr_union sa
= {
390 .un
.sun_family
= AF_UNIX
,
391 .un
.sun_path
= "/run/systemd/journal/dev-log",
394 s
->syslog_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
395 if (s
->syslog_fd
< 0)
396 return log_error_errno(errno
, "socket() failed: %m");
398 unlink(sa
.un
.sun_path
);
400 r
= bind(s
->syslog_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
));
402 return log_error_errno(errno
, "bind(%s) failed: %m", sa
.un
.sun_path
);
404 (void) chmod(sa
.un
.sun_path
, 0666);
406 fd_nonblock(s
->syslog_fd
, 1);
408 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
));
410 return log_error_errno(errno
, "SO_PASSCRED failed: %m");
413 if (mac_selinux_use()) {
414 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_PASSSEC
, &one
, sizeof(one
));
416 log_warning_errno(errno
, "SO_PASSSEC failed: %m");
420 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_TIMESTAMP
, &one
, sizeof(one
));
422 return log_error_errno(errno
, "SO_TIMESTAMP failed: %m");
424 r
= sd_event_add_io(s
->event
, &s
->syslog_event_source
, s
->syslog_fd
, EPOLLIN
, server_process_datagram
, s
);
426 return log_error_errno(r
, "Failed to add syslog server fd to event loop: %m");
431 void server_maybe_warn_forward_syslog_missed(Server
*s
) {
435 if (s
->n_forward_syslog_missed
<= 0)
438 n
= now(CLOCK_MONOTONIC
);
439 if (s
->last_warn_forward_syslog_missed
+ WARN_FORWARD_SYSLOG_MISSED_USEC
> n
)
442 server_driver_message(s
, SD_MESSAGE_FORWARD_SYSLOG_MISSED
, "Forwarding to syslog missed %u messages.", s
->n_forward_syslog_missed
);
444 s
->n_forward_syslog_missed
= 0;
445 s
->last_warn_forward_syslog_missed
= n
;