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