]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/coredump/coredumpctl.c
core/namespace: rework the return semantics of clone_device_node yet again
[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 const char* prefix, *pattern;
71 pid_t pid;
72 int r;
73
74 if (strchr(match, '='))
75 prefix = "";
76 else if (strchr(match, '/')) {
77 r = path_make_absolute_cwd(match, &p);
78 if (r < 0)
79 return log_error_errno(r, "path_make_absolute_cwd(\"%s\"): %m", match);
80
81 match = p;
82 prefix = "COREDUMP_EXE=";
83 } else if (parse_pid(match, &pid) >= 0)
84 prefix = "COREDUMP_PID=";
85 else
86 prefix = "COREDUMP_COMM=";
87
88 pattern = strjoina(prefix, match);
89 log_debug("Adding match: %s", pattern);
90 r = sd_journal_add_match(j, pattern, 0);
91 if (r < 0)
92 return log_error_errno(r, "Failed to add match \"%s\": %m", match);
93
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 = TAKE_PTR(filename);
768
769 return 0;
770 }
771 }
772
773 if (path) {
774 const char *vt;
775
776 /* Create a temporary file to write the uncompressed core to. */
777
778 r = var_tmp_dir(&vt);
779 if (r < 0)
780 return log_error_errno(r, "Failed to acquire temporary directory path: %m");
781
782 temp = strjoin(vt, "/coredump-XXXXXX");
783 if (!temp)
784 return log_oom();
785
786 fdt = mkostemp_safe(temp);
787 if (fdt < 0)
788 return log_error_errno(fdt, "Failed to create temporary file: %m");
789 log_debug("Created temporary file %s", temp);
790
791 fd = fdt;
792 } else {
793 /* If neither path or file are specified, we will write to stdout. Let's now check
794 * if stdout is connected to a tty. We checked that the file exists, or that the
795 * core might be stored in the journal. In this second case, if we found the entry,
796 * in all likelyhood we will be able to access the COREDUMP= field. In either case,
797 * we stop before doing any "real" work, i.e. before starting decompression or
798 * reading from the file or creating temporary files.
799 */
800 if (!file) {
801 if (on_tty())
802 return log_error_errno(ENOTTY, "Refusing to dump core to tty"
803 " (use shell redirection or specify --output).");
804 file = stdout;
805 }
806
807 fd = fileno(file);
808 }
809
810 if (filename) {
811 #if HAVE_XZ || HAVE_LZ4
812 _cleanup_close_ int fdf;
813
814 fdf = open(filename, O_RDONLY | O_CLOEXEC);
815 if (fdf < 0) {
816 r = log_error_errno(errno, "Failed to open %s: %m", filename);
817 goto error;
818 }
819
820 r = decompress_stream(filename, fdf, fd, -1);
821 if (r < 0) {
822 log_error_errno(r, "Failed to decompress %s: %m", filename);
823 goto error;
824 }
825 #else
826 log_error("Cannot decompress file. Compiled without compression support.");
827 r = -EOPNOTSUPP;
828 goto error;
829 #endif
830 } else {
831 ssize_t sz;
832
833 /* We want full data, nothing truncated. */
834 sd_journal_set_data_threshold(j, 0);
835
836 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
837 if (r < 0)
838 return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
839
840 assert(len >= 9);
841 data += 9;
842 len -= 9;
843
844 sz = write(fd, data, len);
845 if (sz < 0) {
846 r = log_error_errno(errno, "Failed to write output: %m");
847 goto error;
848 }
849 if (sz != (ssize_t) len) {
850 log_error("Short write to output.");
851 r = -EIO;
852 goto error;
853 }
854 }
855
856 if (temp) {
857 *path = temp;
858 *unlink_temp = true;
859 }
860 return 0;
861
862 error:
863 if (temp) {
864 unlink(temp);
865 log_debug("Removed temporary file %s", temp);
866 }
867 return r;
868 }
869
870 static int dump_core(int argc, char **argv, void *userdata) {
871 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
872 int r;
873
874 if (arg_field) {
875 log_error("Option --field/-F only makes sense with list");
876 return -EINVAL;
877 }
878
879 r = acquire_journal(&j, argv + 1);
880 if (r < 0)
881 return r;
882
883 r = focus(j);
884 if (r < 0)
885 return r;
886
887 print_info(arg_output ? stdout : stderr, j, false);
888
889 r = save_core(j, arg_output, NULL, NULL);
890 if (r < 0)
891 return r;
892
893 r = sd_journal_previous(j);
894 if (r > 0 && !arg_quiet)
895 log_notice("More than one entry matches, ignoring rest.");
896
897 return 0;
898 }
899
900 static int run_gdb(int argc, char **argv, void *userdata) {
901 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
902 _cleanup_free_ char *exe = NULL, *path = NULL;
903 bool unlink_path = false;
904 const char *data;
905 size_t len;
906 pid_t pid;
907 int r;
908
909 if (arg_field) {
910 log_error("Option --field/-F only makes sense with list");
911 return -EINVAL;
912 }
913
914 r = acquire_journal(&j, argv + 1);
915 if (r < 0)
916 return r;
917
918 r = focus(j);
919 if (r < 0)
920 return r;
921
922 print_info(stdout, j, false);
923 fputs("\n", stdout);
924
925 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
926 if (r < 0)
927 return log_error_errno(r, "Failed to retrieve COREDUMP_EXE field: %m");
928
929 assert(len > STRLEN("COREDUMP_EXE="));
930 data += STRLEN("COREDUMP_EXE=");
931 len -= STRLEN("COREDUMP_EXE=");
932
933 exe = strndup(data, len);
934 if (!exe)
935 return log_oom();
936
937 if (endswith(exe, " (deleted)")) {
938 log_error("Binary already deleted.");
939 return -ENOENT;
940 }
941
942 if (!path_is_absolute(exe)) {
943 log_error("Binary is not an absolute path.");
944 return -ENOENT;
945 }
946
947 r = save_core(j, NULL, &path, &unlink_path);
948 if (r < 0)
949 return r;
950
951 /* Don't interfere with gdb and its handling of SIGINT. */
952 (void) ignore_signals(SIGINT, -1);
953
954 r = safe_fork("(gdb)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
955 if (r < 0)
956 goto finish;
957 if (r == 0) {
958 execlp("gdb", "gdb", exe, path, NULL);
959 log_open();
960 log_error_errno(errno, "Failed to invoke gdb: %m");
961 _exit(EXIT_FAILURE);
962 }
963
964 r = wait_for_terminate_and_check("gdb", pid, WAIT_LOG_ABNORMAL);
965
966 finish:
967 (void) default_signals(SIGINT, -1);
968
969 if (unlink_path) {
970 log_debug("Removed temporary file %s", path);
971 unlink(path);
972 }
973
974 return r;
975 }
976
977 static int check_units_active(void) {
978 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
979 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
980 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
981 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
982 int c = 0, r;
983 const char *id, *state, *substate;
984
985 if (arg_quiet)
986 return false;
987
988 r = sd_bus_default_system(&bus);
989 if (r < 0)
990 return log_error_errno(r, "Failed to acquire bus: %m");
991
992 r = sd_bus_message_new_method_call(
993 bus,
994 &m,
995 "org.freedesktop.systemd1",
996 "/org/freedesktop/systemd1",
997 "org.freedesktop.systemd1.Manager",
998 "ListUnitsByPatterns");
999 if (r < 0)
1000 return bus_log_create_error(r);
1001
1002 r = sd_bus_message_append_strv(m, NULL);
1003 if (r < 0)
1004 return bus_log_create_error(r);
1005
1006 r = sd_bus_message_append_strv(m, STRV_MAKE("systemd-coredump@*.service"));
1007 if (r < 0)
1008 return bus_log_create_error(r);
1009
1010 r = sd_bus_call(bus, m, SHORT_BUS_CALL_TIMEOUT_USEC, &error, &reply);
1011 if (r < 0)
1012 return log_error_errno(r, "Failed to check if any systemd-coredump@.service units are running: %s",
1013 bus_error_message(&error, r));
1014
1015 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1016 if (r < 0)
1017 return bus_log_parse_error(r);
1018
1019 while ((r = sd_bus_message_read(
1020 reply, "(ssssssouso)",
1021 &id, NULL, NULL, &state, &substate,
1022 NULL, NULL, NULL, NULL, NULL)) > 0) {
1023 bool found = !STR_IN_SET(state, "inactive", "dead", "failed");
1024 log_debug("Unit %s is %s/%s, %scounting it.", id, state, substate, found ? "" : "not ");
1025 c += found;
1026 }
1027 if (r < 0)
1028 return bus_log_parse_error(r);
1029
1030 r = sd_bus_message_exit_container(reply);
1031 if (r < 0)
1032 return bus_log_parse_error(r);
1033
1034 return c;
1035 }
1036
1037 static int coredumpctl_main(int argc, char *argv[]) {
1038
1039 static const Verb verbs[] = {
1040 { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, dump_list },
1041 { "info", VERB_ANY, VERB_ANY, 0, dump_list },
1042 { "dump", VERB_ANY, VERB_ANY, 0, dump_core },
1043 { "gdb", VERB_ANY, VERB_ANY, 0, run_gdb },
1044 {}
1045 };
1046
1047 return dispatch_verb(argc, argv, verbs, NULL);
1048 }
1049
1050 int main(int argc, char *argv[]) {
1051 int r, units_active;
1052
1053 setlocale(LC_ALL, "");
1054 log_parse_environment();
1055 log_open();
1056
1057 r = parse_argv(argc, argv);
1058 if (r <= 0)
1059 goto end;
1060
1061 sigbus_install();
1062
1063 units_active = check_units_active(); /* error is treated the same as 0 */
1064
1065 r = coredumpctl_main(argc, argv);
1066
1067 if (units_active > 0)
1068 printf("%s-- Notice: %d systemd-coredump@.service %s, output may be incomplete.%s\n",
1069 ansi_highlight_red(),
1070 units_active, units_active == 1 ? "unit is running" : "units are running",
1071 ansi_normal());
1072 end:
1073 pager_close();
1074
1075 safe_fclose(arg_output);
1076
1077 return r >= 0 ? r : EXIT_FAILURE;
1078 }