]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journald-syslog.c
journal: fix size of buffer
[thirdparty/systemd.git] / src / journal / journald-syslog.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
35e2e347 2
4871690d 3#include <stddef.h>
35e2e347 4#include <sys/epoll.h>
07630cea 5#include <unistd.h>
35e2e347 6
07630cea
LP
7#include "sd-messages.h"
8
b5efdb8a 9#include "alloc-util.h"
3ffd4af2 10#include "fd-util.h"
f97b34a6 11#include "format-util.h"
afc5dbf3 12#include "io-util.h"
3b7124a8 13#include "journald-console.h"
07630cea
LP
14#include "journald-kmsg.h"
15#include "journald-server.h"
3ffd4af2 16#include "journald-syslog.h"
40b71e89 17#include "journald-wall.h"
0b452006 18#include "process-util.h"
07630cea
LP
19#include "selinux-util.h"
20#include "socket-util.h"
15a5e950 21#include "stdio-util.h"
07630cea 22#include "string-util.h"
7ccbd1ae 23#include "syslog-util.h"
35e2e347 24
178cc770
LP
25/* Warn once every 30s if we missed syslog message */
26#define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
27
3b3154df 28static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, const struct ucred *ucred, const struct timeval *tv) {
b92bea5d 29
46b13157 30 static const union sockaddr_union sa = {
b92bea5d
ZJS
31 .un.sun_family = AF_UNIX,
32 .un.sun_path = "/run/systemd/journal/syslog",
33 };
34 struct msghdr msghdr = {
35 .msg_iov = (struct iovec *) iovec,
36 .msg_iovlen = n_iovec,
46b13157 37 .msg_name = (struct sockaddr*) &sa.sa,
fc2fffe7 38 .msg_namelen = SOCKADDR_UN_LEN(sa.un),
b92bea5d 39 };
35e2e347
LP
40 struct cmsghdr *cmsg;
41 union {
42 struct cmsghdr cmsghdr;
43 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
44 } control;
35e2e347
LP
45
46 assert(s);
47 assert(iovec);
48 assert(n_iovec > 0);
49
35e2e347
LP
50 if (ucred) {
51 zero(control);
52 msghdr.msg_control = &control;
53 msghdr.msg_controllen = sizeof(control);
54
55 cmsg = CMSG_FIRSTHDR(&msghdr);
56 cmsg->cmsg_level = SOL_SOCKET;
57 cmsg->cmsg_type = SCM_CREDENTIALS;
58 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
59 memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
60 msghdr.msg_controllen = cmsg->cmsg_len;
61 }
62
63 /* Forward the syslog message we received via /dev/log to
64 * /run/systemd/syslog. Unfortunately we currently can't set
65 * the SO_TIMESTAMP auxiliary data, and hence we don't. */
66
67 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
68 return;
69
70 /* The socket is full? I guess the syslog implementation is
71 * too slow, and we shouldn't wait for that... */
178cc770
LP
72 if (errno == EAGAIN) {
73 s->n_forward_syslog_missed++;
35e2e347 74 return;
178cc770 75 }
35e2e347 76
3742095b 77 if (ucred && IN_SET(errno, ESRCH, EPERM)) {
35e2e347
LP
78 struct ucred u;
79
80 /* Hmm, presumably the sender process vanished
ccf23ad5
CS
81 * by now, or we don't have CAP_SYS_AMDIN, so
82 * let's fix it as good as we can, and retry */
35e2e347
LP
83
84 u = *ucred;
df0ff127 85 u.pid = getpid_cached();
35e2e347
LP
86 memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
87
88 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
89 return;
90
178cc770
LP
91 if (errno == EAGAIN) {
92 s->n_forward_syslog_missed++;
35e2e347 93 return;
178cc770 94 }
35e2e347
LP
95 }
96
97 if (errno != ENOENT)
56f64d95 98 log_debug_errno(errno, "Failed to forward syslog message: %m");
35e2e347
LP
99}
100
bb3ff70a 101static void forward_syslog_raw(Server *s, int priority, const char *buffer, size_t buffer_len, const struct ucred *ucred, const struct timeval *tv) {
35e2e347
LP
102 struct iovec iovec;
103
104 assert(s);
105 assert(buffer);
106
107 if (LOG_PRI(priority) > s->max_level_syslog)
108 return;
109
bb3ff70a 110 iovec = IOVEC_MAKE((char *) buffer, buffer_len);
35e2e347
LP
111 forward_syslog_iovec(s, &iovec, 1, ucred, tv);
112}
113
3b3154df 114void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv) {
35e2e347 115 struct iovec iovec[5];
3b97fcbd 116 char header_priority[DECIMAL_STR_MAX(priority) + 3], header_time[64],
fbd0b64f 117 header_pid[STRLEN("[]: ") + DECIMAL_STR_MAX(pid_t) + 1];
35e2e347
LP
118 int n = 0;
119 time_t t;
e0f691e1 120 struct tm tm;
e6a7ec4b 121 _cleanup_free_ char *ident_buf = NULL;
35e2e347
LP
122
123 assert(s);
124 assert(priority >= 0);
125 assert(priority <= 999);
126 assert(message);
127
128 if (LOG_PRI(priority) > s->max_level_syslog)
129 return;
130
131 /* First: priority field */
5ffa8c81 132 xsprintf(header_priority, "<%i>", priority);
e6a7ec4b 133 iovec[n++] = IOVEC_MAKE_STRING(header_priority);
35e2e347
LP
134
135 /* Second: timestamp */
136 t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
e0f691e1 137 if (!localtime_r(&t, &tm))
35e2e347 138 return;
e0f691e1 139 if (strftime(header_time, sizeof(header_time), "%h %e %T ", &tm) <= 0)
35e2e347 140 return;
e6a7ec4b 141 iovec[n++] = IOVEC_MAKE_STRING(header_time);
35e2e347
LP
142
143 /* Third: identifier and PID */
144 if (ucred) {
145 if (!identifier) {
146 get_process_comm(ucred->pid, &ident_buf);
147 identifier = ident_buf;
148 }
149
5ffa8c81 150 xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid);
35e2e347
LP
151
152 if (identifier)
e6a7ec4b 153 iovec[n++] = IOVEC_MAKE_STRING(identifier);
35e2e347 154
e6a7ec4b 155 iovec[n++] = IOVEC_MAKE_STRING(header_pid);
35e2e347 156 } else if (identifier) {
e6a7ec4b
LP
157 iovec[n++] = IOVEC_MAKE_STRING(identifier);
158 iovec[n++] = IOVEC_MAKE_STRING(": ");
35e2e347
LP
159 }
160
161 /* Fourth: message */
e6a7ec4b 162 iovec[n++] = IOVEC_MAKE_STRING(message);
35e2e347
LP
163
164 forward_syslog_iovec(s, iovec, n, ucred, tv);
35e2e347
LP
165}
166
167int syslog_fixup_facility(int priority) {
168
169 if ((priority & LOG_FACMASK) == 0)
170 return (priority & LOG_PRIMASK) | LOG_USER;
171
172 return priority;
173}
174
e88baee8 175size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) {
35e2e347
LP
176 const char *p;
177 char *t;
178 size_t l, e;
179
180 assert(buf);
181 assert(identifier);
182 assert(pid);
183
184 p = *buf;
185
186 p += strspn(p, WHITESPACE);
187 l = strcspn(p, WHITESPACE);
188
189 if (l <= 0 ||
190 p[l-1] != ':')
e88baee8 191 return 0;
35e2e347
LP
192
193 e = l;
194 l--;
195
a6aadf4a 196 if (l > 0 && p[l-1] == ']') {
35e2e347
LP
197 size_t k = l-1;
198
199 for (;;) {
200
201 if (p[k] == '[') {
202 t = strndup(p+k+1, l-k-2);
203 if (t)
204 *pid = t;
205
206 l = k;
207 break;
208 }
209
210 if (k == 0)
211 break;
212
213 k--;
214 }
215 }
216
217 t = strndup(p, l);
218 if (t)
219 *identifier = t;
220
a6aadf4a
YW
221 e += strspn(p + e, WHITESPACE);
222
35e2e347 223 *buf = p + e;
e88baee8 224 return e;
35e2e347
LP
225}
226
7c30c3c4 227static int syslog_skip_timestamp(const char **buf) {
5809560d
LP
228 enum {
229 LETTER,
230 SPACE,
231 NUMBER,
232 SPACE_OR_NUMBER,
233 COLON
234 } sequence[] = {
235 LETTER, LETTER, LETTER,
236 SPACE,
237 SPACE_OR_NUMBER, NUMBER,
238 SPACE,
239 SPACE_OR_NUMBER, NUMBER,
240 COLON,
241 SPACE_OR_NUMBER, NUMBER,
242 COLON,
243 SPACE_OR_NUMBER, NUMBER,
244 SPACE
245 };
246
7c30c3c4 247 const char *p, *t;
5809560d
LP
248 unsigned i;
249
250 assert(buf);
251 assert(*buf);
252
7c30c3c4 253 for (i = 0, p = *buf; i < ELEMENTSOF(sequence); i++, p++) {
5809560d 254 if (!*p)
7c30c3c4 255 return 0;
5809560d
LP
256
257 switch (sequence[i]) {
258
259 case SPACE:
260 if (*p != ' ')
7c30c3c4 261 return 0;
5809560d
LP
262 break;
263
264 case SPACE_OR_NUMBER:
265 if (*p == ' ')
266 break;
267
4831981d 268 _fallthrough_;
5809560d
LP
269 case NUMBER:
270 if (*p < '0' || *p > '9')
7c30c3c4 271 return 0;
5809560d
LP
272
273 break;
274
275 case LETTER:
276 if (!(*p >= 'A' && *p <= 'Z') &&
277 !(*p >= 'a' && *p <= 'z'))
7c30c3c4 278 return 0;
5809560d
LP
279
280 break;
281
282 case COLON:
283 if (*p != ':')
7c30c3c4 284 return 0;
5809560d
LP
285 break;
286
287 }
288 }
289
7c30c3c4 290 t = *buf;
5809560d 291 *buf = p;
7c30c3c4 292 return p - t;
5809560d
LP
293}
294
35e2e347 295void server_process_syslog_message(
23be5709
LP
296 Server *s,
297 const char *buf,
c3950a9b 298 size_t raw_len,
23be5709
LP
299 const struct ucred *ucred,
300 const struct timeval *tv,
301 const char *label,
302 size_t label_len) {
35e2e347 303
7c30c3c4
ZJS
304 char *t, syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
305 syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
306 const char *msg, *syslog_ts, *a;
75f8d86e
YW
307 _cleanup_free_ char *identifier = NULL, *pid = NULL,
308 *dummy = NULL, *msg_msg = NULL, *msg_raw = NULL;
22e3a02b
LP
309 int priority = LOG_USER | LOG_INFO, r;
310 ClientContext *context = NULL;
d3070fbd 311 struct iovec *iovec;
7c30c3c4 312 size_t n = 0, m, i, leading_ws, syslog_ts_len;
df8701a3 313 bool store_raw;
35e2e347
LP
314
315 assert(s);
316 assert(buf);
c3950a9b
ZJS
317 /* The message cannot be empty. */
318 assert(raw_len > 0);
319 /* The buffer NUL-terminated and can be used a string. raw_len is the length
320 * without the terminating NUL byte, the buffer is actually one bigger. */
321 assert(buf[raw_len] == '\0');
35e2e347 322
d3070fbd
LP
323 if (ucred && pid_is_valid(ucred->pid)) {
324 r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
325 if (r < 0)
326 log_warning_errno(r, "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m", ucred->pid);
327 }
328
c3950a9b
ZJS
329 /* We are creating a copy of the message because we want to forward the original message
330 verbatim to the legacy syslog implementation */
331 for (i = raw_len; i > 0; i--)
bb3ff70a
MS
332 if (!strchr(WHITESPACE, buf[i-1]))
333 break;
334
c3950a9b 335 leading_ws = strspn(buf, WHITESPACE);
bb3ff70a 336
57019d5f
YW
337 if (i == 0)
338 /* The message contains only whitespaces */
339 msg = buf + raw_len;
340 else if (i == raw_len)
c3950a9b
ZJS
341 /* Nice! No need to strip anything on the end, let's optimize this a bit */
342 msg = buf + leading_ws;
343 else {
75f8d86e
YW
344 msg = dummy = new(char, i - leading_ws + 1);
345 if (!dummy) {
346 log_oom();
347 return;
348 }
349
350 memcpy(dummy, buf + leading_ws, i - leading_ws);
351 dummy[i - leading_ws] = 0;
c3950a9b 352 }
bb3ff70a 353
df8701a3
ZJS
354 /* We will add the SYSLOG_RAW= field when we stripped anything
355 * _or_ if the input message contained NUL bytes. */
356 store_raw = msg != buf || strlen(msg) != raw_len;
357
c3950a9b 358 syslog_parse_priority(&msg, &priority, true);
35e2e347 359
d3070fbd
LP
360 if (!client_context_test_priority(context, priority))
361 return;
362
7c30c3c4
ZJS
363 syslog_ts = msg;
364 syslog_ts_len = syslog_skip_timestamp(&msg);
365 if (syslog_ts_len == 0)
366 /* We failed to parse the full timestamp, store the raw message too */
367 store_raw = true;
368
c3950a9b 369 syslog_parse_identifier(&msg, &identifier, &pid);
35e2e347 370
c3950a9b
ZJS
371 if (s->forward_to_syslog)
372 forward_syslog_raw(s, priority, buf, raw_len, ucred, tv);
35e2e347
LP
373
374 if (s->forward_to_kmsg)
bb3ff70a 375 server_forward_kmsg(s, priority, identifier, msg, ucred);
35e2e347
LP
376
377 if (s->forward_to_console)
bb3ff70a 378 server_forward_console(s, priority, identifier, msg, ucred);
35e2e347 379
40b71e89 380 if (s->forward_to_wall)
bb3ff70a 381 server_forward_wall(s, priority, identifier, msg, ucred);
40b71e89 382
7c30c3c4 383 m = N_IOVEC_META_FIELDS + 8 + client_context_extra_fields_n_iovec(context);
d3070fbd
LP
384 iovec = newa(struct iovec, m);
385
e6a7ec4b 386 iovec[n++] = IOVEC_MAKE_STRING("_TRANSPORT=syslog");
35e2e347 387
d054f0a4 388 xsprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
e6a7ec4b 389 iovec[n++] = IOVEC_MAKE_STRING(syslog_priority);
35e2e347 390
8457f8d6 391 if (priority & LOG_FACMASK) {
d054f0a4 392 xsprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
e6a7ec4b 393 iovec[n++] = IOVEC_MAKE_STRING(syslog_facility);
8457f8d6 394 }
35e2e347
LP
395
396 if (identifier) {
7c30c3c4
ZJS
397 a = strjoina("SYSLOG_IDENTIFIER=", identifier);
398 iovec[n++] = IOVEC_MAKE_STRING(a);
35e2e347
LP
399 }
400
401 if (pid) {
7c30c3c4
ZJS
402 a = strjoina("SYSLOG_PID=", pid);
403 iovec[n++] = IOVEC_MAKE_STRING(a);
35e2e347
LP
404 }
405
7c30c3c4
ZJS
406 if (syslog_ts_len > 0) {
407 const size_t hlen = strlen("SYSLOG_TIMESTAMP=");
408
b9b8874f 409 t = newa(char, hlen + syslog_ts_len);
7c30c3c4
ZJS
410 memcpy(t, "SYSLOG_TIMESTAMP=", hlen);
411 memcpy(t + hlen, syslog_ts, syslog_ts_len);
412
413 iovec[n++] = IOVEC_MAKE(t, hlen + syslog_ts_len);
35e2e347
LP
414 }
415
75f8d86e
YW
416 msg_msg = strjoin("MESSAGE=", msg);
417 if (!msg_msg) {
418 log_oom();
419 return;
420 }
421 iovec[n++] = IOVEC_MAKE_STRING(msg_msg);
df8701a3
ZJS
422
423 if (store_raw) {
424 const size_t hlen = strlen("SYSLOG_RAW=");
df8701a3 425
75f8d86e
YW
426 msg_raw = new(char, hlen + raw_len);
427 if (!msg_raw) {
428 log_oom();
429 return;
430 }
431
432 memcpy(msg_raw, "SYSLOG_RAW=", hlen);
433 memcpy(msg_raw + hlen, buf, raw_len);
df8701a3 434
75f8d86e 435 iovec[n++] = IOVEC_MAKE(msg_raw, hlen + raw_len);
df8701a3 436 }
35e2e347 437
d3070fbd 438 server_dispatch_message(s, iovec, n, m, context, tv, priority, 0);
35e2e347
LP
439}
440
441int server_open_syslog_socket(Server *s) {
fc2fffe7
LP
442
443 static const union sockaddr_union sa = {
444 .un.sun_family = AF_UNIX,
445 .un.sun_path = "/run/systemd/journal/dev-log",
446 };
3b3154df
LP
447 static const int one = 1;
448 int r;
35e2e347
LP
449
450 assert(s);
451
452 if (s->syslog_fd < 0) {
35e2e347 453 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
4a62c710
MS
454 if (s->syslog_fd < 0)
455 return log_error_errno(errno, "socket() failed: %m");
35e2e347 456
fc2fffe7 457 (void) unlink(sa.un.sun_path);
35e2e347 458
fc2fffe7 459 r = bind(s->syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
4a62c710
MS
460 if (r < 0)
461 return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
35e2e347 462
4a61c3e5 463 (void) chmod(sa.un.sun_path, 0666);
35e2e347
LP
464 } else
465 fd_nonblock(s->syslog_fd, 1);
466
35e2e347 467 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
4a62c710
MS
468 if (r < 0)
469 return log_error_errno(errno, "SO_PASSCRED failed: %m");
35e2e347 470
349cc4a5 471#if HAVE_SELINUX
6d395665 472 if (mac_selinux_use()) {
d682b3a7
LP
473 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
474 if (r < 0)
56f64d95 475 log_warning_errno(errno, "SO_PASSSEC failed: %m");
d682b3a7 476 }
35e2e347
LP
477#endif
478
35e2e347 479 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
4a62c710
MS
480 if (r < 0)
481 return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
35e2e347 482
8531ae70 483 r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, server_process_datagram, s);
23bbb0de
MS
484 if (r < 0)
485 return log_error_errno(r, "Failed to add syslog server fd to event loop: %m");
35e2e347 486
48cef295
VC
487 r = sd_event_source_set_priority(s->syslog_event_source, SD_EVENT_PRIORITY_NORMAL+5);
488 if (r < 0)
489 return log_error_errno(r, "Failed to adjust syslog event source priority: %m");
490
35e2e347
LP
491 return 0;
492}
178cc770
LP
493
494void server_maybe_warn_forward_syslog_missed(Server *s) {
495 usec_t n;
fc2fffe7 496
178cc770
LP
497 assert(s);
498
499 if (s->n_forward_syslog_missed <= 0)
500 return;
501
502 n = now(CLOCK_MONOTONIC);
503 if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
504 return;
505
13181942 506 server_driver_message(s, 0,
2b044526 507 "MESSAGE_ID=" SD_MESSAGE_FORWARD_SYSLOG_MISSED_STR,
8a03c9ef
ZJS
508 LOG_MESSAGE("Forwarding to syslog missed %u messages.",
509 s->n_forward_syslog_missed),
510 NULL);
178cc770
LP
511
512 s->n_forward_syslog_missed = 0;
513 s->last_warn_forward_syslog_missed = n;
514}