]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journald-native.c
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 "socket-util.h"
27 #include "path-util.h"
28 #include "journald-server.h"
29 #include "journald-native.h"
30 #include "journald-kmsg.h"
31 #include "journald-console.h"
32 #include "journald-syslog.h"
34 /* Make sure not to make this smaller than the maximum coredump
35 * size. See COREDUMP_MAX in coredump.c */
36 #define ENTRY_SIZE_MAX (1024*1024*768)
37 #define DATA_SIZE_MAX (1024*1024*768)
39 static bool valid_user_field(const char *p
, size_t l
) {
42 /* We kinda enforce POSIX syntax recommendations for
43 environment variables here, but make a couple of additional
46 http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
48 /* No empty field names */
52 /* Don't allow names longer than 64 chars */
56 /* Variables starting with an underscore are protected */
60 /* Don't allow digits as first character */
61 if (p
[0] >= '0' && p
[0] <= '9')
64 /* Only allow A-Z0-9 and '_' */
65 for (a
= p
; a
< p
+ l
; a
++)
66 if (!((*a
>= 'A' && *a
<= 'Z') ||
67 (*a
>= '0' && *a
<= '9') ||
74 void server_process_native_message(
76 const void *buffer
, size_t buffer_size
,
79 const char *label
, size_t label_len
) {
81 struct iovec
*iovec
= NULL
;
82 unsigned n
= 0, m
= 0, j
, tn
= (unsigned) -1;
85 int priority
= LOG_INFO
;
86 char *identifier
= NULL
, *message
= NULL
;
89 assert(buffer
|| buffer_size
== 0);
92 remaining
= buffer_size
;
94 while (remaining
> 0) {
97 e
= memchr(p
, '\n', remaining
);
100 /* Trailing noise, let's ignore it, and flush what we collected */
101 log_debug("Received message with trailing noise, ignoring.");
106 /* Entry separator */
107 server_dispatch_message(s
, iovec
, n
, m
, ucred
, tv
, label
, label_len
, NULL
, priority
);
116 if (*p
== '.' || *p
== '#') {
117 /* Ignore control commands for now, and
119 remaining
-= (e
- p
) + 1;
124 /* A property follows */
126 /* n received properties, +1 for _TRANSPORT */
127 if (n
+ 1 + N_IOVEC_META_FIELDS
>= m
) {
131 u
= MAX((n
+ 1 + N_IOVEC_META_FIELDS
) * 2U, 4U);
132 c
= realloc(iovec
, u
* sizeof(struct iovec
));
142 q
= memchr(p
, '=', e
- p
);
144 if (valid_user_field(p
, q
- p
)) {
149 /* If the field name starts with an
150 * underscore, skip the variable,
151 * since that indidates a trusted
153 iovec
[n
].iov_base
= (char*) p
;
154 iovec
[n
].iov_len
= l
;
157 /* We need to determine the priority
158 * of this entry for the rate limiting
161 hasprefix(p
, "PRIORITY=") &&
162 p
[9] >= '0' && p
[9] <= '9')
163 priority
= (priority
& LOG_FACMASK
) | (p
[9] - '0');
166 hasprefix(p
, "SYSLOG_FACILITY=") &&
167 p
[16] >= '0' && p
[16] <= '9')
168 priority
= (priority
& LOG_PRIMASK
) | ((p
[16] - '0') << 3);
171 hasprefix(p
, "SYSLOG_FACILITY=") &&
172 p
[16] >= '0' && p
[16] <= '9' &&
173 p
[17] >= '0' && p
[17] <= '9')
174 priority
= (priority
& LOG_PRIMASK
) | (((p
[16] - '0')*10 + (p
[17] - '0')) << 3);
177 hasprefix(p
, "SYSLOG_IDENTIFIER=")) {
180 t
= strndup(p
+ 18, l
- 18);
186 hasprefix(p
, "MESSAGE=")) {
189 t
= strndup(p
+ 8, l
- 8);
197 remaining
-= (e
- p
) + 1;
205 if (remaining
< e
- p
+ 1 + sizeof(uint64_t) + 1) {
206 log_debug("Failed to parse message, ignoring.");
210 memcpy(&l_le
, e
+ 1, sizeof(uint64_t));
213 if (l
> DATA_SIZE_MAX
) {
214 log_debug("Received binary data block too large, ignoring.");
218 if ((uint64_t) remaining
< e
- p
+ 1 + sizeof(uint64_t) + l
+ 1 ||
219 e
[1+sizeof(uint64_t)+l
] != '\n') {
220 log_debug("Failed to parse message, ignoring.");
224 k
= malloc((e
- p
) + 1 + l
);
232 memcpy(k
+ (e
- p
) + 1, e
+ 1 + sizeof(uint64_t), l
);
234 if (valid_user_field(p
, e
- p
)) {
235 iovec
[n
].iov_base
= k
;
236 iovec
[n
].iov_len
= (e
- p
) + 1 + l
;
241 remaining
-= (e
- p
) + 1 + sizeof(uint64_t) + l
+ 1;
242 p
= e
+ 1 + sizeof(uint64_t) + l
+ 1;
250 IOVEC_SET_STRING(iovec
[tn
], "_TRANSPORT=journal");
253 if (s
->forward_to_syslog
)
254 server_forward_syslog(s
, priority
, identifier
, message
, ucred
, tv
);
256 if (s
->forward_to_kmsg
)
257 server_forward_kmsg(s
, priority
, identifier
, message
, ucred
);
259 if (s
->forward_to_console
)
260 server_forward_console(s
, priority
, identifier
, message
, ucred
);
263 server_dispatch_message(s
, iovec
, n
, m
, ucred
, tv
, label
, label_len
, NULL
, priority
);
266 for (j
= 0; j
< n
; j
++) {
270 if (iovec
[j
].iov_base
< buffer
||
271 (const uint8_t*) iovec
[j
].iov_base
>= (const uint8_t*) buffer
+ buffer_size
)
272 free(iovec
[j
].iov_base
);
280 void server_process_native_file(
285 const char *label
, size_t label_len
) {
288 _cleanup_free_
void *p
= NULL
;
295 if (!ucred
|| ucred
->uid
!= 0) {
296 _cleanup_free_
char *sl
= NULL
, *k
= NULL
;
299 if (asprintf(&sl
, "/proc/self/fd/%i", fd
) < 0) {
304 r
= readlink_malloc(sl
, &k
);
306 log_error("readlink(%s) failed: %m", sl
);
310 e
= path_startswith(k
, "/dev/shm/");
312 e
= path_startswith(k
, "/tmp/");
314 e
= path_startswith(k
, "/var/tmp/");
316 log_error("Received file outside of allowed directories. Refusing.");
320 if (!filename_is_safe(e
)) {
321 log_error("Received file in subdirectory of allowed directories. Refusing.");
326 /* Data is in the passed file, since it didn't fit in a
327 * datagram. We can't map the file here, since clients might
328 * then truncate it and trigger a SIGBUS for us. So let's
329 * stupidly read it */
331 if (fstat(fd
, &st
) < 0) {
332 log_error("Failed to stat passed file, ignoring: %m");
336 if (!S_ISREG(st
.st_mode
)) {
337 log_error("File passed is not regular. Ignoring.");
344 if (st
.st_size
> ENTRY_SIZE_MAX
) {
345 log_error("File passed too large. Ignoring.");
349 p
= malloc(st
.st_size
);
355 n
= pread(fd
, p
, st
.st_size
, 0);
357 log_error("Failed to read file, ignoring: %s", strerror(-n
));
359 server_process_native_message(s
, p
, n
, ucred
, tv
, label
, label_len
);
362 int server_open_native_socket(Server
*s
) {
363 union sockaddr_union sa
;
365 struct epoll_event ev
;
369 if (s
->native_fd
< 0) {
371 s
->native_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
372 if (s
->native_fd
< 0) {
373 log_error("socket() failed: %m");
378 sa
.un
.sun_family
= AF_UNIX
;
379 strncpy(sa
.un
.sun_path
, "/run/systemd/journal/socket", sizeof(sa
.un
.sun_path
));
381 unlink(sa
.un
.sun_path
);
383 r
= bind(s
->native_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
));
385 log_error("bind() failed: %m");
389 chmod(sa
.un
.sun_path
, 0666);
391 fd_nonblock(s
->native_fd
, 1);
394 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
));
396 log_error("SO_PASSCRED failed: %m");
402 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_PASSSEC
, &one
, sizeof(one
));
404 log_warning("SO_PASSSEC failed: %m");
408 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_TIMESTAMP
, &one
, sizeof(one
));
410 log_error("SO_TIMESTAMP failed: %m");
416 ev
.data
.fd
= s
->native_fd
;
417 if (epoll_ctl(s
->epoll_fd
, EPOLL_CTL_ADD
, s
->native_fd
, &ev
) < 0) {
418 log_error("Failed to add native server fd to epoll object: %m");