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