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