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