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