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"
29 #include "formats-util.h"
31 #include "journald-console.h"
32 #include "journald-kmsg.h"
33 #include "journald-server.h"
34 #include "journald-syslog.h"
35 #include "journald-wall.h"
36 #include "process-util.h"
37 #include "selinux-util.h"
38 #include "socket-util.h"
39 #include "stdio-util.h"
40 #include "string-util.h"
41 #include "syslog-util.h"
43 /* Warn once every 30s if we missed syslog message */
44 #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
46 static void forward_syslog_iovec(Server
*s
, const struct iovec
*iovec
, unsigned n_iovec
, const struct ucred
*ucred
, const struct timeval
*tv
) {
48 static const union sockaddr_union sa
= {
49 .un
.sun_family
= AF_UNIX
,
50 .un
.sun_path
= "/run/systemd/journal/syslog",
52 struct msghdr msghdr
= {
53 .msg_iov
= (struct iovec
*) iovec
,
54 .msg_iovlen
= n_iovec
,
55 .msg_name
= (struct sockaddr
*) &sa
.sa
,
56 .msg_namelen
= offsetof(union sockaddr_union
, un
.sun_path
)
57 + strlen("/run/systemd/journal/syslog"),
61 struct cmsghdr cmsghdr
;
62 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
))];
71 msghdr
.msg_control
= &control
;
72 msghdr
.msg_controllen
= sizeof(control
);
74 cmsg
= CMSG_FIRSTHDR(&msghdr
);
75 cmsg
->cmsg_level
= SOL_SOCKET
;
76 cmsg
->cmsg_type
= SCM_CREDENTIALS
;
77 cmsg
->cmsg_len
= CMSG_LEN(sizeof(struct ucred
));
78 memcpy(CMSG_DATA(cmsg
), ucred
, sizeof(struct ucred
));
79 msghdr
.msg_controllen
= cmsg
->cmsg_len
;
82 /* Forward the syslog message we received via /dev/log to
83 * /run/systemd/syslog. Unfortunately we currently can't set
84 * the SO_TIMESTAMP auxiliary data, and hence we don't. */
86 if (sendmsg(s
->syslog_fd
, &msghdr
, MSG_NOSIGNAL
) >= 0)
89 /* The socket is full? I guess the syslog implementation is
90 * too slow, and we shouldn't wait for that... */
91 if (errno
== EAGAIN
) {
92 s
->n_forward_syslog_missed
++;
96 if (ucred
&& (errno
== ESRCH
|| errno
== EPERM
)) {
99 /* Hmm, presumably the sender process vanished
100 * by now, or we don't have CAP_SYS_AMDIN, so
101 * let's fix it as good as we can, and retry */
105 memcpy(CMSG_DATA(cmsg
), &u
, sizeof(struct ucred
));
107 if (sendmsg(s
->syslog_fd
, &msghdr
, MSG_NOSIGNAL
) >= 0)
110 if (errno
== EAGAIN
) {
111 s
->n_forward_syslog_missed
++;
117 log_debug_errno(errno
, "Failed to forward syslog message: %m");
120 static void forward_syslog_raw(Server
*s
, int priority
, const char *buffer
, const struct ucred
*ucred
, const struct timeval
*tv
) {
126 if (LOG_PRI(priority
) > s
->max_level_syslog
)
129 IOVEC_SET_STRING(iovec
, buffer
);
130 forward_syslog_iovec(s
, &iovec
, 1, ucred
, tv
);
133 void server_forward_syslog(Server
*s
, int priority
, const char *identifier
, const char *message
, const struct ucred
*ucred
, const struct timeval
*tv
) {
134 struct iovec iovec
[5];
135 char header_priority
[DECIMAL_STR_MAX(priority
) + 3], header_time
[64],
136 header_pid
[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t
) + 1];
140 char *ident_buf
= NULL
;
143 assert(priority
>= 0);
144 assert(priority
<= 999);
147 if (LOG_PRI(priority
) > s
->max_level_syslog
)
150 /* First: priority field */
151 xsprintf(header_priority
, "<%i>", priority
);
152 IOVEC_SET_STRING(iovec
[n
++], header_priority
);
154 /* Second: timestamp */
155 t
= tv
? tv
->tv_sec
: ((time_t) (now(CLOCK_REALTIME
) / USEC_PER_SEC
));
159 if (strftime(header_time
, sizeof(header_time
), "%h %e %T ", tm
) <= 0)
161 IOVEC_SET_STRING(iovec
[n
++], header_time
);
163 /* Third: identifier and PID */
166 get_process_comm(ucred
->pid
, &ident_buf
);
167 identifier
= ident_buf
;
170 xsprintf(header_pid
, "["PID_FMT
"]: ", ucred
->pid
);
173 IOVEC_SET_STRING(iovec
[n
++], identifier
);
175 IOVEC_SET_STRING(iovec
[n
++], header_pid
);
176 } else if (identifier
) {
177 IOVEC_SET_STRING(iovec
[n
++], identifier
);
178 IOVEC_SET_STRING(iovec
[n
++], ": ");
181 /* Fourth: message */
182 IOVEC_SET_STRING(iovec
[n
++], message
);
184 forward_syslog_iovec(s
, iovec
, n
, ucred
, tv
);
189 int syslog_fixup_facility(int priority
) {
191 if ((priority
& LOG_FACMASK
) == 0)
192 return (priority
& LOG_PRIMASK
) | LOG_USER
;
197 size_t syslog_parse_identifier(const char **buf
, char **identifier
, char **pid
) {
208 p
+= strspn(p
, WHITESPACE
);
209 l
= strcspn(p
, WHITESPACE
);
224 t
= strndup(p
+k
+1, l
-k
-2);
243 if (strchr(WHITESPACE
, p
[e
]))
249 static void syslog_skip_date(char **buf
) {
257 LETTER
, LETTER
, LETTER
,
259 SPACE_OR_NUMBER
, NUMBER
,
261 SPACE_OR_NUMBER
, NUMBER
,
263 SPACE_OR_NUMBER
, NUMBER
,
265 SPACE_OR_NUMBER
, NUMBER
,
277 for (i
= 0; i
< ELEMENTSOF(sequence
); i
++, p
++) {
282 switch (sequence
[i
]) {
289 case SPACE_OR_NUMBER
:
296 if (*p
< '0' || *p
> '9')
302 if (!(*p
>= 'A' && *p
<= 'Z') &&
303 !(*p
>= 'a' && *p
<= 'z'))
319 void server_process_syslog_message(
322 const struct ucred
*ucred
,
323 const struct timeval
*tv
,
327 char syslog_priority
[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
328 syslog_facility
[sizeof("SYSLOG_FACILITY") + DECIMAL_STR_MAX(int)];
329 const char *message
= NULL
, *syslog_identifier
= NULL
, *syslog_pid
= NULL
;
330 struct iovec iovec
[N_IOVEC_META_FIELDS
+ 6];
332 int priority
= LOG_USER
| LOG_INFO
;
333 _cleanup_free_
char *identifier
= NULL
, *pid
= NULL
;
340 syslog_parse_priority(&buf
, &priority
, true);
342 if (s
->forward_to_syslog
)
343 forward_syslog_raw(s
, priority
, orig
, ucred
, tv
);
345 syslog_skip_date((char**) &buf
);
346 syslog_parse_identifier(&buf
, &identifier
, &pid
);
348 if (s
->forward_to_kmsg
)
349 server_forward_kmsg(s
, priority
, identifier
, buf
, ucred
);
351 if (s
->forward_to_console
)
352 server_forward_console(s
, priority
, identifier
, buf
, ucred
);
354 if (s
->forward_to_wall
)
355 server_forward_wall(s
, priority
, identifier
, buf
, ucred
);
357 IOVEC_SET_STRING(iovec
[n
++], "_TRANSPORT=syslog");
359 sprintf(syslog_priority
, "PRIORITY=%i", priority
& LOG_PRIMASK
);
360 IOVEC_SET_STRING(iovec
[n
++], syslog_priority
);
362 if (priority
& LOG_FACMASK
) {
363 sprintf(syslog_facility
, "SYSLOG_FACILITY=%i", LOG_FAC(priority
));
364 IOVEC_SET_STRING(iovec
[n
++], syslog_facility
);
368 syslog_identifier
= strjoina("SYSLOG_IDENTIFIER=", identifier
);
369 if (syslog_identifier
)
370 IOVEC_SET_STRING(iovec
[n
++], syslog_identifier
);
374 syslog_pid
= strjoina("SYSLOG_PID=", pid
);
376 IOVEC_SET_STRING(iovec
[n
++], syslog_pid
);
379 message
= strjoina("MESSAGE=", buf
);
381 IOVEC_SET_STRING(iovec
[n
++], message
);
383 server_dispatch_message(s
, iovec
, n
, ELEMENTSOF(iovec
), ucred
, tv
, label
, label_len
, NULL
, priority
, 0);
386 int server_open_syslog_socket(Server
*s
) {
387 static const int one
= 1;
392 if (s
->syslog_fd
< 0) {
393 static const union sockaddr_union sa
= {
394 .un
.sun_family
= AF_UNIX
,
395 .un
.sun_path
= "/run/systemd/journal/dev-log",
398 s
->syslog_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
399 if (s
->syslog_fd
< 0)
400 return log_error_errno(errno
, "socket() failed: %m");
402 unlink(sa
.un
.sun_path
);
404 r
= bind(s
->syslog_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
));
406 return log_error_errno(errno
, "bind(%s) failed: %m", sa
.un
.sun_path
);
408 (void) chmod(sa
.un
.sun_path
, 0666);
410 fd_nonblock(s
->syslog_fd
, 1);
412 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
));
414 return log_error_errno(errno
, "SO_PASSCRED failed: %m");
417 if (mac_selinux_use()) {
418 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_PASSSEC
, &one
, sizeof(one
));
420 log_warning_errno(errno
, "SO_PASSSEC failed: %m");
424 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_TIMESTAMP
, &one
, sizeof(one
));
426 return log_error_errno(errno
, "SO_TIMESTAMP failed: %m");
428 r
= sd_event_add_io(s
->event
, &s
->syslog_event_source
, s
->syslog_fd
, EPOLLIN
, server_process_datagram
, s
);
430 return log_error_errno(r
, "Failed to add syslog server fd to event loop: %m");
435 void server_maybe_warn_forward_syslog_missed(Server
*s
) {
439 if (s
->n_forward_syslog_missed
<= 0)
442 n
= now(CLOCK_MONOTONIC
);
443 if (s
->last_warn_forward_syslog_missed
+ WARN_FORWARD_SYSLOG_MISSED_USEC
> n
)
446 server_driver_message(s
, SD_MESSAGE_FORWARD_SYSLOG_MISSED
, "Forwarding to syslog missed %u messages.", s
->n_forward_syslog_missed
);
448 s
->n_forward_syslog_missed
= 0;
449 s
->last_warn_forward_syslog_missed
= n
;