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