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