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