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