]> git.proxmox.com Git - systemd.git/blob - src/coredump/coredumpctl.c
New upstream version 252
[systemd.git] / src / coredump / coredumpctl.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <fcntl.h>
4 #include <getopt.h>
5 #include <locale.h>
6 #include <stdio.h>
7 #include <unistd.h>
8
9 #include "sd-bus.h"
10 #include "sd-journal.h"
11 #include "sd-messages.h"
12
13 #include "alloc-util.h"
14 #include "bus-error.h"
15 #include "bus-locator.h"
16 #include "bus-util.h"
17 #include "chase-symlinks.h"
18 #include "compress.h"
19 #include "def.h"
20 #include "dissect-image.h"
21 #include "fd-util.h"
22 #include "format-table.h"
23 #include "fs-util.h"
24 #include "glob-util.h"
25 #include "journal-internal.h"
26 #include "journal-util.h"
27 #include "log.h"
28 #include "macro.h"
29 #include "main-func.h"
30 #include "mount-util.h"
31 #include "pager.h"
32 #include "parse-argument.h"
33 #include "parse-util.h"
34 #include "path-util.h"
35 #include "pretty-print.h"
36 #include "process-util.h"
37 #include "rlimit-util.h"
38 #include "sigbus.h"
39 #include "signal-util.h"
40 #include "string-util.h"
41 #include "strv.h"
42 #include "terminal-util.h"
43 #include "tmpfile-util.h"
44 #include "user-util.h"
45 #include "util.h"
46 #include "verbs.h"
47
48 #define SHORT_BUS_CALL_TIMEOUT_USEC (3 * USEC_PER_SEC)
49
50 static usec_t arg_since = USEC_INFINITY, arg_until = USEC_INFINITY;
51 static const char* arg_field = NULL;
52 static const char *arg_debugger = NULL;
53 static char **arg_debugger_args = NULL;
54 static const char *arg_directory = NULL;
55 static char *arg_root = NULL;
56 static char *arg_image = NULL;
57 static char **arg_file = NULL;
58 static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
59 static PagerFlags arg_pager_flags = 0;
60 static int arg_legend = true;
61 static size_t arg_rows_max = SIZE_MAX;
62 static const char* arg_output = NULL;
63 static bool arg_reverse = false;
64 static bool arg_quiet = false;
65 static bool arg_all = false;
66
67 STATIC_DESTRUCTOR_REGISTER(arg_debugger_args, strv_freep);
68 STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
69
70 static int add_match(sd_journal *j, const char *match) {
71 _cleanup_free_ char *p = NULL;
72 const char* prefix, *pattern;
73 pid_t pid;
74 int r;
75
76 if (strchr(match, '='))
77 prefix = "";
78 else if (strchr(match, '/')) {
79 r = path_make_absolute_cwd(match, &p);
80 if (r < 0)
81 return log_error_errno(r, "path_make_absolute_cwd(\"%s\"): %m", match);
82
83 match = p;
84 prefix = "COREDUMP_EXE=";
85 } else if (parse_pid(match, &pid) >= 0)
86 prefix = "COREDUMP_PID=";
87 else
88 prefix = "COREDUMP_COMM=";
89
90 pattern = strjoina(prefix, match);
91 log_debug("Adding match: %s", pattern);
92 r = sd_journal_add_match(j, pattern, 0);
93 if (r < 0)
94 return log_error_errno(r, "Failed to add match \"%s\": %m", match);
95
96 return 0;
97 }
98
99 static int add_matches(sd_journal *j, char **matches) {
100 int r;
101
102 r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR, 0);
103 if (r < 0)
104 return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR);
105
106 r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR, 0);
107 if (r < 0)
108 return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR);
109
110 STRV_FOREACH(match, matches) {
111 r = add_match(j, *match);
112 if (r < 0)
113 return r;
114 }
115
116 return 0;
117 }
118
119 static int acquire_journal(sd_journal **ret, char **matches) {
120 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
121 int r;
122
123 assert(ret);
124
125 if (arg_directory) {
126 r = sd_journal_open_directory(&j, arg_directory, 0);
127 if (r < 0)
128 return log_error_errno(r, "Failed to open journals in directory: %s: %m", arg_directory);
129 } else if (arg_root) {
130 r = sd_journal_open_directory(&j, arg_root, SD_JOURNAL_OS_ROOT);
131 if (r < 0)
132 return log_error_errno(r, "Failed to open journals in root directory: %s: %m", arg_root);
133 } else if (arg_file) {
134 r = sd_journal_open_files(&j, (const char**)arg_file, 0);
135 if (r < 0)
136 return log_error_errno(r, "Failed to open journal files: %m");
137 } else {
138 r = sd_journal_open(&j, arg_all ? 0 : SD_JOURNAL_LOCAL_ONLY);
139 if (r < 0)
140 return log_error_errno(r, "Failed to open journal: %m");
141 }
142
143 r = journal_access_check_and_warn(j, arg_quiet, true);
144 if (r < 0)
145 return r;
146
147 r = add_matches(j, matches);
148 if (r < 0)
149 return r;
150
151 if (DEBUG_LOGGING) {
152 _cleanup_free_ char *filter = NULL;
153
154 filter = journal_make_match_string(j);
155 log_debug("Journal filter: %s", filter);
156 }
157
158 *ret = TAKE_PTR(j);
159
160 return 0;
161 }
162
163 static int verb_help(int argc, char **argv, void *userdata) {
164 _cleanup_free_ char *link = NULL;
165 int r;
166
167 r = terminal_urlify_man("coredumpctl", "1", &link);
168 if (r < 0)
169 return log_oom();
170
171 printf("%1$s [OPTIONS...] COMMAND ...\n\n"
172 "%5$sList or retrieve coredumps from the journal.%6$s\n"
173 "\n%3$sCommands:%4$s\n"
174 " list [MATCHES...] List available coredumps (default)\n"
175 " info [MATCHES...] Show detailed information about one or more coredumps\n"
176 " dump [MATCHES...] Print first matching coredump to stdout\n"
177 " debug [MATCHES...] Start a debugger for the first matching coredump\n"
178 "\n%3$sOptions:%4$s\n"
179 " -h --help Show this help\n"
180 " --version Print version string\n"
181 " --no-pager Do not pipe output into a pager\n"
182 " --no-legend Do not print the column headers\n"
183 " --json=pretty|short|off\n"
184 " Generate JSON output\n"
185 " --debugger=DEBUGGER Use the given debugger\n"
186 " -A --debugger-arguments=ARGS Pass the given arguments to the debugger\n"
187 " -n INT Show maximum number of rows\n"
188 " -1 Show information about most recent entry only\n"
189 " -S --since=DATE Only print coredumps since the date\n"
190 " -U --until=DATE Only print coredumps until the date\n"
191 " -r --reverse Show the newest entries first\n"
192 " -F --field=FIELD List all values a certain field takes\n"
193 " -o --output=FILE Write output to FILE\n"
194 " --file=PATH Use journal file\n"
195 " -D --directory=DIR Use journal files from directory\n\n"
196 " -q --quiet Do not show info messages and privilege warning\n"
197 " --all Look at all journal files instead of local ones\n"
198 " --root=PATH Operate on an alternate filesystem root\n"
199 " --image=PATH Operate on disk image as filesystem root\n"
200 "\nSee the %2$s for details.\n",
201 program_invocation_short_name,
202 link,
203 ansi_underline(),
204 ansi_normal(),
205 ansi_highlight(),
206 ansi_normal());
207
208 return 0;
209 }
210
211 static int parse_argv(int argc, char *argv[]) {
212 enum {
213 ARG_VERSION = 0x100,
214 ARG_NO_PAGER,
215 ARG_NO_LEGEND,
216 ARG_JSON,
217 ARG_DEBUGGER,
218 ARG_FILE,
219 ARG_ROOT,
220 ARG_IMAGE,
221 ARG_ALL,
222 };
223
224 int c, r;
225
226 static const struct option options[] = {
227 { "help", no_argument, NULL, 'h' },
228 { "version" , no_argument, NULL, ARG_VERSION },
229 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
230 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
231 { "debugger", required_argument, NULL, ARG_DEBUGGER },
232 { "debugger-arguments", required_argument, NULL, 'A' },
233 { "output", required_argument, NULL, 'o' },
234 { "field", required_argument, NULL, 'F' },
235 { "file", required_argument, NULL, ARG_FILE },
236 { "directory", required_argument, NULL, 'D' },
237 { "reverse", no_argument, NULL, 'r' },
238 { "since", required_argument, NULL, 'S' },
239 { "until", required_argument, NULL, 'U' },
240 { "quiet", no_argument, NULL, 'q' },
241 { "json", required_argument, NULL, ARG_JSON },
242 { "root", required_argument, NULL, ARG_ROOT },
243 { "image", required_argument, NULL, ARG_IMAGE },
244 { "all", no_argument, NULL, ARG_ALL },
245 {}
246 };
247
248 assert(argc >= 0);
249 assert(argv);
250
251 while ((c = getopt_long(argc, argv, "hA:o:F:1D:rS:U:qn:", options, NULL)) >= 0)
252 switch (c) {
253 case 'h':
254 return verb_help(0, NULL, NULL);
255
256 case ARG_VERSION:
257 return version();
258
259 case ARG_NO_PAGER:
260 arg_pager_flags |= PAGER_DISABLE;
261 break;
262
263 case ARG_NO_LEGEND:
264 arg_legend = false;
265 break;
266
267 case ARG_DEBUGGER:
268 arg_debugger = optarg;
269 break;
270
271 case 'A': {
272 _cleanup_strv_free_ char **l = NULL;
273 r = strv_split_full(&l, optarg, WHITESPACE, EXTRACT_UNQUOTE);
274 if (r < 0)
275 return log_error_errno(r, "Failed to parse debugger arguments '%s': %m", optarg);
276 strv_free_and_replace(arg_debugger_args, l);
277 break;
278 }
279
280 case ARG_FILE:
281 r = glob_extend(&arg_file, optarg, GLOB_NOCHECK);
282 if (r < 0)
283 return log_error_errno(r, "Failed to add paths: %m");
284 break;
285
286 case 'o':
287 if (arg_output)
288 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
289 "Cannot set output more than once.");
290
291 arg_output = optarg;
292 break;
293
294 case 'S':
295 r = parse_timestamp(optarg, &arg_since);
296 if (r < 0)
297 return log_error_errno(r, "Failed to parse timestamp '%s': %m", optarg);
298 break;
299
300 case 'U':
301 r = parse_timestamp(optarg, &arg_until);
302 if (r < 0)
303 return log_error_errno(r, "Failed to parse timestamp '%s': %m", optarg);
304 break;
305
306 case 'F':
307 if (arg_field)
308 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
309 "Cannot use --field/-F more than once.");
310 arg_field = optarg;
311 break;
312
313 case '1':
314 arg_rows_max = 1;
315 arg_reverse = true;
316 break;
317
318 case 'n': {
319 unsigned n;
320
321 r = safe_atou(optarg, &n);
322 if (r < 0 || n < 1)
323 return log_error_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL),
324 "Invalid numeric parameter to -n: %s", optarg);
325
326 arg_rows_max = n;
327 break;
328 }
329
330 case 'D':
331 arg_directory = optarg;
332 break;
333
334 case ARG_ROOT:
335 r = parse_path_argument(optarg, false, &arg_root);
336 if (r < 0)
337 return r;
338 break;
339
340 case ARG_IMAGE:
341 r = parse_path_argument(optarg, false, &arg_image);
342 if (r < 0)
343 return r;
344 break;
345
346 case 'r':
347 arg_reverse = true;
348 break;
349
350 case 'q':
351 arg_quiet = true;
352 break;
353
354 case ARG_JSON:
355 r = parse_json_argument(optarg, &arg_json_format_flags);
356 if (r <= 0)
357 return r;
358
359 break;
360
361 case ARG_ALL:
362 arg_all = true;
363 break;
364
365 case '?':
366 return -EINVAL;
367
368 default:
369 assert_not_reached();
370 }
371
372 if (arg_since != USEC_INFINITY && arg_until != USEC_INFINITY &&
373 arg_since > arg_until)
374 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
375 "--since= must be before --until=.");
376
377 if ((!!arg_directory + !!arg_image + !!arg_root) > 1)
378 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root=, --image= or -D/--directory=, the combination of these options is not supported.");
379
380 return 1;
381 }
382
383 static int retrieve(const void *data,
384 size_t len,
385 const char *name,
386 char **var) {
387
388 size_t ident;
389 char *v;
390
391 ident = strlen(name) + 1; /* name + "=" */
392
393 if (len < ident)
394 return 0;
395
396 if (memcmp(data, name, ident - 1) != 0)
397 return 0;
398
399 if (((const char*) data)[ident - 1] != '=')
400 return 0;
401
402 v = strndup((const char*)data + ident, len - ident);
403 if (!v)
404 return log_oom();
405
406 free_and_replace(*var, v);
407 return 1;
408 }
409
410 static int print_field(FILE* file, sd_journal *j) {
411 const void *d;
412 size_t l;
413
414 assert(file);
415 assert(j);
416
417 assert(arg_field);
418
419 /* A (user-specified) field may appear more than once for a given entry.
420 * We will print all of the occurrences.
421 * This is different below for fields that systemd-coredump uses,
422 * because they cannot meaningfully appear more than once.
423 */
424 SD_JOURNAL_FOREACH_DATA(j, d, l) {
425 _cleanup_free_ char *value = NULL;
426 int r;
427
428 r = retrieve(d, l, arg_field, &value);
429 if (r < 0)
430 return r;
431 if (r > 0)
432 fprintf(file, "%s\n", value);
433 }
434
435 return 0;
436 }
437
438 #define RETRIEVE(d, l, name, arg) \
439 { \
440 int _r = retrieve(d, l, name, &arg); \
441 if (_r < 0) \
442 return _r; \
443 if (_r > 0) \
444 continue; \
445 }
446
447 static void analyze_coredump_file(
448 const char *path,
449 const char **ret_state,
450 const char **ret_color,
451 uint64_t *ret_size) {
452
453 _cleanup_close_ int fd = -1;
454 struct stat st;
455 int r;
456
457 assert(path);
458 assert(ret_state);
459 assert(ret_color);
460 assert(ret_size);
461
462 fd = open(path, O_PATH|O_CLOEXEC);
463 if (fd < 0) {
464 if (errno == ENOENT) {
465 *ret_state = "missing";
466 *ret_color = ansi_grey();
467 *ret_size = UINT64_MAX;
468 return;
469 }
470
471 r = -errno;
472 } else
473 r = access_fd(fd, R_OK);
474 if (ERRNO_IS_PRIVILEGE(r)) {
475 *ret_state = "inaccessible";
476 *ret_color = ansi_highlight_yellow();
477 *ret_size = UINT64_MAX;
478 return;
479 }
480 if (r < 0)
481 goto error;
482
483 if (fstat(fd, &st) < 0)
484 goto error;
485
486 if (!S_ISREG(st.st_mode))
487 goto error;
488
489 *ret_state = "present";
490 *ret_color = NULL;
491 *ret_size = st.st_size;
492 return;
493
494 error:
495 *ret_state = "error";
496 *ret_color = ansi_highlight_red();
497 *ret_size = UINT64_MAX;
498 }
499
500 static int resolve_filename(const char *root, char **p) {
501 char *resolved = NULL;
502 int r;
503
504 if (!*p)
505 return 0;
506
507 r = chase_symlinks(*p, root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, NULL);
508 if (r < 0)
509 return log_error_errno(r, "Failed to resolve \"%s%s\": %m", strempty(root), *p);
510
511 free_and_replace(*p, resolved);
512
513 /* chase_symlinks() with flag CHASE_NONEXISTENT will return 0 if the file doesn't exist and 1 if it does.
514 * Return that to the caller
515 */
516 return r;
517 }
518
519 static int print_list(FILE* file, sd_journal *j, Table *t) {
520 _cleanup_free_ char
521 *mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL,
522 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
523 *filename = NULL, *truncated = NULL, *coredump = NULL;
524 const void *d;
525 size_t l;
526 usec_t ts;
527 int r, signal_as_int = 0;
528 const char *present = NULL, *color = NULL;
529 uint64_t size = UINT64_MAX;
530 bool normal_coredump;
531 uid_t uid_as_int = UID_INVALID;
532 gid_t gid_as_int = GID_INVALID;
533 pid_t pid_as_int = 0;
534
535 assert(file);
536 assert(j);
537 assert(t);
538
539 SD_JOURNAL_FOREACH_DATA(j, d, l) {
540 RETRIEVE(d, l, "MESSAGE_ID", mid);
541 RETRIEVE(d, l, "COREDUMP_PID", pid);
542 RETRIEVE(d, l, "COREDUMP_UID", uid);
543 RETRIEVE(d, l, "COREDUMP_GID", gid);
544 RETRIEVE(d, l, "COREDUMP_SIGNAL", sgnl);
545 RETRIEVE(d, l, "COREDUMP_EXE", exe);
546 RETRIEVE(d, l, "COREDUMP_COMM", comm);
547 RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline);
548 RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
549 RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated);
550 RETRIEVE(d, l, "COREDUMP", coredump);
551 }
552
553 if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename)
554 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Empty coredump log entry");
555
556 (void) parse_uid(uid, &uid_as_int);
557 (void) parse_gid(gid, &gid_as_int);
558 (void) parse_pid(pid, &pid_as_int);
559 signal_as_int = signal_from_string(sgnl);
560
561 r = sd_journal_get_realtime_usec(j, &ts);
562 if (r < 0)
563 return log_error_errno(r, "Failed to get realtime timestamp: %m");
564
565 normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR);
566
567 if (filename) {
568 r = resolve_filename(arg_root, &filename);
569 if (r < 0)
570 return r;
571
572 analyze_coredump_file(filename, &present, &color, &size);
573 } else if (coredump)
574 present = "journal";
575 else if (normal_coredump) {
576 present = "none";
577 color = ansi_grey();
578 } else
579 present = NULL;
580
581 if (STRPTR_IN_SET(present, "present", "journal") && truncated && parse_boolean(truncated) > 0)
582 present = "truncated";
583
584 r = table_add_many(
585 t,
586 TABLE_TIMESTAMP, ts,
587 TABLE_PID, pid_as_int,
588 TABLE_UID, uid_as_int,
589 TABLE_GID, gid_as_int,
590 TABLE_SIGNAL, normal_coredump ? signal_as_int : 0,
591 TABLE_STRING, present,
592 TABLE_SET_COLOR, color,
593 TABLE_STRING, exe ?: comm ?: cmdline,
594 TABLE_SIZE, size);
595 if (r < 0)
596 return table_log_add_error(r);
597
598 return 0;
599 }
600
601 static int print_info(FILE *file, sd_journal *j, bool need_space) {
602 _cleanup_free_ char
603 *mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL,
604 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
605 *unit = NULL, *user_unit = NULL, *session = NULL,
606 *boot_id = NULL, *machine_id = NULL, *hostname = NULL,
607 *slice = NULL, *cgroup = NULL, *owner_uid = NULL,
608 *message = NULL, *timestamp = NULL, *filename = NULL,
609 *truncated = NULL, *coredump = NULL,
610 *pkgmeta_name = NULL, *pkgmeta_version = NULL, *pkgmeta_json = NULL;
611 const void *d;
612 size_t l;
613 bool normal_coredump;
614 int r;
615
616 assert(file);
617 assert(j);
618
619 (void) sd_journal_set_data_threshold(j, 0);
620
621 SD_JOURNAL_FOREACH_DATA(j, d, l) {
622 RETRIEVE(d, l, "MESSAGE_ID", mid);
623 RETRIEVE(d, l, "COREDUMP_PID", pid);
624 RETRIEVE(d, l, "COREDUMP_UID", uid);
625 RETRIEVE(d, l, "COREDUMP_GID", gid);
626 RETRIEVE(d, l, "COREDUMP_SIGNAL", sgnl);
627 RETRIEVE(d, l, "COREDUMP_EXE", exe);
628 RETRIEVE(d, l, "COREDUMP_COMM", comm);
629 RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline);
630 RETRIEVE(d, l, "COREDUMP_HOSTNAME", hostname);
631 RETRIEVE(d, l, "COREDUMP_UNIT", unit);
632 RETRIEVE(d, l, "COREDUMP_USER_UNIT", user_unit);
633 RETRIEVE(d, l, "COREDUMP_SESSION", session);
634 RETRIEVE(d, l, "COREDUMP_OWNER_UID", owner_uid);
635 RETRIEVE(d, l, "COREDUMP_SLICE", slice);
636 RETRIEVE(d, l, "COREDUMP_CGROUP", cgroup);
637 RETRIEVE(d, l, "COREDUMP_TIMESTAMP", timestamp);
638 RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
639 RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated);
640 RETRIEVE(d, l, "COREDUMP", coredump);
641 RETRIEVE(d, l, "COREDUMP_PACKAGE_NAME", pkgmeta_name);
642 RETRIEVE(d, l, "COREDUMP_PACKAGE_VERSION", pkgmeta_version);
643 RETRIEVE(d, l, "COREDUMP_PACKAGE_JSON", pkgmeta_json);
644 RETRIEVE(d, l, "_BOOT_ID", boot_id);
645 RETRIEVE(d, l, "_MACHINE_ID", machine_id);
646 RETRIEVE(d, l, "MESSAGE", message);
647 }
648
649 if (need_space)
650 fputs("\n", file);
651
652 normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR);
653
654 if (comm)
655 fprintf(file,
656 " PID: %s%s%s (%s)\n",
657 ansi_highlight(), strna(pid), ansi_normal(), comm);
658 else
659 fprintf(file,
660 " PID: %s%s%s\n",
661 ansi_highlight(), strna(pid), ansi_normal());
662
663 if (uid) {
664 uid_t n;
665
666 if (parse_uid(uid, &n) >= 0) {
667 _cleanup_free_ char *u = NULL;
668
669 u = uid_to_name(n);
670 fprintf(file,
671 " UID: %s (%s)\n",
672 uid, u);
673 } else {
674 fprintf(file,
675 " UID: %s\n",
676 uid);
677 }
678 }
679
680 if (gid) {
681 gid_t n;
682
683 if (parse_gid(gid, &n) >= 0) {
684 _cleanup_free_ char *g = NULL;
685
686 g = gid_to_name(n);
687 fprintf(file,
688 " GID: %s (%s)\n",
689 gid, g);
690 } else {
691 fprintf(file,
692 " GID: %s\n",
693 gid);
694 }
695 }
696
697 if (sgnl) {
698 int sig;
699 const char *name = normal_coredump ? "Signal" : "Reason";
700
701 if (normal_coredump && safe_atoi(sgnl, &sig) >= 0)
702 fprintf(file, " %s: %s (%s)\n", name, sgnl, signal_to_string(sig));
703 else
704 fprintf(file, " %s: %s\n", name, sgnl);
705 }
706
707 if (timestamp) {
708 usec_t u;
709
710 r = safe_atou64(timestamp, &u);
711 if (r >= 0)
712 fprintf(file, " Timestamp: %s (%s)\n",
713 FORMAT_TIMESTAMP(u), FORMAT_TIMESTAMP_RELATIVE(u));
714
715 else
716 fprintf(file, " Timestamp: %s\n", timestamp);
717 }
718
719 if (cmdline)
720 fprintf(file, " Command Line: %s\n", cmdline);
721 if (exe)
722 fprintf(file, " Executable: %s%s%s\n", ansi_highlight(), exe, ansi_normal());
723 if (cgroup)
724 fprintf(file, " Control Group: %s\n", cgroup);
725 if (unit)
726 fprintf(file, " Unit: %s\n", unit);
727 if (user_unit)
728 fprintf(file, " User Unit: %s\n", user_unit);
729 if (slice)
730 fprintf(file, " Slice: %s\n", slice);
731 if (session)
732 fprintf(file, " Session: %s\n", session);
733 if (owner_uid) {
734 uid_t n;
735
736 if (parse_uid(owner_uid, &n) >= 0) {
737 _cleanup_free_ char *u = NULL;
738
739 u = uid_to_name(n);
740 fprintf(file,
741 " Owner UID: %s (%s)\n",
742 owner_uid, u);
743 } else {
744 fprintf(file,
745 " Owner UID: %s\n",
746 owner_uid);
747 }
748 }
749 if (boot_id)
750 fprintf(file, " Boot ID: %s\n", boot_id);
751 if (machine_id)
752 fprintf(file, " Machine ID: %s\n", machine_id);
753 if (hostname)
754 fprintf(file, " Hostname: %s\n", hostname);
755
756 if (filename) {
757 r = resolve_filename(arg_root, &filename);
758 if (r < 0)
759 return r;
760
761 const char *state = NULL, *color = NULL;
762 uint64_t size = UINT64_MAX;
763
764 analyze_coredump_file(filename, &state, &color, &size);
765
766 if (STRPTR_IN_SET(state, "present", "journal") && truncated && parse_boolean(truncated) > 0)
767 state = "truncated";
768
769 fprintf(file,
770 " Storage: %s%s (%s)%s\n",
771 strempty(color),
772 filename,
773 state,
774 ansi_normal());
775
776 if (size != UINT64_MAX)
777 fprintf(file, " Size on Disk: %s\n", FORMAT_BYTES(size));
778
779 } else if (coredump)
780 fprintf(file, " Storage: journal\n");
781 else
782 fprintf(file, " Storage: none\n");
783
784 if (pkgmeta_name && pkgmeta_version)
785 fprintf(file, " Package: %s/%s\n", pkgmeta_name, pkgmeta_version);
786
787 /* Print out the build-id of the 'main' ELF module, by matching the JSON key
788 * with the 'exe' field. */
789 if (exe && pkgmeta_json) {
790 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
791
792 r = json_parse(pkgmeta_json, 0, &v, NULL, NULL);
793 if (r < 0)
794 log_warning_errno(r, "json_parse on %s failed, ignoring: %m", pkgmeta_json);
795 else {
796 const char *module_name;
797 JsonVariant *module_json;
798
799 JSON_VARIANT_OBJECT_FOREACH(module_name, module_json, v) {
800 JsonVariant *build_id;
801
802 /* We only print the build-id for the 'main' ELF module */
803 if (!path_equal_filename(module_name, exe))
804 continue;
805
806 build_id = json_variant_by_key(module_json, "buildId");
807 if (build_id)
808 fprintf(file, " build-id: %s\n", json_variant_string(build_id));
809
810 break;
811 }
812 }
813 }
814
815 if (message) {
816 _cleanup_free_ char *m = NULL;
817
818 m = strreplace(message, "\n", "\n ");
819
820 fprintf(file, " Message: %s\n", strstrip(m ?: message));
821 }
822
823 return 0;
824 }
825
826 static int focus(sd_journal *j) {
827 int r;
828
829 r = sd_journal_seek_tail(j);
830 if (r == 0)
831 r = sd_journal_previous(j);
832 if (r < 0)
833 return log_error_errno(r, "Failed to search journal: %m");
834 if (r == 0)
835 return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
836 "No match found.");
837 return r;
838 }
839
840 static int print_entry(
841 sd_journal *j,
842 size_t n_found,
843 Table *t) {
844
845 assert(j);
846
847 if (t)
848 return print_list(stdout, j, t);
849 else if (arg_field)
850 return print_field(stdout, j);
851 else
852 return print_info(stdout, j, n_found > 0);
853 }
854
855 static int dump_list(int argc, char **argv, void *userdata) {
856 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
857 _cleanup_(table_unrefp) Table *t = NULL;
858 size_t n_found = 0;
859 bool verb_is_info;
860 int r;
861
862 verb_is_info = argc >= 1 && streq(argv[0], "info");
863
864 r = acquire_journal(&j, argv + 1);
865 if (r < 0)
866 return r;
867
868 /* The coredumps are likely compressed, and for just listing them we don't need to decompress them,
869 * so let's pick a fairly low data threshold here */
870 (void) sd_journal_set_data_threshold(j, 4096);
871
872 if (!verb_is_info && !arg_field) {
873 t = table_new("time", "pid", "uid", "gid", "sig", "corefile", "exe", "size");
874 if (!t)
875 return log_oom();
876
877 (void) table_set_align_percent(t, TABLE_HEADER_CELL(1), 100);
878 (void) table_set_align_percent(t, TABLE_HEADER_CELL(2), 100);
879 (void) table_set_align_percent(t, TABLE_HEADER_CELL(3), 100);
880 (void) table_set_align_percent(t, TABLE_HEADER_CELL(7), 100);
881
882 table_set_ersatz_string(t, TABLE_ERSATZ_DASH);
883 } else
884 pager_open(arg_pager_flags);
885
886 /* "info" without pattern implies "-1" */
887 if ((arg_rows_max == 1 && arg_reverse) || (verb_is_info && argc == 1)) {
888 r = focus(j);
889 if (r < 0)
890 return r;
891
892 r = print_entry(j, 0, t);
893 if (r < 0)
894 return r;
895 } else {
896 if (arg_since != USEC_INFINITY && !arg_reverse)
897 r = sd_journal_seek_realtime_usec(j, arg_since);
898 else if (arg_until != USEC_INFINITY && arg_reverse)
899 r = sd_journal_seek_realtime_usec(j, arg_until);
900 else if (arg_reverse)
901 r = sd_journal_seek_tail(j);
902 else
903 r = sd_journal_seek_head(j);
904 if (r < 0)
905 return log_error_errno(r, "Failed to seek to date: %m");
906
907 for (;;) {
908 if (!arg_reverse)
909 r = sd_journal_next(j);
910 else
911 r = sd_journal_previous(j);
912 if (r < 0)
913 return log_error_errno(r, "Failed to iterate through journal: %m");
914 if (r == 0)
915 break;
916
917 if (arg_until != USEC_INFINITY && !arg_reverse) {
918 usec_t usec;
919
920 r = sd_journal_get_realtime_usec(j, &usec);
921 if (r < 0)
922 return log_error_errno(r, "Failed to determine timestamp: %m");
923 if (usec > arg_until)
924 continue;
925 }
926
927 if (arg_since != USEC_INFINITY && arg_reverse) {
928 usec_t usec;
929
930 r = sd_journal_get_realtime_usec(j, &usec);
931 if (r < 0)
932 return log_error_errno(r, "Failed to determine timestamp: %m");
933 if (usec < arg_since)
934 continue;
935 }
936
937 r = print_entry(j, n_found++, t);
938 if (r < 0)
939 return r;
940
941 if (arg_rows_max != SIZE_MAX && n_found >= arg_rows_max)
942 break;
943 }
944
945 if (!arg_field && n_found <= 0) {
946 if (!arg_quiet)
947 log_notice("No coredumps found.");
948 return -ESRCH;
949 }
950 }
951
952 if (t) {
953 r = table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
954 if (r < 0)
955 return r;
956 }
957
958 return 0;
959 }
960
961 static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) {
962 const char *data;
963 _cleanup_free_ char *filename = NULL;
964 size_t len;
965 int r, fd;
966 _cleanup_close_ int fdt = -1;
967 char *temp = NULL;
968
969 assert(!(file && path)); /* At most one can be specified */
970 assert(!!path == !!unlink_temp); /* Those must be specified together */
971
972 /* Look for a coredump on disk first. */
973 r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len);
974 if (r == 0) {
975 _cleanup_free_ char *resolved = NULL;
976
977 r = retrieve(data, len, "COREDUMP_FILENAME", &filename);
978 if (r < 0)
979 return r;
980 assert(r > 0);
981
982 r = chase_symlinks_and_access(filename, arg_root, CHASE_PREFIX_ROOT, F_OK, &resolved, NULL);
983 if (r < 0)
984 return log_error_errno(r, "Cannot access \"%s%s\": %m", strempty(arg_root), filename);
985
986 free_and_replace(filename, resolved);
987
988 if (path && !ENDSWITH_SET(filename, ".xz", ".lz4", ".zst")) {
989 *path = TAKE_PTR(filename);
990
991 return 0;
992 }
993
994 } else {
995 if (r != -ENOENT)
996 return log_error_errno(r, "Failed to retrieve COREDUMP_FILENAME field: %m");
997 /* Check that we can have a COREDUMP field. We still haven't set a high
998 * data threshold, so we'll get a few kilobytes at most.
999 */
1000
1001 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
1002 if (r == -ENOENT)
1003 return log_error_errno(r, "Coredump entry has no core attached (neither internally in the journal nor externally on disk).");
1004 if (r < 0)
1005 return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
1006 }
1007
1008 if (path) {
1009 const char *vt;
1010
1011 /* Create a temporary file to write the uncompressed core to. */
1012
1013 r = var_tmp_dir(&vt);
1014 if (r < 0)
1015 return log_error_errno(r, "Failed to acquire temporary directory path: %m");
1016
1017 temp = path_join(vt, "coredump-XXXXXX");
1018 if (!temp)
1019 return log_oom();
1020
1021 fdt = mkostemp_safe(temp);
1022 if (fdt < 0)
1023 return log_error_errno(fdt, "Failed to create temporary file: %m");
1024 log_debug("Created temporary file %s", temp);
1025
1026 fd = fdt;
1027 } else {
1028 /* If neither path or file are specified, we will write to stdout. Let's now check
1029 * if stdout is connected to a tty. We checked that the file exists, or that the
1030 * core might be stored in the journal. In this second case, if we found the entry,
1031 * in all likelihood we will be able to access the COREDUMP= field. In either case,
1032 * we stop before doing any "real" work, i.e. before starting decompression or
1033 * reading from the file or creating temporary files.
1034 */
1035 if (!file) {
1036 if (on_tty())
1037 return log_error_errno(SYNTHETIC_ERRNO(ENOTTY),
1038 "Refusing to dump core to tty"
1039 " (use shell redirection or specify --output).");
1040 file = stdout;
1041 }
1042
1043 fd = fileno(file);
1044 }
1045
1046 if (filename) {
1047 #if HAVE_COMPRESSION
1048 _cleanup_close_ int fdf = -1;
1049
1050 fdf = open(filename, O_RDONLY | O_CLOEXEC);
1051 if (fdf < 0) {
1052 r = log_error_errno(errno, "Failed to open %s: %m", filename);
1053 goto error;
1054 }
1055
1056 r = decompress_stream(filename, fdf, fd, -1);
1057 if (r < 0) {
1058 log_error_errno(r, "Failed to decompress %s: %m", filename);
1059 goto error;
1060 }
1061 #else
1062 r = log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1063 "Cannot decompress file. Compiled without compression support.");
1064 goto error;
1065 #endif
1066 } else {
1067 ssize_t sz;
1068
1069 /* We want full data, nothing truncated. */
1070 sd_journal_set_data_threshold(j, 0);
1071
1072 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
1073 if (r < 0)
1074 return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
1075
1076 assert(len >= 9);
1077 data += 9;
1078 len -= 9;
1079
1080 sz = write(fd, data, len);
1081 if (sz < 0) {
1082 r = log_error_errno(errno, "Failed to write output: %m");
1083 goto error;
1084 }
1085 if (sz != (ssize_t) len) {
1086 log_error("Short write to output.");
1087 r = -EIO;
1088 goto error;
1089 }
1090 }
1091
1092 if (temp) {
1093 *path = temp;
1094 *unlink_temp = true;
1095 }
1096 return 0;
1097
1098 error:
1099 if (temp) {
1100 (void) unlink(temp);
1101 log_debug("Removed temporary file %s", temp);
1102 }
1103 return r;
1104 }
1105
1106 static int dump_core(int argc, char **argv, void *userdata) {
1107 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
1108 _cleanup_fclose_ FILE *f = NULL;
1109 int r;
1110
1111 if (arg_field)
1112 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1113 "Option --field/-F only makes sense with list");
1114
1115 r = acquire_journal(&j, argv + 1);
1116 if (r < 0)
1117 return r;
1118
1119 r = focus(j);
1120 if (r < 0)
1121 return r;
1122
1123 if (arg_output) {
1124 f = fopen(arg_output, "we");
1125 if (!f)
1126 return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", arg_output);
1127 }
1128
1129 print_info(f ? stdout : stderr, j, false);
1130
1131 r = save_core(j, f, NULL, NULL);
1132 if (r < 0)
1133 return r;
1134
1135 r = sd_journal_previous(j);
1136 if (r > 0 && !arg_quiet)
1137 log_notice("More than one entry matches, ignoring rest.");
1138
1139 return 0;
1140 }
1141
1142 static int run_debug(int argc, char **argv, void *userdata) {
1143 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
1144 _cleanup_free_ char *exe = NULL, *path = NULL;
1145 _cleanup_strv_free_ char **debugger_call = NULL;
1146 bool unlink_path = false;
1147 const char *data, *fork_name;
1148 size_t len;
1149 pid_t pid;
1150 int r;
1151
1152 if (!arg_debugger) {
1153 char *env_debugger;
1154
1155 env_debugger = getenv("SYSTEMD_DEBUGGER");
1156 if (env_debugger)
1157 arg_debugger = env_debugger;
1158 else
1159 arg_debugger = "gdb";
1160 }
1161
1162 r = strv_extend(&debugger_call, arg_debugger);
1163 if (r < 0)
1164 return log_oom();
1165
1166 r = strv_extend_strv(&debugger_call, arg_debugger_args, false);
1167 if (r < 0)
1168 return log_oom();
1169
1170 if (arg_field)
1171 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1172 "Option --field/-F only makes sense with list");
1173
1174 r = acquire_journal(&j, argv + 1);
1175 if (r < 0)
1176 return r;
1177
1178 r = focus(j);
1179 if (r < 0)
1180 return r;
1181
1182 if (!arg_quiet) {
1183 print_info(stdout, j, false);
1184 fputs("\n", stdout);
1185 }
1186
1187 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
1188 if (r < 0)
1189 return log_error_errno(r, "Failed to retrieve COREDUMP_EXE field: %m");
1190
1191 assert(len > STRLEN("COREDUMP_EXE="));
1192 data += STRLEN("COREDUMP_EXE=");
1193 len -= STRLEN("COREDUMP_EXE=");
1194
1195 exe = strndup(data, len);
1196 if (!exe)
1197 return log_oom();
1198
1199 if (endswith(exe, " (deleted)"))
1200 return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
1201 "Binary already deleted.");
1202
1203 if (!path_is_absolute(exe))
1204 return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
1205 "Binary is not an absolute path.");
1206
1207 r = resolve_filename(arg_root, &exe);
1208 if (r < 0)
1209 return r;
1210
1211 r = save_core(j, NULL, &path, &unlink_path);
1212 if (r < 0)
1213 return r;
1214
1215 r = strv_extend_strv(&debugger_call, STRV_MAKE(exe, "-c", path), false);
1216 if (r < 0)
1217 return log_oom();
1218
1219 if (arg_root) {
1220 if (streq(arg_debugger, "gdb")) {
1221 const char *sysroot_cmd;
1222 sysroot_cmd = strjoina("set sysroot ", arg_root);
1223
1224 r = strv_extend_strv(&debugger_call, STRV_MAKE("-iex", sysroot_cmd), false);
1225 if (r < 0)
1226 return log_oom();
1227 } else if (streq(arg_debugger, "lldb")) {
1228 const char *sysroot_cmd;
1229 sysroot_cmd = strjoina("platform select --sysroot ", arg_root, " host");
1230
1231 r = strv_extend_strv(&debugger_call, STRV_MAKE("-O", sysroot_cmd), false);
1232 if (r < 0)
1233 return log_oom();
1234 }
1235 }
1236
1237 /* Don't interfere with gdb and its handling of SIGINT. */
1238 (void) ignore_signals(SIGINT);
1239
1240 fork_name = strjoina("(", debugger_call[0], ")");
1241
1242 r = safe_fork(fork_name, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG|FORK_FLUSH_STDIO, &pid);
1243 if (r < 0)
1244 goto finish;
1245 if (r == 0) {
1246 execvp(debugger_call[0], debugger_call);
1247 log_open();
1248 log_error_errno(errno, "Failed to invoke %s: %m", debugger_call[0]);
1249 _exit(EXIT_FAILURE);
1250 }
1251
1252 r = wait_for_terminate_and_check(debugger_call[0], pid, WAIT_LOG_ABNORMAL);
1253
1254 finish:
1255 (void) default_signals(SIGINT);
1256
1257 if (unlink_path) {
1258 log_debug("Removed temporary file %s", path);
1259 (void) unlink(path);
1260 }
1261
1262 return r;
1263 }
1264
1265 static int check_units_active(void) {
1266 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1267 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1268 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1269 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1270 int c = 0, r;
1271 const char *id, *state, *substate;
1272
1273 if (arg_quiet)
1274 return false;
1275
1276 r = sd_bus_default_system(&bus);
1277 if (r == -ENOENT) {
1278 log_debug("D-Bus is not running, skipping active unit check");
1279 return 0;
1280 }
1281 if (r < 0)
1282 return log_error_errno(r, "Failed to acquire bus: %m");
1283
1284 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitsByPatterns");
1285 if (r < 0)
1286 return bus_log_create_error(r);
1287
1288 r = sd_bus_message_append_strv(m, NULL);
1289 if (r < 0)
1290 return bus_log_create_error(r);
1291
1292 r = sd_bus_message_append_strv(m, STRV_MAKE("systemd-coredump@*.service"));
1293 if (r < 0)
1294 return bus_log_create_error(r);
1295
1296 r = sd_bus_call(bus, m, SHORT_BUS_CALL_TIMEOUT_USEC, &error, &reply);
1297 if (r < 0)
1298 return log_error_errno(r, "Failed to check if any systemd-coredump@.service units are running: %s",
1299 bus_error_message(&error, r));
1300
1301 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1302 if (r < 0)
1303 return bus_log_parse_error(r);
1304
1305 while ((r = sd_bus_message_read(
1306 reply, "(ssssssouso)",
1307 &id, NULL, NULL, &state, &substate,
1308 NULL, NULL, NULL, NULL, NULL)) > 0) {
1309 bool found = !STR_IN_SET(state, "inactive", "dead", "failed");
1310 log_debug("Unit %s is %s/%s, %scounting it.", id, state, substate, found ? "" : "not ");
1311 c += found;
1312 }
1313 if (r < 0)
1314 return bus_log_parse_error(r);
1315
1316 r = sd_bus_message_exit_container(reply);
1317 if (r < 0)
1318 return bus_log_parse_error(r);
1319
1320 return c;
1321 }
1322
1323 static int coredumpctl_main(int argc, char *argv[]) {
1324
1325 static const Verb verbs[] = {
1326 { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, dump_list },
1327 { "info", VERB_ANY, VERB_ANY, 0, dump_list },
1328 { "dump", VERB_ANY, VERB_ANY, 0, dump_core },
1329 { "debug", VERB_ANY, VERB_ANY, 0, run_debug },
1330 { "gdb", VERB_ANY, VERB_ANY, 0, run_debug },
1331 { "help", VERB_ANY, 1, 0, verb_help },
1332 {}
1333 };
1334
1335 return dispatch_verb(argc, argv, verbs, NULL);
1336 }
1337
1338 static int run(int argc, char *argv[]) {
1339 _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
1340 _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
1341 int r, units_active;
1342
1343 setlocale(LC_ALL, "");
1344 log_setup();
1345
1346 /* The journal merging logic potentially needs a lot of fds. */
1347 (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
1348
1349 r = parse_argv(argc, argv);
1350 if (r <= 0)
1351 return r;
1352
1353 sigbus_install();
1354
1355 units_active = check_units_active(); /* error is treated the same as 0 */
1356
1357 if (arg_image) {
1358 assert(!arg_root);
1359
1360 r = mount_image_privately_interactively(
1361 arg_image,
1362 DISSECT_IMAGE_GENERIC_ROOT |
1363 DISSECT_IMAGE_REQUIRE_ROOT |
1364 DISSECT_IMAGE_RELAX_VAR_CHECK |
1365 DISSECT_IMAGE_VALIDATE_OS,
1366 &mounted_dir,
1367 &loop_device);
1368 if (r < 0)
1369 return r;
1370
1371 arg_root = strdup(mounted_dir);
1372 if (!arg_root)
1373 return log_oom();
1374 }
1375
1376 r = coredumpctl_main(argc, argv);
1377
1378 if (units_active > 0)
1379 printf("%s-- Notice: %d systemd-coredump@.service %s, output may be incomplete.%s\n",
1380 ansi_highlight_red(),
1381 units_active, units_active == 1 ? "unit is running" : "units are running",
1382 ansi_normal());
1383
1384 return r;
1385 }
1386
1387 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);