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