Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <errno.h>
4 : #include <fcntl.h>
5 : #include <inttypes.h>
6 : #include <limits.h>
7 : #include <stdarg.h>
8 : #include <stddef.h>
9 : #include <stdio.h>
10 : #include <string.h>
11 : #include <sys/signalfd.h>
12 : #include <sys/socket.h>
13 : #include <sys/time.h>
14 : #include <sys/uio.h>
15 : #include <sys/un.h>
16 : #include <time.h>
17 : #include <unistd.h>
18 :
19 : #include "sd-messages.h"
20 :
21 : #include "alloc-util.h"
22 : #include "errno-util.h"
23 : #include "fd-util.h"
24 : #include "format-util.h"
25 : #include "io-util.h"
26 : #include "log.h"
27 : #include "macro.h"
28 : #include "missing.h"
29 : #include "parse-util.h"
30 : #include "proc-cmdline.h"
31 : #include "process-util.h"
32 : #include "signal-util.h"
33 : #include "socket-util.h"
34 : #include "stdio-util.h"
35 : #include "string-table.h"
36 : #include "string-util.h"
37 : #include "syslog-util.h"
38 : #include "terminal-util.h"
39 : #include "time-util.h"
40 : #include "utf8.h"
41 :
42 : #define SNDBUF_SIZE (8*1024*1024)
43 :
44 : static LogTarget log_target = LOG_TARGET_CONSOLE;
45 : static int log_max_level[] = {LOG_INFO, LOG_INFO};
46 : assert_cc(ELEMENTSOF(log_max_level) == _LOG_REALM_MAX);
47 : static int log_facility = LOG_DAEMON;
48 :
49 : static int console_fd = STDERR_FILENO;
50 : static int syslog_fd = -1;
51 : static int kmsg_fd = -1;
52 : static int journal_fd = -1;
53 :
54 : static bool syslog_is_stream = false;
55 :
56 : static bool show_color = false;
57 : static bool show_location = false;
58 :
59 : static bool upgrade_syslog_to_journal = false;
60 : static bool always_reopen_console = false;
61 : static bool open_when_needed = false;
62 : static bool prohibit_ipc = false;
63 :
64 : /* Akin to glibc's __abort_msg; which is private and we hence cannot
65 : * use here. */
66 : static char *log_abort_msg = NULL;
67 :
68 : /* An assert to use in logging functions that does not call recursively
69 : * into our logging functions (since that might lead to a loop). */
70 : #define assert_raw(expr) \
71 : do { \
72 : if (_unlikely_(!(expr))) { \
73 : fputs(#expr "\n", stderr); \
74 : abort(); \
75 : } \
76 : } while (false)
77 :
78 108 : static void log_close_console(void) {
79 108 : console_fd = safe_close_above_stdio(console_fd);
80 108 : }
81 :
82 299 : static int log_open_console(void) {
83 :
84 299 : if (!always_reopen_console) {
85 299 : console_fd = STDERR_FILENO;
86 299 : return 0;
87 : }
88 :
89 0 : if (console_fd < 3) {
90 : int fd;
91 :
92 0 : fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
93 0 : if (fd < 0)
94 0 : return fd;
95 :
96 0 : console_fd = fd_move_above_stdio(fd);
97 : }
98 :
99 0 : return 0;
100 : }
101 :
102 0 : static void log_close_kmsg(void) {
103 0 : kmsg_fd = safe_close(kmsg_fd);
104 0 : }
105 :
106 1 : static int log_open_kmsg(void) {
107 :
108 1 : if (kmsg_fd >= 0)
109 0 : return 0;
110 :
111 1 : kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC);
112 1 : if (kmsg_fd < 0)
113 1 : return -errno;
114 :
115 0 : kmsg_fd = fd_move_above_stdio(kmsg_fd);
116 0 : return 0;
117 : }
118 :
119 405 : static void log_close_syslog(void) {
120 405 : syslog_fd = safe_close(syslog_fd);
121 405 : }
122 :
123 101 : static int create_log_socket(int type) {
124 : struct timeval tv;
125 : int fd;
126 :
127 101 : fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0);
128 101 : if (fd < 0)
129 0 : return -errno;
130 :
131 101 : fd = fd_move_above_stdio(fd);
132 101 : (void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
133 :
134 : /* We need a blocking fd here since we'd otherwise lose messages way too early. However, let's not hang forever
135 : * in the unlikely case of a deadlock. */
136 101 : if (getpid_cached() == 1)
137 0 : timeval_store(&tv, 10 * USEC_PER_MSEC);
138 : else
139 101 : timeval_store(&tv, 10 * USEC_PER_SEC);
140 101 : (void) setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
141 :
142 101 : return fd;
143 : }
144 :
145 2 : static int log_open_syslog(void) {
146 :
147 : static const union sockaddr_union sa = {
148 : .un.sun_family = AF_UNIX,
149 : .un.sun_path = "/dev/log",
150 : };
151 :
152 : int r;
153 :
154 2 : if (syslog_fd >= 0)
155 1 : return 0;
156 :
157 1 : syslog_fd = create_log_socket(SOCK_DGRAM);
158 1 : if (syslog_fd < 0) {
159 0 : r = syslog_fd;
160 0 : goto fail;
161 : }
162 :
163 1 : if (connect(syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) {
164 0 : safe_close(syslog_fd);
165 :
166 : /* Some legacy syslog systems still use stream
167 : * sockets. They really shouldn't. But what can we
168 : * do... */
169 0 : syslog_fd = create_log_socket(SOCK_STREAM);
170 0 : if (syslog_fd < 0) {
171 0 : r = syslog_fd;
172 0 : goto fail;
173 : }
174 :
175 0 : if (connect(syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) {
176 0 : r = -errno;
177 0 : goto fail;
178 : }
179 :
180 0 : syslog_is_stream = true;
181 : } else
182 1 : syslog_is_stream = false;
183 :
184 1 : return 0;
185 :
186 0 : fail:
187 0 : log_close_syslog();
188 0 : return r;
189 : }
190 :
191 302 : static void log_close_journal(void) {
192 302 : journal_fd = safe_close(journal_fd);
193 302 : }
194 :
195 105 : static int log_open_journal(void) {
196 :
197 : static const union sockaddr_union sa = {
198 : .un.sun_family = AF_UNIX,
199 : .un.sun_path = "/run/systemd/journal/socket",
200 : };
201 :
202 : int r;
203 :
204 105 : if (journal_fd >= 0)
205 5 : return 0;
206 :
207 100 : journal_fd = create_log_socket(SOCK_DGRAM);
208 100 : if (journal_fd < 0) {
209 0 : r = journal_fd;
210 0 : goto fail;
211 : }
212 :
213 100 : if (connect(journal_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) {
214 0 : r = -errno;
215 0 : goto fail;
216 : }
217 :
218 100 : return 0;
219 :
220 0 : fail:
221 0 : log_close_journal();
222 0 : return r;
223 : }
224 :
225 407 : int log_open(void) {
226 : int r;
227 :
228 : /* Do not call from library code. */
229 :
230 : /* If we don't use the console we close it here, to not get
231 : * killed by SAK. If we don't use syslog we close it here so
232 : * that we are not confused by somebody deleting the socket in
233 : * the fs, and to make sure we don't use it if prohibit_ipc is
234 : * set. If we don't use /dev/kmsg we still keep it open,
235 : * because there is no reason to close it. */
236 :
237 407 : if (log_target == LOG_TARGET_NULL) {
238 1 : log_close_journal();
239 1 : log_close_syslog();
240 1 : log_close_console();
241 1 : return 0;
242 : }
243 :
244 505 : if (log_target != LOG_TARGET_AUTO ||
245 198 : getpid_cached() == 1 ||
246 99 : isatty(STDERR_FILENO) <= 0) {
247 :
248 406 : if (!prohibit_ipc &&
249 387 : IN_SET(log_target, LOG_TARGET_AUTO,
250 : LOG_TARGET_JOURNAL_OR_KMSG,
251 : LOG_TARGET_JOURNAL)) {
252 105 : r = log_open_journal();
253 105 : if (r >= 0) {
254 105 : log_close_syslog();
255 105 : log_close_console();
256 105 : return r;
257 : }
258 : }
259 :
260 301 : if (!prohibit_ipc &&
261 282 : IN_SET(log_target, LOG_TARGET_SYSLOG_OR_KMSG,
262 : LOG_TARGET_SYSLOG)) {
263 2 : r = log_open_syslog();
264 2 : if (r >= 0) {
265 2 : log_close_journal();
266 2 : log_close_console();
267 2 : return r;
268 : }
269 : }
270 :
271 299 : if (IN_SET(log_target, LOG_TARGET_AUTO,
272 : LOG_TARGET_JOURNAL_OR_KMSG,
273 : LOG_TARGET_SYSLOG_OR_KMSG,
274 : LOG_TARGET_KMSG)) {
275 1 : r = log_open_kmsg();
276 1 : if (r >= 0) {
277 0 : log_close_journal();
278 0 : log_close_syslog();
279 0 : log_close_console();
280 0 : return r;
281 : }
282 : }
283 : }
284 :
285 299 : log_close_journal();
286 299 : log_close_syslog();
287 :
288 299 : return log_open_console();
289 : }
290 :
291 145 : void log_set_target(LogTarget target) {
292 145 : assert(target >= 0);
293 145 : assert(target < _LOG_TARGET_MAX);
294 :
295 145 : if (upgrade_syslog_to_journal) {
296 0 : if (target == LOG_TARGET_SYSLOG)
297 0 : target = LOG_TARGET_JOURNAL;
298 0 : else if (target == LOG_TARGET_SYSLOG_OR_KMSG)
299 0 : target = LOG_TARGET_JOURNAL_OR_KMSG;
300 : }
301 :
302 145 : log_target = target;
303 145 : }
304 :
305 0 : void log_close(void) {
306 : /* Do not call from library code. */
307 :
308 0 : log_close_journal();
309 0 : log_close_syslog();
310 0 : log_close_kmsg();
311 0 : log_close_console();
312 0 : }
313 :
314 0 : void log_forget_fds(void) {
315 : /* Do not call from library code. */
316 :
317 0 : console_fd = kmsg_fd = syslog_fd = journal_fd = -1;
318 0 : }
319 :
320 309 : void log_set_max_level_realm(LogRealm realm, int level) {
321 309 : assert((level & LOG_PRIMASK) == level);
322 309 : assert(realm < ELEMENTSOF(log_max_level));
323 :
324 309 : log_max_level[realm] = level;
325 309 : }
326 :
327 0 : void log_set_facility(int facility) {
328 0 : log_facility = facility;
329 0 : }
330 :
331 33909 : static int write_to_console(
332 : int level,
333 : int error,
334 : const char *file,
335 : int line,
336 : const char *func,
337 : const char *buffer) {
338 :
339 : char location[256], prefix[1 + DECIMAL_STR_MAX(int) + 2];
340 33909 : struct iovec iovec[6] = {};
341 33909 : const char *on = NULL, *off = NULL;
342 33909 : size_t n = 0;
343 :
344 33909 : if (console_fd < 0)
345 0 : return 0;
346 :
347 33909 : if (log_target == LOG_TARGET_CONSOLE_PREFIXED) {
348 8 : xsprintf(prefix, "<%i>", level);
349 8 : iovec[n++] = IOVEC_MAKE_STRING(prefix);
350 : }
351 :
352 33909 : if (show_color)
353 1 : get_log_colors(LOG_PRI(level), &on, &off, NULL);
354 :
355 33909 : if (show_location) {
356 0 : const char *lon = "", *loff = "";
357 0 : if (show_color) {
358 0 : lon = ANSI_HIGHLIGHT_YELLOW4;
359 0 : loff = ANSI_NORMAL;
360 : }
361 :
362 0 : (void) snprintf(location, sizeof location, "%s%s:%i%s: ", lon, file, line, loff);
363 0 : iovec[n++] = IOVEC_MAKE_STRING(location);
364 : }
365 :
366 33909 : if (on)
367 1 : iovec[n++] = IOVEC_MAKE_STRING(on);
368 33909 : iovec[n++] = IOVEC_MAKE_STRING(buffer);
369 33909 : if (off)
370 1 : iovec[n++] = IOVEC_MAKE_STRING(off);
371 33909 : iovec[n++] = IOVEC_MAKE_STRING("\n");
372 :
373 33909 : if (writev(console_fd, iovec, n) < 0) {
374 :
375 0 : if (errno == EIO && getpid_cached() == 1) {
376 :
377 : /* If somebody tried to kick us from our console tty (via vhangup() or suchlike), try
378 : * to reconnect. */
379 :
380 0 : log_close_console();
381 0 : (void) log_open_console();
382 0 : if (console_fd < 0)
383 0 : return 0;
384 :
385 0 : if (writev(console_fd, iovec, n) < 0)
386 0 : return -errno;
387 : } else
388 0 : return -errno;
389 : }
390 :
391 33909 : return 1;
392 : }
393 :
394 16 : static int write_to_syslog(
395 : int level,
396 : int error,
397 : const char *file,
398 : int line,
399 : const char *func,
400 : const char *buffer) {
401 :
402 : char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
403 : header_time[64],
404 : header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1];
405 16 : struct iovec iovec[5] = {};
406 16 : struct msghdr msghdr = {
407 : .msg_iov = iovec,
408 : .msg_iovlen = ELEMENTSOF(iovec),
409 : };
410 : time_t t;
411 : struct tm tm;
412 :
413 16 : if (syslog_fd < 0)
414 0 : return 0;
415 :
416 16 : xsprintf(header_priority, "<%i>", level);
417 :
418 16 : t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
419 16 : if (!localtime_r(&t, &tm))
420 0 : return -EINVAL;
421 :
422 16 : if (strftime(header_time, sizeof(header_time), "%h %e %T ", &tm) <= 0)
423 0 : return -EINVAL;
424 :
425 16 : xsprintf(header_pid, "["PID_FMT"]: ", getpid_cached());
426 :
427 16 : iovec[0] = IOVEC_MAKE_STRING(header_priority);
428 16 : iovec[1] = IOVEC_MAKE_STRING(header_time);
429 16 : iovec[2] = IOVEC_MAKE_STRING(program_invocation_short_name);
430 16 : iovec[3] = IOVEC_MAKE_STRING(header_pid);
431 16 : iovec[4] = IOVEC_MAKE_STRING(buffer);
432 :
433 : /* When using syslog via SOCK_STREAM separate the messages by NUL chars */
434 16 : if (syslog_is_stream)
435 0 : iovec[4].iov_len++;
436 :
437 0 : for (;;) {
438 : ssize_t n;
439 :
440 16 : n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL);
441 16 : if (n < 0)
442 0 : return -errno;
443 :
444 16 : if (!syslog_is_stream ||
445 0 : (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec)))
446 : break;
447 :
448 0 : IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n);
449 : }
450 :
451 16 : return 1;
452 : }
453 :
454 8 : static int write_to_kmsg(
455 : int level,
456 : int error,
457 : const char *file,
458 : int line,
459 : const char *func,
460 : const char *buffer) {
461 :
462 : char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
463 : header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1];
464 8 : struct iovec iovec[5] = {};
465 :
466 8 : if (kmsg_fd < 0)
467 8 : return 0;
468 :
469 0 : xsprintf(header_priority, "<%i>", level);
470 0 : xsprintf(header_pid, "["PID_FMT"]: ", getpid_cached());
471 :
472 0 : iovec[0] = IOVEC_MAKE_STRING(header_priority);
473 0 : iovec[1] = IOVEC_MAKE_STRING(program_invocation_short_name);
474 0 : iovec[2] = IOVEC_MAKE_STRING(header_pid);
475 0 : iovec[3] = IOVEC_MAKE_STRING(buffer);
476 0 : iovec[4] = IOVEC_MAKE_STRING("\n");
477 :
478 0 : if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0)
479 0 : return -errno;
480 :
481 0 : return 1;
482 : }
483 :
484 80 : static int log_do_header(
485 : char *header,
486 : size_t size,
487 : int level,
488 : int error,
489 : const char *file, int line, const char *func,
490 : const char *object_field, const char *object,
491 : const char *extra_field, const char *extra) {
492 : int r;
493 :
494 80 : error = IS_SYNTHETIC_ERRNO(error) ? 0 : ERRNO_VALUE(error);
495 :
496 960 : r = snprintf(header, size,
497 : "PRIORITY=%i\n"
498 : "SYSLOG_FACILITY=%i\n"
499 : "%s%.256s%s" /* CODE_FILE */
500 : "%s%.*i%s" /* CODE_LINE */
501 : "%s%.256s%s" /* CODE_FUNC */
502 : "%s%.*i%s" /* ERRNO */
503 : "%s%.256s%s" /* object */
504 : "%s%.256s%s" /* extra */
505 : "SYSLOG_IDENTIFIER=%.256s\n",
506 : LOG_PRI(level),
507 80 : LOG_FAC(level),
508 80 : isempty(file) ? "" : "CODE_FILE=",
509 80 : isempty(file) ? "" : file,
510 80 : isempty(file) ? "" : "\n",
511 : line ? "CODE_LINE=" : "",
512 : line ? 1 : 0, line, /* %.0d means no output too, special case for 0 */
513 : line ? "\n" : "",
514 80 : isempty(func) ? "" : "CODE_FUNC=",
515 80 : isempty(func) ? "" : func,
516 80 : isempty(func) ? "" : "\n",
517 : error ? "ERRNO=" : "",
518 : error ? 1 : 0, error,
519 : error ? "\n" : "",
520 80 : isempty(object) ? "" : object_field,
521 80 : isempty(object) ? "" : object,
522 80 : isempty(object) ? "" : "\n",
523 80 : isempty(extra) ? "" : extra_field,
524 80 : isempty(extra) ? "" : extra,
525 80 : isempty(extra) ? "" : "\n",
526 : program_invocation_short_name);
527 80 : assert_raw((size_t) r < size);
528 :
529 80 : return 0;
530 : }
531 :
532 59 : static int write_to_journal(
533 : int level,
534 : int error,
535 : const char *file,
536 : int line,
537 : const char *func,
538 : const char *object_field,
539 : const char *object,
540 : const char *extra_field,
541 : const char *extra,
542 : const char *buffer) {
543 :
544 : char header[LINE_MAX];
545 59 : struct iovec iovec[4] = {};
546 59 : struct msghdr mh = {};
547 :
548 59 : if (journal_fd < 0)
549 0 : return 0;
550 :
551 59 : log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object, extra_field, extra);
552 :
553 59 : iovec[0] = IOVEC_MAKE_STRING(header);
554 59 : iovec[1] = IOVEC_MAKE_STRING("MESSAGE=");
555 59 : iovec[2] = IOVEC_MAKE_STRING(buffer);
556 59 : iovec[3] = IOVEC_MAKE_STRING("\n");
557 :
558 59 : mh.msg_iov = iovec;
559 59 : mh.msg_iovlen = ELEMENTSOF(iovec);
560 :
561 59 : if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0)
562 0 : return -errno;
563 :
564 59 : return 1;
565 : }
566 :
567 33828 : int log_dispatch_internal(
568 : int level,
569 : int error,
570 : const char *file,
571 : int line,
572 : const char *func,
573 : const char *object_field,
574 : const char *object,
575 : const char *extra_field,
576 : const char *extra,
577 : char *buffer) {
578 :
579 33828 : assert_raw(buffer);
580 :
581 33828 : if (log_target == LOG_TARGET_NULL)
582 2 : return -ERRNO_VALUE(error);
583 :
584 : /* Patch in LOG_DAEMON facility if necessary */
585 33826 : if ((level & LOG_FACMASK) == 0)
586 33621 : level |= log_facility;
587 :
588 33826 : if (open_when_needed)
589 0 : (void) log_open();
590 :
591 : do {
592 : char *e;
593 34126 : int k = 0;
594 :
595 34126 : buffer += strspn(buffer, NEWLINE);
596 :
597 34126 : if (buffer[0] == 0)
598 142 : break;
599 :
600 33984 : if ((e = strpbrk(buffer, NEWLINE)))
601 300 : *(e++) = 0;
602 :
603 33984 : if (IN_SET(log_target, LOG_TARGET_AUTO,
604 : LOG_TARGET_JOURNAL_OR_KMSG,
605 : LOG_TARGET_JOURNAL)) {
606 :
607 59 : k = write_to_journal(level, error, file, line, func, object_field, object, extra_field, extra, buffer);
608 59 : if (k < 0 && k != -EAGAIN)
609 0 : log_close_journal();
610 : }
611 :
612 33984 : if (IN_SET(log_target, LOG_TARGET_SYSLOG_OR_KMSG,
613 : LOG_TARGET_SYSLOG)) {
614 :
615 16 : k = write_to_syslog(level, error, file, line, func, buffer);
616 16 : if (k < 0 && k != -EAGAIN)
617 0 : log_close_syslog();
618 : }
619 :
620 33984 : if (k <= 0 &&
621 33909 : IN_SET(log_target, LOG_TARGET_AUTO,
622 : LOG_TARGET_SYSLOG_OR_KMSG,
623 : LOG_TARGET_JOURNAL_OR_KMSG,
624 : LOG_TARGET_KMSG)) {
625 :
626 8 : if (k < 0)
627 0 : log_open_kmsg();
628 :
629 8 : k = write_to_kmsg(level, error, file, line, func, buffer);
630 8 : if (k < 0) {
631 0 : log_close_kmsg();
632 0 : (void) log_open_console();
633 : }
634 : }
635 :
636 33984 : if (k <= 0)
637 33909 : (void) write_to_console(level, error, file, line, func, buffer);
638 :
639 33984 : buffer = e;
640 33984 : } while (buffer);
641 :
642 33826 : if (open_when_needed)
643 0 : log_close();
644 :
645 33826 : return -ERRNO_VALUE(error);
646 : }
647 :
648 0 : int log_dump_internal(
649 : int level,
650 : int error,
651 : const char *file,
652 : int line,
653 : const char *func,
654 : char *buffer) {
655 :
656 0 : LogRealm realm = LOG_REALM_REMOVE_LEVEL(level);
657 0 : PROTECT_ERRNO;
658 :
659 : /* This modifies the buffer... */
660 :
661 0 : if (_likely_(LOG_PRI(level) > log_max_level[realm]))
662 0 : return -ERRNO_VALUE(error);
663 :
664 0 : return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer);
665 : }
666 :
667 32756 : int log_internalv_realm(
668 : int level,
669 : int error,
670 : const char *file,
671 : int line,
672 : const char *func,
673 : const char *format,
674 : va_list ap) {
675 :
676 32756 : LogRealm realm = LOG_REALM_REMOVE_LEVEL(level);
677 : char buffer[LINE_MAX];
678 32756 : PROTECT_ERRNO;
679 :
680 32756 : if (_likely_(LOG_PRI(level) > log_max_level[realm]))
681 0 : return -ERRNO_VALUE(error);
682 :
683 : /* Make sure that %m maps to the specified error (or "Success"). */
684 32756 : errno = ERRNO_VALUE(error);
685 :
686 32756 : (void) vsnprintf(buffer, sizeof buffer, format, ap);
687 :
688 32756 : return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer);
689 : }
690 :
691 32756 : int log_internal_realm(
692 : int level,
693 : int error,
694 : const char *file,
695 : int line,
696 : const char *func,
697 : const char *format, ...) {
698 :
699 : va_list ap;
700 : int r;
701 :
702 32756 : va_start(ap, format);
703 32756 : r = log_internalv_realm(level, error, file, line, func, format, ap);
704 32756 : va_end(ap);
705 :
706 32756 : return r;
707 : }
708 :
709 2362 : int log_object_internalv(
710 : int level,
711 : int error,
712 : const char *file,
713 : int line,
714 : const char *func,
715 : const char *object_field,
716 : const char *object,
717 : const char *extra_field,
718 : const char *extra,
719 : const char *format,
720 : va_list ap) {
721 :
722 2362 : PROTECT_ERRNO;
723 : char *buffer, *b;
724 :
725 2362 : if (_likely_(LOG_PRI(level) > log_max_level[LOG_REALM_SYSTEMD]))
726 1655 : return -ERRNO_VALUE(error);
727 :
728 : /* Make sure that %m maps to the specified error (or "Success"). */
729 707 : errno = ERRNO_VALUE(error);
730 :
731 : /* Prepend the object name before the message */
732 707 : if (object) {
733 : size_t n;
734 :
735 707 : n = strlen(object);
736 707 : buffer = newa(char, n + 2 + LINE_MAX);
737 707 : b = stpcpy(stpcpy(buffer, object), ": ");
738 : } else
739 0 : b = buffer = newa(char, LINE_MAX);
740 :
741 707 : (void) vsnprintf(b, LINE_MAX, format, ap);
742 :
743 707 : return log_dispatch_internal(level, error, file, line, func,
744 : object_field, object, extra_field, extra, buffer);
745 : }
746 :
747 2362 : int log_object_internal(
748 : int level,
749 : int error,
750 : const char *file,
751 : int line,
752 : const char *func,
753 : const char *object_field,
754 : const char *object,
755 : const char *extra_field,
756 : const char *extra,
757 : const char *format, ...) {
758 :
759 : va_list ap;
760 : int r;
761 :
762 2362 : va_start(ap, format);
763 2362 : r = log_object_internalv(level, error, file, line, func, object_field, object, extra_field, extra, format, ap);
764 2362 : va_end(ap);
765 :
766 2362 : return r;
767 : }
768 :
769 208 : static void log_assert(
770 : int level,
771 : const char *text,
772 : const char *file,
773 : int line,
774 : const char *func,
775 : const char *format) {
776 :
777 : static char buffer[LINE_MAX];
778 208 : LogRealm realm = LOG_REALM_REMOVE_LEVEL(level);
779 :
780 208 : if (_likely_(LOG_PRI(level) > log_max_level[realm]))
781 48 : return;
782 :
783 : DISABLE_WARNING_FORMAT_NONLITERAL;
784 160 : (void) snprintf(buffer, sizeof buffer, format, text, file, line, func);
785 : REENABLE_WARNING;
786 :
787 160 : log_abort_msg = buffer;
788 :
789 160 : log_dispatch_internal(level, 0, file, line, func, NULL, NULL, NULL, NULL, buffer);
790 : }
791 :
792 0 : _noreturn_ void log_assert_failed_realm(
793 : LogRealm realm,
794 : const char *text,
795 : const char *file,
796 : int line,
797 : const char *func) {
798 0 : (void) log_open();
799 0 : log_assert(LOG_REALM_PLUS_LEVEL(realm, LOG_CRIT), text, file, line, func,
800 : "Assertion '%s' failed at %s:%u, function %s(). Aborting.");
801 0 : abort();
802 : }
803 :
804 0 : _noreturn_ void log_assert_failed_unreachable_realm(
805 : LogRealm realm,
806 : const char *text,
807 : const char *file,
808 : int line,
809 : const char *func) {
810 0 : (void) log_open();
811 0 : log_assert(LOG_REALM_PLUS_LEVEL(realm, LOG_CRIT), text, file, line, func,
812 : "Code should not be reached '%s' at %s:%u, function %s(). Aborting.");
813 0 : abort();
814 : }
815 :
816 208 : void log_assert_failed_return_realm(
817 : LogRealm realm,
818 : const char *text,
819 : const char *file,
820 : int line,
821 : const char *func) {
822 208 : PROTECT_ERRNO;
823 208 : log_assert(LOG_REALM_PLUS_LEVEL(realm, LOG_DEBUG), text, file, line, func,
824 : "Assertion '%s' failed at %s:%u, function %s(). Ignoring.");
825 208 : }
826 :
827 0 : int log_oom_internal(LogRealm realm, const char *file, int line, const char *func) {
828 0 : return log_internal_realm(LOG_REALM_PLUS_LEVEL(realm, LOG_ERR),
829 : ENOMEM, file, line, func, "Out of memory.");
830 : }
831 :
832 21 : int log_format_iovec(
833 : struct iovec *iovec,
834 : size_t iovec_len,
835 : size_t *n,
836 : bool newline_separator,
837 : int error,
838 : const char *format,
839 : va_list ap) {
840 :
841 : static const char nl = '\n';
842 :
843 93 : while (format && *n + 1 < iovec_len) {
844 : va_list aq;
845 : char *m;
846 : int r;
847 :
848 : /* We need to copy the va_list structure,
849 : * since vasprintf() leaves it afterwards at
850 : * an undefined location */
851 :
852 72 : errno = ERRNO_VALUE(error);
853 :
854 72 : va_copy(aq, ap);
855 72 : r = vasprintf(&m, format, aq);
856 72 : va_end(aq);
857 72 : if (r < 0)
858 0 : return -EINVAL;
859 :
860 : /* Now, jump enough ahead, so that we point to
861 : * the next format string */
862 168 : VA_FORMAT_ADVANCE(format, ap);
863 :
864 72 : iovec[(*n)++] = IOVEC_MAKE_STRING(m);
865 :
866 72 : if (newline_separator) {
867 72 : iovec[*n] = IOVEC_MAKE((char *)&nl, 1);
868 72 : (*n)++;
869 : }
870 :
871 72 : format = va_arg(ap, char *);
872 : }
873 21 : return 0;
874 : }
875 :
876 236 : int log_struct_internal(
877 : int level,
878 : int error,
879 : const char *file,
880 : int line,
881 : const char *func,
882 : const char *format, ...) {
883 :
884 236 : LogRealm realm = LOG_REALM_REMOVE_LEVEL(level);
885 : char buf[LINE_MAX];
886 236 : bool found = false;
887 236 : PROTECT_ERRNO;
888 : va_list ap;
889 :
890 236 : if (_likely_(LOG_PRI(level) > log_max_level[realm]) ||
891 230 : log_target == LOG_TARGET_NULL)
892 10 : return -ERRNO_VALUE(error);
893 :
894 226 : if ((level & LOG_FACMASK) == 0)
895 226 : level |= log_facility;
896 :
897 226 : if (IN_SET(log_target,
898 : LOG_TARGET_AUTO,
899 : LOG_TARGET_JOURNAL_OR_KMSG,
900 : LOG_TARGET_JOURNAL)) {
901 :
902 21 : if (open_when_needed)
903 0 : log_open_journal();
904 :
905 21 : if (journal_fd >= 0) {
906 : char header[LINE_MAX];
907 21 : struct iovec iovec[17] = {};
908 21 : size_t n = 0, i;
909 : int r;
910 21 : struct msghdr mh = {
911 : .msg_iov = iovec,
912 : };
913 21 : bool fallback = false;
914 :
915 : /* If the journal is available do structured logging.
916 : * Do not report the errno if it is synthetic. */
917 21 : log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL);
918 21 : iovec[n++] = IOVEC_MAKE_STRING(header);
919 :
920 21 : va_start(ap, format);
921 21 : r = log_format_iovec(iovec, ELEMENTSOF(iovec), &n, true, error, format, ap);
922 21 : if (r < 0)
923 0 : fallback = true;
924 : else {
925 21 : mh.msg_iovlen = n;
926 21 : (void) sendmsg(journal_fd, &mh, MSG_NOSIGNAL);
927 : }
928 :
929 21 : va_end(ap);
930 93 : for (i = 1; i < n; i += 2)
931 72 : free(iovec[i].iov_base);
932 :
933 21 : if (!fallback) {
934 21 : if (open_when_needed)
935 0 : log_close();
936 :
937 21 : return -ERRNO_VALUE(error);
938 : }
939 : }
940 : }
941 :
942 : /* Fallback if journal logging is not available or didn't work. */
943 :
944 205 : va_start(ap, format);
945 711 : while (format) {
946 : va_list aq;
947 :
948 711 : errno = ERRNO_VALUE(error);
949 :
950 711 : va_copy(aq, ap);
951 711 : (void) vsnprintf(buf, sizeof buf, format, aq);
952 711 : va_end(aq);
953 :
954 711 : if (startswith(buf, "MESSAGE=")) {
955 205 : found = true;
956 205 : break;
957 : }
958 :
959 842 : VA_FORMAT_ADVANCE(format, ap);
960 :
961 506 : format = va_arg(ap, char *);
962 : }
963 205 : va_end(ap);
964 :
965 205 : if (!found) {
966 0 : if (open_when_needed)
967 0 : log_close();
968 :
969 0 : return -ERRNO_VALUE(error);
970 : }
971 :
972 205 : return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, buf + 8);
973 : }
974 :
975 0 : int log_struct_iovec_internal(
976 : int level,
977 : int error,
978 : const char *file,
979 : int line,
980 : const char *func,
981 : const struct iovec input_iovec[],
982 : size_t n_input_iovec) {
983 :
984 0 : LogRealm realm = LOG_REALM_REMOVE_LEVEL(level);
985 0 : PROTECT_ERRNO;
986 : size_t i;
987 : char *m;
988 :
989 0 : if (_likely_(LOG_PRI(level) > log_max_level[realm]) ||
990 0 : log_target == LOG_TARGET_NULL)
991 0 : return -ERRNO_VALUE(error);
992 :
993 0 : if ((level & LOG_FACMASK) == 0)
994 0 : level |= log_facility;
995 :
996 0 : if (IN_SET(log_target, LOG_TARGET_AUTO,
997 : LOG_TARGET_JOURNAL_OR_KMSG,
998 0 : LOG_TARGET_JOURNAL) &&
999 0 : journal_fd >= 0) {
1000 :
1001 0 : struct iovec iovec[1 + n_input_iovec*2];
1002 : char header[LINE_MAX];
1003 0 : struct msghdr mh = {
1004 : .msg_iov = iovec,
1005 0 : .msg_iovlen = 1 + n_input_iovec*2,
1006 : };
1007 :
1008 0 : log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL);
1009 0 : iovec[0] = IOVEC_MAKE_STRING(header);
1010 :
1011 0 : for (i = 0; i < n_input_iovec; i++) {
1012 0 : iovec[1+i*2] = input_iovec[i];
1013 0 : iovec[1+i*2+1] = IOVEC_MAKE_STRING("\n");
1014 : }
1015 :
1016 0 : if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) >= 0)
1017 0 : return -ERRNO_VALUE(error);
1018 : }
1019 :
1020 0 : for (i = 0; i < n_input_iovec; i++)
1021 0 : if (memory_startswith(input_iovec[i].iov_base, input_iovec[i].iov_len, "MESSAGE="))
1022 0 : break;
1023 :
1024 0 : if (_unlikely_(i >= n_input_iovec)) /* Couldn't find MESSAGE=? */
1025 0 : return -ERRNO_VALUE(error);
1026 :
1027 0 : m = strndupa(input_iovec[i].iov_base + STRLEN("MESSAGE="),
1028 : input_iovec[i].iov_len - STRLEN("MESSAGE="));
1029 :
1030 0 : return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, m);
1031 : }
1032 :
1033 19 : int log_set_target_from_string(const char *e) {
1034 : LogTarget t;
1035 :
1036 19 : t = log_target_from_string(e);
1037 19 : if (t < 0)
1038 0 : return -EINVAL;
1039 :
1040 19 : log_set_target(t);
1041 19 : return 0;
1042 : }
1043 :
1044 193 : int log_set_max_level_from_string_realm(LogRealm realm, const char *e) {
1045 : int t;
1046 :
1047 193 : t = log_level_from_string(e);
1048 193 : if (t < 0)
1049 0 : return -EINVAL;
1050 :
1051 193 : log_set_max_level_realm(realm, t);
1052 193 : return 0;
1053 : }
1054 :
1055 2412 : static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
1056 :
1057 : /*
1058 : * The systemd.log_xyz= settings are parsed by all tools, and
1059 : * so is "debug".
1060 : *
1061 : * However, "quiet" is only parsed by PID 1, and only turns of
1062 : * status output to /dev/console, but does not alter the log
1063 : * level.
1064 : */
1065 :
1066 2412 : if (streq(key, "debug") && !value)
1067 0 : log_set_max_level(LOG_DEBUG);
1068 :
1069 2412 : else if (proc_cmdline_key_streq(key, "systemd.log_target")) {
1070 :
1071 0 : if (proc_cmdline_value_missing(key, value))
1072 0 : return 0;
1073 :
1074 0 : if (log_set_target_from_string(value) < 0)
1075 0 : log_warning("Failed to parse log target '%s'. Ignoring.", value);
1076 :
1077 2412 : } else if (proc_cmdline_key_streq(key, "systemd.log_level")) {
1078 :
1079 0 : if (proc_cmdline_value_missing(key, value))
1080 0 : return 0;
1081 :
1082 0 : if (log_set_max_level_from_string(value) < 0)
1083 0 : log_warning("Failed to parse log level '%s'. Ignoring.", value);
1084 :
1085 2412 : } else if (proc_cmdline_key_streq(key, "systemd.log_color")) {
1086 :
1087 0 : if (log_show_color_from_string(value ?: "1") < 0)
1088 0 : log_warning("Failed to parse log color setting '%s'. Ignoring.", value);
1089 :
1090 2412 : } else if (proc_cmdline_key_streq(key, "systemd.log_location")) {
1091 :
1092 0 : if (log_show_location_from_string(value ?: "1") < 0)
1093 0 : log_warning("Failed to parse log location setting '%s'. Ignoring.", value);
1094 : }
1095 :
1096 2412 : return 0;
1097 : }
1098 :
1099 402 : void log_parse_environment_realm(LogRealm realm) {
1100 : /* Do not call from library code. */
1101 :
1102 : const char *e;
1103 :
1104 402 : if (getpid_cached() == 1 || get_ctty_devnr(0, NULL) < 0)
1105 : /* Only try to read the command line in daemons. We assume that anything that has a
1106 : * controlling tty is user stuff. For PID1 we do a special check in case it hasn't
1107 : * closed the console yet. */
1108 402 : (void) proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
1109 :
1110 402 : e = getenv("SYSTEMD_LOG_TARGET");
1111 402 : if (e && log_set_target_from_string(e) < 0)
1112 0 : log_warning("Failed to parse log target '%s'. Ignoring.", e);
1113 :
1114 402 : e = getenv("SYSTEMD_LOG_LEVEL");
1115 402 : if (e && log_set_max_level_from_string_realm(realm, e) < 0)
1116 0 : log_warning("Failed to parse log level '%s'. Ignoring.", e);
1117 :
1118 402 : e = getenv("SYSTEMD_LOG_COLOR");
1119 402 : if (e && log_show_color_from_string(e) < 0)
1120 0 : log_warning("Failed to parse log color '%s'. Ignoring.", e);
1121 :
1122 402 : e = getenv("SYSTEMD_LOG_LOCATION");
1123 402 : if (e && log_show_location_from_string(e) < 0)
1124 0 : log_warning("Failed to parse log location '%s'. Ignoring.", e);
1125 402 : }
1126 :
1127 0 : LogTarget log_get_target(void) {
1128 0 : return log_target;
1129 : }
1130 :
1131 97235 : int log_get_max_level_realm(LogRealm realm) {
1132 97235 : return log_max_level[realm];
1133 : }
1134 :
1135 112 : void log_show_color(bool b) {
1136 112 : show_color = b;
1137 112 : }
1138 :
1139 46 : bool log_get_show_color(void) {
1140 46 : return show_color;
1141 : }
1142 :
1143 0 : void log_show_location(bool b) {
1144 0 : show_location = b;
1145 0 : }
1146 :
1147 0 : bool log_get_show_location(void) {
1148 0 : return show_location;
1149 : }
1150 :
1151 0 : int log_show_color_from_string(const char *e) {
1152 : int t;
1153 :
1154 0 : t = parse_boolean(e);
1155 0 : if (t < 0)
1156 0 : return t;
1157 :
1158 0 : log_show_color(t);
1159 0 : return 0;
1160 : }
1161 :
1162 0 : int log_show_location_from_string(const char *e) {
1163 : int t;
1164 :
1165 0 : t = parse_boolean(e);
1166 0 : if (t < 0)
1167 0 : return t;
1168 :
1169 0 : log_show_location(t);
1170 0 : return 0;
1171 : }
1172 :
1173 50 : bool log_on_console(void) {
1174 50 : if (IN_SET(log_target, LOG_TARGET_CONSOLE,
1175 : LOG_TARGET_CONSOLE_PREFIXED))
1176 50 : return true;
1177 :
1178 0 : return syslog_fd < 0 && kmsg_fd < 0 && journal_fd < 0;
1179 : }
1180 :
1181 : static const char *const log_target_table[_LOG_TARGET_MAX] = {
1182 : [LOG_TARGET_CONSOLE] = "console",
1183 : [LOG_TARGET_CONSOLE_PREFIXED] = "console-prefixed",
1184 : [LOG_TARGET_KMSG] = "kmsg",
1185 : [LOG_TARGET_JOURNAL] = "journal",
1186 : [LOG_TARGET_JOURNAL_OR_KMSG] = "journal-or-kmsg",
1187 : [LOG_TARGET_SYSLOG] = "syslog",
1188 : [LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg",
1189 : [LOG_TARGET_AUTO] = "auto",
1190 : [LOG_TARGET_NULL] = "null",
1191 : };
1192 :
1193 41 : DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);
1194 :
1195 0 : void log_received_signal(int level, const struct signalfd_siginfo *si) {
1196 0 : assert(si);
1197 :
1198 0 : if (pid_is_valid(si->ssi_pid)) {
1199 0 : _cleanup_free_ char *p = NULL;
1200 :
1201 0 : (void) get_process_comm(si->ssi_pid, &p);
1202 :
1203 0 : log_full(level,
1204 : "Received SIG%s from PID %"PRIu32" (%s).",
1205 : signal_to_string(si->ssi_signo),
1206 : si->ssi_pid, strna(p));
1207 : } else
1208 0 : log_full(level,
1209 : "Received SIG%s.",
1210 : signal_to_string(si->ssi_signo));
1211 0 : }
1212 :
1213 175 : int log_syntax_internal(
1214 : const char *unit,
1215 : int level,
1216 : const char *config_file,
1217 : unsigned config_line,
1218 : int error,
1219 : const char *file,
1220 : int line,
1221 : const char *func,
1222 : const char *format, ...) {
1223 :
1224 175 : PROTECT_ERRNO;
1225 : char buffer[LINE_MAX];
1226 : va_list ap;
1227 175 : const char *unit_fmt = NULL;
1228 :
1229 175 : if (_likely_(LOG_PRI(level) > log_max_level[LOG_REALM_SYSTEMD]) ||
1230 175 : log_target == LOG_TARGET_NULL)
1231 3 : return -ERRNO_VALUE(error);
1232 :
1233 172 : errno = ERRNO_VALUE(error);
1234 :
1235 172 : va_start(ap, format);
1236 172 : (void) vsnprintf(buffer, sizeof buffer, format, ap);
1237 172 : va_end(ap);
1238 :
1239 172 : if (unit)
1240 115 : unit_fmt = getpid_cached() == 1 ? "UNIT=%s" : "USER_UNIT=%s";
1241 :
1242 172 : if (config_file) {
1243 172 : if (config_line > 0)
1244 168 : return log_struct_internal(
1245 : LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, level),
1246 : error,
1247 : file, line, func,
1248 : "MESSAGE_ID=" SD_MESSAGE_INVALID_CONFIGURATION_STR,
1249 : "CONFIG_FILE=%s", config_file,
1250 : "CONFIG_LINE=%u", config_line,
1251 : LOG_MESSAGE("%s:%u: %s", config_file, config_line, buffer),
1252 : unit_fmt, unit,
1253 : NULL);
1254 : else
1255 4 : return log_struct_internal(
1256 : LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, level),
1257 : error,
1258 : file, line, func,
1259 : "MESSAGE_ID=" SD_MESSAGE_INVALID_CONFIGURATION_STR,
1260 : "CONFIG_FILE=%s", config_file,
1261 : LOG_MESSAGE("%s: %s", config_file, buffer),
1262 : unit_fmt, unit,
1263 : NULL);
1264 0 : } else if (unit)
1265 0 : return log_struct_internal(
1266 : LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, level),
1267 : error,
1268 : file, line, func,
1269 : "MESSAGE_ID=" SD_MESSAGE_INVALID_CONFIGURATION_STR,
1270 : LOG_MESSAGE("%s: %s", unit, buffer),
1271 : unit_fmt, unit,
1272 : NULL);
1273 : else
1274 0 : return log_struct_internal(
1275 : LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, level),
1276 : error,
1277 : file, line, func,
1278 : "MESSAGE_ID=" SD_MESSAGE_INVALID_CONFIGURATION_STR,
1279 : LOG_MESSAGE("%s", buffer),
1280 : NULL);
1281 : }
1282 :
1283 1 : int log_syntax_invalid_utf8_internal(
1284 : const char *unit,
1285 : int level,
1286 : const char *config_file,
1287 : unsigned config_line,
1288 : const char *file,
1289 : int line,
1290 : const char *func,
1291 : const char *rvalue) {
1292 :
1293 2 : _cleanup_free_ char *p = NULL;
1294 :
1295 1 : if (rvalue)
1296 1 : p = utf8_escape_invalid(rvalue);
1297 :
1298 1 : log_syntax_internal(unit, level, config_file, config_line, 0, file, line, func,
1299 : "String is not UTF-8 clean, ignoring assignment: %s", strna(p));
1300 :
1301 1 : return -EINVAL;
1302 : }
1303 :
1304 0 : void log_set_upgrade_syslog_to_journal(bool b) {
1305 0 : upgrade_syslog_to_journal = b;
1306 :
1307 : /* Make the change effective immediately */
1308 0 : if (b) {
1309 0 : if (log_target == LOG_TARGET_SYSLOG)
1310 0 : log_target = LOG_TARGET_JOURNAL;
1311 0 : else if (log_target == LOG_TARGET_SYSLOG_OR_KMSG)
1312 0 : log_target = LOG_TARGET_JOURNAL_OR_KMSG;
1313 : }
1314 0 : }
1315 :
1316 0 : void log_set_always_reopen_console(bool b) {
1317 0 : always_reopen_console = b;
1318 0 : }
1319 :
1320 0 : void log_set_open_when_needed(bool b) {
1321 0 : open_when_needed = b;
1322 0 : }
1323 :
1324 19 : void log_set_prohibit_ipc(bool b) {
1325 19 : prohibit_ipc = b;
1326 19 : }
1327 :
1328 0 : int log_emergency_level(void) {
1329 : /* Returns the log level to use for log_emergency() logging. We use LOG_EMERG only when we are PID 1, as only
1330 : * then the system of the whole system is obviously affected. */
1331 :
1332 0 : return getpid_cached() == 1 ? LOG_EMERG : LOG_ERR;
1333 : }
1334 :
1335 0 : int log_dup_console(void) {
1336 : int copy;
1337 :
1338 : /* Duplicate the fd we use for fd logging if it's < 3 and use the copy from now on. This call is useful
1339 : * whenever we want to continue logging through the original fd, but want to rearrange stderr. */
1340 :
1341 0 : if (console_fd >= 3)
1342 0 : return 0;
1343 :
1344 0 : copy = fcntl(console_fd, F_DUPFD_CLOEXEC, 3);
1345 0 : if (copy < 0)
1346 0 : return -errno;
1347 :
1348 0 : console_fd = copy;
1349 0 : return 0;
1350 : }
1351 :
1352 109 : void log_setup_service(void) {
1353 : /* Sets up logging the way it is most appropriate for running a program as a service. Note that using this
1354 : * doesn't make the binary unsuitable for invocation on the command line, as log output will still go to the
1355 : * terminal if invoked interactively. */
1356 :
1357 109 : log_set_target(LOG_TARGET_AUTO);
1358 109 : log_parse_environment();
1359 109 : (void) log_open();
1360 109 : }
|