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"
30 #include "journald-console.h"
31 #include "journald-kmsg.h"
32 #include "journald-server.h"
33 #include "journald-syslog.h"
34 #include "journald-wall.h"
35 #include "process-util.h"
36 #include "selinux-util.h"
37 #include "socket-util.h"
38 #include "string-util.h"
39 #include "syslog-util.h"
41 /* Warn once every 30s if we missed syslog message */
42 #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
44 static void forward_syslog_iovec(Server
*s
, const struct iovec
*iovec
, unsigned n_iovec
, const struct ucred
*ucred
, const struct timeval
*tv
) {
46 static const union sockaddr_union sa
= {
47 .un
.sun_family
= AF_UNIX
,
48 .un
.sun_path
= "/run/systemd/journal/syslog",
50 struct msghdr msghdr
= {
51 .msg_iov
= (struct iovec
*) iovec
,
52 .msg_iovlen
= n_iovec
,
53 .msg_name
= (struct sockaddr
*) &sa
.sa
,
54 .msg_namelen
= offsetof(union sockaddr_union
, un
.sun_path
)
55 + strlen("/run/systemd/journal/syslog"),
59 struct cmsghdr cmsghdr
;
60 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
))];
69 msghdr
.msg_control
= &control
;
70 msghdr
.msg_controllen
= sizeof(control
);
72 cmsg
= CMSG_FIRSTHDR(&msghdr
);
73 cmsg
->cmsg_level
= SOL_SOCKET
;
74 cmsg
->cmsg_type
= SCM_CREDENTIALS
;
75 cmsg
->cmsg_len
= CMSG_LEN(sizeof(struct ucred
));
76 memcpy(CMSG_DATA(cmsg
), ucred
, sizeof(struct ucred
));
77 msghdr
.msg_controllen
= cmsg
->cmsg_len
;
80 /* Forward the syslog message we received via /dev/log to
81 * /run/systemd/syslog. Unfortunately we currently can't set
82 * the SO_TIMESTAMP auxiliary data, and hence we don't. */
84 if (sendmsg(s
->syslog_fd
, &msghdr
, MSG_NOSIGNAL
) >= 0)
87 /* The socket is full? I guess the syslog implementation is
88 * too slow, and we shouldn't wait for that... */
89 if (errno
== EAGAIN
) {
90 s
->n_forward_syslog_missed
++;
94 if (ucred
&& (errno
== ESRCH
|| errno
== EPERM
)) {
97 /* Hmm, presumably the sender process vanished
98 * by now, or we don't have CAP_SYS_AMDIN, so
99 * let's fix it as good as we can, and retry */
103 memcpy(CMSG_DATA(cmsg
), &u
, sizeof(struct ucred
));
105 if (sendmsg(s
->syslog_fd
, &msghdr
, MSG_NOSIGNAL
) >= 0)
108 if (errno
== EAGAIN
) {
109 s
->n_forward_syslog_missed
++;
115 log_debug_errno(errno
, "Failed to forward syslog message: %m");
118 static void forward_syslog_raw(Server
*s
, int priority
, const char *buffer
, const struct ucred
*ucred
, const struct timeval
*tv
) {
124 if (LOG_PRI(priority
) > s
->max_level_syslog
)
127 IOVEC_SET_STRING(iovec
, buffer
);
128 forward_syslog_iovec(s
, &iovec
, 1, ucred
, tv
);
131 void server_forward_syslog(Server
*s
, int priority
, const char *identifier
, const char *message
, const struct ucred
*ucred
, const struct timeval
*tv
) {
132 struct iovec iovec
[5];
133 char header_priority
[DECIMAL_STR_MAX(priority
) + 3], header_time
[64],
134 header_pid
[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t
) + 1];
138 char *ident_buf
= NULL
;
141 assert(priority
>= 0);
142 assert(priority
<= 999);
145 if (LOG_PRI(priority
) > s
->max_level_syslog
)
148 /* First: priority field */
149 xsprintf(header_priority
, "<%i>", priority
);
150 IOVEC_SET_STRING(iovec
[n
++], header_priority
);
152 /* Second: timestamp */
153 t
= tv
? tv
->tv_sec
: ((time_t) (now(CLOCK_REALTIME
) / USEC_PER_SEC
));
157 if (strftime(header_time
, sizeof(header_time
), "%h %e %T ", tm
) <= 0)
159 IOVEC_SET_STRING(iovec
[n
++], header_time
);
161 /* Third: identifier and PID */
164 get_process_comm(ucred
->pid
, &ident_buf
);
165 identifier
= ident_buf
;
168 xsprintf(header_pid
, "["PID_FMT
"]: ", ucred
->pid
);
171 IOVEC_SET_STRING(iovec
[n
++], identifier
);
173 IOVEC_SET_STRING(iovec
[n
++], header_pid
);
174 } else if (identifier
) {
175 IOVEC_SET_STRING(iovec
[n
++], identifier
);
176 IOVEC_SET_STRING(iovec
[n
++], ": ");
179 /* Fourth: message */
180 IOVEC_SET_STRING(iovec
[n
++], message
);
182 forward_syslog_iovec(s
, iovec
, n
, ucred
, tv
);
187 int syslog_fixup_facility(int priority
) {
189 if ((priority
& LOG_FACMASK
) == 0)
190 return (priority
& LOG_PRIMASK
) | LOG_USER
;
195 size_t syslog_parse_identifier(const char **buf
, char **identifier
, char **pid
) {
206 p
+= strspn(p
, WHITESPACE
);
207 l
= strcspn(p
, WHITESPACE
);
222 t
= strndup(p
+k
+1, l
-k
-2);
241 if (strchr(WHITESPACE
, p
[e
]))
247 static void syslog_skip_date(char **buf
) {
255 LETTER
, LETTER
, LETTER
,
257 SPACE_OR_NUMBER
, NUMBER
,
259 SPACE_OR_NUMBER
, NUMBER
,
261 SPACE_OR_NUMBER
, NUMBER
,
263 SPACE_OR_NUMBER
, NUMBER
,
275 for (i
= 0; i
< ELEMENTSOF(sequence
); i
++, p
++) {
280 switch (sequence
[i
]) {
287 case SPACE_OR_NUMBER
:
294 if (*p
< '0' || *p
> '9')
300 if (!(*p
>= 'A' && *p
<= 'Z') &&
301 !(*p
>= 'a' && *p
<= 'z'))
317 void server_process_syslog_message(
320 const struct ucred
*ucred
,
321 const struct timeval
*tv
,
325 char syslog_priority
[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
326 syslog_facility
[sizeof("SYSLOG_FACILITY") + DECIMAL_STR_MAX(int)];
327 const char *message
= NULL
, *syslog_identifier
= NULL
, *syslog_pid
= NULL
;
328 struct iovec iovec
[N_IOVEC_META_FIELDS
+ 6];
330 int priority
= LOG_USER
| LOG_INFO
;
331 _cleanup_free_
char *identifier
= NULL
, *pid
= NULL
;
338 syslog_parse_priority(&buf
, &priority
, true);
340 if (s
->forward_to_syslog
)
341 forward_syslog_raw(s
, priority
, orig
, ucred
, tv
);
343 syslog_skip_date((char**) &buf
);
344 syslog_parse_identifier(&buf
, &identifier
, &pid
);
346 if (s
->forward_to_kmsg
)
347 server_forward_kmsg(s
, priority
, identifier
, buf
, ucred
);
349 if (s
->forward_to_console
)
350 server_forward_console(s
, priority
, identifier
, buf
, ucred
);
352 if (s
->forward_to_wall
)
353 server_forward_wall(s
, priority
, identifier
, buf
, ucred
);
355 IOVEC_SET_STRING(iovec
[n
++], "_TRANSPORT=syslog");
357 sprintf(syslog_priority
, "PRIORITY=%i", priority
& LOG_PRIMASK
);
358 IOVEC_SET_STRING(iovec
[n
++], syslog_priority
);
360 if (priority
& LOG_FACMASK
) {
361 sprintf(syslog_facility
, "SYSLOG_FACILITY=%i", LOG_FAC(priority
));
362 IOVEC_SET_STRING(iovec
[n
++], syslog_facility
);
366 syslog_identifier
= strjoina("SYSLOG_IDENTIFIER=", identifier
);
367 if (syslog_identifier
)
368 IOVEC_SET_STRING(iovec
[n
++], syslog_identifier
);
372 syslog_pid
= strjoina("SYSLOG_PID=", pid
);
374 IOVEC_SET_STRING(iovec
[n
++], syslog_pid
);
377 message
= strjoina("MESSAGE=", buf
);
379 IOVEC_SET_STRING(iovec
[n
++], message
);
381 server_dispatch_message(s
, iovec
, n
, ELEMENTSOF(iovec
), ucred
, tv
, label
, label_len
, NULL
, priority
, 0);
384 int server_open_syslog_socket(Server
*s
) {
385 static const int one
= 1;
390 if (s
->syslog_fd
< 0) {
391 static const union sockaddr_union sa
= {
392 .un
.sun_family
= AF_UNIX
,
393 .un
.sun_path
= "/run/systemd/journal/dev-log",
396 s
->syslog_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
397 if (s
->syslog_fd
< 0)
398 return log_error_errno(errno
, "socket() failed: %m");
400 unlink(sa
.un
.sun_path
);
402 r
= bind(s
->syslog_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
));
404 return log_error_errno(errno
, "bind(%s) failed: %m", sa
.un
.sun_path
);
406 (void) chmod(sa
.un
.sun_path
, 0666);
408 fd_nonblock(s
->syslog_fd
, 1);
410 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
));
412 return log_error_errno(errno
, "SO_PASSCRED failed: %m");
415 if (mac_selinux_use()) {
416 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_PASSSEC
, &one
, sizeof(one
));
418 log_warning_errno(errno
, "SO_PASSSEC failed: %m");
422 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_TIMESTAMP
, &one
, sizeof(one
));
424 return log_error_errno(errno
, "SO_TIMESTAMP failed: %m");
426 r
= sd_event_add_io(s
->event
, &s
->syslog_event_source
, s
->syslog_fd
, EPOLLIN
, server_process_datagram
, s
);
428 return log_error_errno(r
, "Failed to add syslog server fd to event loop: %m");
433 void server_maybe_warn_forward_syslog_missed(Server
*s
) {
437 if (s
->n_forward_syslog_missed
<= 0)
440 n
= now(CLOCK_MONOTONIC
);
441 if (s
->last_warn_forward_syslog_missed
+ WARN_FORWARD_SYSLOG_MISSED_USEC
> n
)
444 server_driver_message(s
, SD_MESSAGE_FORWARD_SYSLOG_MISSED
, "Forwarding to syslog missed %u messages.", s
->n_forward_syslog_missed
);
446 s
->n_forward_syslog_missed
= 0;
447 s
->last_warn_forward_syslog_missed
= n
;