]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/coredumpctl.c
treewide: use log_*_errno whenever %m is in the format string
[thirdparty/systemd.git] / src / journal / coredumpctl.c
CommitLineData
5de0409e
ZJS
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Zbigniew Jędrzejewski-Szmek
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
a9cdc94f 22#include <locale.h>
5de0409e
ZJS
23#include <stdio.h>
24#include <string.h>
25#include <getopt.h>
ada45c78
LP
26#include <fcntl.h>
27#include <unistd.h>
5de0409e 28
73f860db 29#include "systemd/sd-journal.h"
5de0409e
ZJS
30
31#include "build.h"
32#include "set.h"
33#include "util.h"
34#include "log.h"
35#include "path-util.h"
36#include "pager.h"
763c7aa2 37#include "macro.h"
dfb33a97 38#include "journal-internal.h"
a276ae74 39#include "copy.h"
9fe13294 40#include "compress.h"
5de0409e
ZJS
41
42static enum {
43 ACTION_NONE,
e15758cc 44 ACTION_INFO,
5de0409e
ZJS
45 ACTION_LIST,
46 ACTION_DUMP,
ada45c78 47 ACTION_GDB,
5de0409e 48} arg_action = ACTION_LIST;
e15758cc 49static const char* arg_field = NULL;
ccc40358 50static int arg_no_pager = false;
9a340880 51static int arg_no_legend = false;
0c51aada
LP
52static int arg_one = false;
53
54static FILE* output = NULL;
5de0409e
ZJS
55
56static Set *new_matches(void) {
57 Set *set;
58 char *tmp;
59 int r;
60
d5099efc 61 set = set_new(NULL);
5de0409e
ZJS
62 if (!set) {
63 log_oom();
64 return NULL;
65 }
66
67 tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
68 if (!tmp) {
69 log_oom();
4a207bb2 70 set_free(set);
5de0409e
ZJS
71 return NULL;
72 }
73
ef42202a 74 r = set_consume(set, tmp);
5de0409e 75 if (r < 0) {
da927ba9 76 log_error_errno(r, "failed to add to set: %m");
4a207bb2 77 set_free(set);
5de0409e
ZJS
78 return NULL;
79 }
80
81 return set;
82}
83
5de0409e
ZJS
84static int add_match(Set *set, const char *match) {
85 int r = -ENOMEM;
86 unsigned pid;
87 const char* prefix;
88 char *pattern = NULL;
7fd1b19b 89 _cleanup_free_ char *p = NULL;
5de0409e
ZJS
90
91 if (strchr(match, '='))
92 prefix = "";
93 else if (strchr(match, '/')) {
94 p = path_make_absolute_cwd(match);
95 if (!p)
96 goto fail;
97
98 match = p;
99 prefix = "COREDUMP_EXE=";
100 }
101 else if (safe_atou(match, &pid) == 0)
102 prefix = "COREDUMP_PID=";
103 else
104 prefix = "COREDUMP_COMM=";
105
106 pattern = strjoin(prefix, match, NULL);
107 if (!pattern)
108 goto fail;
109
ef42202a 110 log_debug("Adding pattern: %s", pattern);
f7a5bb28 111 r = set_consume(set, pattern);
5de0409e 112 if (r < 0) {
da927ba9 113 log_error_errno(r, "Failed to add pattern: %m");
5de0409e
ZJS
114 goto fail;
115 }
5de0409e
ZJS
116
117 return 0;
118fail:
23bbb0de 119 return log_error_errno(r, "Failed to add match: %m");
5de0409e
ZJS
120}
121
601185b4
ZJS
122static void help(void) {
123 printf("%s [OPTIONS...]\n\n"
124 "List or retrieve coredumps from the journal.\n\n"
125 "Flags:\n"
126 " -h --help Show this help\n"
127 " --version Print version string\n"
128 " --no-pager Do not pipe output into a pager\n"
129 " --no-legend Do not print the column headers.\n"
130 " -1 Show information about most recent entry only\n"
131 " -F --field=FIELD List all values a certain field takes\n"
132 " -o --output=FILE Write output to FILE\n\n"
133
134 "Commands:\n"
135 " list [MATCHES...] List available coredumps (default)\n"
136 " info [MATCHES...] Show detailed information about one or more coredumps\n"
137 " dump [MATCHES...] Print first matching coredump to stdout\n"
138 " gdb [MATCHES...] Start gdb for the first matching coredump\n"
139 , program_invocation_short_name);
140}
141
763c7aa2 142static int parse_argv(int argc, char *argv[], Set *matches) {
5de0409e
ZJS
143 enum {
144 ARG_VERSION = 0x100,
145 ARG_NO_PAGER,
9a340880 146 ARG_NO_LEGEND,
5de0409e
ZJS
147 };
148
149 int r, c;
150
151 static const struct option options[] = {
57ce4bd4
ZJS
152 { "help", no_argument, NULL, 'h' },
153 { "version" , no_argument, NULL, ARG_VERSION },
154 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
9a340880 155 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
57ce4bd4 156 { "output", required_argument, NULL, 'o' },
4f76ae1b 157 { "field", required_argument, NULL, 'F' },
eb9da376 158 {}
5de0409e
ZJS
159 };
160
161 assert(argc >= 0);
162 assert(argv);
163
0c51aada 164 while ((c = getopt_long(argc, argv, "ho:F:1", options, NULL)) >= 0)
5de0409e 165 switch(c) {
eb9da376 166
5de0409e 167 case 'h':
5de0409e 168 arg_action = ACTION_NONE;
601185b4
ZJS
169 help();
170 return 0;
5de0409e
ZJS
171
172 case ARG_VERSION:
eb9da376 173 arg_action = ACTION_NONE;
5de0409e 174 puts(PACKAGE_STRING);
5de0409e 175 puts(SYSTEMD_FEATURES);
5de0409e
ZJS
176 return 0;
177
178 case ARG_NO_PAGER:
179 arg_no_pager = true;
180 break;
181
9a340880
ZJS
182 case ARG_NO_LEGEND:
183 arg_no_legend = true;
184 break;
185
5de0409e
ZJS
186 case 'o':
187 if (output) {
188 log_error("cannot set output more than once");
189 return -EINVAL;
190 }
191
192 output = fopen(optarg, "we");
193 if (!output) {
56f64d95 194 log_error_errno(errno, "writing to '%s': %m", optarg);
5de0409e
ZJS
195 return -errno;
196 }
197
198 break;
57ce4bd4 199
4f76ae1b 200 case 'F':
a276ae74 201 if (arg_field) {
4f76ae1b
ZJS
202 log_error("cannot use --field/-F more than once");
203 return -EINVAL;
204 }
a276ae74 205 arg_field = optarg;
4f76ae1b
ZJS
206 break;
207
0c51aada
LP
208 case '1':
209 arg_one = true;
210 break;
211
57ce4bd4
ZJS
212 case '?':
213 return -EINVAL;
214
5de0409e 215 default:
eb9da376 216 assert_not_reached("Unhandled option");
5de0409e
ZJS
217 }
218
219 if (optind < argc) {
220 const char *cmd = argv[optind++];
f168c273 221 if (streq(cmd, "list"))
5de0409e
ZJS
222 arg_action = ACTION_LIST;
223 else if (streq(cmd, "dump"))
224 arg_action = ACTION_DUMP;
ada45c78
LP
225 else if (streq(cmd, "gdb"))
226 arg_action = ACTION_GDB;
e15758cc
LP
227 else if (streq(cmd, "info"))
228 arg_action = ACTION_INFO;
5de0409e
ZJS
229 else {
230 log_error("Unknown action '%s'", cmd);
231 return -EINVAL;
232 }
233 }
234
a276ae74 235 if (arg_field && arg_action != ACTION_LIST) {
4f76ae1b
ZJS
236 log_error("Option --field/-F only makes sense with list");
237 return -EINVAL;
238 }
239
5de0409e
ZJS
240 while (optind < argc) {
241 r = add_match(matches, argv[optind]);
242 if (r != 0)
243 return r;
244 optind++;
245 }
246
247 return 0;
248}
249
8bc8ab83
LP
250static int retrieve(const void *data,
251 size_t len,
252 const char *name,
a276ae74 253 char **var) {
5de0409e 254
4f76ae1b 255 size_t ident;
a276ae74 256 char *v;
8bc8ab83 257
4f76ae1b 258 ident = strlen(name) + 1; /* name + "=" */
8bc8ab83 259
4f76ae1b 260 if (len < ident)
8bc8ab83 261 return 0;
5de0409e 262
4f76ae1b 263 if (memcmp(data, name, ident - 1) != 0)
8bc8ab83
LP
264 return 0;
265
4f76ae1b 266 if (((const char*) data)[ident - 1] != '=')
8bc8ab83 267 return 0;
5de0409e 268
a276ae74
LP
269 v = strndup((const char*)data + ident, len - ident);
270 if (!v)
5de0409e
ZJS
271 return log_oom();
272
a276ae74
LP
273 free(*var);
274 *var = v;
275
5de0409e
ZJS
276 return 0;
277}
278
4f76ae1b 279static void print_field(FILE* file, sd_journal *j) {
a276ae74 280 _cleanup_free_ char *value = NULL;
4f76ae1b
ZJS
281 const void *d;
282 size_t l;
283
e15758cc
LP
284 assert(file);
285 assert(j);
286
a276ae74 287 assert(arg_field);
4f76ae1b
ZJS
288
289 SD_JOURNAL_FOREACH_DATA(j, d, l)
a276ae74
LP
290 retrieve(d, l, arg_field, &value);
291
4f76ae1b
ZJS
292 if (value)
293 fprintf(file, "%s\n", value);
294}
295
e15758cc 296static int print_list(FILE* file, sd_journal *j, int had_legend) {
a276ae74 297 _cleanup_free_ char
5de0409e 298 *pid = NULL, *uid = NULL, *gid = NULL,
9fe13294
ZJS
299 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
300 *filename = NULL;
8bc8ab83
LP
301 const void *d;
302 size_t l;
684341b0
LP
303 usec_t t;
304 char buf[FORMAT_TIMESTAMP_MAX];
305 int r;
9fe13294 306 bool present;
8bc8ab83 307
e15758cc
LP
308 assert(file);
309 assert(j);
310
8bc8ab83 311 SD_JOURNAL_FOREACH_DATA(j, d, l) {
8bc8ab83
LP
312 retrieve(d, l, "COREDUMP_PID", &pid);
313 retrieve(d, l, "COREDUMP_UID", &uid);
314 retrieve(d, l, "COREDUMP_GID", &gid);
315 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
316 retrieve(d, l, "COREDUMP_EXE", &exe);
a276ae74
LP
317 retrieve(d, l, "COREDUMP_COMM", &comm);
318 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
9fe13294 319 retrieve(d, l, "COREDUMP_FILENAME", &filename);
8bc8ab83 320 }
5de0409e 321
9fe13294 322 if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename) {
684341b0
LP
323 log_warning("Empty coredump log entry");
324 return -EINVAL;
325 }
326
327 r = sd_journal_get_realtime_usec(j, &t);
23bbb0de
MS
328 if (r < 0)
329 return log_error_errno(r, "Failed to get realtime timestamp: %m");
5de0409e 330
684341b0 331 format_timestamp(buf, sizeof(buf), t);
9fe13294 332 present = filename && access(filename, F_OK) == 0;
684341b0 333
9a340880 334 if (!had_legend && !arg_no_legend)
9fe13294 335 fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
c3f84106 336 FORMAT_TIMESTAMP_WIDTH, "TIME",
5de0409e
ZJS
337 6, "PID",
338 5, "UID",
339 5, "GID",
684341b0 340 3, "SIG",
9fe13294 341 1, "PRESENT",
684341b0 342 "EXE");
5de0409e 343
9fe13294 344 fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
c3f84106 345 FORMAT_TIMESTAMP_WIDTH, buf,
a276ae74
LP
346 6, strna(pid),
347 5, strna(uid),
348 5, strna(gid),
349 3, strna(sgnl),
9fe13294 350 1, present ? "*" : "",
a276ae74 351 strna(exe ?: (comm ?: cmdline)));
684341b0
LP
352
353 return 0;
5de0409e
ZJS
354}
355
e15758cc
LP
356static int print_info(FILE *file, sd_journal *j, bool need_space) {
357 _cleanup_free_ char
358 *pid = NULL, *uid = NULL, *gid = NULL,
359 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
360 *unit = NULL, *user_unit = NULL, *session = NULL,
a035f819 361 *boot_id = NULL, *machine_id = NULL, *hostname = NULL,
9fe13294
ZJS
362 *slice = NULL, *cgroup = NULL, *owner_uid = NULL,
363 *message = NULL, *timestamp = NULL, *filename = NULL;
e15758cc
LP
364 const void *d;
365 size_t l;
4b8cbe9a 366 int r;
e15758cc
LP
367
368 assert(file);
369 assert(j);
370
371 SD_JOURNAL_FOREACH_DATA(j, d, l) {
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_UNIT", &unit);
380 retrieve(d, l, "COREDUMP_USER_UNIT", &user_unit);
381 retrieve(d, l, "COREDUMP_SESSION", &session);
a035f819
LP
382 retrieve(d, l, "COREDUMP_OWNER_UID", &owner_uid);
383 retrieve(d, l, "COREDUMP_SLICE", &slice);
384 retrieve(d, l, "COREDUMP_CGROUP", &cgroup);
4b8cbe9a 385 retrieve(d, l, "COREDUMP_TIMESTAMP", &timestamp);
9fe13294 386 retrieve(d, l, "COREDUMP_FILENAME", &filename);
e15758cc
LP
387 retrieve(d, l, "_BOOT_ID", &boot_id);
388 retrieve(d, l, "_MACHINE_ID", &machine_id);
389 retrieve(d, l, "_HOSTNAME", &hostname);
8d4e028f 390 retrieve(d, l, "MESSAGE", &message);
e15758cc
LP
391 }
392
393 if (need_space)
394 fputs("\n", file);
395
81cef14f
LP
396 if (comm)
397 fprintf(file,
398 " PID: %s%s%s (%s)\n",
399 ansi_highlight(), strna(pid), ansi_highlight_off(), comm);
400 else
401 fprintf(file,
402 " PID: %s%s%s\n",
403 ansi_highlight(), strna(pid), ansi_highlight_off());
a035f819
LP
404
405 if (uid) {
406 uid_t n;
407
408 if (parse_uid(uid, &n) >= 0) {
409 _cleanup_free_ char *u = NULL;
410
411 u = uid_to_name(n);
412 fprintf(file,
413 " UID: %s (%s)\n",
414 uid, u);
415 } else {
416 fprintf(file,
417 " UID: %s\n",
418 uid);
419 }
420 }
421
422 if (gid) {
423 gid_t n;
424
425 if (parse_gid(gid, &n) >= 0) {
426 _cleanup_free_ char *g = NULL;
427
428 g = gid_to_name(n);
429 fprintf(file,
430 " GID: %s (%s)\n",
431 gid, g);
432 } else {
433 fprintf(file,
434 " GID: %s\n",
435 gid);
436 }
437 }
e15758cc
LP
438
439 if (sgnl) {
440 int sig;
441
442 if (safe_atoi(sgnl, &sig) >= 0)
443 fprintf(file, " Signal: %s (%s)\n", sgnl, signal_to_string(sig));
444 else
445 fprintf(file, " Signal: %s\n", sgnl);
446 }
447
4b8cbe9a
LP
448 if (timestamp) {
449 usec_t u;
450
451 r = safe_atou64(timestamp, &u);
452 if (r >= 0) {
453 char absolute[FORMAT_TIMESTAMP_MAX], relative[FORMAT_TIMESPAN_MAX];
454
455 fprintf(file,
456 " Timestamp: %s (%s)\n",
457 format_timestamp(absolute, sizeof(absolute), u),
458 format_timestamp_relative(relative, sizeof(relative), u));
459
460 } else
461 fprintf(file, " Timestamp: %s\n", timestamp);
462 }
463
e15758cc
LP
464 if (cmdline)
465 fprintf(file, " Command Line: %s\n", cmdline);
81cef14f
LP
466 if (exe)
467 fprintf(file, " Executable: %s%s%s\n", ansi_highlight(), exe, ansi_highlight_off());
a035f819
LP
468 if (cgroup)
469 fprintf(file, " Control Group: %s\n", cgroup);
e15758cc
LP
470 if (unit)
471 fprintf(file, " Unit: %s\n", unit);
472 if (user_unit)
473 fprintf(file, " User Unit: %s\n", unit);
a035f819
LP
474 if (slice)
475 fprintf(file, " Slice: %s\n", slice);
e15758cc
LP
476 if (session)
477 fprintf(file, " Session: %s\n", session);
a035f819
LP
478 if (owner_uid) {
479 uid_t n;
480
481 if (parse_uid(owner_uid, &n) >= 0) {
482 _cleanup_free_ char *u = NULL;
483
484 u = uid_to_name(n);
485 fprintf(file,
486 " Owner UID: %s (%s)\n",
487 owner_uid, u);
488 } else {
489 fprintf(file,
490 " Owner UID: %s\n",
491 owner_uid);
492 }
493 }
e15758cc
LP
494 if (boot_id)
495 fprintf(file, " Boot ID: %s\n", boot_id);
496 if (machine_id)
497 fprintf(file, " Machine ID: %s\n", machine_id);
498 if (hostname)
499 fprintf(file, " Hostname: %s\n", hostname);
500
9fe13294
ZJS
501 if (filename && access(filename, F_OK) == 0)
502 fprintf(file, " Coredump: %s\n", filename);
e15758cc 503
8d4e028f
LP
504 if (message) {
505 _cleanup_free_ char *m = NULL;
506
507 m = strreplace(message, "\n", "\n ");
508
509 fprintf(file, " Message: %s\n", strstrip(m ?: message));
510 }
511
e15758cc
LP
512 return 0;
513}
514
ada45c78 515static int focus(sd_journal *j) {
5de0409e
ZJS
516 int r;
517
5de0409e
ZJS
518 r = sd_journal_seek_tail(j);
519 if (r == 0)
520 r = sd_journal_previous(j);
23bbb0de
MS
521 if (r < 0)
522 return log_error_errno(r, "Failed to search journal: %m");
8bc8ab83 523 if (r == 0) {
0c51aada 524 log_error("No match found.");
8bc8ab83
LP
525 return -ESRCH;
526 }
ada45c78
LP
527 return r;
528}
5de0409e 529
0c51aada
LP
530static void print_entry(sd_journal *j, unsigned n_found) {
531 assert(j);
532
533 if (arg_action == ACTION_INFO)
534 print_info(stdout, j, n_found);
535 else if (arg_field)
536 print_field(stdout, j);
537 else
538 print_list(stdout, j, n_found);
539}
540
541static int dump_list(sd_journal *j) {
542 unsigned n_found = 0;
543 int r;
544
545 assert(j);
546
547 /* The coredumps are likely to compressed, and for just
548 * listing them we don't need to decompress them, so let's
549 * pick a fairly low data threshold here */
550 sd_journal_set_data_threshold(j, 4096);
551
552 if (arg_one) {
553 r = focus(j);
554 if (r < 0)
555 return r;
556
557 print_entry(j, 0);
558 } else {
559 SD_JOURNAL_FOREACH(j)
560 print_entry(j, n_found++);
561
562 if (!arg_field && n_found <= 0) {
563 log_notice("No coredumps found.");
564 return -ESRCH;
565 }
566 }
567
568 return 0;
569}
570
9fe13294
ZJS
571static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) {
572 const char *data;
573 _cleanup_free_ char *filename = NULL;
574 size_t len;
ada45c78
LP
575 int r;
576
9fe13294
ZJS
577 assert((fd >= 0) != !!path);
578 assert(!!path == !!unlink_temp);
ada45c78 579
9fe13294
ZJS
580 /* Prefer uncompressed file to journal (probably cached) to
581 * compressed file (probably uncached). */
582 r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len);
583 if (r < 0 && r != -ENOENT)
da927ba9 584 log_warning_errno(r, "Failed to retrieve COREDUMP_FILENAME: %m");
9fe13294
ZJS
585 else if (r == 0)
586 retrieve(data, len, "COREDUMP_FILENAME", &filename);
93b73b06 587
9fe13294 588 if (filename && access(filename, R_OK) < 0) {
31cda3d1
ZJS
589 log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
590 "File %s is not readable: %m", filename);
9fe13294
ZJS
591 free(filename);
592 filename = NULL;
5de0409e
ZJS
593 }
594
d89c8fdf 595 if (filename && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) {
d0c8806d
TA
596 if (path) {
597 *path = filename;
598 filename = NULL;
599 }
a276ae74 600
9fe13294
ZJS
601 return 0;
602 } else {
603 _cleanup_close_ int fdt = -1;
604 char *temp = NULL;
a276ae74 605
a276ae74 606 if (fd < 0) {
9fe13294
ZJS
607 temp = strdup("/var/tmp/coredump-XXXXXX");
608 if (!temp)
609 return log_oom();
610
611 fdt = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
612 if (fdt < 0) {
56f64d95 613 log_error_errno(errno, "Failed to create temporary file: %m");
9fe13294
ZJS
614 return -errno;
615 }
616 log_debug("Created temporary file %s", temp);
617
618 fd = fdt;
619 }
620
621 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
622 if (r == 0) {
623 ssize_t sz;
624
625 assert(len >= 9);
626 data += 9;
627 len -= 9;
628
629 sz = write(fdt, data, len);
630 if (sz < 0) {
56f64d95 631 log_error_errno(errno, "Failed to write temporary file: %m");
9fe13294
ZJS
632 r = -errno;
633 goto error;
634 }
635 if (sz != (ssize_t) len) {
636 log_error("Short write to temporary file.");
637 r = -EIO;
638 goto error;
639 }
640 } else if (filename) {
d89c8fdf 641#if defined(HAVE_XZ) || defined(HAVE_LZ4)
9fe13294
ZJS
642 _cleanup_close_ int fdf;
643
644 fdf = open(filename, O_RDONLY | O_CLOEXEC);
645 if (fdf < 0) {
56f64d95 646 log_error_errno(errno, "Failed to open %s: %m", filename);
9fe13294
ZJS
647 r = -errno;
648 goto error;
649 }
650
d89c8fdf 651 r = decompress_stream(filename, fdf, fd, -1);
9fe13294 652 if (r < 0) {
da927ba9 653 log_error_errno(r, "Failed to decompress %s: %m", filename);
9fe13294
ZJS
654 goto error;
655 }
2fb8159f 656#else
d89c8fdf 657 log_error("Cannot decompress file. Compiled without compression support.");
2fb8159f
DM
658 r = -ENOTSUP;
659 goto error;
660#endif
9fe13294
ZJS
661 } else {
662 if (r == -ENOENT)
31cda3d1 663 log_error("Cannot retrieve coredump from journal nor disk.");
a276ae74 664 else
da927ba9 665 log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
9fe13294
ZJS
666 goto error;
667 }
a276ae74 668
9fe13294
ZJS
669 if (temp) {
670 *path = temp;
671 *unlink_temp = true;
a276ae74
LP
672 }
673
9fe13294
ZJS
674 return 0;
675
676error:
677 if (temp) {
678 unlink(temp);
679 log_debug("Removed temporary file %s", temp);
a276ae74 680 }
9fe13294
ZJS
681 return r;
682 }
683}
a276ae74 684
9fe13294
ZJS
685static int dump_core(sd_journal* j) {
686 int r;
687
688 assert(j);
689
690 r = focus(j);
691 if (r < 0)
ada45c78 692 return r;
ada45c78 693
9fe13294
ZJS
694 print_info(output ? stdout : stderr, j, false);
695
696 if (on_tty() && !output) {
697 log_error("Refusing to dump core to tty.");
698 return -ENOTTY;
699 }
700
701 r = save_core(j, output ? fileno(output) : STDOUT_FILENO, NULL, NULL);
23bbb0de
MS
702 if (r < 0)
703 return log_error_errno(r, "Coredump retrieval failed: %m");
5de0409e
ZJS
704
705 r = sd_journal_previous(j);
706 if (r >= 0)
9f6445e3 707 log_warning("More than one entry matches, ignoring rest.");
5de0409e
ZJS
708
709 return 0;
710}
711
ada45c78 712static int run_gdb(sd_journal *j) {
de8f6e54 713 _cleanup_free_ char *exe = NULL, *path = NULL;
9fe13294
ZJS
714 bool unlink_path = false;
715 const char *data;
a276ae74 716 siginfo_t st;
ada45c78 717 size_t len;
ada45c78 718 pid_t pid;
ada45c78 719 int r;
ada45c78
LP
720
721 assert(j);
722
723 r = focus(j);
724 if (r < 0)
725 return r;
726
e15758cc
LP
727 print_info(stdout, j, false);
728 fputs("\n", stdout);
ada45c78
LP
729
730 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
23bbb0de
MS
731 if (r < 0)
732 return log_error_errno(r, "Failed to retrieve COREDUMP_EXE field: %m");
ada45c78 733
9fe13294
ZJS
734 assert(len > strlen("COREDUMP_EXE="));
735 data += strlen("COREDUMP_EXE=");
736 len -= strlen("COREDUMP_EXE=");
ada45c78
LP
737
738 exe = strndup(data, len);
739 if (!exe)
740 return log_oom();
741
742 if (endswith(exe, " (deleted)")) {
743 log_error("Binary already deleted.");
744 return -ENOENT;
745 }
746
a276ae74
LP
747 if (!path_is_absolute(exe)) {
748 log_error("Binary is not an absolute path.");
749 return -ENOENT;
750 }
751
9fe13294 752 r = save_core(j, -1, &path, &unlink_path);
23bbb0de
MS
753 if (r < 0)
754 return log_error_errno(r, "Failed to retrieve core: %m");
ada45c78
LP
755
756 pid = fork();
757 if (pid < 0) {
56f64d95 758 log_error_errno(errno, "Failed to fork(): %m");
ada45c78
LP
759 r = -errno;
760 goto finish;
761 }
762 if (pid == 0) {
763 execlp("gdb", "gdb", exe, path, NULL);
a276ae74 764
56f64d95 765 log_error_errno(errno, "Failed to invoke gdb: %m");
ada45c78
LP
766 _exit(1);
767 }
768
769 r = wait_for_terminate(pid, &st);
770 if (r < 0) {
56f64d95 771 log_error_errno(errno, "Failed to wait for gdb: %m");
ada45c78
LP
772 goto finish;
773 }
774
775 r = st.si_code == CLD_EXITED ? st.si_status : 255;
776
777finish:
9fe13294
ZJS
778 if (unlink_path) {
779 log_debug("Removed temporary file %s", path);
780 unlink(path);
781 }
a276ae74 782
ada45c78
LP
783 return r;
784}
785
5de0409e 786int main(int argc, char *argv[]) {
7fd1b19b 787 _cleanup_journal_close_ sd_journal*j = NULL;
5de0409e
ZJS
788 const char* match;
789 Iterator it;
790 int r = 0;
7fd1b19b 791 _cleanup_set_free_free_ Set *matches = NULL;
5de0409e 792
a9cdc94f 793 setlocale(LC_ALL, "");
5de0409e
ZJS
794 log_parse_environment();
795 log_open();
796
797 matches = new_matches();
2fb7a5ce
ZJS
798 if (!matches) {
799 r = -ENOMEM;
5de0409e 800 goto end;
2fb7a5ce 801 }
5de0409e 802
763c7aa2 803 r = parse_argv(argc, argv, matches);
2fb7a5ce 804 if (r < 0)
5de0409e
ZJS
805 goto end;
806
807 if (arg_action == ACTION_NONE)
808 goto end;
809
810 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
811 if (r < 0) {
da927ba9 812 log_error_errno(r, "Failed to open journal: %m");
5de0409e
ZJS
813 goto end;
814 }
815
9fe13294
ZJS
816 /* We want full data, nothing truncated. */
817 sd_journal_set_data_threshold(j, 0);
818
5de0409e 819 SET_FOREACH(match, matches, it) {
5de0409e
ZJS
820 r = sd_journal_add_match(j, match, strlen(match));
821 if (r != 0) {
c33b3297
MS
822 log_error_errno(r, "Failed to add match '%s': %m",
823 match);
5de0409e
ZJS
824 goto end;
825 }
826 }
827
6c17bf04
ZJS
828 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
829 _cleanup_free_ char *filter;
830
831 filter = journal_make_match_string(j);
832 log_debug("Journal filter: %s", filter);
833 }
834
5de0409e 835 switch(arg_action) {
ada45c78 836
5de0409e 837 case ACTION_LIST:
e15758cc 838 case ACTION_INFO:
5de0409e 839 if (!arg_no_pager)
1b12a7b5 840 pager_open(false);
5de0409e
ZJS
841
842 r = dump_list(j);
843 break;
ada45c78 844
5de0409e
ZJS
845 case ACTION_DUMP:
846 r = dump_core(j);
847 break;
ada45c78
LP
848
849 case ACTION_GDB:
850 r = run_gdb(j);
851 break;
852
853 default:
5de0409e
ZJS
854 assert_not_reached("Shouldn't be here");
855 }
856
857end:
5de0409e
ZJS
858 pager_close();
859
860 if (output)
861 fclose(output);
862
ada45c78 863 return r >= 0 ? r : EXIT_FAILURE;
5de0409e 864}