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