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/>.
24 #include <sys/epoll.h>
26 #include "systemd/sd-messages.h"
27 #include "socket-util.h"
28 #include "selinux-util.h"
29 #include "journald-server.h"
30 #include "journald-syslog.h"
31 #include "journald-kmsg.h"
32 #include "journald-console.h"
33 #include "journald-wall.h"
34 #include "formats-util.h"
35 #include "process-util.h"
37 /* Warn once every 30s if we missed syslog message */
38 #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
40 static void forward_syslog_iovec(Server
*s
, const struct iovec
*iovec
, unsigned n_iovec
, const struct ucred
*ucred
, const struct timeval
*tv
) {
42 static const union sockaddr_union sa
= {
43 .un
.sun_family
= AF_UNIX
,
44 .un
.sun_path
= "/run/systemd/journal/syslog",
46 struct msghdr msghdr
= {
47 .msg_iov
= (struct iovec
*) iovec
,
48 .msg_iovlen
= n_iovec
,
49 .msg_name
= (struct sockaddr
*) &sa
.sa
,
50 .msg_namelen
= offsetof(union sockaddr_union
, un
.sun_path
)
51 + strlen("/run/systemd/journal/syslog"),
55 struct cmsghdr cmsghdr
;
56 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
))];
65 msghdr
.msg_control
= &control
;
66 msghdr
.msg_controllen
= sizeof(control
);
68 cmsg
= CMSG_FIRSTHDR(&msghdr
);
69 cmsg
->cmsg_level
= SOL_SOCKET
;
70 cmsg
->cmsg_type
= SCM_CREDENTIALS
;
71 cmsg
->cmsg_len
= CMSG_LEN(sizeof(struct ucred
));
72 memcpy(CMSG_DATA(cmsg
), ucred
, sizeof(struct ucred
));
73 msghdr
.msg_controllen
= cmsg
->cmsg_len
;
76 /* Forward the syslog message we received via /dev/log to
77 * /run/systemd/syslog. Unfortunately we currently can't set
78 * the SO_TIMESTAMP auxiliary data, and hence we don't. */
80 if (sendmsg(s
->syslog_fd
, &msghdr
, MSG_NOSIGNAL
) >= 0)
83 /* The socket is full? I guess the syslog implementation is
84 * too slow, and we shouldn't wait for that... */
85 if (errno
== EAGAIN
) {
86 s
->n_forward_syslog_missed
++;
90 if (ucred
&& (errno
== ESRCH
|| errno
== EPERM
)) {
93 /* Hmm, presumably the sender process vanished
94 * by now, or we don't have CAP_SYS_AMDIN, so
95 * let's fix it as good as we can, and retry */
99 memcpy(CMSG_DATA(cmsg
), &u
, sizeof(struct ucred
));
101 if (sendmsg(s
->syslog_fd
, &msghdr
, MSG_NOSIGNAL
) >= 0)
104 if (errno
== EAGAIN
) {
105 s
->n_forward_syslog_missed
++;
111 log_debug_errno(errno
, "Failed to forward syslog message: %m");
114 static void forward_syslog_raw(Server
*s
, int priority
, const char *buffer
, const struct ucred
*ucred
, const struct timeval
*tv
) {
120 if (LOG_PRI(priority
) > s
->max_level_syslog
)
123 IOVEC_SET_STRING(iovec
, buffer
);
124 forward_syslog_iovec(s
, &iovec
, 1, ucred
, tv
);
127 void server_forward_syslog(Server
*s
, int priority
, const char *identifier
, const char *message
, const struct ucred
*ucred
, const struct timeval
*tv
) {
128 struct iovec iovec
[5];
129 char header_priority
[DECIMAL_STR_MAX(priority
) + 3], header_time
[64],
130 header_pid
[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t
) + 1];
134 char *ident_buf
= NULL
;
137 assert(priority
>= 0);
138 assert(priority
<= 999);
141 if (LOG_PRI(priority
) > s
->max_level_syslog
)
144 /* First: priority field */
145 xsprintf(header_priority
, "<%i>", priority
);
146 IOVEC_SET_STRING(iovec
[n
++], header_priority
);
148 /* Second: timestamp */
149 t
= tv
? tv
->tv_sec
: ((time_t) (now(CLOCK_REALTIME
) / USEC_PER_SEC
));
153 if (strftime(header_time
, sizeof(header_time
), "%h %e %T ", tm
) <= 0)
155 IOVEC_SET_STRING(iovec
[n
++], header_time
);
157 /* Third: identifier and PID */
160 get_process_comm(ucred
->pid
, &ident_buf
);
161 identifier
= ident_buf
;
164 xsprintf(header_pid
, "["PID_FMT
"]: ", ucred
->pid
);
167 IOVEC_SET_STRING(iovec
[n
++], identifier
);
169 IOVEC_SET_STRING(iovec
[n
++], header_pid
);
170 } else if (identifier
) {
171 IOVEC_SET_STRING(iovec
[n
++], identifier
);
172 IOVEC_SET_STRING(iovec
[n
++], ": ");
175 /* Fourth: message */
176 IOVEC_SET_STRING(iovec
[n
++], message
);
178 forward_syslog_iovec(s
, iovec
, n
, ucred
, tv
);
183 int syslog_fixup_facility(int priority
) {
185 if ((priority
& LOG_FACMASK
) == 0)
186 return (priority
& LOG_PRIMASK
) | LOG_USER
;
191 size_t syslog_parse_identifier(const char **buf
, char **identifier
, char **pid
) {
202 p
+= strspn(p
, WHITESPACE
);
203 l
= strcspn(p
, WHITESPACE
);
218 t
= strndup(p
+k
+1, l
-k
-2);
237 if (strchr(WHITESPACE
, p
[e
]))
243 static void syslog_skip_date(char **buf
) {
251 LETTER
, LETTER
, LETTER
,
253 SPACE_OR_NUMBER
, NUMBER
,
255 SPACE_OR_NUMBER
, NUMBER
,
257 SPACE_OR_NUMBER
, NUMBER
,
259 SPACE_OR_NUMBER
, NUMBER
,
271 for (i
= 0; i
< ELEMENTSOF(sequence
); i
++, p
++) {
276 switch (sequence
[i
]) {
283 case SPACE_OR_NUMBER
:
290 if (*p
< '0' || *p
> '9')
296 if (!(*p
>= 'A' && *p
<= 'Z') &&
297 !(*p
>= 'a' && *p
<= 'z'))
313 void server_process_syslog_message(
316 const struct ucred
*ucred
,
317 const struct timeval
*tv
,
321 char syslog_priority
[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
322 syslog_facility
[sizeof("SYSLOG_FACILITY") + DECIMAL_STR_MAX(int)];
323 const char *message
= NULL
, *syslog_identifier
= NULL
, *syslog_pid
= NULL
;
324 struct iovec iovec
[N_IOVEC_META_FIELDS
+ 6];
326 int priority
= LOG_USER
| LOG_INFO
;
327 _cleanup_free_
char *identifier
= NULL
, *pid
= NULL
;
334 syslog_parse_priority(&buf
, &priority
, true);
336 if (s
->forward_to_syslog
)
337 forward_syslog_raw(s
, priority
, orig
, ucred
, tv
);
339 syslog_skip_date((char**) &buf
);
340 syslog_parse_identifier(&buf
, &identifier
, &pid
);
342 if (s
->forward_to_kmsg
)
343 server_forward_kmsg(s
, priority
, identifier
, buf
, ucred
);
345 if (s
->forward_to_console
)
346 server_forward_console(s
, priority
, identifier
, buf
, ucred
);
348 if (s
->forward_to_wall
)
349 server_forward_wall(s
, priority
, identifier
, buf
, ucred
);
351 IOVEC_SET_STRING(iovec
[n
++], "_TRANSPORT=syslog");
353 sprintf(syslog_priority
, "PRIORITY=%i", priority
& LOG_PRIMASK
);
354 IOVEC_SET_STRING(iovec
[n
++], syslog_priority
);
356 if (priority
& LOG_FACMASK
) {
357 sprintf(syslog_facility
, "SYSLOG_FACILITY=%i", LOG_FAC(priority
));
358 IOVEC_SET_STRING(iovec
[n
++], syslog_facility
);
362 syslog_identifier
= strjoina("SYSLOG_IDENTIFIER=", identifier
);
363 if (syslog_identifier
)
364 IOVEC_SET_STRING(iovec
[n
++], syslog_identifier
);
368 syslog_pid
= strjoina("SYSLOG_PID=", pid
);
370 IOVEC_SET_STRING(iovec
[n
++], syslog_pid
);
373 message
= strjoina("MESSAGE=", buf
);
375 IOVEC_SET_STRING(iovec
[n
++], message
);
377 server_dispatch_message(s
, iovec
, n
, ELEMENTSOF(iovec
), ucred
, tv
, label
, label_len
, NULL
, priority
, 0);
380 int server_open_syslog_socket(Server
*s
) {
381 static const int one
= 1;
386 if (s
->syslog_fd
< 0) {
387 static const union sockaddr_union sa
= {
388 .un
.sun_family
= AF_UNIX
,
389 .un
.sun_path
= "/run/systemd/journal/dev-log",
392 s
->syslog_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
393 if (s
->syslog_fd
< 0)
394 return log_error_errno(errno
, "socket() failed: %m");
396 unlink(sa
.un
.sun_path
);
398 r
= bind(s
->syslog_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
));
400 return log_error_errno(errno
, "bind(%s) failed: %m", sa
.un
.sun_path
);
402 (void) chmod(sa
.un
.sun_path
, 0666);
404 fd_nonblock(s
->syslog_fd
, 1);
406 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
));
408 return log_error_errno(errno
, "SO_PASSCRED failed: %m");
411 if (mac_selinux_use()) {
412 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_PASSSEC
, &one
, sizeof(one
));
414 log_warning_errno(errno
, "SO_PASSSEC failed: %m");
418 r
= setsockopt(s
->syslog_fd
, SOL_SOCKET
, SO_TIMESTAMP
, &one
, sizeof(one
));
420 return log_error_errno(errno
, "SO_TIMESTAMP failed: %m");
422 r
= sd_event_add_io(s
->event
, &s
->syslog_event_source
, s
->syslog_fd
, EPOLLIN
, server_process_datagram
, s
);
424 return log_error_errno(r
, "Failed to add syslog server fd to event loop: %m");
429 void server_maybe_warn_forward_syslog_missed(Server
*s
) {
433 if (s
->n_forward_syslog_missed
<= 0)
436 n
= now(CLOCK_MONOTONIC
);
437 if (s
->last_warn_forward_syslog_missed
+ WARN_FORWARD_SYSLOG_MISSED_USEC
> n
)
440 server_driver_message(s
, SD_MESSAGE_FORWARD_SYSLOG_MISSED
, "Forwarding to syslog missed %u messages.", s
->n_forward_syslog_missed
);
442 s
->n_forward_syslog_missed
= 0;
443 s
->last_warn_forward_syslog_missed
= n
;