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