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