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