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