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