]> git.ipfire.org Git - thirdparty/systemd.git/blob - 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
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <printf.h>
25 #include <stddef.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include <unistd.h>
29
30 #define SD_JOURNAL_SUPPRESS_LOCATION
31
32 #include "sd-journal.h"
33
34 #include "memfd-util.h"
35 #include "socket-util.h"
36 #include "string-util.h"
37 #include "util.h"
38
39 #define SNDBUF_SIZE (8*1024*1024)
40
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
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
57 static int journal_fd(void) {
58 int fd;
59 static int fd_plus_one = 0;
60
61 retry:
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
69 fd_inc_sndbuf(fd, SNDBUF_SIZE);
70
71 if (!__sync_bool_compare_and_swap(&fd_plus_one, 0, fd+1)) {
72 safe_close(fd);
73 goto retry;
74 }
75
76 return fd;
77 }
78
79 _public_ int sd_journal_print(int priority, const char *format, ...) {
80 int r;
81 va_list ap;
82
83 va_start(ap, format);
84 r = sd_journal_printv(priority, format, ap);
85 va_end(ap);
86
87 return r;
88 }
89
90 _public_ int sd_journal_printv(int priority, const char *format, va_list ap) {
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
95 char buffer[8 + LINE_MAX], p[sizeof("PRIORITY=")-1 + DECIMAL_STR_MAX(int) + 1];
96 struct iovec iov[2];
97
98 assert_return(priority >= 0, -EINVAL);
99 assert_return(priority <= 7, -EINVAL);
100 assert_return(format, -EINVAL);
101
102 xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK);
103
104 memcpy(buffer, "MESSAGE=", 8);
105 vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap);
106
107 zero(iov);
108 IOVEC_SET_STRING(iov[0], buffer);
109 IOVEC_SET_STRING(iov[1], p);
110
111 return sd_journal_sendv(iov, 2);
112 }
113
114 _printf_(1, 0) static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) {
115 PROTECT_ERRNO;
116 int r, n = 0, i = 0, j;
117 struct iovec *iov = NULL;
118
119 assert(_iov);
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;
130 }
131
132 while (format) {
133 struct iovec *c;
134 char *buffer;
135 va_list aq;
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
148 va_copy(aq, ap);
149 if (vasprintf(&buffer, format, aq) < 0) {
150 va_end(aq);
151 r = -ENOMEM;
152 goto fail;
153 }
154 va_end(aq);
155
156 VA_FORMAT_ADVANCE(format, ap);
157
158 IOVEC_SET_STRING(iov[i++], buffer);
159
160 format = va_arg(ap, char *);
161 }
162
163 *_iov = iov;
164
165 return i;
166
167 fail:
168 for (j = 0; j < i; j++)
169 free(iov[j].iov_base);
170
171 free(iov);
172
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);
183 va_end(ap);
184
185 if (_unlikely_(i < 0)) {
186 r = i;
187 goto finish;
188 }
189
190 r = sd_journal_sendv(iov, i);
191
192 finish:
193 for (j = 0; j < i; j++)
194 free(iov[j].iov_base);
195
196 free(iov);
197
198 return r;
199 }
200
201 _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
202 PROTECT_ERRNO;
203 int fd, r;
204 _cleanup_close_ int buffer_fd = -1;
205 struct iovec *w;
206 uint64_t *l;
207 int i, j = 0;
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 };
216 ssize_t k;
217 bool have_syslog_identifier = false;
218 bool seal = true;
219
220 assert_return(iov, -EINVAL);
221 assert_return(n > 0, -EINVAL);
222
223 w = alloca(sizeof(struct iovec) * n * 5 + 3);
224 l = alloca(sizeof(uint64_t) * n);
225
226 for (i = 0; i < n; i++) {
227 char *c, *nl;
228
229 if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1))
230 return -EINVAL;
231
232 c = memchr(iov[i].iov_base, '=', iov[i].iov_len);
233 if (_unlikely_(!c || c == iov[i].iov_base))
234 return -EINVAL;
235
236 have_syslog_identifier = have_syslog_identifier ||
237 (c == (char *) iov[i].iov_base + 17 &&
238 startswith(iov[i].iov_base, "SYSLOG_IDENTIFIER"));
239
240 nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len);
241 if (nl) {
242 if (_unlikely_(nl < c))
243 return -EINVAL;
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
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
287 fd = journal_fd();
288 if (_unlikely_(fd < 0))
289 return fd;
290
291 mh.msg_iov = w;
292 mh.msg_iovlen = j;
293
294 k = sendmsg(fd, &mh, MSG_NOSIGNAL);
295 if (k >= 0)
296 return 0;
297
298 /* Fail silently if the journal is not available */
299 if (errno == ENOENT)
300 return 0;
301
302 if (errno != EMSGSIZE && errno != ENOBUFS)
303 return -errno;
304
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.
308 *
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. */
313 buffer_fd = memfd_new(NULL);
314 if (buffer_fd < 0) {
315 if (buffer_fd == -ENOSYS) {
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
322 return buffer_fd;
323 }
324
325 n = writev(buffer_fd, w, j);
326 if (n < 0)
327 return -errno;
328
329 if (seal) {
330 r = memfd_set_sealed(buffer_fd);
331 if (r < 0)
332 return r;
333 }
334
335 return send_one_fd(fd, buffer_fd, 0);
336 }
337
338 static int fill_iovec_perror_and_send(const char *message, int skip, struct iovec iov[]) {
339 PROTECT_ERRNO;
340 size_t n, k;
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;
350 j = strerror_r(_saved_errno_, buffer + 8 + k, n - 8 - k);
351 if (errno == 0) {
352 char error[sizeof("ERRNO=")-1 + DECIMAL_STR_MAX(int) + 1];
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
364 xsprintf(error, "ERRNO=%i", _saved_errno_);
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
370 return sd_journal_sendv(iov, skip + 3);
371 }
372
373 if (errno != ERANGE)
374 return -errno;
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
386 _public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) {
387 union sockaddr_union sa = {
388 .un.sun_family = AF_UNIX,
389 .un.sun_path = "/run/systemd/journal/stdout",
390 };
391 _cleanup_close_ int fd = -1;
392 char *header;
393 size_t l;
394 int r;
395
396 assert_return(priority >= 0, -EINVAL);
397 assert_return(priority <= 7, -EINVAL);
398
399 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
400 if (fd < 0)
401 return -errno;
402
403 r = connect(fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
404 if (r < 0)
405 return -errno;
406
407 if (shutdown(fd, SHUT_RD) < 0)
408 return -errno;
409
410 fd_inc_sndbuf(fd, SNDBUF_SIZE);
411
412 if (!identifier)
413 identifier = "";
414
415 l = strlen(identifier);
416 header = alloca(l + 1 + 1 + 2 + 2 + 2 + 2 + 2);
417
418 memcpy(header, identifier, l);
419 header[l++] = '\n';
420 header[l++] = '\n'; /* unit id */
421 header[l++] = '0' + priority;
422 header[l++] = '\n';
423 header[l++] = '0' + !!level_prefix;
424 header[l++] = '\n';
425 header[l++] = '0';
426 header[l++] = '\n';
427 header[l++] = '0';
428 header[l++] = '\n';
429 header[l++] = '0';
430 header[l++] = '\n';
431
432 r = loop_write(fd, header, l, false);
433 if (r < 0)
434 return r;
435
436 r = fd;
437 fd = -1;
438 return r;
439 }
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) {
453 char buffer[8 + LINE_MAX], p[sizeof("PRIORITY=")-1 + DECIMAL_STR_MAX(int) + 1];
454 struct iovec iov[5];
455 char *f;
456
457 assert_return(priority >= 0, -EINVAL);
458 assert_return(priority <= 7, -EINVAL);
459 assert_return(format, -EINVAL);
460
461 xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK);
462
463 memcpy(buffer, "MESSAGE=", 8);
464 vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap);
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. */
469 ALLOCA_CODE_FUNC(f, func);
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;
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
496 ALLOCA_CODE_FUNC(f, func);
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
504 finish:
505 for (j = 3; j < i; j++)
506 free(iov[j].iov_base);
507
508 free(iov);
509
510 return r;
511 }
512
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
518 struct iovec *niov;
519 char *f;
520
521 assert_return(iov, -EINVAL);
522 assert_return(n > 0, -EINVAL);
523
524 niov = alloca(sizeof(struct iovec) * (n + 3));
525 memcpy(niov, iov, sizeof(struct iovec) * n);
526
527 ALLOCA_CODE_FUNC(f, func);
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 }
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];
542 char *f;
543
544 ALLOCA_CODE_FUNC(f, func);
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 }