]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/coredump/coredumpctl.c
Merge pull request #7817 from medhefgo/systemd-boot
[thirdparty/systemd.git] / src / coredump / coredumpctl.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2012 Zbigniew Jędrzejewski-Szmek
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <fcntl.h>
22 #include <getopt.h>
23 #include <locale.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include "sd-bus.h"
29 #include "sd-journal.h"
30 #include "sd-messages.h"
31
32 #include "alloc-util.h"
33 #include "bus-error.h"
34 #include "bus-util.h"
35 #include "compress.h"
36 #include "fd-util.h"
37 #include "fileio.h"
38 #include "fs-util.h"
39 #include "journal-internal.h"
40 #include "journal-util.h"
41 #include "log.h"
42 #include "macro.h"
43 #include "pager.h"
44 #include "parse-util.h"
45 #include "path-util.h"
46 #include "process-util.h"
47 #include "sigbus.h"
48 #include "signal-util.h"
49 #include "string-util.h"
50 #include "strv.h"
51 #include "terminal-util.h"
52 #include "user-util.h"
53 #include "util.h"
54
55 #define SHORT_BUS_CALL_TIMEOUT_USEC (3 * USEC_PER_SEC)
56
57 static usec_t arg_since = USEC_INFINITY, arg_until = USEC_INFINITY;
58
59 static enum {
60 ACTION_NONE,
61 ACTION_INFO,
62 ACTION_LIST,
63 ACTION_DUMP,
64 ACTION_GDB,
65 } arg_action = ACTION_LIST;
66 static const char* arg_field = NULL;
67 static const char *arg_directory = NULL;
68 static bool arg_no_pager = false;
69 static int arg_no_legend = false;
70 static int arg_one = false;
71 static FILE* arg_output = NULL;
72 static bool arg_reverse = false;
73 static char** arg_matches = NULL;
74 static bool arg_quiet = false;
75
76 static int add_match(sd_journal *j, const char *match) {
77 _cleanup_free_ char *p = NULL;
78 char *pattern = NULL;
79 const char* prefix;
80 pid_t pid;
81 int r;
82
83 if (strchr(match, '='))
84 prefix = "";
85 else if (strchr(match, '/')) {
86 r = path_make_absolute_cwd(match, &p);
87 if (r < 0)
88 return log_error_errno(r, "path_make_absolute_cwd(\"%s\"): %m", match);
89
90 match = p;
91 prefix = "COREDUMP_EXE=";
92 } else if (parse_pid(match, &pid) >= 0)
93 prefix = "COREDUMP_PID=";
94 else
95 prefix = "COREDUMP_COMM=";
96
97 pattern = strjoina(prefix, match);
98 log_debug("Adding match: %s", pattern);
99 r = sd_journal_add_match(j, pattern, 0);
100 if (r < 0)
101 return log_error_errno(r, "Failed to add match \"%s\": %m", match);
102 return 0;
103 }
104
105 static int add_matches(sd_journal *j) {
106 char **match;
107 int r;
108
109 r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR, 0);
110 if (r < 0)
111 return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR);
112
113 r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR, 0);
114 if (r < 0)
115 return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR);
116
117 STRV_FOREACH(match, arg_matches) {
118 r = add_match(j, *match);
119 if (r < 0)
120 return r;
121 }
122
123 return 0;
124 }
125
126 static void help(void) {
127 printf("%s [OPTIONS...]\n\n"
128 "List or retrieve coredumps from the journal.\n\n"
129 "Flags:\n"
130 " -h --help Show this help\n"
131 " --version Print version string\n"
132 " --no-pager Do not pipe output into a pager\n"
133 " --no-legend Do not print the column headers.\n"
134 " -1 Show information about most recent entry only\n"
135 " -S --since=DATE Only print coredumps since the date\n"
136 " -U --until=DATE Only print coredumps until the date\n"
137 " -r --reverse Show the newest entries first\n"
138 " -F --field=FIELD List all values a certain field takes\n"
139 " -o --output=FILE Write output to FILE\n"
140 " -D --directory=DIR Use journal files from directory\n\n"
141 " -q --quiet Do not show info messages and privilege warning\n"
142 "Commands:\n"
143 " list [MATCHES...] List available coredumps (default)\n"
144 " info [MATCHES...] Show detailed information about one or more coredumps\n"
145 " dump [MATCHES...] Print first matching coredump to stdout\n"
146 " gdb [MATCHES...] Start gdb for the first matching coredump\n"
147 , program_invocation_short_name);
148 }
149
150 static int parse_argv(int argc, char *argv[]) {
151 enum {
152 ARG_VERSION = 0x100,
153 ARG_NO_PAGER,
154 ARG_NO_LEGEND,
155 };
156
157 int c, r;
158
159 static const struct option options[] = {
160 { "help", no_argument, NULL, 'h' },
161 { "version" , no_argument, NULL, ARG_VERSION },
162 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
163 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
164 { "output", required_argument, NULL, 'o' },
165 { "field", required_argument, NULL, 'F' },
166 { "directory", required_argument, NULL, 'D' },
167 { "reverse", no_argument, NULL, 'r' },
168 { "since", required_argument, NULL, 'S' },
169 { "until", required_argument, NULL, 'U' },
170 { "quiet", no_argument, NULL, 'q' },
171 {}
172 };
173
174 assert(argc >= 0);
175 assert(argv);
176
177 while ((c = getopt_long(argc, argv, "ho:F:1D:rS:U:q", options, NULL)) >= 0)
178 switch(c) {
179 case 'h':
180 arg_action = ACTION_NONE;
181 help();
182 return 0;
183
184 case ARG_VERSION:
185 arg_action = ACTION_NONE;
186 return version();
187
188 case ARG_NO_PAGER:
189 arg_no_pager = true;
190 break;
191
192 case ARG_NO_LEGEND:
193 arg_no_legend = true;
194 break;
195
196 case 'o':
197 if (arg_output) {
198 log_error("cannot set output more than once");
199 return -EINVAL;
200 }
201
202 arg_output = fopen(optarg, "we");
203 if (!arg_output)
204 return log_error_errno(errno, "writing to '%s': %m", optarg);
205
206 break;
207
208 case 'S':
209 r = parse_timestamp(optarg, &arg_since);
210 if (r < 0)
211 return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
212 break;
213
214 case 'U':
215 r = parse_timestamp(optarg, &arg_until);
216 if (r < 0)
217 return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
218 break;
219
220 case 'F':
221 if (arg_field) {
222 log_error("cannot use --field/-F more than once");
223 return -EINVAL;
224 }
225 arg_field = optarg;
226 break;
227
228 case '1':
229 arg_one = true;
230 break;
231
232 case 'D':
233 arg_directory = optarg;
234 break;
235
236 case 'r':
237 arg_reverse = true;
238 break;
239
240 case 'q':
241 arg_quiet = true;
242 break;
243
244 case '?':
245 return -EINVAL;
246
247 default:
248 assert_not_reached("Unhandled option");
249 }
250
251 if (arg_since != USEC_INFINITY && arg_until != USEC_INFINITY &&
252 arg_since > arg_until) {
253 log_error("--since= must be before --until=.");
254 return -EINVAL;
255 }
256
257 if (optind < argc) {
258 const char *cmd = argv[optind++];
259 if (streq(cmd, "list"))
260 arg_action = ACTION_LIST;
261 else if (streq(cmd, "dump"))
262 arg_action = ACTION_DUMP;
263 else if (streq(cmd, "gdb"))
264 arg_action = ACTION_GDB;
265 else if (streq(cmd, "info"))
266 arg_action = ACTION_INFO;
267 else {
268 log_error("Unknown action '%s'", cmd);
269 return -EINVAL;
270 }
271 }
272
273 if (arg_field && arg_action != ACTION_LIST) {
274 log_error("Option --field/-F only makes sense with list");
275 return -EINVAL;
276 }
277
278 if (optind < argc)
279 arg_matches = argv + optind;
280
281 return 0;
282 }
283
284 static int retrieve(const void *data,
285 size_t len,
286 const char *name,
287 char **var) {
288
289 size_t ident;
290 char *v;
291
292 ident = strlen(name) + 1; /* name + "=" */
293
294 if (len < ident)
295 return 0;
296
297 if (memcmp(data, name, ident - 1) != 0)
298 return 0;
299
300 if (((const char*) data)[ident - 1] != '=')
301 return 0;
302
303 v = strndup((const char*)data + ident, len - ident);
304 if (!v)
305 return log_oom();
306
307 free(*var);
308 *var = v;
309
310 return 1;
311 }
312
313 static int print_field(FILE* file, sd_journal *j) {
314 const void *d;
315 size_t l;
316
317 assert(file);
318 assert(j);
319
320 assert(arg_field);
321
322 /* A (user-specified) field may appear more than once for a given entry.
323 * We will print all of the occurences.
324 * This is different below for fields that systemd-coredump uses,
325 * because they cannot meaningfully appear more than once.
326 */
327 SD_JOURNAL_FOREACH_DATA(j, d, l) {
328 _cleanup_free_ char *value = NULL;
329 int r;
330
331 r = retrieve(d, l, arg_field, &value);
332 if (r < 0)
333 return r;
334 if (r > 0)
335 fprintf(file, "%s\n", value);
336 }
337
338 return 0;
339 }
340
341 #define RETRIEVE(d, l, name, arg) \
342 { \
343 int _r = retrieve(d, l, name, &arg); \
344 if (_r < 0) \
345 return _r; \
346 if (_r > 0) \
347 continue; \
348 }
349
350 static int print_list(FILE* file, sd_journal *j, int had_legend) {
351 _cleanup_free_ char
352 *mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL,
353 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
354 *filename = NULL, *truncated = NULL, *coredump = NULL;
355 const void *d;
356 size_t l;
357 usec_t t;
358 char buf[FORMAT_TIMESTAMP_MAX];
359 int r;
360 const char *present;
361 bool normal_coredump;
362
363 assert(file);
364 assert(j);
365
366 SD_JOURNAL_FOREACH_DATA(j, d, l) {
367 RETRIEVE(d, l, "MESSAGE_ID", mid);
368 RETRIEVE(d, l, "COREDUMP_PID", pid);
369 RETRIEVE(d, l, "COREDUMP_UID", uid);
370 RETRIEVE(d, l, "COREDUMP_GID", gid);
371 RETRIEVE(d, l, "COREDUMP_SIGNAL", sgnl);
372 RETRIEVE(d, l, "COREDUMP_EXE", exe);
373 RETRIEVE(d, l, "COREDUMP_COMM", comm);
374 RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline);
375 RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
376 RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated);
377 RETRIEVE(d, l, "COREDUMP", coredump);
378 }
379
380 if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename) {
381 log_warning("Empty coredump log entry");
382 return -EINVAL;
383 }
384
385 r = sd_journal_get_realtime_usec(j, &t);
386 if (r < 0)
387 return log_error_errno(r, "Failed to get realtime timestamp: %m");
388
389 format_timestamp(buf, sizeof(buf), t);
390
391 if (!had_legend && !arg_no_legend)
392 fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n",
393 FORMAT_TIMESTAMP_WIDTH, "TIME",
394 6, "PID",
395 5, "UID",
396 5, "GID",
397 3, "SIG",
398 9, "COREFILE",
399 "EXE");
400
401 normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR);
402
403 if (filename)
404 if (access(filename, R_OK) == 0)
405 present = "present";
406 else if (errno == ENOENT)
407 present = "missing";
408 else
409 present = "error";
410 else if (coredump)
411 present = "journal";
412 else if (normal_coredump)
413 present = "none";
414 else
415 present = "-";
416
417 if (STR_IN_SET(present, "present", "journal") && truncated && parse_boolean(truncated) > 0)
418 present = "truncated";
419
420 fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n",
421 FORMAT_TIMESTAMP_WIDTH, buf,
422 6, strna(pid),
423 5, strna(uid),
424 5, strna(gid),
425 3, normal_coredump ? strna(sgnl) : "-",
426 9, present,
427 strna(exe ?: (comm ?: cmdline)));
428
429 return 0;
430 }
431
432 static int print_info(FILE *file, sd_journal *j, bool need_space) {
433 _cleanup_free_ char
434 *mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL,
435 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
436 *unit = NULL, *user_unit = NULL, *session = NULL,
437 *boot_id = NULL, *machine_id = NULL, *hostname = NULL,
438 *slice = NULL, *cgroup = NULL, *owner_uid = NULL,
439 *message = NULL, *timestamp = NULL, *filename = NULL,
440 *truncated = NULL, *coredump = NULL;
441 const void *d;
442 size_t l;
443 bool normal_coredump;
444 int r;
445
446 assert(file);
447 assert(j);
448
449 SD_JOURNAL_FOREACH_DATA(j, d, l) {
450 RETRIEVE(d, l, "MESSAGE_ID", mid);
451 RETRIEVE(d, l, "COREDUMP_PID", pid);
452 RETRIEVE(d, l, "COREDUMP_UID", uid);
453 RETRIEVE(d, l, "COREDUMP_GID", gid);
454 RETRIEVE(d, l, "COREDUMP_SIGNAL", sgnl);
455 RETRIEVE(d, l, "COREDUMP_EXE", exe);
456 RETRIEVE(d, l, "COREDUMP_COMM", comm);
457 RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline);
458 RETRIEVE(d, l, "COREDUMP_UNIT", unit);
459 RETRIEVE(d, l, "COREDUMP_USER_UNIT", user_unit);
460 RETRIEVE(d, l, "COREDUMP_SESSION", session);
461 RETRIEVE(d, l, "COREDUMP_OWNER_UID", owner_uid);
462 RETRIEVE(d, l, "COREDUMP_SLICE", slice);
463 RETRIEVE(d, l, "COREDUMP_CGROUP", cgroup);
464 RETRIEVE(d, l, "COREDUMP_TIMESTAMP", timestamp);
465 RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
466 RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated);
467 RETRIEVE(d, l, "COREDUMP", coredump);
468 RETRIEVE(d, l, "_BOOT_ID", boot_id);
469 RETRIEVE(d, l, "_MACHINE_ID", machine_id);
470 RETRIEVE(d, l, "_HOSTNAME", hostname);
471 RETRIEVE(d, l, "MESSAGE", message);
472 }
473
474 if (need_space)
475 fputs("\n", file);
476
477 normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR);
478
479 if (comm)
480 fprintf(file,
481 " PID: %s%s%s (%s)\n",
482 ansi_highlight(), strna(pid), ansi_normal(), comm);
483 else
484 fprintf(file,
485 " PID: %s%s%s\n",
486 ansi_highlight(), strna(pid), ansi_normal());
487
488 if (uid) {
489 uid_t n;
490
491 if (parse_uid(uid, &n) >= 0) {
492 _cleanup_free_ char *u = NULL;
493
494 u = uid_to_name(n);
495 fprintf(file,
496 " UID: %s (%s)\n",
497 uid, u);
498 } else {
499 fprintf(file,
500 " UID: %s\n",
501 uid);
502 }
503 }
504
505 if (gid) {
506 gid_t n;
507
508 if (parse_gid(gid, &n) >= 0) {
509 _cleanup_free_ char *g = NULL;
510
511 g = gid_to_name(n);
512 fprintf(file,
513 " GID: %s (%s)\n",
514 gid, g);
515 } else {
516 fprintf(file,
517 " GID: %s\n",
518 gid);
519 }
520 }
521
522 if (sgnl) {
523 int sig;
524 const char *name = normal_coredump ? "Signal" : "Reason";
525
526 if (normal_coredump && safe_atoi(sgnl, &sig) >= 0)
527 fprintf(file, " %s: %s (%s)\n", name, sgnl, signal_to_string(sig));
528 else
529 fprintf(file, " %s: %s\n", name, sgnl);
530 }
531
532 if (timestamp) {
533 usec_t u;
534
535 r = safe_atou64(timestamp, &u);
536 if (r >= 0) {
537 char absolute[FORMAT_TIMESTAMP_MAX], relative[FORMAT_TIMESPAN_MAX];
538
539 fprintf(file,
540 " Timestamp: %s (%s)\n",
541 format_timestamp(absolute, sizeof(absolute), u),
542 format_timestamp_relative(relative, sizeof(relative), u));
543
544 } else
545 fprintf(file, " Timestamp: %s\n", timestamp);
546 }
547
548 if (cmdline)
549 fprintf(file, " Command Line: %s\n", cmdline);
550 if (exe)
551 fprintf(file, " Executable: %s%s%s\n", ansi_highlight(), exe, ansi_normal());
552 if (cgroup)
553 fprintf(file, " Control Group: %s\n", cgroup);
554 if (unit)
555 fprintf(file, " Unit: %s\n", unit);
556 if (user_unit)
557 fprintf(file, " User Unit: %s\n", user_unit);
558 if (slice)
559 fprintf(file, " Slice: %s\n", slice);
560 if (session)
561 fprintf(file, " Session: %s\n", session);
562 if (owner_uid) {
563 uid_t n;
564
565 if (parse_uid(owner_uid, &n) >= 0) {
566 _cleanup_free_ char *u = NULL;
567
568 u = uid_to_name(n);
569 fprintf(file,
570 " Owner UID: %s (%s)\n",
571 owner_uid, u);
572 } else {
573 fprintf(file,
574 " Owner UID: %s\n",
575 owner_uid);
576 }
577 }
578 if (boot_id)
579 fprintf(file, " Boot ID: %s\n", boot_id);
580 if (machine_id)
581 fprintf(file, " Machine ID: %s\n", machine_id);
582 if (hostname)
583 fprintf(file, " Hostname: %s\n", hostname);
584
585 if (filename) {
586 bool inacc, trunc;
587
588 inacc = access(filename, R_OK) < 0;
589 trunc = truncated && parse_boolean(truncated) > 0;
590
591 if (inacc || trunc)
592 fprintf(file, " Storage: %s%s (%s%s%s)%s\n",
593 ansi_highlight_red(),
594 filename,
595 inacc ? "inaccessible" : "",
596 inacc && trunc ? ", " : "",
597 trunc ? "truncated" : "",
598 ansi_normal());
599 else
600 fprintf(file, " Storage: %s\n", filename);
601 }
602
603 else if (coredump)
604 fprintf(file, " Storage: journal\n");
605 else
606 fprintf(file, " Storage: none\n");
607
608 if (message) {
609 _cleanup_free_ char *m = NULL;
610
611 m = strreplace(message, "\n", "\n ");
612
613 fprintf(file, " Message: %s\n", strstrip(m ?: message));
614 }
615
616 return 0;
617 }
618
619 static int focus(sd_journal *j) {
620 int r;
621
622 r = sd_journal_seek_tail(j);
623 if (r == 0)
624 r = sd_journal_previous(j);
625 if (r < 0)
626 return log_error_errno(r, "Failed to search journal: %m");
627 if (r == 0) {
628 log_error("No match found.");
629 return -ESRCH;
630 }
631 return r;
632 }
633
634 static int print_entry(sd_journal *j, unsigned n_found) {
635 assert(j);
636
637 if (arg_action == ACTION_INFO)
638 return print_info(stdout, j, n_found);
639 else if (arg_field)
640 return print_field(stdout, j);
641 else
642 return print_list(stdout, j, n_found);
643 }
644
645 static int dump_list(sd_journal *j) {
646 unsigned n_found = 0;
647 int r;
648
649 assert(j);
650
651 /* The coredumps are likely to compressed, and for just
652 * listing them we don't need to decompress them, so let's
653 * pick a fairly low data threshold here */
654 sd_journal_set_data_threshold(j, 4096);
655
656 if (arg_one) {
657 r = focus(j);
658 if (r < 0)
659 return r;
660
661 return print_entry(j, 0);
662 } else {
663 if (arg_since != USEC_INFINITY && !arg_reverse)
664 r = sd_journal_seek_realtime_usec(j, arg_since);
665 else if (arg_until != USEC_INFINITY && arg_reverse)
666 r = sd_journal_seek_realtime_usec(j, arg_until);
667 else if (arg_reverse)
668 r = sd_journal_seek_tail(j);
669 else
670 r = sd_journal_seek_head(j);
671 if (r < 0)
672 return log_error_errno(r, "Failed to seek to date: %m");
673
674 for (;;) {
675 if (!arg_reverse)
676 r = sd_journal_next(j);
677 else
678 r = sd_journal_previous(j);
679
680 if (r < 0)
681 return log_error_errno(r, "Failed to iterate through journal: %m");
682
683 if (r == 0)
684 break;
685
686 if (arg_until != USEC_INFINITY && !arg_reverse) {
687 usec_t usec;
688
689 r = sd_journal_get_realtime_usec(j, &usec);
690 if (r < 0)
691 return log_error_errno(r, "Failed to determine timestamp: %m");
692 if (usec > arg_until)
693 continue;
694 }
695
696 if (arg_since != USEC_INFINITY && arg_reverse) {
697 usec_t usec;
698
699 r = sd_journal_get_realtime_usec(j, &usec);
700 if (r < 0)
701 return log_error_errno(r, "Failed to determine timestamp: %m");
702 if (usec < arg_since)
703 continue;
704 }
705
706 r = print_entry(j, n_found++);
707 if (r < 0)
708 return r;
709 }
710
711 if (!arg_field && n_found <= 0) {
712 if (!arg_quiet)
713 log_notice("No coredumps found.");
714 return -ESRCH;
715 }
716 }
717
718 return 0;
719 }
720
721 static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) {
722 const char *data;
723 _cleanup_free_ char *filename = NULL;
724 size_t len;
725 int r, fd;
726 _cleanup_close_ int fdt = -1;
727 char *temp = NULL;
728
729 assert(!(file && path)); /* At most one can be specified */
730 assert(!!path == !!unlink_temp); /* Those must be specified together */
731
732 /* Look for a coredump on disk first. */
733 r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len);
734 if (r == 0)
735 retrieve(data, len, "COREDUMP_FILENAME", &filename);
736 else {
737 if (r != -ENOENT)
738 return log_error_errno(r, "Failed to retrieve COREDUMP_FILENAME field: %m");
739 /* Check that we can have a COREDUMP field. We still haven't set a high
740 * data threshold, so we'll get a few kilobytes at most.
741 */
742
743 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
744 if (r == -ENOENT)
745 return log_error_errno(r, "Coredump entry has no core attached (neither internally in the journal nor externally on disk).");
746 if (r < 0)
747 return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
748 }
749
750 if (filename) {
751 if (access(filename, R_OK) < 0)
752 return log_error_errno(errno, "File \"%s\" is not readable: %m", filename);
753
754 if (path && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) {
755 *path = filename;
756 filename = NULL;
757
758 return 0;
759 }
760 }
761
762 if (path) {
763 const char *vt;
764
765 /* Create a temporary file to write the uncompressed core to. */
766
767 r = var_tmp_dir(&vt);
768 if (r < 0)
769 return log_error_errno(r, "Failed to acquire temporary directory path: %m");
770
771 temp = strjoin(vt, "/coredump-XXXXXX");
772 if (!temp)
773 return log_oom();
774
775 fdt = mkostemp_safe(temp);
776 if (fdt < 0)
777 return log_error_errno(fdt, "Failed to create temporary file: %m");
778 log_debug("Created temporary file %s", temp);
779
780 fd = fdt;
781 } else {
782 /* If neither path or file are specified, we will write to stdout. Let's now check
783 * if stdout is connected to a tty. We checked that the file exists, or that the
784 * core might be stored in the journal. In this second case, if we found the entry,
785 * in all likelyhood we will be able to access the COREDUMP= field. In either case,
786 * we stop before doing any "real" work, i.e. before starting decompression or
787 * reading from the file or creating temporary files.
788 */
789 if (!file) {
790 if (on_tty())
791 return log_error_errno(ENOTTY, "Refusing to dump core to tty"
792 " (use shell redirection or specify --output).");
793 file = stdout;
794 }
795
796 fd = fileno(file);
797 }
798
799 if (filename) {
800 #if HAVE_XZ || HAVE_LZ4
801 _cleanup_close_ int fdf;
802
803 fdf = open(filename, O_RDONLY | O_CLOEXEC);
804 if (fdf < 0) {
805 r = log_error_errno(errno, "Failed to open %s: %m", filename);
806 goto error;
807 }
808
809 r = decompress_stream(filename, fdf, fd, -1);
810 if (r < 0) {
811 log_error_errno(r, "Failed to decompress %s: %m", filename);
812 goto error;
813 }
814 #else
815 log_error("Cannot decompress file. Compiled without compression support.");
816 r = -EOPNOTSUPP;
817 goto error;
818 #endif
819 } else {
820 ssize_t sz;
821
822 /* We want full data, nothing truncated. */
823 sd_journal_set_data_threshold(j, 0);
824
825 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
826 if (r < 0)
827 return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
828
829 assert(len >= 9);
830 data += 9;
831 len -= 9;
832
833 sz = write(fd, data, len);
834 if (sz < 0) {
835 r = log_error_errno(errno, "Failed to write output: %m");
836 goto error;
837 }
838 if (sz != (ssize_t) len) {
839 log_error("Short write to output.");
840 r = -EIO;
841 goto error;
842 }
843 }
844
845 if (temp) {
846 *path = temp;
847 *unlink_temp = true;
848 }
849 return 0;
850
851 error:
852 if (temp) {
853 unlink(temp);
854 log_debug("Removed temporary file %s", temp);
855 }
856 return r;
857 }
858
859 static int dump_core(sd_journal* j) {
860 int r;
861
862 assert(j);
863
864 r = focus(j);
865 if (r < 0)
866 return r;
867
868 print_info(arg_output ? stdout : stderr, j, false);
869
870 r = save_core(j, arg_output, NULL, NULL);
871 if (r < 0)
872 return r;
873
874 r = sd_journal_previous(j);
875 if (r > 0 && !arg_quiet)
876 log_notice("More than one entry matches, ignoring rest.");
877
878 return 0;
879 }
880
881 static int run_gdb(sd_journal *j) {
882 _cleanup_free_ char *exe = NULL, *path = NULL;
883 bool unlink_path = false;
884 const char *data;
885 size_t len;
886 pid_t pid;
887 int r;
888
889 assert(j);
890
891 r = focus(j);
892 if (r < 0)
893 return r;
894
895 print_info(stdout, j, false);
896 fputs("\n", stdout);
897
898 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
899 if (r < 0)
900 return log_error_errno(r, "Failed to retrieve COREDUMP_EXE field: %m");
901
902 assert(len > STRLEN("COREDUMP_EXE="));
903 data += STRLEN("COREDUMP_EXE=");
904 len -= STRLEN("COREDUMP_EXE=");
905
906 exe = strndup(data, len);
907 if (!exe)
908 return log_oom();
909
910 if (endswith(exe, " (deleted)")) {
911 log_error("Binary already deleted.");
912 return -ENOENT;
913 }
914
915 if (!path_is_absolute(exe)) {
916 log_error("Binary is not an absolute path.");
917 return -ENOENT;
918 }
919
920 r = save_core(j, NULL, &path, &unlink_path);
921 if (r < 0)
922 return r;
923
924 /* Don't interfere with gdb and its handling of SIGINT. */
925 (void) ignore_signals(SIGINT, -1);
926
927 r = safe_fork("(gdb)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
928 if (r < 0)
929 goto finish;
930 if (r == 0) {
931 execlp("gdb", "gdb", exe, path, NULL);
932 log_open();
933 log_error_errno(errno, "Failed to invoke gdb: %m");
934 _exit(EXIT_FAILURE);
935 }
936
937 r = wait_for_terminate_and_check("gdb", pid, WAIT_LOG_ABNORMAL);
938
939 finish:
940 (void) default_signals(SIGINT, -1);
941
942 if (unlink_path) {
943 log_debug("Removed temporary file %s", path);
944 unlink(path);
945 }
946
947 return r;
948 }
949
950 static int check_units_active(void) {
951 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
952 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
953 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
954 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
955 int c = 0, r;
956 const char *id, *state, *substate;
957
958 if (arg_quiet)
959 return false;
960
961 r = sd_bus_default_system(&bus);
962 if (r < 0)
963 return log_error_errno(r, "Failed to acquire bus: %m");
964
965 r = sd_bus_message_new_method_call(
966 bus,
967 &m,
968 "org.freedesktop.systemd1",
969 "/org/freedesktop/systemd1",
970 "org.freedesktop.systemd1.Manager",
971 "ListUnitsByPatterns");
972 if (r < 0)
973 return bus_log_create_error(r);
974
975 r = sd_bus_message_append_strv(m, NULL);
976 if (r < 0)
977 return bus_log_create_error(r);
978
979 r = sd_bus_message_append_strv(m, STRV_MAKE("systemd-coredump@*.service"));
980 if (r < 0)
981 return bus_log_create_error(r);
982
983 r = sd_bus_call(bus, m, SHORT_BUS_CALL_TIMEOUT_USEC, &error, &reply);
984 if (r < 0)
985 return log_error_errno(r, "Failed to check if any systemd-coredump@.service units are running: %s",
986 bus_error_message(&error, r));
987
988 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
989 if (r < 0)
990 return bus_log_parse_error(r);
991
992 while ((r = sd_bus_message_read(
993 reply, "(ssssssouso)",
994 &id, NULL, NULL, &state, &substate,
995 NULL, NULL, NULL, NULL, NULL)) > 0) {
996 bool found = !STR_IN_SET(state, "inactive", "dead", "failed");
997 log_debug("Unit %s is %s/%s, %scounting it.", id, state, substate, found ? "" : "not ");
998 c += found;
999 }
1000 if (r < 0)
1001 return bus_log_parse_error(r);
1002
1003 r = sd_bus_message_exit_container(reply);
1004 if (r < 0)
1005 return bus_log_parse_error(r);
1006
1007 return c;
1008 }
1009
1010 int main(int argc, char *argv[]) {
1011 _cleanup_(sd_journal_closep) sd_journal*j = NULL;
1012 int r = 0, units_active;
1013
1014 setlocale(LC_ALL, "");
1015 log_parse_environment();
1016 log_open();
1017
1018 r = parse_argv(argc, argv);
1019 if (r < 0)
1020 goto end;
1021
1022 if (arg_action == ACTION_NONE)
1023 goto end;
1024
1025 sigbus_install();
1026
1027 if (arg_directory) {
1028 r = sd_journal_open_directory(&j, arg_directory, 0);
1029 if (r < 0) {
1030 log_error_errno(r, "Failed to open journals in directory: %s: %m", arg_directory);
1031 goto end;
1032 }
1033 } else {
1034 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
1035 if (r < 0) {
1036 log_error_errno(r, "Failed to open journal: %m");
1037 goto end;
1038 }
1039 }
1040
1041 r = journal_access_check_and_warn(j, arg_quiet, true);
1042 if (r < 0)
1043 goto end;
1044
1045 r = add_matches(j);
1046 if (r < 0)
1047 goto end;
1048
1049 if (DEBUG_LOGGING) {
1050 _cleanup_free_ char *filter;
1051
1052 filter = journal_make_match_string(j);
1053 log_debug("Journal filter: %s", filter);
1054 }
1055
1056 units_active = check_units_active(); /* error is treated the same as 0 */
1057
1058 switch(arg_action) {
1059
1060 case ACTION_LIST:
1061 case ACTION_INFO:
1062 pager_open(arg_no_pager, false);
1063 r = dump_list(j);
1064 break;
1065
1066 case ACTION_DUMP:
1067 r = dump_core(j);
1068 break;
1069
1070 case ACTION_GDB:
1071 r = run_gdb(j);
1072 break;
1073
1074 default:
1075 assert_not_reached("Shouldn't be here");
1076 }
1077
1078 if (units_active > 0)
1079 printf("%s-- Notice: %d systemd-coredump@.service %s, output may be incomplete.%s\n",
1080 ansi_highlight_red(),
1081 units_active, units_active == 1 ? "unit is running" : "units are running",
1082 ansi_normal());
1083 end:
1084 pager_close();
1085
1086 if (arg_output)
1087 fclose(arg_output);
1088
1089 return r >= 0 ? r : EXIT_FAILURE;
1090 }