]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journal-send.c
Add __attribute__((const, pure, format)) in various places
[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>
72f1d5a2 28#include <printf.h>
7f3e6257 29
b070e7f3
LP
30#define SD_JOURNAL_SUPPRESS_LOCATION
31
7f3e6257
LP
32#include "sd-journal.h"
33#include "util.h"
fe652127 34#include "socket-util.h"
7f3e6257 35
bb99a35a
LP
36#define SNDBUF_SIZE (8*1024*1024)
37
3ed08c44
LP
38#define ALLOCA_CODE_FUNC(f, func) \
39 do { \
40 size_t _fl; \
41 const char *_func = (func); \
42 char **_f = &(f); \
43 _fl = strlen(_func) + 1; \
44 *_f = alloca(_fl + 10); \
45 memcpy(*_f, "CODE_FUNC=", 10); \
46 memcpy(*_f + 10, _func, _fl); \
47 } while(false)
48
7f3e6257
LP
49/* We open a single fd, and we'll share it with the current process,
50 * all its threads, and all its subprocesses. This means we need to
51 * initialize it atomically, and need to operate on it atomically
52 * never assuming we are the only user */
53
54static int journal_fd(void) {
55 int fd;
56 static int fd_plus_one = 0;
57
58retry:
59 if (fd_plus_one > 0)
60 return fd_plus_one - 1;
61
62 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
63 if (fd < 0)
64 return -errno;
65
bb99a35a
LP
66 fd_inc_sndbuf(fd, SNDBUF_SIZE);
67
7f3e6257
LP
68 if (!__sync_bool_compare_and_swap(&fd_plus_one, 0, fd+1)) {
69 close_nointr_nofail(fd);
70 goto retry;
71 }
72
73 return fd;
74}
75
a5344d2c 76_public_ int sd_journal_print(int priority, const char *format, ...) {
7f3e6257
LP
77 int r;
78 va_list ap;
79
80 va_start(ap, format);
d0bbc21c 81 r = sd_journal_printv(priority, format, ap);
7f3e6257
LP
82 va_end(ap);
83
84 return r;
85}
86
a5344d2c 87_public_ int sd_journal_printv(int priority, const char *format, va_list ap) {
18c7ed18
LP
88
89 /* FIXME: Instead of limiting things to LINE_MAX we could do a
90 C99 variable-length array on the stack here in a loop. */
91
92 char buffer[8 + LINE_MAX], p[11]; struct iovec iov[2];
d0bbc21c 93
fe652127
LP
94 if (priority < 0 || priority > 7)
95 return -EINVAL;
96
97 if (!format)
98 return -EINVAL;
99
d0bbc21c
LP
100 snprintf(p, sizeof(p), "PRIORITY=%i", priority & LOG_PRIMASK);
101 char_array_0(p);
7f3e6257
LP
102
103 memcpy(buffer, "MESSAGE=", 8);
104 vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap);
7f3e6257
LP
105 char_array_0(buffer);
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
44a6b1b6 114_printf_attr_(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;
0dad12c1 203 int fd, buffer_fd;
7f3e6257
LP
204 struct iovec *w;
205 uint64_t *l;
5c0aa72a 206 int i, j = 0;
7f3e6257
LP
207 struct msghdr mh;
208 struct sockaddr_un sa;
0dad12c1
LP
209 ssize_t k;
210 union {
211 struct cmsghdr cmsghdr;
212 uint8_t buf[CMSG_SPACE(sizeof(int))];
213 } control;
214 struct cmsghdr *cmsg;
9058851b
LP
215 /* We use /dev/shm instead of /tmp here, since we want this to
216 * be a tmpfs, and one that is available from early boot on
217 * and where unprivileged users can create files. */
218 char path[] = "/dev/shm/journal.XXXXXX";
ee55db41 219 bool have_syslog_identifier = false;
7f3e6257 220
b070e7f3
LP
221 if (_unlikely_(!iov))
222 return -EINVAL;
223
224 if (_unlikely_(n <= 0))
7f3e6257
LP
225 return -EINVAL;
226
ee55db41 227 w = alloca(sizeof(struct iovec) * n * 5 + 3);
7f3e6257
LP
228 l = alloca(sizeof(uint64_t) * n);
229
230 for (i = 0; i < n; i++) {
231 char *c, *nl;
232
5c0aa72a
LP
233 if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1))
234 return -EINVAL;
a5344d2c 235
7f3e6257 236 c = memchr(iov[i].iov_base, '=', iov[i].iov_len);
5c0aa72a
LP
237 if (_unlikely_(!c || c == iov[i].iov_base))
238 return -EINVAL;
7f3e6257 239
29abad10
ZJS
240 have_syslog_identifier = have_syslog_identifier ||
241 (c == (char *) iov[i].iov_base + 17 &&
242 memcmp(iov[i].iov_base, "SYSLOG_IDENTIFIER", 17) == 0);
ee55db41 243
7f3e6257
LP
244 nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len);
245 if (nl) {
5c0aa72a
LP
246 if (_unlikely_(nl < c))
247 return -EINVAL;
7f3e6257
LP
248
249 /* Already includes a newline? Bummer, then
250 * let's write the variable name, then a
251 * newline, then the size (64bit LE), followed
252 * by the data and a final newline */
253
254 w[j].iov_base = iov[i].iov_base;
255 w[j].iov_len = c - (char*) iov[i].iov_base;
256 j++;
257
258 IOVEC_SET_STRING(w[j++], "\n");
259
260 l[i] = htole64(iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1);
261 w[j].iov_base = &l[i];
262 w[j].iov_len = sizeof(uint64_t);
263 j++;
264
265 w[j].iov_base = c + 1;
266 w[j].iov_len = iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1;
267 j++;
268
269 } else
270 /* Nothing special? Then just add the line and
271 * append a newline */
272 w[j++] = iov[i];
273
274 IOVEC_SET_STRING(w[j++], "\n");
275 }
276
ee55db41
LP
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 IOVEC_SET_STRING(w[j++], "SYSLOG_IDENTIFIER=");
287 IOVEC_SET_STRING(w[j++], program_invocation_short_name);
288 IOVEC_SET_STRING(w[j++], "\n");
289 }
290
7f3e6257 291 fd = journal_fd();
5c0aa72a
LP
292 if (_unlikely_(fd < 0))
293 return fd;
7f3e6257
LP
294
295 zero(sa);
296 sa.sun_family = AF_UNIX;
5ba081b0 297 strncpy(sa.sun_path, "/run/systemd/journal/socket", sizeof(sa.sun_path));
7f3e6257
LP
298
299 zero(mh);
300 mh.msg_name = &sa;
301 mh.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(sa.sun_path);
302 mh.msg_iov = w;
303 mh.msg_iovlen = j;
304
0dad12c1 305 k = sendmsg(fd, &mh, MSG_NOSIGNAL);
5c0aa72a
LP
306 if (k >= 0)
307 return 0;
0dad12c1 308
5c0aa72a
LP
309 if (errno != EMSGSIZE && errno != ENOBUFS)
310 return -errno;
0dad12c1
LP
311
312 /* Message doesn't fit... Let's dump the data in a temporary
313 * file and just pass a file descriptor of it to the other
314 * side */
315
316 buffer_fd = mkostemp(path, O_CLOEXEC|O_RDWR);
5c0aa72a
LP
317 if (buffer_fd < 0)
318 return -errno;
0dad12c1
LP
319
320 if (unlink(path) < 0) {
321 close_nointr_nofail(buffer_fd);
5c0aa72a 322 return -errno;
0dad12c1
LP
323 }
324
325 n = writev(buffer_fd, w, j);
b070e7f3 326 if (n < 0) {
0dad12c1 327 close_nointr_nofail(buffer_fd);
5c0aa72a 328 return -errno;
0dad12c1
LP
329 }
330
331 mh.msg_iov = NULL;
332 mh.msg_iovlen = 0;
333
334 zero(control);
335 mh.msg_control = &control;
336 mh.msg_controllen = sizeof(control);
337
338 cmsg = CMSG_FIRSTHDR(&mh);
339 cmsg->cmsg_level = SOL_SOCKET;
340 cmsg->cmsg_type = SCM_RIGHTS;
341 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
342 memcpy(CMSG_DATA(cmsg), &buffer_fd, sizeof(int));
343
344 mh.msg_controllen = cmsg->cmsg_len;
345
346 k = sendmsg(fd, &mh, MSG_NOSIGNAL);
347 close_nointr_nofail(buffer_fd);
348
5c0aa72a
LP
349 if (k < 0)
350 return -errno;
7f3e6257 351
5c0aa72a 352 return 0;
7f3e6257 353}
fe652127 354
18c7ed18 355static int fill_iovec_perror_and_send(const char *message, int skip, struct iovec iov[]) {
5c0aa72a
LP
356 PROTECT_ERRNO;
357 size_t n, k;
18c7ed18
LP
358
359 k = isempty(message) ? 0 : strlen(message) + 2;
360 n = 8 + k + 256 + 1;
361
362 for (;;) {
363 char buffer[n];
364 char* j;
365
366 errno = 0;
5c0aa72a 367 j = strerror_r(_saved_errno_, buffer + 8 + k, n - 8 - k);
18c7ed18
LP
368 if (errno == 0) {
369 char error[6 + 10 + 1]; /* for a 32bit value */
370
371 if (j != buffer + 8 + k)
372 memmove(buffer + 8 + k, j, strlen(j)+1);
373
374 memcpy(buffer, "MESSAGE=", 8);
375
376 if (k > 0) {
377 memcpy(buffer + 8, message, k - 2);
378 memcpy(buffer + 8 + k - 2, ": ", 2);
379 }
380
5c0aa72a 381 snprintf(error, sizeof(error), "ERRNO=%u", _saved_errno_);
18c7ed18
LP
382 char_array_0(error);
383
384 IOVEC_SET_STRING(iov[skip+0], "PRIORITY=3");
385 IOVEC_SET_STRING(iov[skip+1], buffer);
386 IOVEC_SET_STRING(iov[skip+2], error);
387
5c0aa72a 388 return sd_journal_sendv(iov, skip + 3);
18c7ed18
LP
389 }
390
5c0aa72a
LP
391 if (errno != ERANGE)
392 return -errno;
18c7ed18
LP
393
394 n *= 2;
395 }
396}
397
398_public_ int sd_journal_perror(const char *message) {
399 struct iovec iovec[3];
400
401 return fill_iovec_perror_and_send(message, 0, iovec);
402}
403
4cd9a9d9 404_public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) {
fe652127
LP
405 union sockaddr_union sa;
406 int fd;
407 char *header;
408 size_t l;
409 ssize_t r;
410
411 if (priority < 0 || priority > 7)
412 return -EINVAL;
413
414 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
415 if (fd < 0)
416 return -errno;
417
418 zero(sa);
419 sa.un.sun_family = AF_UNIX;
259d2e76 420 strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path));
fe652127
LP
421
422 r = connect(fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
423 if (r < 0) {
424 close_nointr_nofail(fd);
425 return -errno;
426 }
427
86b9b8e7
LP
428 if (shutdown(fd, SHUT_RD) < 0) {
429 close_nointr_nofail(fd);
430 return -errno;
431 }
432
bb99a35a
LP
433 fd_inc_sndbuf(fd, SNDBUF_SIZE);
434
4cd9a9d9
LP
435 if (!identifier)
436 identifier = "";
fe652127 437
4cd9a9d9 438 l = strlen(identifier);
8b38f3cc 439 header = alloca(l + 1 + 1 + 2 + 2 + 2 + 2 + 2);
fe652127 440
4cd9a9d9 441 memcpy(header, identifier, l);
fe652127 442 header[l++] = '\n';
a6e87e90 443 header[l++] = '\n'; /* unit id */
fe652127
LP
444 header[l++] = '0' + priority;
445 header[l++] = '\n';
4cd9a9d9 446 header[l++] = '0' + !!level_prefix;
fe652127
LP
447 header[l++] = '\n';
448 header[l++] = '0';
449 header[l++] = '\n';
224f2ee2
LP
450 header[l++] = '0';
451 header[l++] = '\n';
452 header[l++] = '0';
453 header[l++] = '\n';
fe652127
LP
454
455 r = loop_write(fd, header, l, false);
456 if (r < 0) {
457 close_nointr_nofail(fd);
458 return (int) r;
459 }
460
461 if ((size_t) r != l) {
462 close_nointr_nofail(fd);
463 return -errno;
464 }
465
466 return fd;
467}
b070e7f3
LP
468
469_public_ int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) {
470 int r;
471 va_list ap;
472
473 va_start(ap, format);
474 r = sd_journal_printv_with_location(priority, file, line, func, format, ap);
475 va_end(ap);
476
477 return r;
478}
479
480_public_ int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) {
481 char buffer[8 + LINE_MAX], p[11];
482 struct iovec iov[5];
483 char *f;
b070e7f3
LP
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. */
3ed08c44 501 ALLOCA_CODE_FUNC(f, func);
b070e7f3
LP
502
503 zero(iov);
504 IOVEC_SET_STRING(iov[0], buffer);
505 IOVEC_SET_STRING(iov[1], p);
506 IOVEC_SET_STRING(iov[2], file);
507 IOVEC_SET_STRING(iov[3], line);
508 IOVEC_SET_STRING(iov[4], f);
509
510 return sd_journal_sendv(iov, ELEMENTSOF(iov));
511}
512
513_public_ int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) {
514 int r, i, j;
515 va_list ap;
516 struct iovec *iov = NULL;
517 char *f;
b070e7f3
LP
518
519 va_start(ap, format);
520 i = fill_iovec_sprintf(format, ap, 3, &iov);
521 va_end(ap);
522
523 if (_unlikely_(i < 0)) {
524 r = i;
525 goto finish;
526 }
527
3ed08c44 528 ALLOCA_CODE_FUNC(f, func);
b070e7f3
LP
529
530 IOVEC_SET_STRING(iov[0], file);
531 IOVEC_SET_STRING(iov[1], line);
532 IOVEC_SET_STRING(iov[2], f);
533
534 r = sd_journal_sendv(iov, i);
535
536finish:
537 for (j = 3; j < i; j++)
538 free(iov[j].iov_base);
539
540 free(iov);
541
542 return r;
543}
544
18c7ed18
LP
545_public_ int sd_journal_sendv_with_location(
546 const char *file, const char *line,
547 const char *func,
548 const struct iovec *iov, int n) {
549
b070e7f3
LP
550 struct iovec *niov;
551 char *f;
b070e7f3
LP
552
553 if (_unlikely_(!iov))
554 return -EINVAL;
555
556 if (_unlikely_(n <= 0))
557 return -EINVAL;
558
559 niov = alloca(sizeof(struct iovec) * (n + 3));
560 memcpy(niov, iov, sizeof(struct iovec) * n);
561
3ed08c44 562 ALLOCA_CODE_FUNC(f, func);
b070e7f3
LP
563
564 IOVEC_SET_STRING(niov[n++], file);
565 IOVEC_SET_STRING(niov[n++], line);
566 IOVEC_SET_STRING(niov[n++], f);
567
568 return sd_journal_sendv(niov, n);
569}
18c7ed18
LP
570
571_public_ int sd_journal_perror_with_location(
572 const char *file, const char *line,
573 const char *func,
574 const char *message) {
575
576 struct iovec iov[6];
18c7ed18
LP
577 char *f;
578
3ed08c44 579 ALLOCA_CODE_FUNC(f, func);
18c7ed18
LP
580
581 IOVEC_SET_STRING(iov[0], file);
582 IOVEC_SET_STRING(iov[1], line);
583 IOVEC_SET_STRING(iov[2], f);
584
585 return fill_iovec_perror_and_send(message, 3, iov);
586}