]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journal-send.c
Merge pull request #15840 from Werkov/mkosi-opensuse
[thirdparty/systemd.git] / src / journal / journal-send.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
7f3e6257 2
7f3e6257 3#include <errno.h>
0dad12c1 4#include <fcntl.h>
72f1d5a2 5#include <printf.h>
07630cea 6#include <stddef.h>
07630cea
LP
7#include <sys/un.h>
8#include <unistd.h>
7f3e6257 9
b070e7f3
LP
10#define SD_JOURNAL_SUPPRESS_LOCATION
11
7f3e6257 12#include "sd-journal.h"
07630cea 13
b5efdb8a 14#include "alloc-util.h"
2b2fec7d 15#include "errno-util.h"
3ffd4af2 16#include "fd-util.h"
c004493c 17#include "io-util.h"
2eaf435a 18#include "fileio.h"
a09abc4a 19#include "memfd-util.h"
07630cea 20#include "socket-util.h"
15a5e950 21#include "stdio-util.h"
07630cea 22#include "string-util.h"
e4de7287 23#include "tmpfile-util.h"
7f3e6257 24
bb99a35a
LP
25#define SNDBUF_SIZE (8*1024*1024)
26
3ed08c44
LP
27#define ALLOCA_CODE_FUNC(f, func) \
28 do { \
29 size_t _fl; \
30 const char *_func = (func); \
31 char **_f = &(f); \
32 _fl = strlen(_func) + 1; \
6e9417f5 33 *_f = newa(char, _fl + 10); \
3ed08c44
LP
34 memcpy(*_f, "CODE_FUNC=", 10); \
35 memcpy(*_f + 10, _func, _fl); \
9ed794a3 36 } while (false)
3ed08c44 37
7f3e6257
LP
38/* We open a single fd, and we'll share it with the current process,
39 * all its threads, and all its subprocesses. This means we need to
40 * initialize it atomically, and need to operate on it atomically
41 * never assuming we are the only user */
42
43static int journal_fd(void) {
44 int fd;
45 static int fd_plus_one = 0;
46
47retry:
48 if (fd_plus_one > 0)
49 return fd_plus_one - 1;
50
51 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
52 if (fd < 0)
53 return -errno;
54
bb99a35a
LP
55 fd_inc_sndbuf(fd, SNDBUF_SIZE);
56
7f3e6257 57 if (!__sync_bool_compare_and_swap(&fd_plus_one, 0, fd+1)) {
03e334a1 58 safe_close(fd);
7f3e6257
LP
59 goto retry;
60 }
61
62 return fd;
63}
64
a5344d2c 65_public_ int sd_journal_print(int priority, const char *format, ...) {
7f3e6257
LP
66 int r;
67 va_list ap;
68
69 va_start(ap, format);
d0bbc21c 70 r = sd_journal_printv(priority, format, ap);
7f3e6257
LP
71 va_end(ap);
72
73 return r;
74}
75
a5344d2c 76_public_ int sd_journal_printv(int priority, const char *format, va_list ap) {
2eaf435a
AJ
77 char p[STRLEN("PRIORITY=") + DECIMAL_STR_MAX(int) + 1];
78 char sbuf[LINE_MAX + 8] = "MESSAGE=";
5ffa8c81 79 struct iovec iov[2];
2eaf435a
AJ
80 int len;
81 va_list aq;
82 char *buffer = sbuf;
d0bbc21c 83
1ae464e0
TA
84 assert_return(priority >= 0, -EINVAL);
85 assert_return(priority <= 7, -EINVAL);
86 assert_return(format, -EINVAL);
fe652127 87
5ffa8c81 88 xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK);
7f3e6257 89
2eaf435a
AJ
90 va_copy(aq, ap);
91 len = vsnprintf(buffer + 8, LINE_MAX, format, aq);
92 va_end(aq);
93
94 if (len >= (int)LONG_LINE_MAX - 8)
95 return -ENOBUFS;
96
15dd4515 97 /* Allocate large buffer to accommodate big message */
2eaf435a
AJ
98 if (len >= LINE_MAX) {
99 int rlen;
100 buffer = alloca(len + 9);
101 memcpy(buffer, "MESSAGE=", 8);
102 rlen = vsnprintf(buffer + 8, len + 1, format, ap);
103 assert(len == rlen);
104 }
7f3e6257 105
c24f1f9d
LP
106 /* Strip trailing whitespace, keep prefix whitespace. */
107 (void) strstrip(buffer);
108
0d23bc57 109 /* Suppress empty lines */
2eaf435a 110 if (isempty(buffer + 8))
0d23bc57
LP
111 return 0;
112
e6a7ec4b
LP
113 iov[0] = IOVEC_MAKE_STRING(buffer);
114 iov[1] = IOVEC_MAKE_STRING(p);
7f3e6257 115
d0bbc21c 116 return sd_journal_sendv(iov, 2);
7f3e6257
LP
117}
118
44b601bc 119_printf_(1, 0) static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) {
5c0aa72a 120 PROTECT_ERRNO;
25d042e8 121 int r, n = 0, i = 0, j;
7f3e6257 122 struct iovec *iov = NULL;
b070e7f3
LP
123
124 assert(_iov);
b070e7f3
LP
125
126 if (extra > 0) {
127 n = MAX(extra * 2, extra + 4);
128 iov = malloc0(n * sizeof(struct iovec));
129 if (!iov) {
130 r = -ENOMEM;
131 goto fail;
132 }
133
134 i = extra;
25d042e8 135 }
7f3e6257 136
7f3e6257
LP
137 while (format) {
138 struct iovec *c;
139 char *buffer;
72f1d5a2 140 va_list aq;
7f3e6257
LP
141
142 if (i >= n) {
143 n = MAX(i*2, 4);
144 c = realloc(iov, n * sizeof(struct iovec));
145 if (!c) {
146 r = -ENOMEM;
147 goto fail;
148 }
149
150 iov = c;
151 }
152
72f1d5a2
LP
153 va_copy(aq, ap);
154 if (vasprintf(&buffer, format, aq) < 0) {
155 va_end(aq);
7f3e6257
LP
156 r = -ENOMEM;
157 goto fail;
158 }
72f1d5a2
LP
159 va_end(aq);
160
161 VA_FORMAT_ADVANCE(format, ap);
7f3e6257 162
c24f1f9d
LP
163 (void) strstrip(buffer); /* strip trailing whitespace, keep prefixing whitespace */
164
e6a7ec4b 165 iov[i++] = IOVEC_MAKE_STRING(buffer);
7f3e6257
LP
166
167 format = va_arg(ap, char *);
168 }
b070e7f3
LP
169
170 *_iov = iov;
171
b070e7f3
LP
172 return i;
173
174fail:
175 for (j = 0; j < i; j++)
176 free(iov[j].iov_base);
177
178 free(iov);
179
b070e7f3
LP
180 return r;
181}
182
183_public_ int sd_journal_send(const char *format, ...) {
184 int r, i, j;
185 va_list ap;
186 struct iovec *iov = NULL;
187
188 va_start(ap, format);
189 i = fill_iovec_sprintf(format, ap, 0, &iov);
7f3e6257
LP
190 va_end(ap);
191
b070e7f3
LP
192 if (_unlikely_(i < 0)) {
193 r = i;
194 goto finish;
195 }
196
7f3e6257
LP
197 r = sd_journal_sendv(iov, i);
198
b070e7f3 199finish:
7f3e6257
LP
200 for (j = 0; j < i; j++)
201 free(iov[j].iov_base);
202
203 free(iov);
204
205 return r;
206}
207
a5344d2c 208_public_ int sd_journal_sendv(const struct iovec *iov, int n) {
5c0aa72a 209 PROTECT_ERRNO;
c79e98ea 210 int fd, r;
61c024b3 211 _cleanup_close_ int buffer_fd = -1;
7f3e6257
LP
212 struct iovec *w;
213 uint64_t *l;
5c0aa72a 214 int i, j = 0;
fc2fffe7
LP
215 static const union sockaddr_union sa = {
216 .un.sun_family = AF_UNIX,
217 .un.sun_path = "/run/systemd/journal/socket",
6e5abe15
ZJS
218 };
219 struct msghdr mh = {
fc2fffe7
LP
220 .msg_name = (struct sockaddr*) &sa.sa,
221 .msg_namelen = SOCKADDR_UN_LEN(sa.un),
6e5abe15 222 };
0dad12c1 223 ssize_t k;
ee55db41 224 bool have_syslog_identifier = false;
c79e98ea 225 bool seal = true;
7f3e6257 226
1ae464e0
TA
227 assert_return(iov, -EINVAL);
228 assert_return(n > 0, -EINVAL);
7f3e6257 229
9a7800af
ZJS
230 w = newa(struct iovec, n * 5 + 3);
231 l = newa(uint64_t, n);
7f3e6257
LP
232
233 for (i = 0; i < n; i++) {
234 char *c, *nl;
235
5c0aa72a
LP
236 if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1))
237 return -EINVAL;
a5344d2c 238
7f3e6257 239 c = memchr(iov[i].iov_base, '=', iov[i].iov_len);
5c0aa72a
LP
240 if (_unlikely_(!c || c == iov[i].iov_base))
241 return -EINVAL;
7f3e6257 242
29abad10
ZJS
243 have_syslog_identifier = have_syslog_identifier ||
244 (c == (char *) iov[i].iov_base + 17 &&
2a0e0692 245 startswith(iov[i].iov_base, "SYSLOG_IDENTIFIER"));
ee55db41 246
7f3e6257
LP
247 nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len);
248 if (nl) {
5c0aa72a
LP
249 if (_unlikely_(nl < c))
250 return -EINVAL;
7f3e6257
LP
251
252 /* Already includes a newline? Bummer, then
253 * let's write the variable name, then a
254 * newline, then the size (64bit LE), followed
255 * by the data and a final newline */
256
e6a7ec4b
LP
257 w[j++] = IOVEC_MAKE(iov[i].iov_base, c - (char*) iov[i].iov_base);
258 w[j++] = IOVEC_MAKE_STRING("\n");
7f3e6257
LP
259
260 l[i] = htole64(iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1);
e6a7ec4b 261 w[j++] = IOVEC_MAKE(&l[i], sizeof(uint64_t));
7f3e6257 262
e6a7ec4b 263 w[j++] = IOVEC_MAKE(c + 1, iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1);
7f3e6257
LP
264 } else
265 /* Nothing special? Then just add the line and
266 * append a newline */
267 w[j++] = iov[i];
268
e6a7ec4b 269 w[j++] = IOVEC_MAKE_STRING("\n");
7f3e6257
LP
270 }
271
ee55db41
LP
272 if (!have_syslog_identifier &&
273 string_is_safe(program_invocation_short_name)) {
274
275 /* Implicitly add program_invocation_short_name, if it
276 * is not set explicitly. We only do this for
277 * program_invocation_short_name, and nothing else
278 * since everything else is much nicer to retrieve
279 * from the outside. */
280
e6a7ec4b
LP
281 w[j++] = IOVEC_MAKE_STRING("SYSLOG_IDENTIFIER=");
282 w[j++] = IOVEC_MAKE_STRING(program_invocation_short_name);
283 w[j++] = IOVEC_MAKE_STRING("\n");
ee55db41
LP
284 }
285
7f3e6257 286 fd = journal_fd();
5c0aa72a
LP
287 if (_unlikely_(fd < 0))
288 return fd;
7f3e6257 289
7f3e6257
LP
290 mh.msg_iov = w;
291 mh.msg_iovlen = j;
292
0dad12c1 293 k = sendmsg(fd, &mh, MSG_NOSIGNAL);
5c0aa72a
LP
294 if (k >= 0)
295 return 0;
0dad12c1 296
6c045c0b
ZJS
297 /* Fail silently if the journal is not available */
298 if (errno == ENOENT)
299 return 0;
300
ec2ce0c5 301 if (!IN_SET(errno, EMSGSIZE, ENOBUFS))
5c0aa72a 302 return -errno;
0dad12c1 303
c79e98ea
LP
304 /* Message doesn't fit... Let's dump the data in a memfd or
305 * temporary file and just pass a file descriptor of it to the
306 * other side.
8e33886e 307 *
c79e98ea
LP
308 * For the temporary files we use /dev/shm instead of /tmp
309 * here, since we want this to be a tmpfs, and one that is
310 * available from early boot on and where unprivileged users
311 * can create files. */
73843b52 312 buffer_fd = memfd_new(NULL);
c79e98ea 313 if (buffer_fd < 0) {
73843b52 314 if (buffer_fd == -ENOSYS) {
03532f0a 315 buffer_fd = open_tmpfile_unlinkable("/dev/shm", O_RDWR | O_CLOEXEC);
c79e98ea
LP
316 if (buffer_fd < 0)
317 return buffer_fd;
318
319 seal = false;
320 } else
73843b52 321 return buffer_fd;
c79e98ea 322 }
0dad12c1 323
87b02843 324 n = writev(buffer_fd, w, j);
61c024b3 325 if (n < 0)
5c0aa72a 326 return -errno;
0dad12c1 327
c79e98ea 328 if (seal) {
73843b52 329 r = memfd_set_sealed(buffer_fd);
c79e98ea 330 if (r < 0)
73843b52 331 return r;
c79e98ea
LP
332 }
333
4941e4ac
ZJS
334 r = send_one_fd_sa(fd, buffer_fd, mh.msg_name, mh.msg_namelen, 0);
335 if (r == -ENOENT)
336 /* Fail silently if the journal is not available */
337 return 0;
338 return r;
7f3e6257 339}
fe652127 340
18c7ed18 341static int fill_iovec_perror_and_send(const char *message, int skip, struct iovec iov[]) {
5c0aa72a
LP
342 PROTECT_ERRNO;
343 size_t n, k;
18c7ed18
LP
344
345 k = isempty(message) ? 0 : strlen(message) + 2;
346 n = 8 + k + 256 + 1;
347
348 for (;;) {
349 char buffer[n];
350 char* j;
351
352 errno = 0;
5c0aa72a 353 j = strerror_r(_saved_errno_, buffer + 8 + k, n - 8 - k);
18c7ed18 354 if (errno == 0) {
fbd0b64f 355 char error[STRLEN("ERRNO=") + DECIMAL_STR_MAX(int) + 1];
18c7ed18
LP
356
357 if (j != buffer + 8 + k)
358 memmove(buffer + 8 + k, j, strlen(j)+1);
359
360 memcpy(buffer, "MESSAGE=", 8);
361
362 if (k > 0) {
363 memcpy(buffer + 8, message, k - 2);
364 memcpy(buffer + 8 + k - 2, ": ", 2);
365 }
366
5ffa8c81 367 xsprintf(error, "ERRNO=%i", _saved_errno_);
18c7ed18 368
4850d39a 369 assert_cc(3 == LOG_ERR);
e6a7ec4b
LP
370 iov[skip+0] = IOVEC_MAKE_STRING("PRIORITY=3");
371 iov[skip+1] = IOVEC_MAKE_STRING(buffer);
372 iov[skip+2] = IOVEC_MAKE_STRING(error);
18c7ed18 373
5c0aa72a 374 return sd_journal_sendv(iov, skip + 3);
18c7ed18
LP
375 }
376
5c0aa72a
LP
377 if (errno != ERANGE)
378 return -errno;
18c7ed18
LP
379
380 n *= 2;
381 }
382}
383
384_public_ int sd_journal_perror(const char *message) {
385 struct iovec iovec[3];
386
387 return fill_iovec_perror_and_send(message, 0, iovec);
388}
389
4cd9a9d9 390_public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) {
fc2fffe7 391 static const union sockaddr_union sa = {
6e5abe15
ZJS
392 .un.sun_family = AF_UNIX,
393 .un.sun_path = "/run/systemd/journal/stdout",
394 };
61c024b3 395 _cleanup_close_ int fd = -1;
fe652127
LP
396 char *header;
397 size_t l;
61c024b3 398 int r;
fe652127 399
1ae464e0
TA
400 assert_return(priority >= 0, -EINVAL);
401 assert_return(priority <= 7, -EINVAL);
fe652127
LP
402
403 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
404 if (fd < 0)
405 return -errno;
406
fc2fffe7 407 r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
61c024b3 408 if (r < 0)
fe652127 409 return -errno;
fe652127 410
61c024b3 411 if (shutdown(fd, SHUT_RD) < 0)
86b9b8e7 412 return -errno;
86b9b8e7 413
efb8fd6e 414 (void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
bb99a35a 415
ad5d4b17 416 identifier = strempty(identifier);
fe652127 417
4cd9a9d9 418 l = strlen(identifier);
6e9417f5 419 header = newa(char, l + 1 + 1 + 2 + 2 + 2 + 2 + 2);
fe652127 420
4cd9a9d9 421 memcpy(header, identifier, l);
fe652127 422 header[l++] = '\n';
a6e87e90 423 header[l++] = '\n'; /* unit id */
fe652127
LP
424 header[l++] = '0' + priority;
425 header[l++] = '\n';
4cd9a9d9 426 header[l++] = '0' + !!level_prefix;
fe652127
LP
427 header[l++] = '\n';
428 header[l++] = '0';
429 header[l++] = '\n';
224f2ee2
LP
430 header[l++] = '0';
431 header[l++] = '\n';
432 header[l++] = '0';
433 header[l++] = '\n';
fe652127 434
553acb7b 435 r = loop_write(fd, header, l, false);
61c024b3
ZJS
436 if (r < 0)
437 return r;
fe652127 438
c10d6bdb 439 return TAKE_FD(fd);
fe652127 440}
b070e7f3
LP
441
442_public_ int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) {
443 int r;
444 va_list ap;
445
446 va_start(ap, format);
447 r = sd_journal_printv_with_location(priority, file, line, func, format, ap);
448 va_end(ap);
449
450 return r;
451}
452
453_public_ int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) {
2eaf435a
AJ
454 char p[STRLEN("PRIORITY=") + DECIMAL_STR_MAX(int) + 1];
455 char sbuf[LINE_MAX + 8] = "MESSAGE=";
b070e7f3
LP
456 struct iovec iov[5];
457 char *f;
2eaf435a
AJ
458 int len;
459 char *buffer = sbuf;
460 va_list aq;
b070e7f3 461
1ae464e0
TA
462 assert_return(priority >= 0, -EINVAL);
463 assert_return(priority <= 7, -EINVAL);
464 assert_return(format, -EINVAL);
b070e7f3 465
5ffa8c81 466 xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK);
b070e7f3 467
2eaf435a
AJ
468 va_copy(aq, ap);
469 len = vsnprintf(buffer + 8, LINE_MAX, format, aq);
470 va_end(aq);
471
472 if (len >= (int)LONG_LINE_MAX - 8)
473 return -ENOBUFS;
474
15dd4515 475 /* Allocate large buffer to accommodate big message */
2eaf435a
AJ
476 if (len >= LINE_MAX) {
477 int rlen;
478 buffer = alloca(len + 9);
479 memcpy(buffer, "MESSAGE=", 8);
480 rlen = vsnprintf(buffer + 8, len + 1, format, ap);
481 assert(len == rlen);
482 }
b070e7f3 483
0d23bc57
LP
484 /* Strip trailing whitespace, keep prefixing whitespace */
485 (void) strstrip(buffer);
486
487 /* Suppress empty lines */
2eaf435a 488 if (isempty(buffer + 8))
0d23bc57 489 return 0;
c24f1f9d 490
b070e7f3
LP
491 /* func is initialized from __func__ which is not a macro, but
492 * a static const char[], hence cannot easily be prefixed with
493 * CODE_FUNC=, hence let's do it manually here. */
3ed08c44 494 ALLOCA_CODE_FUNC(f, func);
b070e7f3 495
e6a7ec4b
LP
496 iov[0] = IOVEC_MAKE_STRING(buffer);
497 iov[1] = IOVEC_MAKE_STRING(p);
498 iov[2] = IOVEC_MAKE_STRING(file);
499 iov[3] = IOVEC_MAKE_STRING(line);
500 iov[4] = IOVEC_MAKE_STRING(f);
b070e7f3
LP
501
502 return sd_journal_sendv(iov, ELEMENTSOF(iov));
503}
504
505_public_ int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) {
e6a7ec4b 506 _cleanup_free_ struct iovec *iov = NULL;
b070e7f3
LP
507 int r, i, j;
508 va_list ap;
b070e7f3 509 char *f;
b070e7f3
LP
510
511 va_start(ap, format);
512 i = fill_iovec_sprintf(format, ap, 3, &iov);
513 va_end(ap);
514
515 if (_unlikely_(i < 0)) {
516 r = i;
517 goto finish;
518 }
519
3ed08c44 520 ALLOCA_CODE_FUNC(f, func);
b070e7f3 521
e6a7ec4b
LP
522 iov[0] = IOVEC_MAKE_STRING(file);
523 iov[1] = IOVEC_MAKE_STRING(line);
524 iov[2] = IOVEC_MAKE_STRING(f);
b070e7f3
LP
525
526 r = sd_journal_sendv(iov, i);
527
528finish:
529 for (j = 3; j < i; j++)
530 free(iov[j].iov_base);
531
b070e7f3
LP
532 return r;
533}
534
18c7ed18
LP
535_public_ int sd_journal_sendv_with_location(
536 const char *file, const char *line,
537 const char *func,
538 const struct iovec *iov, int n) {
539
b070e7f3
LP
540 struct iovec *niov;
541 char *f;
b070e7f3 542
1ae464e0
TA
543 assert_return(iov, -EINVAL);
544 assert_return(n > 0, -EINVAL);
b070e7f3 545
702bd55c 546 niov = newa(struct iovec, n + 3);
b070e7f3
LP
547 memcpy(niov, iov, sizeof(struct iovec) * n);
548
3ed08c44 549 ALLOCA_CODE_FUNC(f, func);
b070e7f3 550
e6a7ec4b
LP
551 niov[n++] = IOVEC_MAKE_STRING(file);
552 niov[n++] = IOVEC_MAKE_STRING(line);
553 niov[n++] = IOVEC_MAKE_STRING(f);
b070e7f3
LP
554
555 return sd_journal_sendv(niov, n);
556}
18c7ed18
LP
557
558_public_ int sd_journal_perror_with_location(
559 const char *file, const char *line,
560 const char *func,
561 const char *message) {
562
563 struct iovec iov[6];
18c7ed18
LP
564 char *f;
565
3ed08c44 566 ALLOCA_CODE_FUNC(f, func);
18c7ed18 567
e6a7ec4b
LP
568 iov[0] = IOVEC_MAKE_STRING(file);
569 iov[1] = IOVEC_MAKE_STRING(line);
570 iov[2] = IOVEC_MAKE_STRING(f);
18c7ed18
LP
571
572 return fill_iovec_perror_and_send(message, 3, iov);
573}