]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/coredumpctl.c
treewide: auto-convert the simple cases to log_*_errno()
[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
73f860db 29#include "systemd/sd-journal.h"
5de0409e
ZJS
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
d5099efc 61 set = set_new(NULL);
5de0409e
ZJS
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 75 if (r < 0) {
0a1beeb6 76 log_error_errno(-r, "failed to add to set: %m");
4a207bb2 77 set_free(set);
5de0409e
ZJS
78 return NULL;
79 }
80
81 return set;
82}
83
5de0409e
ZJS
84static int add_match(Set *set, const char *match) {
85 int r = -ENOMEM;
86 unsigned pid;
87 const char* prefix;
88 char *pattern = NULL;
7fd1b19b 89 _cleanup_free_ char *p = NULL;
5de0409e
ZJS
90
91 if (strchr(match, '='))
92 prefix = "";
93 else if (strchr(match, '/')) {
94 p = path_make_absolute_cwd(match);
95 if (!p)
96 goto fail;
97
98 match = p;
99 prefix = "COREDUMP_EXE=";
100 }
101 else if (safe_atou(match, &pid) == 0)
102 prefix = "COREDUMP_PID=";
103 else
104 prefix = "COREDUMP_COMM=";
105
106 pattern = strjoin(prefix, match, NULL);
107 if (!pattern)
108 goto fail;
109
ef42202a 110 log_debug("Adding pattern: %s", pattern);
f7a5bb28 111 r = set_consume(set, pattern);
5de0409e 112 if (r < 0) {
0a1beeb6 113 log_error_errno(-r, "Failed to add pattern: %m");
5de0409e
ZJS
114 goto fail;
115 }
5de0409e
ZJS
116
117 return 0;
118fail:
0a1beeb6 119 log_error_errno(-r, "Failed to add match: %m");
5de0409e
ZJS
120 return r;
121}
122
601185b4
ZJS
123static void help(void) {
124 printf("%s [OPTIONS...]\n\n"
125 "List or retrieve coredumps from the journal.\n\n"
126 "Flags:\n"
127 " -h --help Show this help\n"
128 " --version Print version string\n"
129 " --no-pager Do not pipe output into a pager\n"
130 " --no-legend Do not print the column headers.\n"
131 " -1 Show information about most recent entry only\n"
132 " -F --field=FIELD List all values a certain field takes\n"
133 " -o --output=FILE Write output to FILE\n\n"
134
135 "Commands:\n"
136 " list [MATCHES...] List available coredumps (default)\n"
137 " info [MATCHES...] Show detailed information about one or more coredumps\n"
138 " dump [MATCHES...] Print first matching coredump to stdout\n"
139 " gdb [MATCHES...] Start gdb for the first matching coredump\n"
140 , program_invocation_short_name);
141}
142
763c7aa2 143static int parse_argv(int argc, char *argv[], Set *matches) {
5de0409e
ZJS
144 enum {
145 ARG_VERSION = 0x100,
146 ARG_NO_PAGER,
9a340880 147 ARG_NO_LEGEND,
5de0409e
ZJS
148 };
149
150 int r, c;
151
152 static const struct option options[] = {
57ce4bd4
ZJS
153 { "help", no_argument, NULL, 'h' },
154 { "version" , no_argument, NULL, ARG_VERSION },
155 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
9a340880 156 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
57ce4bd4 157 { "output", required_argument, NULL, 'o' },
4f76ae1b 158 { "field", required_argument, NULL, 'F' },
eb9da376 159 {}
5de0409e
ZJS
160 };
161
162 assert(argc >= 0);
163 assert(argv);
164
0c51aada 165 while ((c = getopt_long(argc, argv, "ho:F:1", options, NULL)) >= 0)
5de0409e 166 switch(c) {
eb9da376 167
5de0409e 168 case 'h':
5de0409e 169 arg_action = ACTION_NONE;
601185b4
ZJS
170 help();
171 return 0;
5de0409e
ZJS
172
173 case ARG_VERSION:
eb9da376 174 arg_action = ACTION_NONE;
5de0409e 175 puts(PACKAGE_STRING);
5de0409e 176 puts(SYSTEMD_FEATURES);
5de0409e
ZJS
177 return 0;
178
179 case ARG_NO_PAGER:
180 arg_no_pager = true;
181 break;
182
9a340880
ZJS
183 case ARG_NO_LEGEND:
184 arg_no_legend = true;
185 break;
186
5de0409e
ZJS
187 case 'o':
188 if (output) {
189 log_error("cannot set output more than once");
190 return -EINVAL;
191 }
192
193 output = fopen(optarg, "we");
194 if (!output) {
195 log_error("writing to '%s': %m", optarg);
196 return -errno;
197 }
198
199 break;
57ce4bd4 200
4f76ae1b 201 case 'F':
a276ae74 202 if (arg_field) {
4f76ae1b
ZJS
203 log_error("cannot use --field/-F more than once");
204 return -EINVAL;
205 }
a276ae74 206 arg_field = optarg;
4f76ae1b
ZJS
207 break;
208
0c51aada
LP
209 case '1':
210 arg_one = true;
211 break;
212
57ce4bd4
ZJS
213 case '?':
214 return -EINVAL;
215
5de0409e 216 default:
eb9da376 217 assert_not_reached("Unhandled option");
5de0409e
ZJS
218 }
219
220 if (optind < argc) {
221 const char *cmd = argv[optind++];
f168c273 222 if (streq(cmd, "list"))
5de0409e
ZJS
223 arg_action = ACTION_LIST;
224 else if (streq(cmd, "dump"))
225 arg_action = ACTION_DUMP;
ada45c78
LP
226 else if (streq(cmd, "gdb"))
227 arg_action = ACTION_GDB;
e15758cc
LP
228 else if (streq(cmd, "info"))
229 arg_action = ACTION_INFO;
5de0409e
ZJS
230 else {
231 log_error("Unknown action '%s'", cmd);
232 return -EINVAL;
233 }
234 }
235
a276ae74 236 if (arg_field && arg_action != ACTION_LIST) {
4f76ae1b
ZJS
237 log_error("Option --field/-F only makes sense with list");
238 return -EINVAL;
239 }
240
5de0409e
ZJS
241 while (optind < argc) {
242 r = add_match(matches, argv[optind]);
243 if (r != 0)
244 return r;
245 optind++;
246 }
247
248 return 0;
249}
250
8bc8ab83
LP
251static int retrieve(const void *data,
252 size_t len,
253 const char *name,
a276ae74 254 char **var) {
5de0409e 255
4f76ae1b 256 size_t ident;
a276ae74 257 char *v;
8bc8ab83 258
4f76ae1b 259 ident = strlen(name) + 1; /* name + "=" */
8bc8ab83 260
4f76ae1b 261 if (len < ident)
8bc8ab83 262 return 0;
5de0409e 263
4f76ae1b 264 if (memcmp(data, name, ident - 1) != 0)
8bc8ab83
LP
265 return 0;
266
4f76ae1b 267 if (((const char*) data)[ident - 1] != '=')
8bc8ab83 268 return 0;
5de0409e 269
a276ae74
LP
270 v = strndup((const char*)data + ident, len - ident);
271 if (!v)
5de0409e
ZJS
272 return log_oom();
273
a276ae74
LP
274 free(*var);
275 *var = v;
276
5de0409e
ZJS
277 return 0;
278}
279
4f76ae1b 280static void print_field(FILE* file, sd_journal *j) {
a276ae74 281 _cleanup_free_ char *value = NULL;
4f76ae1b
ZJS
282 const void *d;
283 size_t l;
284
e15758cc
LP
285 assert(file);
286 assert(j);
287
a276ae74 288 assert(arg_field);
4f76ae1b
ZJS
289
290 SD_JOURNAL_FOREACH_DATA(j, d, l)
a276ae74
LP
291 retrieve(d, l, arg_field, &value);
292
4f76ae1b
ZJS
293 if (value)
294 fprintf(file, "%s\n", value);
295}
296
e15758cc 297static int print_list(FILE* file, sd_journal *j, int had_legend) {
a276ae74 298 _cleanup_free_ char
5de0409e 299 *pid = NULL, *uid = NULL, *gid = NULL,
9fe13294
ZJS
300 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
301 *filename = NULL;
8bc8ab83
LP
302 const void *d;
303 size_t l;
684341b0
LP
304 usec_t t;
305 char buf[FORMAT_TIMESTAMP_MAX];
306 int r;
9fe13294 307 bool present;
8bc8ab83 308
e15758cc
LP
309 assert(file);
310 assert(j);
311
8bc8ab83 312 SD_JOURNAL_FOREACH_DATA(j, d, l) {
8bc8ab83
LP
313 retrieve(d, l, "COREDUMP_PID", &pid);
314 retrieve(d, l, "COREDUMP_UID", &uid);
315 retrieve(d, l, "COREDUMP_GID", &gid);
316 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
317 retrieve(d, l, "COREDUMP_EXE", &exe);
a276ae74
LP
318 retrieve(d, l, "COREDUMP_COMM", &comm);
319 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
9fe13294 320 retrieve(d, l, "COREDUMP_FILENAME", &filename);
8bc8ab83 321 }
5de0409e 322
9fe13294 323 if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename) {
684341b0
LP
324 log_warning("Empty coredump log entry");
325 return -EINVAL;
326 }
327
328 r = sd_journal_get_realtime_usec(j, &t);
329 if (r < 0) {
0a1beeb6 330 log_error_errno(-r, "Failed to get realtime timestamp: %m");
684341b0 331 return r;
5de0409e
ZJS
332 }
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",
402 ansi_highlight(), strna(pid), ansi_highlight_off(), comm);
403 else
404 fprintf(file,
405 " PID: %s%s%s\n",
406 ansi_highlight(), strna(pid), ansi_highlight_off());
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
LP
469 if (exe)
470 fprintf(file, " Executable: %s%s%s\n", ansi_highlight(), exe, ansi_highlight_off());
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);
524 if (r < 0) {
0a1beeb6 525 log_error_errno(-r, "Failed to search journal: %m");
5de0409e
ZJS
526 return r;
527 }
8bc8ab83 528 if (r == 0) {
0c51aada 529 log_error("No match found.");
8bc8ab83
LP
530 return -ESRCH;
531 }
ada45c78
LP
532 return r;
533}
5de0409e 534
0c51aada
LP
535static void print_entry(sd_journal *j, unsigned n_found) {
536 assert(j);
537
538 if (arg_action == ACTION_INFO)
539 print_info(stdout, j, n_found);
540 else if (arg_field)
541 print_field(stdout, j);
542 else
543 print_list(stdout, j, n_found);
544}
545
546static int dump_list(sd_journal *j) {
547 unsigned n_found = 0;
548 int r;
549
550 assert(j);
551
552 /* The coredumps are likely to compressed, and for just
553 * listing them we don't need to decompress them, so let's
554 * pick a fairly low data threshold here */
555 sd_journal_set_data_threshold(j, 4096);
556
557 if (arg_one) {
558 r = focus(j);
559 if (r < 0)
560 return r;
561
562 print_entry(j, 0);
563 } else {
564 SD_JOURNAL_FOREACH(j)
565 print_entry(j, n_found++);
566
567 if (!arg_field && n_found <= 0) {
568 log_notice("No coredumps found.");
569 return -ESRCH;
570 }
571 }
572
573 return 0;
574}
575
9fe13294
ZJS
576static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) {
577 const char *data;
578 _cleanup_free_ char *filename = NULL;
579 size_t len;
ada45c78
LP
580 int r;
581
9fe13294
ZJS
582 assert((fd >= 0) != !!path);
583 assert(!!path == !!unlink_temp);
ada45c78 584
9fe13294
ZJS
585 /* Prefer uncompressed file to journal (probably cached) to
586 * compressed file (probably uncached). */
587 r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len);
588 if (r < 0 && r != -ENOENT)
0a1beeb6 589 log_warning_errno(-r, "Failed to retrieve COREDUMP_FILENAME: %m");
9fe13294
ZJS
590 else if (r == 0)
591 retrieve(data, len, "COREDUMP_FILENAME", &filename);
93b73b06 592
9fe13294 593 if (filename && access(filename, R_OK) < 0) {
31cda3d1
ZJS
594 log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
595 "File %s is not readable: %m", filename);
9fe13294
ZJS
596 free(filename);
597 filename = NULL;
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);
617 if (fdt < 0) {
618 log_error("Failed to create temporary file: %m");
619 return -errno;
620 }
621 log_debug("Created temporary file %s", temp);
622
623 fd = fdt;
624 }
625
626 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
627 if (r == 0) {
628 ssize_t sz;
629
630 assert(len >= 9);
631 data += 9;
632 len -= 9;
633
634 sz = write(fdt, data, len);
635 if (sz < 0) {
636 log_error("Failed to write temporary file: %m");
637 r = -errno;
638 goto error;
639 }
640 if (sz != (ssize_t) len) {
641 log_error("Short write to temporary file.");
642 r = -EIO;
643 goto error;
644 }
645 } else if (filename) {
d89c8fdf 646#if defined(HAVE_XZ) || defined(HAVE_LZ4)
9fe13294
ZJS
647 _cleanup_close_ int fdf;
648
649 fdf = open(filename, O_RDONLY | O_CLOEXEC);
650 if (fdf < 0) {
651 log_error("Failed to open %s: %m", filename);
652 r = -errno;
653 goto error;
654 }
655
d89c8fdf 656 r = decompress_stream(filename, fdf, fd, -1);
9fe13294 657 if (r < 0) {
0a1beeb6 658 log_error_errno(-r, "Failed to decompress %s: %m", filename);
9fe13294
ZJS
659 goto error;
660 }
2fb8159f 661#else
d89c8fdf 662 log_error("Cannot decompress file. Compiled without compression support.");
2fb8159f
DM
663 r = -ENOTSUP;
664 goto error;
665#endif
9fe13294
ZJS
666 } else {
667 if (r == -ENOENT)
31cda3d1 668 log_error("Cannot retrieve coredump from journal nor disk.");
a276ae74 669 else
0a1beeb6 670 log_error_errno(-r, "Failed to retrieve COREDUMP field: %m");
9fe13294
ZJS
671 goto error;
672 }
a276ae74 673
9fe13294
ZJS
674 if (temp) {
675 *path = temp;
676 *unlink_temp = true;
a276ae74
LP
677 }
678
9fe13294
ZJS
679 return 0;
680
681error:
682 if (temp) {
683 unlink(temp);
684 log_debug("Removed temporary file %s", temp);
a276ae74 685 }
9fe13294
ZJS
686 return r;
687 }
688}
a276ae74 689
9fe13294
ZJS
690static int dump_core(sd_journal* j) {
691 int r;
692
693 assert(j);
694
695 r = focus(j);
696 if (r < 0)
ada45c78 697 return r;
ada45c78 698
9fe13294
ZJS
699 print_info(output ? stdout : stderr, j, false);
700
701 if (on_tty() && !output) {
702 log_error("Refusing to dump core to tty.");
703 return -ENOTTY;
704 }
705
706 r = save_core(j, output ? fileno(output) : STDOUT_FILENO, NULL, NULL);
707 if (r < 0) {
0a1beeb6 708 log_error_errno(-r, "Coredump retrieval failed: %m");
9fe13294 709 return r;
5de0409e
ZJS
710 }
711
712 r = sd_journal_previous(j);
713 if (r >= 0)
9f6445e3 714 log_warning("More than one entry matches, ignoring rest.");
5de0409e
ZJS
715
716 return 0;
717}
718
ada45c78 719static int run_gdb(sd_journal *j) {
de8f6e54 720 _cleanup_free_ char *exe = NULL, *path = NULL;
9fe13294
ZJS
721 bool unlink_path = false;
722 const char *data;
a276ae74 723 siginfo_t st;
ada45c78 724 size_t len;
ada45c78 725 pid_t pid;
ada45c78 726 int r;
ada45c78
LP
727
728 assert(j);
729
730 r = focus(j);
731 if (r < 0)
732 return r;
733
e15758cc
LP
734 print_info(stdout, j, false);
735 fputs("\n", stdout);
ada45c78
LP
736
737 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
738 if (r < 0) {
0a1beeb6 739 log_error_errno(-r, "Failed to retrieve COREDUMP_EXE field: %m");
ada45c78
LP
740 return r;
741 }
742
9fe13294
ZJS
743 assert(len > strlen("COREDUMP_EXE="));
744 data += strlen("COREDUMP_EXE=");
745 len -= strlen("COREDUMP_EXE=");
ada45c78
LP
746
747 exe = strndup(data, len);
748 if (!exe)
749 return log_oom();
750
751 if (endswith(exe, " (deleted)")) {
752 log_error("Binary already deleted.");
753 return -ENOENT;
754 }
755
a276ae74
LP
756 if (!path_is_absolute(exe)) {
757 log_error("Binary is not an absolute path.");
758 return -ENOENT;
759 }
760
9fe13294
ZJS
761 r = save_core(j, -1, &path, &unlink_path);
762 if (r < 0) {
0a1beeb6 763 log_error_errno(-r, "Failed to retrieve core: %m");
ada45c78 764 return r;
a276ae74 765 }
ada45c78
LP
766
767 pid = fork();
768 if (pid < 0) {
769 log_error("Failed to fork(): %m");
770 r = -errno;
771 goto finish;
772 }
773 if (pid == 0) {
774 execlp("gdb", "gdb", exe, path, NULL);
a276ae74 775
ada45c78
LP
776 log_error("Failed to invoke gdb: %m");
777 _exit(1);
778 }
779
780 r = wait_for_terminate(pid, &st);
781 if (r < 0) {
782 log_error("Failed to wait for gdb: %m");
783 goto finish;
784 }
785
786 r = st.si_code == CLD_EXITED ? st.si_status : 255;
787
788finish:
9fe13294
ZJS
789 if (unlink_path) {
790 log_debug("Removed temporary file %s", path);
791 unlink(path);
792 }
a276ae74 793
ada45c78
LP
794 return r;
795}
796
5de0409e 797int main(int argc, char *argv[]) {
7fd1b19b 798 _cleanup_journal_close_ sd_journal*j = NULL;
5de0409e
ZJS
799 const char* match;
800 Iterator it;
801 int r = 0;
7fd1b19b 802 _cleanup_set_free_free_ Set *matches = NULL;
5de0409e 803
a9cdc94f 804 setlocale(LC_ALL, "");
5de0409e
ZJS
805 log_parse_environment();
806 log_open();
807
808 matches = new_matches();
2fb7a5ce
ZJS
809 if (!matches) {
810 r = -ENOMEM;
5de0409e 811 goto end;
2fb7a5ce 812 }
5de0409e 813
763c7aa2 814 r = parse_argv(argc, argv, matches);
2fb7a5ce 815 if (r < 0)
5de0409e
ZJS
816 goto end;
817
818 if (arg_action == ACTION_NONE)
819 goto end;
820
821 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
822 if (r < 0) {
0a1beeb6 823 log_error_errno(-r, "Failed to open journal: %m");
5de0409e
ZJS
824 goto end;
825 }
826
9fe13294
ZJS
827 /* We want full data, nothing truncated. */
828 sd_journal_set_data_threshold(j, 0);
829
5de0409e 830 SET_FOREACH(match, matches, it) {
5de0409e
ZJS
831 r = sd_journal_add_match(j, match, strlen(match));
832 if (r != 0) {
833 log_error("Failed to add match '%s': %s",
834 match, strerror(-r));
835 goto end;
836 }
837 }
838
6c17bf04
ZJS
839 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
840 _cleanup_free_ char *filter;
841
842 filter = journal_make_match_string(j);
843 log_debug("Journal filter: %s", filter);
844 }
845
5de0409e 846 switch(arg_action) {
ada45c78 847
5de0409e 848 case ACTION_LIST:
e15758cc 849 case ACTION_INFO:
5de0409e 850 if (!arg_no_pager)
1b12a7b5 851 pager_open(false);
5de0409e
ZJS
852
853 r = dump_list(j);
854 break;
ada45c78 855
5de0409e
ZJS
856 case ACTION_DUMP:
857 r = dump_core(j);
858 break;
ada45c78
LP
859
860 case ACTION_GDB:
861 r = run_gdb(j);
862 break;
863
864 default:
5de0409e
ZJS
865 assert_not_reached("Shouldn't be here");
866 }
867
868end:
5de0409e
ZJS
869 pager_close();
870
871 if (output)
872 fclose(output);
873
ada45c78 874 return r >= 0 ? r : EXIT_FAILURE;
5de0409e 875}