]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journal-send.c
rules: don't limit some of the rules to the "add" action
[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;
6e5abe15
ZJS
207 struct sockaddr_un sa = {
208 .sun_family = AF_UNIX,
209 .sun_path = "/run/systemd/journal/socket",
210 };
211 struct msghdr mh = {
212 .msg_name = &sa,
213 .msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(sa.sun_path),
214 };
0dad12c1
LP
215 ssize_t k;
216 union {
217 struct cmsghdr cmsghdr;
218 uint8_t buf[CMSG_SPACE(sizeof(int))];
219 } control;
220 struct cmsghdr *cmsg;
9058851b
LP
221 /* We use /dev/shm instead of /tmp here, since we want this to
222 * be a tmpfs, and one that is available from early boot on
223 * and where unprivileged users can create files. */
224 char path[] = "/dev/shm/journal.XXXXXX";
ee55db41 225 bool have_syslog_identifier = false;
7f3e6257 226
b070e7f3
LP
227 if (_unlikely_(!iov))
228 return -EINVAL;
229
230 if (_unlikely_(n <= 0))
7f3e6257
LP
231 return -EINVAL;
232
ee55db41 233 w = alloca(sizeof(struct iovec) * n * 5 + 3);
7f3e6257
LP
234 l = alloca(sizeof(uint64_t) * n);
235
236 for (i = 0; i < n; i++) {
237 char *c, *nl;
238
5c0aa72a
LP
239 if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1))
240 return -EINVAL;
a5344d2c 241
7f3e6257 242 c = memchr(iov[i].iov_base, '=', iov[i].iov_len);
5c0aa72a
LP
243 if (_unlikely_(!c || c == iov[i].iov_base))
244 return -EINVAL;
7f3e6257 245
29abad10
ZJS
246 have_syslog_identifier = have_syslog_identifier ||
247 (c == (char *) iov[i].iov_base + 17 &&
2a0e0692 248 startswith(iov[i].iov_base, "SYSLOG_IDENTIFIER"));
ee55db41 249
7f3e6257
LP
250 nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len);
251 if (nl) {
5c0aa72a
LP
252 if (_unlikely_(nl < c))
253 return -EINVAL;
7f3e6257
LP
254
255 /* Already includes a newline? Bummer, then
256 * let's write the variable name, then a
257 * newline, then the size (64bit LE), followed
258 * by the data and a final newline */
259
260 w[j].iov_base = iov[i].iov_base;
261 w[j].iov_len = c - (char*) iov[i].iov_base;
262 j++;
263
264 IOVEC_SET_STRING(w[j++], "\n");
265
266 l[i] = htole64(iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1);
267 w[j].iov_base = &l[i];
268 w[j].iov_len = sizeof(uint64_t);
269 j++;
270
271 w[j].iov_base = c + 1;
272 w[j].iov_len = iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1;
273 j++;
274
275 } else
276 /* Nothing special? Then just add the line and
277 * append a newline */
278 w[j++] = iov[i];
279
280 IOVEC_SET_STRING(w[j++], "\n");
281 }
282
ee55db41
LP
283 if (!have_syslog_identifier &&
284 string_is_safe(program_invocation_short_name)) {
285
286 /* Implicitly add program_invocation_short_name, if it
287 * is not set explicitly. We only do this for
288 * program_invocation_short_name, and nothing else
289 * since everything else is much nicer to retrieve
290 * from the outside. */
291
292 IOVEC_SET_STRING(w[j++], "SYSLOG_IDENTIFIER=");
293 IOVEC_SET_STRING(w[j++], program_invocation_short_name);
294 IOVEC_SET_STRING(w[j++], "\n");
295 }
296
7f3e6257 297 fd = journal_fd();
5c0aa72a
LP
298 if (_unlikely_(fd < 0))
299 return fd;
7f3e6257 300
7f3e6257
LP
301 mh.msg_iov = w;
302 mh.msg_iovlen = j;
303
0dad12c1 304 k = sendmsg(fd, &mh, MSG_NOSIGNAL);
5c0aa72a
LP
305 if (k >= 0)
306 return 0;
0dad12c1 307
5c0aa72a
LP
308 if (errno != EMSGSIZE && errno != ENOBUFS)
309 return -errno;
0dad12c1
LP
310
311 /* Message doesn't fit... Let's dump the data in a temporary
312 * file and just pass a file descriptor of it to the other
313 * side */
314
315 buffer_fd = mkostemp(path, O_CLOEXEC|O_RDWR);
5c0aa72a
LP
316 if (buffer_fd < 0)
317 return -errno;
0dad12c1
LP
318
319 if (unlink(path) < 0) {
320 close_nointr_nofail(buffer_fd);
5c0aa72a 321 return -errno;
0dad12c1
LP
322 }
323
324 n = writev(buffer_fd, w, j);
b070e7f3 325 if (n < 0) {
0dad12c1 326 close_nointr_nofail(buffer_fd);
5c0aa72a 327 return -errno;
0dad12c1
LP
328 }
329
330 mh.msg_iov = NULL;
331 mh.msg_iovlen = 0;
332
333 zero(control);
334 mh.msg_control = &control;
335 mh.msg_controllen = sizeof(control);
336
337 cmsg = CMSG_FIRSTHDR(&mh);
338 cmsg->cmsg_level = SOL_SOCKET;
339 cmsg->cmsg_type = SCM_RIGHTS;
340 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
341 memcpy(CMSG_DATA(cmsg), &buffer_fd, sizeof(int));
342
343 mh.msg_controllen = cmsg->cmsg_len;
344
345 k = sendmsg(fd, &mh, MSG_NOSIGNAL);
346 close_nointr_nofail(buffer_fd);
347
5c0aa72a
LP
348 if (k < 0)
349 return -errno;
7f3e6257 350
5c0aa72a 351 return 0;
7f3e6257 352}
fe652127 353
18c7ed18 354static int fill_iovec_perror_and_send(const char *message, int skip, struct iovec iov[]) {
5c0aa72a
LP
355 PROTECT_ERRNO;
356 size_t n, k;
18c7ed18
LP
357
358 k = isempty(message) ? 0 : strlen(message) + 2;
359 n = 8 + k + 256 + 1;
360
361 for (;;) {
362 char buffer[n];
363 char* j;
364
365 errno = 0;
5c0aa72a 366 j = strerror_r(_saved_errno_, buffer + 8 + k, n - 8 - k);
18c7ed18
LP
367 if (errno == 0) {
368 char error[6 + 10 + 1]; /* for a 32bit value */
369
370 if (j != buffer + 8 + k)
371 memmove(buffer + 8 + k, j, strlen(j)+1);
372
373 memcpy(buffer, "MESSAGE=", 8);
374
375 if (k > 0) {
376 memcpy(buffer + 8, message, k - 2);
377 memcpy(buffer + 8 + k - 2, ": ", 2);
378 }
379
5c0aa72a 380 snprintf(error, sizeof(error), "ERRNO=%u", _saved_errno_);
18c7ed18
LP
381 char_array_0(error);
382
383 IOVEC_SET_STRING(iov[skip+0], "PRIORITY=3");
384 IOVEC_SET_STRING(iov[skip+1], buffer);
385 IOVEC_SET_STRING(iov[skip+2], error);
386
5c0aa72a 387 return sd_journal_sendv(iov, skip + 3);
18c7ed18
LP
388 }
389
5c0aa72a
LP
390 if (errno != ERANGE)
391 return -errno;
18c7ed18
LP
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) {
6e5abe15
ZJS
404 union sockaddr_union sa = {
405 .un.sun_family = AF_UNIX,
406 .un.sun_path = "/run/systemd/journal/stdout",
407 };
fe652127
LP
408 int fd;
409 char *header;
410 size_t l;
411 ssize_t r;
412
413 if (priority < 0 || priority > 7)
414 return -EINVAL;
415
416 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
417 if (fd < 0)
418 return -errno;
419
fe652127
LP
420 r = connect(fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
421 if (r < 0) {
422 close_nointr_nofail(fd);
423 return -errno;
424 }
425
86b9b8e7
LP
426 if (shutdown(fd, SHUT_RD) < 0) {
427 close_nointr_nofail(fd);
428 return -errno;
429 }
430
bb99a35a
LP
431 fd_inc_sndbuf(fd, SNDBUF_SIZE);
432
4cd9a9d9
LP
433 if (!identifier)
434 identifier = "";
fe652127 435
4cd9a9d9 436 l = strlen(identifier);
8b38f3cc 437 header = alloca(l + 1 + 1 + 2 + 2 + 2 + 2 + 2);
fe652127 438
4cd9a9d9 439 memcpy(header, identifier, l);
fe652127 440 header[l++] = '\n';
a6e87e90 441 header[l++] = '\n'; /* unit id */
fe652127
LP
442 header[l++] = '0' + priority;
443 header[l++] = '\n';
4cd9a9d9 444 header[l++] = '0' + !!level_prefix;
fe652127
LP
445 header[l++] = '\n';
446 header[l++] = '0';
447 header[l++] = '\n';
224f2ee2
LP
448 header[l++] = '0';
449 header[l++] = '\n';
450 header[l++] = '0';
451 header[l++] = '\n';
fe652127
LP
452
453 r = loop_write(fd, header, l, false);
454 if (r < 0) {
455 close_nointr_nofail(fd);
456 return (int) r;
457 }
458
459 if ((size_t) r != l) {
460 close_nointr_nofail(fd);
461 return -errno;
462 }
463
464 return fd;
465}
b070e7f3
LP
466
467_public_ int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) {
468 int r;
469 va_list ap;
470
471 va_start(ap, format);
472 r = sd_journal_printv_with_location(priority, file, line, func, format, ap);
473 va_end(ap);
474
475 return r;
476}
477
478_public_ int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) {
479 char buffer[8 + LINE_MAX], p[11];
480 struct iovec iov[5];
481 char *f;
b070e7f3
LP
482
483 if (priority < 0 || priority > 7)
484 return -EINVAL;
485
486 if (_unlikely_(!format))
487 return -EINVAL;
488
489 snprintf(p, sizeof(p), "PRIORITY=%i", priority & LOG_PRIMASK);
490 char_array_0(p);
491
492 memcpy(buffer, "MESSAGE=", 8);
493 vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap);
494 char_array_0(buffer);
495
496 /* func is initialized from __func__ which is not a macro, but
497 * a static const char[], hence cannot easily be prefixed with
498 * CODE_FUNC=, hence let's do it manually here. */
3ed08c44 499 ALLOCA_CODE_FUNC(f, func);
b070e7f3
LP
500
501 zero(iov);
502 IOVEC_SET_STRING(iov[0], buffer);
503 IOVEC_SET_STRING(iov[1], p);
504 IOVEC_SET_STRING(iov[2], file);
505 IOVEC_SET_STRING(iov[3], line);
506 IOVEC_SET_STRING(iov[4], f);
507
508 return sd_journal_sendv(iov, ELEMENTSOF(iov));
509}
510
511_public_ int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) {
512 int r, i, j;
513 va_list ap;
514 struct iovec *iov = NULL;
515 char *f;
b070e7f3
LP
516
517 va_start(ap, format);
518 i = fill_iovec_sprintf(format, ap, 3, &iov);
519 va_end(ap);
520
521 if (_unlikely_(i < 0)) {
522 r = i;
523 goto finish;
524 }
525
3ed08c44 526 ALLOCA_CODE_FUNC(f, func);
b070e7f3
LP
527
528 IOVEC_SET_STRING(iov[0], file);
529 IOVEC_SET_STRING(iov[1], line);
530 IOVEC_SET_STRING(iov[2], f);
531
532 r = sd_journal_sendv(iov, i);
533
534finish:
535 for (j = 3; j < i; j++)
536 free(iov[j].iov_base);
537
538 free(iov);
539
540 return r;
541}
542
18c7ed18
LP
543_public_ int sd_journal_sendv_with_location(
544 const char *file, const char *line,
545 const char *func,
546 const struct iovec *iov, int n) {
547
b070e7f3
LP
548 struct iovec *niov;
549 char *f;
b070e7f3
LP
550
551 if (_unlikely_(!iov))
552 return -EINVAL;
553
554 if (_unlikely_(n <= 0))
555 return -EINVAL;
556
557 niov = alloca(sizeof(struct iovec) * (n + 3));
558 memcpy(niov, iov, sizeof(struct iovec) * n);
559
3ed08c44 560 ALLOCA_CODE_FUNC(f, func);
b070e7f3
LP
561
562 IOVEC_SET_STRING(niov[n++], file);
563 IOVEC_SET_STRING(niov[n++], line);
564 IOVEC_SET_STRING(niov[n++], f);
565
566 return sd_journal_sendv(niov, n);
567}
18c7ed18
LP
568
569_public_ int sd_journal_perror_with_location(
570 const char *file, const char *line,
571 const char *func,
572 const char *message) {
573
574 struct iovec iov[6];
18c7ed18
LP
575 char *f;
576
3ed08c44 577 ALLOCA_CODE_FUNC(f, func);
18c7ed18
LP
578
579 IOVEC_SET_STRING(iov[0], file);
580 IOVEC_SET_STRING(iov[1], line);
581 IOVEC_SET_STRING(iov[2], f);
582
583 return fill_iovec_perror_and_send(message, 3, iov);
584}