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