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