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