]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journal-send.c
b07bb592add44df9e485c9c74a7df374fdc99cbd
[thirdparty/systemd.git] / src / journal / journal-send.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <printf.h>
6 #include <stddef.h>
7 #include <sys/un.h>
8 #include <unistd.h>
9
10 #define SD_JOURNAL_SUPPRESS_LOCATION
11
12 #include "sd-journal.h"
13
14 #include "alloc-util.h"
15 #include "errno-util.h"
16 #include "fd-util.h"
17 #include "io-util.h"
18 #include "fileio.h"
19 #include "memfd-util.h"
20 #include "socket-util.h"
21 #include "stdio-util.h"
22 #include "string-util.h"
23 #include "tmpfile-util.h"
24
25 #define SNDBUF_SIZE (8*1024*1024)
26
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; \
33 *_f = newa(char, _fl + 10); \
34 memcpy(*_f, "CODE_FUNC=", 10); \
35 memcpy(*_f + 10, _func, _fl); \
36 } while (false)
37
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
43 static int journal_fd(void) {
44 int fd;
45 static int fd_plus_one = 0;
46
47 retry:
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
55 fd_inc_sndbuf(fd, SNDBUF_SIZE);
56
57 if (!__sync_bool_compare_and_swap(&fd_plus_one, 0, fd+1)) {
58 safe_close(fd);
59 goto retry;
60 }
61
62 return fd;
63 }
64
65 _public_ int sd_journal_print(int priority, const char *format, ...) {
66 int r;
67 va_list ap;
68
69 va_start(ap, format);
70 r = sd_journal_printv(priority, format, ap);
71 va_end(ap);
72
73 return r;
74 }
75
76 _public_ int sd_journal_printv(int priority, const char *format, va_list ap) {
77 char p[STRLEN("PRIORITY=") + DECIMAL_STR_MAX(int) + 1];
78 char sbuf[LINE_MAX + 8] = "MESSAGE=";
79 struct iovec iov[2];
80 int len;
81 va_list aq;
82 char *buffer = sbuf;
83
84 assert_return(priority >= 0, -EINVAL);
85 assert_return(priority <= 7, -EINVAL);
86 assert_return(format, -EINVAL);
87
88 xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK);
89
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
97 /* Allocate large buffer to accomodate big message */
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 }
105
106 /* Strip trailing whitespace, keep prefix whitespace. */
107 (void) strstrip(buffer);
108
109 /* Suppress empty lines */
110 if (isempty(buffer + 8))
111 return 0;
112
113 iov[0] = IOVEC_MAKE_STRING(buffer);
114 iov[1] = IOVEC_MAKE_STRING(p);
115
116 return sd_journal_sendv(iov, 2);
117 }
118
119 _printf_(1, 0) static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) {
120 PROTECT_ERRNO;
121 int r, n = 0, i = 0, j;
122 struct iovec *iov = NULL;
123
124 assert(_iov);
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;
135 }
136
137 while (format) {
138 struct iovec *c;
139 char *buffer;
140 va_list aq;
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
153 va_copy(aq, ap);
154 if (vasprintf(&buffer, format, aq) < 0) {
155 va_end(aq);
156 r = -ENOMEM;
157 goto fail;
158 }
159 va_end(aq);
160
161 VA_FORMAT_ADVANCE(format, ap);
162
163 (void) strstrip(buffer); /* strip trailing whitespace, keep prefixing whitespace */
164
165 iov[i++] = IOVEC_MAKE_STRING(buffer);
166
167 format = va_arg(ap, char *);
168 }
169
170 *_iov = iov;
171
172 return i;
173
174 fail:
175 for (j = 0; j < i; j++)
176 free(iov[j].iov_base);
177
178 free(iov);
179
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);
190 va_end(ap);
191
192 if (_unlikely_(i < 0)) {
193 r = i;
194 goto finish;
195 }
196
197 r = sd_journal_sendv(iov, i);
198
199 finish:
200 for (j = 0; j < i; j++)
201 free(iov[j].iov_base);
202
203 free(iov);
204
205 return r;
206 }
207
208 _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
209 PROTECT_ERRNO;
210 int fd, r;
211 _cleanup_close_ int buffer_fd = -1;
212 struct iovec *w;
213 uint64_t *l;
214 int i, j = 0;
215 static const union sockaddr_union sa = {
216 .un.sun_family = AF_UNIX,
217 .un.sun_path = "/run/systemd/journal/socket",
218 };
219 struct msghdr mh = {
220 .msg_name = (struct sockaddr*) &sa.sa,
221 .msg_namelen = SOCKADDR_UN_LEN(sa.un),
222 };
223 ssize_t k;
224 bool have_syslog_identifier = false;
225 bool seal = true;
226
227 assert_return(iov, -EINVAL);
228 assert_return(n > 0, -EINVAL);
229
230 w = newa(struct iovec, n * 5 + 3);
231 l = newa(uint64_t, n);
232
233 for (i = 0; i < n; i++) {
234 char *c, *nl;
235
236 if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1))
237 return -EINVAL;
238
239 c = memchr(iov[i].iov_base, '=', iov[i].iov_len);
240 if (_unlikely_(!c || c == iov[i].iov_base))
241 return -EINVAL;
242
243 have_syslog_identifier = have_syslog_identifier ||
244 (c == (char *) iov[i].iov_base + 17 &&
245 startswith(iov[i].iov_base, "SYSLOG_IDENTIFIER"));
246
247 nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len);
248 if (nl) {
249 if (_unlikely_(nl < c))
250 return -EINVAL;
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
257 w[j++] = IOVEC_MAKE(iov[i].iov_base, c - (char*) iov[i].iov_base);
258 w[j++] = IOVEC_MAKE_STRING("\n");
259
260 l[i] = htole64(iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1);
261 w[j++] = IOVEC_MAKE(&l[i], sizeof(uint64_t));
262
263 w[j++] = IOVEC_MAKE(c + 1, iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1);
264 } else
265 /* Nothing special? Then just add the line and
266 * append a newline */
267 w[j++] = iov[i];
268
269 w[j++] = IOVEC_MAKE_STRING("\n");
270 }
271
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
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");
284 }
285
286 fd = journal_fd();
287 if (_unlikely_(fd < 0))
288 return fd;
289
290 mh.msg_iov = w;
291 mh.msg_iovlen = j;
292
293 k = sendmsg(fd, &mh, MSG_NOSIGNAL);
294 if (k >= 0)
295 return 0;
296
297 /* Fail silently if the journal is not available */
298 if (errno == ENOENT)
299 return 0;
300
301 if (!IN_SET(errno, EMSGSIZE, ENOBUFS))
302 return -errno;
303
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.
307 *
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. */
312 buffer_fd = memfd_new(NULL);
313 if (buffer_fd < 0) {
314 if (buffer_fd == -ENOSYS) {
315 buffer_fd = open_tmpfile_unlinkable("/dev/shm", O_RDWR | O_CLOEXEC);
316 if (buffer_fd < 0)
317 return buffer_fd;
318
319 seal = false;
320 } else
321 return buffer_fd;
322 }
323
324 n = writev(buffer_fd, w, j);
325 if (n < 0)
326 return -errno;
327
328 if (seal) {
329 r = memfd_set_sealed(buffer_fd);
330 if (r < 0)
331 return r;
332 }
333
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;
339 }
340
341 static int fill_iovec_perror_and_send(const char *message, int skip, struct iovec iov[]) {
342 PROTECT_ERRNO;
343 size_t n, k;
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;
353 j = strerror_r(_saved_errno_, buffer + 8 + k, n - 8 - k);
354 if (errno == 0) {
355 char error[STRLEN("ERRNO=") + DECIMAL_STR_MAX(int) + 1];
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
367 xsprintf(error, "ERRNO=%i", _saved_errno_);
368
369 assert_cc(3 == LOG_ERR);
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);
373
374 return sd_journal_sendv(iov, skip + 3);
375 }
376
377 if (errno != ERANGE)
378 return -errno;
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
390 _public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) {
391 static const union sockaddr_union sa = {
392 .un.sun_family = AF_UNIX,
393 .un.sun_path = "/run/systemd/journal/stdout",
394 };
395 _cleanup_close_ int fd = -1;
396 char *header;
397 size_t l;
398 int r;
399
400 assert_return(priority >= 0, -EINVAL);
401 assert_return(priority <= 7, -EINVAL);
402
403 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
404 if (fd < 0)
405 return -errno;
406
407 r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
408 if (r < 0)
409 return -errno;
410
411 if (shutdown(fd, SHUT_RD) < 0)
412 return -errno;
413
414 (void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
415
416 identifier = strempty(identifier);
417
418 l = strlen(identifier);
419 header = newa(char, l + 1 + 1 + 2 + 2 + 2 + 2 + 2);
420
421 memcpy(header, identifier, l);
422 header[l++] = '\n';
423 header[l++] = '\n'; /* unit id */
424 header[l++] = '0' + priority;
425 header[l++] = '\n';
426 header[l++] = '0' + !!level_prefix;
427 header[l++] = '\n';
428 header[l++] = '0';
429 header[l++] = '\n';
430 header[l++] = '0';
431 header[l++] = '\n';
432 header[l++] = '0';
433 header[l++] = '\n';
434
435 r = loop_write(fd, header, l, false);
436 if (r < 0)
437 return r;
438
439 return TAKE_FD(fd);
440 }
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) {
454 char p[STRLEN("PRIORITY=") + DECIMAL_STR_MAX(int) + 1];
455 char sbuf[LINE_MAX + 8] = "MESSAGE=";
456 struct iovec iov[5];
457 char *f;
458 int len;
459 char *buffer = sbuf;
460 va_list aq;
461
462 assert_return(priority >= 0, -EINVAL);
463 assert_return(priority <= 7, -EINVAL);
464 assert_return(format, -EINVAL);
465
466 xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK);
467
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
475 /* Allocate large buffer to accomodate big message */
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 }
483
484 /* Strip trailing whitespace, keep prefixing whitespace */
485 (void) strstrip(buffer);
486
487 /* Suppress empty lines */
488 if (isempty(buffer + 8))
489 return 0;
490
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. */
494 ALLOCA_CODE_FUNC(f, func);
495
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);
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, ...) {
506 _cleanup_free_ struct iovec *iov = NULL;
507 int r, i, j;
508 va_list ap;
509 char *f;
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
520 ALLOCA_CODE_FUNC(f, func);
521
522 iov[0] = IOVEC_MAKE_STRING(file);
523 iov[1] = IOVEC_MAKE_STRING(line);
524 iov[2] = IOVEC_MAKE_STRING(f);
525
526 r = sd_journal_sendv(iov, i);
527
528 finish:
529 for (j = 3; j < i; j++)
530 free(iov[j].iov_base);
531
532 return r;
533 }
534
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
540 struct iovec *niov;
541 char *f;
542
543 assert_return(iov, -EINVAL);
544 assert_return(n > 0, -EINVAL);
545
546 niov = newa(struct iovec, n + 3);
547 memcpy(niov, iov, sizeof(struct iovec) * n);
548
549 ALLOCA_CODE_FUNC(f, func);
550
551 niov[n++] = IOVEC_MAKE_STRING(file);
552 niov[n++] = IOVEC_MAKE_STRING(line);
553 niov[n++] = IOVEC_MAKE_STRING(f);
554
555 return sd_journal_sendv(niov, n);
556 }
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];
564 char *f;
565
566 ALLOCA_CODE_FUNC(f, func);
567
568 iov[0] = IOVEC_MAKE_STRING(file);
569 iov[1] = IOVEC_MAKE_STRING(line);
570 iov[2] = IOVEC_MAKE_STRING(f);
571
572 return fill_iovec_perror_and_send(message, 3, iov);
573 }