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