1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2011 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/epoll.h>
25 #include "sd-messages.h"
27 #include "alloc-util.h"
29 #include "format-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
= SOCKADDR_UN_LEN(sa
.un
),
60 struct cmsghdr cmsghdr
;
61 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
))];
70 msghdr
.msg_control
= &control
;
71 msghdr
.msg_controllen
= sizeof(control
);
73 cmsg
= CMSG_FIRSTHDR(&msghdr
);
74 cmsg
->cmsg_level
= SOL_SOCKET
;
75 cmsg
->cmsg_type
= SCM_CREDENTIALS
;
76 cmsg
->cmsg_len
= CMSG_LEN(sizeof(struct ucred
));
77 memcpy(CMSG_DATA(cmsg
), ucred
, sizeof(struct ucred
));
78 msghdr
.msg_controllen
= cmsg
->cmsg_len
;
81 /* Forward the syslog message we received via /dev/log to
82 * /run/systemd/syslog. Unfortunately we currently can't set
83 * the SO_TIMESTAMP auxiliary data, and hence we don't. */
85 if (sendmsg(s
->syslog_fd
, &msghdr
, MSG_NOSIGNAL
) >= 0)
88 /* The socket is full? I guess the syslog implementation is
89 * too slow, and we shouldn't wait for that... */
90 if (errno
== EAGAIN
) {
91 s
->n_forward_syslog_missed
++;
95 if (ucred
&& IN_SET(errno
, ESRCH
, EPERM
)) {
98 /* Hmm, presumably the sender process vanished
99 * by now, or we don't have CAP_SYS_AMDIN, so
100 * let's fix it as good as we can, and retry */
103 u
.pid
= getpid_cached();
104 memcpy(CMSG_DATA(cmsg
), &u
, sizeof(struct ucred
));
106 if (sendmsg(s
->syslog_fd
, &msghdr
, MSG_NOSIGNAL
) >= 0)
109 if (errno
== EAGAIN
) {
110 s
->n_forward_syslog_missed
++;
116 log_debug_errno(errno
, "Failed to forward syslog message: %m");
119 static void forward_syslog_raw(Server
*s
, int priority
, const char *buffer
, const struct ucred
*ucred
, const struct timeval
*tv
) {
125 if (LOG_PRI(priority
) > s
->max_level_syslog
)
128 iovec
= IOVEC_MAKE_STRING(buffer
);
129 forward_syslog_iovec(s
, &iovec
, 1, ucred
, tv
);
132 void server_forward_syslog(Server
*s
, int priority
, const char *identifier
, const char *message
, const struct ucred
*ucred
, const struct timeval
*tv
) {
133 struct iovec iovec
[5];
134 char header_priority
[DECIMAL_STR_MAX(priority
) + 3], header_time
[64],
135 header_pid
[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t
) + 1];
139 _cleanup_free_
char *ident_buf
= NULL
;
142 assert(priority
>= 0);
143 assert(priority
<= 999);
146 if (LOG_PRI(priority
) > s
->max_level_syslog
)
149 /* First: priority field */
150 xsprintf(header_priority
, "<%i>", priority
);
151 iovec
[n
++] = IOVEC_MAKE_STRING(header_priority
);
153 /* Second: timestamp */
154 t
= tv
? tv
->tv_sec
: ((time_t) (now(CLOCK_REALTIME
) / USEC_PER_SEC
));
158 if (strftime(header_time
, sizeof(header_time
), "%h %e %T ", tm
) <= 0)
160 iovec
[n
++] = IOVEC_MAKE_STRING(header_time
);
162 /* Third: identifier and PID */
165 get_process_comm(ucred
->pid
, &ident_buf
);
166 identifier
= ident_buf
;
169 xsprintf(header_pid
, "["PID_FMT
"]: ", ucred
->pid
);
172 iovec
[n
++] = IOVEC_MAKE_STRING(identifier
);
174 iovec
[n
++] = IOVEC_MAKE_STRING(header_pid
);
175 } else if (identifier
) {
176 iovec
[n
++] = IOVEC_MAKE_STRING(identifier
);
177 iovec
[n
++] = IOVEC_MAKE_STRING(": ");
180 /* Fourth: message */
181 iovec
[n
++] = IOVEC_MAKE_STRING(message
);
183 forward_syslog_iovec(s
, iovec
, n
, ucred
, tv
);
186 int syslog_fixup_facility(int priority
) {
188 if ((priority
& LOG_FACMASK
) == 0)
189 return (priority
& LOG_PRIMASK
) | LOG_USER
;
194 size_t syslog_parse_identifier(const char **buf
, char **identifier
, char **pid
) {
205 p
+= strspn(p
, WHITESPACE
);
206 l
= strcspn(p
, WHITESPACE
);
221 t
= strndup(p
+k
+1, l
-k
-2);
240 if (strchr(WHITESPACE
, p
[e
]))
246 static void syslog_skip_date(char **buf
) {
254 LETTER
, LETTER
, LETTER
,
256 SPACE_OR_NUMBER
, NUMBER
,
258 SPACE_OR_NUMBER
, NUMBER
,
260 SPACE_OR_NUMBER
, NUMBER
,
262 SPACE_OR_NUMBER
, NUMBER
,
274 for (i
= 0; i
< ELEMENTSOF(sequence
); i
++, p
++) {
279 switch (sequence
[i
]) {
286 case SPACE_OR_NUMBER
:
293 if (*p
< '0' || *p
> '9')
299 if (!(*p
>= 'A' && *p
<= 'Z') &&
300 !(*p
>= 'a' && *p
<= 'z'))
316 void server_process_syslog_message(
319 const struct ucred
*ucred
,
320 const struct timeval
*tv
,
324 char syslog_priority
[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
325 syslog_facility
[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
326 const char *message
= NULL
, *syslog_identifier
= NULL
, *syslog_pid
= NULL
;
327 _cleanup_free_
char *identifier
= NULL
, *pid
= NULL
;
328 int priority
= LOG_USER
| LOG_INFO
, r
;
329 ClientContext
*context
= NULL
;
337 if (ucred
&& pid_is_valid(ucred
->pid
)) {
338 r
= client_context_get(s
, ucred
->pid
, ucred
, label
, label_len
, NULL
, &context
);
340 log_warning_errno(r
, "Failed to retrieve credentials for PID " PID_FMT
", ignoring: %m", ucred
->pid
);
344 syslog_parse_priority(&buf
, &priority
, true);
346 if (!client_context_test_priority(context
, priority
))
349 if (s
->forward_to_syslog
)
350 forward_syslog_raw(s
, priority
, orig
, ucred
, tv
);
352 syslog_skip_date((char**) &buf
);
353 syslog_parse_identifier(&buf
, &identifier
, &pid
);
355 if (s
->forward_to_kmsg
)
356 server_forward_kmsg(s
, priority
, identifier
, buf
, ucred
);
358 if (s
->forward_to_console
)
359 server_forward_console(s
, priority
, identifier
, buf
, ucred
);
361 if (s
->forward_to_wall
)
362 server_forward_wall(s
, priority
, identifier
, buf
, ucred
);
364 m
= N_IOVEC_META_FIELDS
+ 6 + client_context_extra_fields_n_iovec(context
);
365 iovec
= newa(struct iovec
, m
);
367 iovec
[n
++] = IOVEC_MAKE_STRING("_TRANSPORT=syslog");
369 xsprintf(syslog_priority
, "PRIORITY=%i", priority
& LOG_PRIMASK
);
370 iovec
[n
++] = IOVEC_MAKE_STRING(syslog_priority
);
372 if (priority
& LOG_FACMASK
) {
373 xsprintf(syslog_facility
, "SYSLOG_FACILITY=%i", LOG_FAC(priority
));
374 iovec
[n
++] = IOVEC_MAKE_STRING(syslog_facility
);
378 syslog_identifier
= strjoina("SYSLOG_IDENTIFIER=", identifier
);
379 iovec
[n
++] = IOVEC_MAKE_STRING(syslog_identifier
);
383 syslog_pid
= strjoina("SYSLOG_PID=", pid
);
384 iovec
[n
++] = IOVEC_MAKE_STRING(syslog_pid
);
387 message
= strjoina("MESSAGE=", buf
);
389 iovec
[n
++] = IOVEC_MAKE_STRING(message
);
391 server_dispatch_message(s
, iovec
, n
, m
, context
, tv
, priority
, 0);
394 int server_open_syslog_socket(Server
*s
) {
396 static const union sockaddr_union sa
= {
397 .un
.sun_family
= AF_UNIX
,
398 .un
.sun_path
= "/run/systemd/journal/dev-log",
400 static const int one
= 1;
405 if (s
->syslog_fd
< 0) {
406 s
->syslog_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
407 if (s
->syslog_fd
< 0)
408 return log_error_errno(errno
, "socket() failed: %m");
410 (void) unlink(sa
.un
.sun_path
);
412 r
= bind(s
->syslog_fd
, &sa
.sa
, SOCKADDR_UN_LEN(sa
.un
));
414 return log_error_errno(errno
, "bind(%s) failed: %m", sa
.un
.sun_path
);
416 (void) chmod(sa
.un
.sun_path
, 0666);
418 fd_nonblock(s
->syslog_fd
, 1);
420 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
));
422 return log_error_errno(errno
, "SO_PASSCRED failed: %m");
425 if (mac_selinux_use()) {
426 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_PASSSEC
, &one
, sizeof(one
));
428 log_warning_errno(errno
, "SO_PASSSEC failed: %m");
432 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_TIMESTAMP
, &one
, sizeof(one
));
434 return log_error_errno(errno
, "SO_TIMESTAMP failed: %m");
436 r
= sd_event_add_io(s
->event
, &s
->syslog_event_source
, s
->syslog_fd
, EPOLLIN
, server_process_datagram
, s
);
438 return log_error_errno(r
, "Failed to add syslog server fd to event loop: %m");
440 r
= sd_event_source_set_priority(s
->syslog_event_source
, SD_EVENT_PRIORITY_NORMAL
+5);
442 return log_error_errno(r
, "Failed to adjust syslog event source priority: %m");
447 void server_maybe_warn_forward_syslog_missed(Server
*s
) {
452 if (s
->n_forward_syslog_missed
<= 0)
455 n
= now(CLOCK_MONOTONIC
);
456 if (s
->last_warn_forward_syslog_missed
+ WARN_FORWARD_SYSLOG_MISSED_USEC
> n
)
459 server_driver_message(s
, 0,
460 "MESSAGE_ID=" SD_MESSAGE_FORWARD_SYSLOG_MISSED_STR
,
461 LOG_MESSAGE("Forwarding to syslog missed %u messages.",
462 s
->n_forward_syslog_missed
),
465 s
->n_forward_syslog_missed
= 0;
466 s
->last_warn_forward_syslog_missed
= n
;