Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <errno.h>
4 : : #include <fcntl.h>
5 : : #include <printf.h>
6 : : #include <stddef.h>
7 : : #include <sys/socket.h>
8 : : #include <sys/un.h>
9 : : #include <unistd.h>
10 : :
11 : : #define SD_JOURNAL_SUPPRESS_LOCATION
12 : :
13 : : #include "sd-journal.h"
14 : :
15 : : #include "alloc-util.h"
16 : : #include "errno-util.h"
17 : : #include "fd-util.h"
18 : : #include "io-util.h"
19 : : #include "memfd-util.h"
20 : : #include "socket-util.h"
21 : : #include "stdio-util.h"
22 : : #include "string-util.h"
23 : : #include "tmpfile-util.h"
24 : :
25 : : #define SNDBUF_SIZE (8*1024*1024)
26 : :
27 : : #define ALLOCA_CODE_FUNC(f, func) \
28 : : do { \
29 : : size_t _fl; \
30 : : const char *_func = (func); \
31 : : char **_f = &(f); \
32 : : _fl = strlen(_func) + 1; \
33 : : *_f = newa(char, _fl + 10); \
34 : : memcpy(*_f, "CODE_FUNC=", 10); \
35 : : memcpy(*_f + 10, _func, _fl); \
36 : : } while (false)
37 : :
38 : : /* We open a single fd, and we'll share it with the current process,
39 : : * all its threads, and all its subprocesses. This means we need to
40 : : * initialize it atomically, and need to operate on it atomically
41 : : * never assuming we are the only user */
42 : :
43 : 64 : static int journal_fd(void) {
44 : : int fd;
45 : : static int fd_plus_one = 0;
46 : :
47 : 64 : retry:
48 [ + + ]: 64 : if (fd_plus_one > 0)
49 : 60 : return fd_plus_one - 1;
50 : :
51 : 4 : fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
52 [ - + ]: 4 : if (fd < 0)
53 : 0 : return -errno;
54 : :
55 : 4 : fd_inc_sndbuf(fd, SNDBUF_SIZE);
56 : :
57 [ - + ]: 4 : if (!__sync_bool_compare_and_swap(&fd_plus_one, 0, fd+1)) {
58 : 0 : safe_close(fd);
59 : 0 : goto retry;
60 : : }
61 : :
62 : 4 : return fd;
63 : : }
64 : :
65 : 0 : _public_ int sd_journal_print(int priority, const char *format, ...) {
66 : : int r;
67 : : va_list ap;
68 : :
69 : 0 : va_start(ap, format);
70 : 0 : r = sd_journal_printv(priority, format, ap);
71 : 0 : va_end(ap);
72 : :
73 : 0 : return r;
74 : : }
75 : :
76 : 0 : _public_ int sd_journal_printv(int priority, const char *format, va_list ap) {
77 : :
78 : : /* FIXME: Instead of limiting things to LINE_MAX we could do a
79 : : C99 variable-length array on the stack here in a loop. */
80 : :
81 : : char buffer[8 + LINE_MAX], p[STRLEN("PRIORITY=") + DECIMAL_STR_MAX(int) + 1];
82 : : struct iovec iov[2];
83 : :
84 [ # # # # ]: 0 : assert_return(priority >= 0, -EINVAL);
85 [ # # # # ]: 0 : assert_return(priority <= 7, -EINVAL);
86 [ # # # # ]: 0 : assert_return(format, -EINVAL);
87 : :
88 [ # # ]: 0 : xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK);
89 : :
90 : 0 : memcpy(buffer, "MESSAGE=", 8);
91 : 0 : vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap);
92 : :
93 : : /* Strip trailing whitespace, keep prefix whitespace. */
94 : 0 : (void) strstrip(buffer);
95 : :
96 : : /* Suppress empty lines */
97 [ # # ]: 0 : if (isempty(buffer+8))
98 : 0 : return 0;
99 : :
100 : 0 : iov[0] = IOVEC_MAKE_STRING(buffer);
101 : 0 : iov[1] = IOVEC_MAKE_STRING(p);
102 : :
103 : 0 : return sd_journal_sendv(iov, 2);
104 : : }
105 : :
106 : 16 : _printf_(1, 0) static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) {
107 : 16 : PROTECT_ERRNO;
108 : 16 : int r, n = 0, i = 0, j;
109 : 16 : struct iovec *iov = NULL;
110 : :
111 [ - + ]: 16 : assert(_iov);
112 : :
113 [ + - ]: 16 : if (extra > 0) {
114 : 16 : n = MAX(extra * 2, extra + 4);
115 : 16 : iov = malloc0(n * sizeof(struct iovec));
116 [ - + ]: 16 : if (!iov) {
117 : 0 : r = -ENOMEM;
118 : 0 : goto fail;
119 : : }
120 : :
121 : 16 : i = extra;
122 : : }
123 : :
124 [ + + ]: 92 : while (format) {
125 : : struct iovec *c;
126 : : char *buffer;
127 : : va_list aq;
128 : :
129 [ + + ]: 76 : if (i >= n) {
130 : 8 : n = MAX(i*2, 4);
131 : 8 : c = realloc(iov, n * sizeof(struct iovec));
132 [ - + ]: 8 : if (!c) {
133 : 0 : r = -ENOMEM;
134 : 0 : goto fail;
135 : : }
136 : :
137 : 8 : iov = c;
138 : : }
139 : :
140 : 76 : va_copy(aq, ap);
141 [ - + ]: 76 : if (vasprintf(&buffer, format, aq) < 0) {
142 : 0 : va_end(aq);
143 : 0 : r = -ENOMEM;
144 : 0 : goto fail;
145 : : }
146 : 76 : va_end(aq);
147 : :
148 [ - + - + : 96 : VA_FORMAT_ADVANCE(format, ap);
+ + - - +
- - - +
+ ]
149 : :
150 : 76 : (void) strstrip(buffer); /* strip trailing whitespace, keep prefixing whitespace */
151 : :
152 : 76 : iov[i++] = IOVEC_MAKE_STRING(buffer);
153 : :
154 : 76 : format = va_arg(ap, char *);
155 : : }
156 : :
157 : 16 : *_iov = iov;
158 : :
159 : 16 : return i;
160 : :
161 : 0 : fail:
162 [ # # ]: 0 : for (j = 0; j < i; j++)
163 : 0 : free(iov[j].iov_base);
164 : :
165 : 0 : free(iov);
166 : :
167 : 0 : return r;
168 : : }
169 : :
170 : 0 : _public_ int sd_journal_send(const char *format, ...) {
171 : : int r, i, j;
172 : : va_list ap;
173 : 0 : struct iovec *iov = NULL;
174 : :
175 : 0 : va_start(ap, format);
176 : 0 : i = fill_iovec_sprintf(format, ap, 0, &iov);
177 : 0 : va_end(ap);
178 : :
179 [ # # ]: 0 : if (_unlikely_(i < 0)) {
180 : 0 : r = i;
181 : 0 : goto finish;
182 : : }
183 : :
184 : 0 : r = sd_journal_sendv(iov, i);
185 : :
186 : 0 : finish:
187 [ # # ]: 0 : for (j = 0; j < i; j++)
188 : 0 : free(iov[j].iov_base);
189 : :
190 : 0 : free(iov);
191 : :
192 : 0 : return r;
193 : : }
194 : :
195 : 64 : _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
196 : 64 : PROTECT_ERRNO;
197 : : int fd, r;
198 : 64 : _cleanup_close_ int buffer_fd = -1;
199 : : struct iovec *w;
200 : : uint64_t *l;
201 : 64 : int i, j = 0;
202 : : static const union sockaddr_union sa = {
203 : : .un.sun_family = AF_UNIX,
204 : : .un.sun_path = "/run/systemd/journal/socket",
205 : : };
206 : 128 : struct msghdr mh = {
207 : : .msg_name = (struct sockaddr*) &sa.sa,
208 [ - + - + ]: 64 : .msg_namelen = SOCKADDR_UN_LEN(sa.un),
209 : : };
210 : : ssize_t k;
211 : 64 : bool have_syslog_identifier = false;
212 : 64 : bool seal = true;
213 : :
214 [ - + - + ]: 64 : assert_return(iov, -EINVAL);
215 [ - + - + ]: 64 : assert_return(n > 0, -EINVAL);
216 : :
217 [ - + - + ]: 64 : w = newa(struct iovec, n * 5 + 3);
218 [ - + - + ]: 64 : l = newa(uint64_t, n);
219 : :
220 [ + + ]: 356 : for (i = 0; i < n; i++) {
221 : : char *c, *nl;
222 : :
223 [ + - - + ]: 292 : if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1))
224 : 0 : return -EINVAL;
225 : :
226 : 292 : c = memchr(iov[i].iov_base, '=', iov[i].iov_len);
227 [ + - - + ]: 292 : if (_unlikely_(!c || c == iov[i].iov_base))
228 : 0 : return -EINVAL;
229 : :
230 [ + - ]: 584 : have_syslog_identifier = have_syslog_identifier ||
231 [ - + # # ]: 292 : (c == (char *) iov[i].iov_base + 17 &&
232 : 0 : startswith(iov[i].iov_base, "SYSLOG_IDENTIFIER"));
233 : :
234 : 292 : nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len);
235 [ + + ]: 292 : if (nl) {
236 [ - + ]: 16 : if (_unlikely_(nl < c))
237 : 0 : return -EINVAL;
238 : :
239 : : /* Already includes a newline? Bummer, then
240 : : * let's write the variable name, then a
241 : : * newline, then the size (64bit LE), followed
242 : : * by the data and a final newline */
243 : :
244 : 16 : w[j++] = IOVEC_MAKE(iov[i].iov_base, c - (char*) iov[i].iov_base);
245 : 16 : w[j++] = IOVEC_MAKE_STRING("\n");
246 : :
247 : 16 : l[i] = htole64(iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1);
248 : 16 : w[j++] = IOVEC_MAKE(&l[i], sizeof(uint64_t));
249 : :
250 : 16 : w[j++] = IOVEC_MAKE(c + 1, iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1);
251 : : } else
252 : : /* Nothing special? Then just add the line and
253 : : * append a newline */
254 : 276 : w[j++] = iov[i];
255 : :
256 : 292 : w[j++] = IOVEC_MAKE_STRING("\n");
257 : : }
258 : :
259 [ + - ]: 64 : if (!have_syslog_identifier &&
260 [ + - ]: 64 : string_is_safe(program_invocation_short_name)) {
261 : :
262 : : /* Implicitly add program_invocation_short_name, if it
263 : : * is not set explicitly. We only do this for
264 : : * program_invocation_short_name, and nothing else
265 : : * since everything else is much nicer to retrieve
266 : : * from the outside. */
267 : :
268 : 64 : w[j++] = IOVEC_MAKE_STRING("SYSLOG_IDENTIFIER=");
269 : 64 : w[j++] = IOVEC_MAKE_STRING(program_invocation_short_name);
270 : 64 : w[j++] = IOVEC_MAKE_STRING("\n");
271 : : }
272 : :
273 : 64 : fd = journal_fd();
274 [ - + ]: 64 : if (_unlikely_(fd < 0))
275 : 0 : return fd;
276 : :
277 : 64 : mh.msg_iov = w;
278 : 64 : mh.msg_iovlen = j;
279 : :
280 : 64 : k = sendmsg(fd, &mh, MSG_NOSIGNAL);
281 [ + + ]: 64 : if (k >= 0)
282 : 60 : return 0;
283 : :
284 : : /* Fail silently if the journal is not available */
285 [ - + ]: 4 : if (errno == ENOENT)
286 : 0 : return 0;
287 : :
288 [ + - - + ]: 4 : if (!IN_SET(errno, EMSGSIZE, ENOBUFS))
289 : 0 : return -errno;
290 : :
291 : : /* Message doesn't fit... Let's dump the data in a memfd or
292 : : * temporary file and just pass a file descriptor of it to the
293 : : * other side.
294 : : *
295 : : * For the temporary files we use /dev/shm instead of /tmp
296 : : * here, since we want this to be a tmpfs, and one that is
297 : : * available from early boot on and where unprivileged users
298 : : * can create files. */
299 : 4 : buffer_fd = memfd_new(NULL);
300 [ - + ]: 4 : if (buffer_fd < 0) {
301 [ # # ]: 0 : if (buffer_fd == -ENOSYS) {
302 : 0 : buffer_fd = open_tmpfile_unlinkable("/dev/shm", O_RDWR | O_CLOEXEC);
303 [ # # ]: 0 : if (buffer_fd < 0)
304 : 0 : return buffer_fd;
305 : :
306 : 0 : seal = false;
307 : : } else
308 : 0 : return buffer_fd;
309 : : }
310 : :
311 : 4 : n = writev(buffer_fd, w, j);
312 [ - + ]: 4 : if (n < 0)
313 : 0 : return -errno;
314 : :
315 [ + - ]: 4 : if (seal) {
316 : 4 : r = memfd_set_sealed(buffer_fd);
317 [ - + ]: 4 : if (r < 0)
318 : 0 : return r;
319 : : }
320 : :
321 : 4 : r = send_one_fd_sa(fd, buffer_fd, mh.msg_name, mh.msg_namelen, 0);
322 [ - + ]: 4 : if (r == -ENOENT)
323 : : /* Fail silently if the journal is not available */
324 : 0 : return 0;
325 : 4 : return r;
326 : : }
327 : :
328 : 8 : static int fill_iovec_perror_and_send(const char *message, int skip, struct iovec iov[]) {
329 : 8 : PROTECT_ERRNO;
330 : : size_t n, k;
331 : :
332 [ + + ]: 8 : k = isempty(message) ? 0 : strlen(message) + 2;
333 : 8 : n = 8 + k + 256 + 1;
334 : :
335 : 0 : for (;;) {
336 : 8 : char buffer[n];
337 : : char* j;
338 : :
339 : 8 : errno = 0;
340 : 8 : j = strerror_r(_saved_errno_, buffer + 8 + k, n - 8 - k);
341 [ + - ]: 8 : if (errno == 0) {
342 : : char error[STRLEN("ERRNO=") + DECIMAL_STR_MAX(int) + 1];
343 : :
344 [ + - ]: 8 : if (j != buffer + 8 + k)
345 : 8 : memmove(buffer + 8 + k, j, strlen(j)+1);
346 : :
347 : 8 : memcpy(buffer, "MESSAGE=", 8);
348 : :
349 [ + + ]: 8 : if (k > 0) {
350 : 4 : memcpy(buffer + 8, message, k - 2);
351 : 4 : memcpy(buffer + 8 + k - 2, ": ", 2);
352 : : }
353 : :
354 [ - + ]: 8 : xsprintf(error, "ERRNO=%i", _saved_errno_);
355 : :
356 : : assert_cc(3 == LOG_ERR);
357 : 8 : iov[skip+0] = IOVEC_MAKE_STRING("PRIORITY=3");
358 : 8 : iov[skip+1] = IOVEC_MAKE_STRING(buffer);
359 : 8 : iov[skip+2] = IOVEC_MAKE_STRING(error);
360 : :
361 : 8 : return sd_journal_sendv(iov, skip + 3);
362 : : }
363 : :
364 [ # # ]: 0 : if (errno != ERANGE)
365 : 0 : return -errno;
366 : :
367 : 0 : n *= 2;
368 : : }
369 : : }
370 : :
371 : 0 : _public_ int sd_journal_perror(const char *message) {
372 : : struct iovec iovec[3];
373 : :
374 : 0 : return fill_iovec_perror_and_send(message, 0, iovec);
375 : : }
376 : :
377 : 0 : _public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) {
378 : : static const union sockaddr_union sa = {
379 : : .un.sun_family = AF_UNIX,
380 : : .un.sun_path = "/run/systemd/journal/stdout",
381 : : };
382 : 0 : _cleanup_close_ int fd = -1;
383 : : char *header;
384 : : size_t l;
385 : : int r;
386 : :
387 [ # # # # ]: 0 : assert_return(priority >= 0, -EINVAL);
388 [ # # # # ]: 0 : assert_return(priority <= 7, -EINVAL);
389 : :
390 : 0 : fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
391 [ # # ]: 0 : if (fd < 0)
392 : 0 : return -errno;
393 : :
394 [ # # # # ]: 0 : r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
395 [ # # ]: 0 : if (r < 0)
396 : 0 : return -errno;
397 : :
398 [ # # ]: 0 : if (shutdown(fd, SHUT_RD) < 0)
399 : 0 : return -errno;
400 : :
401 : 0 : (void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
402 : :
403 : 0 : identifier = strempty(identifier);
404 : :
405 : 0 : l = strlen(identifier);
406 [ # # # # ]: 0 : header = newa(char, l + 1 + 1 + 2 + 2 + 2 + 2 + 2);
407 : :
408 : 0 : memcpy(header, identifier, l);
409 : 0 : header[l++] = '\n';
410 : 0 : header[l++] = '\n'; /* unit id */
411 : 0 : header[l++] = '0' + priority;
412 : 0 : header[l++] = '\n';
413 [ # # ]: 0 : header[l++] = '0' + !!level_prefix;
414 : 0 : header[l++] = '\n';
415 : 0 : header[l++] = '0';
416 : 0 : header[l++] = '\n';
417 : 0 : header[l++] = '0';
418 : 0 : header[l++] = '\n';
419 : 0 : header[l++] = '0';
420 : 0 : header[l++] = '\n';
421 : :
422 : 0 : r = loop_write(fd, header, l, false);
423 [ # # ]: 0 : if (r < 0)
424 : 0 : return r;
425 : :
426 : 0 : return TAKE_FD(fd);
427 : : }
428 : :
429 : 8 : _public_ int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) {
430 : : int r;
431 : : va_list ap;
432 : :
433 : 8 : va_start(ap, format);
434 : 8 : r = sd_journal_printv_with_location(priority, file, line, func, format, ap);
435 : 8 : va_end(ap);
436 : :
437 : 8 : return r;
438 : : }
439 : :
440 : 8 : _public_ int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) {
441 : : char buffer[8 + LINE_MAX], p[STRLEN("PRIORITY=") + DECIMAL_STR_MAX(int) + 1];
442 : : struct iovec iov[5];
443 : : char *f;
444 : :
445 [ - + - + ]: 8 : assert_return(priority >= 0, -EINVAL);
446 [ - + - + ]: 8 : assert_return(priority <= 7, -EINVAL);
447 [ - + - + ]: 8 : assert_return(format, -EINVAL);
448 : :
449 [ - + ]: 8 : xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK);
450 : :
451 : 8 : memcpy(buffer, "MESSAGE=", 8);
452 : 8 : vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap);
453 : :
454 : : /* Strip trailing whitespace, keep prefixing whitespace */
455 : 8 : (void) strstrip(buffer);
456 : :
457 : : /* Suppress empty lines */
458 [ - + ]: 8 : if (isempty(buffer+8))
459 : 0 : return 0;
460 : :
461 : : /* func is initialized from __func__ which is not a macro, but
462 : : * a static const char[], hence cannot easily be prefixed with
463 : : * CODE_FUNC=, hence let's do it manually here. */
464 [ - + - + ]: 8 : ALLOCA_CODE_FUNC(f, func);
465 : :
466 : 8 : iov[0] = IOVEC_MAKE_STRING(buffer);
467 : 8 : iov[1] = IOVEC_MAKE_STRING(p);
468 : 8 : iov[2] = IOVEC_MAKE_STRING(file);
469 : 8 : iov[3] = IOVEC_MAKE_STRING(line);
470 : 8 : iov[4] = IOVEC_MAKE_STRING(f);
471 : :
472 : 8 : return sd_journal_sendv(iov, ELEMENTSOF(iov));
473 : : }
474 : :
475 : 16 : _public_ int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) {
476 : 32 : _cleanup_free_ struct iovec *iov = NULL;
477 : : int r, i, j;
478 : : va_list ap;
479 : : char *f;
480 : :
481 : 16 : va_start(ap, format);
482 : 16 : i = fill_iovec_sprintf(format, ap, 3, &iov);
483 : 16 : va_end(ap);
484 : :
485 [ - + ]: 16 : if (_unlikely_(i < 0)) {
486 : 0 : r = i;
487 : 0 : goto finish;
488 : : }
489 : :
490 [ - + - + ]: 16 : ALLOCA_CODE_FUNC(f, func);
491 : :
492 : 16 : iov[0] = IOVEC_MAKE_STRING(file);
493 : 16 : iov[1] = IOVEC_MAKE_STRING(line);
494 : 16 : iov[2] = IOVEC_MAKE_STRING(f);
495 : :
496 : 16 : r = sd_journal_sendv(iov, i);
497 : :
498 : 16 : finish:
499 [ + + ]: 92 : for (j = 3; j < i; j++)
500 : 76 : free(iov[j].iov_base);
501 : :
502 : 16 : return r;
503 : : }
504 : :
505 : 16 : _public_ int sd_journal_sendv_with_location(
506 : : const char *file, const char *line,
507 : : const char *func,
508 : : const struct iovec *iov, int n) {
509 : :
510 : : struct iovec *niov;
511 : : char *f;
512 : :
513 [ - + - + ]: 16 : assert_return(iov, -EINVAL);
514 [ - + - + ]: 16 : assert_return(n > 0, -EINVAL);
515 : :
516 [ - + - + ]: 16 : niov = newa(struct iovec, n + 3);
517 : 16 : memcpy(niov, iov, sizeof(struct iovec) * n);
518 : :
519 [ - + - + ]: 16 : ALLOCA_CODE_FUNC(f, func);
520 : :
521 : 16 : niov[n++] = IOVEC_MAKE_STRING(file);
522 : 16 : niov[n++] = IOVEC_MAKE_STRING(line);
523 : 16 : niov[n++] = IOVEC_MAKE_STRING(f);
524 : :
525 : 16 : return sd_journal_sendv(niov, n);
526 : : }
527 : :
528 : 8 : _public_ int sd_journal_perror_with_location(
529 : : const char *file, const char *line,
530 : : const char *func,
531 : : const char *message) {
532 : :
533 : : struct iovec iov[6];
534 : : char *f;
535 : :
536 [ - + - + ]: 8 : ALLOCA_CODE_FUNC(f, func);
537 : :
538 : 8 : iov[0] = IOVEC_MAKE_STRING(file);
539 : 8 : iov[1] = IOVEC_MAKE_STRING(line);
540 : 8 : iov[2] = IOVEC_MAKE_STRING(f);
541 : :
542 : 8 : return fill_iovec_perror_and_send(message, 3, iov);
543 : : }
|