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 "alloc-util.h"
30 #include "formats-util.h"
32 #include "journald-console.h"
33 #include "journald-kmsg.h"
34 #include "journald-server.h"
35 #include "journald-syslog.h"
36 #include "journald-wall.h"
37 #include "process-util.h"
38 #include "selinux-util.h"
39 #include "socket-util.h"
40 #include "stdio-util.h"
41 #include "string-util.h"
42 #include "syslog-util.h"
44 /* Warn once every 30s if we missed syslog message */
45 #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
47 static void forward_syslog_iovec(Server
*s
, const struct iovec
*iovec
, unsigned n_iovec
, const struct ucred
*ucred
, const struct timeval
*tv
) {
49 static const union sockaddr_union sa
= {
50 .un
.sun_family
= AF_UNIX
,
51 .un
.sun_path
= "/run/systemd/journal/syslog",
53 struct msghdr msghdr
= {
54 .msg_iov
= (struct iovec
*) iovec
,
55 .msg_iovlen
= n_iovec
,
56 .msg_name
= (struct sockaddr
*) &sa
.sa
,
57 .msg_namelen
= offsetof(union sockaddr_union
, un
.sun_path
)
58 + strlen("/run/systemd/journal/syslog"),
62 struct cmsghdr cmsghdr
;
63 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
))];
72 msghdr
.msg_control
= &control
;
73 msghdr
.msg_controllen
= sizeof(control
);
75 cmsg
= CMSG_FIRSTHDR(&msghdr
);
76 cmsg
->cmsg_level
= SOL_SOCKET
;
77 cmsg
->cmsg_type
= SCM_CREDENTIALS
;
78 cmsg
->cmsg_len
= CMSG_LEN(sizeof(struct ucred
));
79 memcpy(CMSG_DATA(cmsg
), ucred
, sizeof(struct ucred
));
80 msghdr
.msg_controllen
= cmsg
->cmsg_len
;
83 /* Forward the syslog message we received via /dev/log to
84 * /run/systemd/syslog. Unfortunately we currently can't set
85 * the SO_TIMESTAMP auxiliary data, and hence we don't. */
87 if (sendmsg(s
->syslog_fd
, &msghdr
, MSG_NOSIGNAL
) >= 0)
90 /* The socket is full? I guess the syslog implementation is
91 * too slow, and we shouldn't wait for that... */
92 if (errno
== EAGAIN
) {
93 s
->n_forward_syslog_missed
++;
97 if (ucred
&& (errno
== ESRCH
|| errno
== EPERM
)) {
100 /* Hmm, presumably the sender process vanished
101 * by now, or we don't have CAP_SYS_AMDIN, so
102 * let's fix it as good as we can, and retry */
106 memcpy(CMSG_DATA(cmsg
), &u
, sizeof(struct ucred
));
108 if (sendmsg(s
->syslog_fd
, &msghdr
, MSG_NOSIGNAL
) >= 0)
111 if (errno
== EAGAIN
) {
112 s
->n_forward_syslog_missed
++;
118 log_debug_errno(errno
, "Failed to forward syslog message: %m");
121 static void forward_syslog_raw(Server
*s
, int priority
, const char *buffer
, const struct ucred
*ucred
, const struct timeval
*tv
) {
127 if (LOG_PRI(priority
) > s
->max_level_syslog
)
130 IOVEC_SET_STRING(iovec
, buffer
);
131 forward_syslog_iovec(s
, &iovec
, 1, ucred
, tv
);
134 void server_forward_syslog(Server
*s
, int priority
, const char *identifier
, const char *message
, const struct ucred
*ucred
, const struct timeval
*tv
) {
135 struct iovec iovec
[5];
136 char header_priority
[DECIMAL_STR_MAX(priority
) + 3], header_time
[64],
137 header_pid
[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t
) + 1];
141 char *ident_buf
= NULL
;
144 assert(priority
>= 0);
145 assert(priority
<= 999);
148 if (LOG_PRI(priority
) > s
->max_level_syslog
)
151 /* First: priority field */
152 xsprintf(header_priority
, "<%i>", priority
);
153 IOVEC_SET_STRING(iovec
[n
++], header_priority
);
155 /* Second: timestamp */
156 t
= tv
? tv
->tv_sec
: ((time_t) (now(CLOCK_REALTIME
) / USEC_PER_SEC
));
160 if (strftime(header_time
, sizeof(header_time
), "%h %e %T ", tm
) <= 0)
162 IOVEC_SET_STRING(iovec
[n
++], header_time
);
164 /* Third: identifier and PID */
167 get_process_comm(ucred
->pid
, &ident_buf
);
168 identifier
= ident_buf
;
171 xsprintf(header_pid
, "["PID_FMT
"]: ", ucred
->pid
);
174 IOVEC_SET_STRING(iovec
[n
++], identifier
);
176 IOVEC_SET_STRING(iovec
[n
++], header_pid
);
177 } else if (identifier
) {
178 IOVEC_SET_STRING(iovec
[n
++], identifier
);
179 IOVEC_SET_STRING(iovec
[n
++], ": ");
182 /* Fourth: message */
183 IOVEC_SET_STRING(iovec
[n
++], message
);
185 forward_syslog_iovec(s
, iovec
, n
, ucred
, tv
);
190 int syslog_fixup_facility(int priority
) {
192 if ((priority
& LOG_FACMASK
) == 0)
193 return (priority
& LOG_PRIMASK
) | LOG_USER
;
198 size_t syslog_parse_identifier(const char **buf
, char **identifier
, char **pid
) {
209 p
+= strspn(p
, WHITESPACE
);
210 l
= strcspn(p
, WHITESPACE
);
225 t
= strndup(p
+k
+1, l
-k
-2);
244 if (strchr(WHITESPACE
, p
[e
]))
250 static void syslog_skip_date(char **buf
) {
258 LETTER
, LETTER
, LETTER
,
260 SPACE_OR_NUMBER
, NUMBER
,
262 SPACE_OR_NUMBER
, NUMBER
,
264 SPACE_OR_NUMBER
, NUMBER
,
266 SPACE_OR_NUMBER
, NUMBER
,
278 for (i
= 0; i
< ELEMENTSOF(sequence
); i
++, p
++) {
283 switch (sequence
[i
]) {
290 case SPACE_OR_NUMBER
:
297 if (*p
< '0' || *p
> '9')
303 if (!(*p
>= 'A' && *p
<= 'Z') &&
304 !(*p
>= 'a' && *p
<= 'z'))
320 void server_process_syslog_message(
323 const struct ucred
*ucred
,
324 const struct timeval
*tv
,
328 char syslog_priority
[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
329 syslog_facility
[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
330 const char *message
= NULL
, *syslog_identifier
= NULL
, *syslog_pid
= NULL
;
331 struct iovec iovec
[N_IOVEC_META_FIELDS
+ 6];
333 int priority
= LOG_USER
| LOG_INFO
;
334 _cleanup_free_
char *identifier
= NULL
, *pid
= NULL
;
341 syslog_parse_priority(&buf
, &priority
, true);
343 if (s
->forward_to_syslog
)
344 forward_syslog_raw(s
, priority
, orig
, ucred
, tv
);
346 syslog_skip_date((char**) &buf
);
347 syslog_parse_identifier(&buf
, &identifier
, &pid
);
349 if (s
->forward_to_kmsg
)
350 server_forward_kmsg(s
, priority
, identifier
, buf
, ucred
);
352 if (s
->forward_to_console
)
353 server_forward_console(s
, priority
, identifier
, buf
, ucred
);
355 if (s
->forward_to_wall
)
356 server_forward_wall(s
, priority
, identifier
, buf
, ucred
);
358 IOVEC_SET_STRING(iovec
[n
++], "_TRANSPORT=syslog");
360 xsprintf(syslog_priority
, "PRIORITY=%i", priority
& LOG_PRIMASK
);
361 IOVEC_SET_STRING(iovec
[n
++], syslog_priority
);
363 if (priority
& LOG_FACMASK
) {
364 xsprintf(syslog_facility
, "SYSLOG_FACILITY=%i", LOG_FAC(priority
));
365 IOVEC_SET_STRING(iovec
[n
++], syslog_facility
);
369 syslog_identifier
= strjoina("SYSLOG_IDENTIFIER=", identifier
);
370 if (syslog_identifier
)
371 IOVEC_SET_STRING(iovec
[n
++], syslog_identifier
);
375 syslog_pid
= strjoina("SYSLOG_PID=", pid
);
377 IOVEC_SET_STRING(iovec
[n
++], syslog_pid
);
380 message
= strjoina("MESSAGE=", buf
);
382 IOVEC_SET_STRING(iovec
[n
++], message
);
384 server_dispatch_message(s
, iovec
, n
, ELEMENTSOF(iovec
), ucred
, tv
, label
, label_len
, NULL
, priority
, 0);
387 int server_open_syslog_socket(Server
*s
) {
388 static const int one
= 1;
393 if (s
->syslog_fd
< 0) {
394 static const union sockaddr_union sa
= {
395 .un
.sun_family
= AF_UNIX
,
396 .un
.sun_path
= "/run/systemd/journal/dev-log",
399 s
->syslog_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
400 if (s
->syslog_fd
< 0)
401 return log_error_errno(errno
, "socket() failed: %m");
403 unlink(sa
.un
.sun_path
);
405 r
= bind(s
->syslog_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
));
407 return log_error_errno(errno
, "bind(%s) failed: %m", sa
.un
.sun_path
);
409 (void) chmod(sa
.un
.sun_path
, 0666);
411 fd_nonblock(s
->syslog_fd
, 1);
413 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
));
415 return log_error_errno(errno
, "SO_PASSCRED failed: %m");
418 if (mac_selinux_have()) {
419 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_PASSSEC
, &one
, sizeof(one
));
421 log_warning_errno(errno
, "SO_PASSSEC failed: %m");
425 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_TIMESTAMP
, &one
, sizeof(one
));
427 return log_error_errno(errno
, "SO_TIMESTAMP failed: %m");
429 r
= sd_event_add_io(s
->event
, &s
->syslog_event_source
, s
->syslog_fd
, EPOLLIN
, server_process_datagram
, s
);
431 return log_error_errno(r
, "Failed to add syslog server fd to event loop: %m");
433 r
= sd_event_source_set_priority(s
->syslog_event_source
, SD_EVENT_PRIORITY_NORMAL
+5);
435 return log_error_errno(r
, "Failed to adjust syslog event source priority: %m");
440 void server_maybe_warn_forward_syslog_missed(Server
*s
) {
444 if (s
->n_forward_syslog_missed
<= 0)
447 n
= now(CLOCK_MONOTONIC
);
448 if (s
->last_warn_forward_syslog_missed
+ WARN_FORWARD_SYSLOG_MISSED_USEC
> n
)
451 server_driver_message(s
, SD_MESSAGE_FORWARD_SYSLOG_MISSED
, "Forwarding to syslog missed %u messages.", s
->n_forward_syslog_missed
);
453 s
->n_forward_syslog_missed
= 0;
454 s
->last_warn_forward_syslog_missed
= n
;