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