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