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