]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/coredump/coredumpctl.c
Add SPDX license identifiers to source files under the LGPL
[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 = strjoin(prefix, match);
98 if (!pattern)
99 return log_oom();
100
101 log_debug("Adding match: %s", pattern);
102 r = sd_journal_add_match(j, pattern, 0);
103 if (r < 0)
104 return log_error_errno(r, "Failed to add match \"%s\": %m", match);
105 return 0;
106 }
107
108 static int add_matches(sd_journal *j) {
109 char **match;
110 int r;
111
112 r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR, 0);
113 if (r < 0)
114 return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR);
115
116 r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR, 0);
117 if (r < 0)
118 return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR);
119
120 STRV_FOREACH(match, arg_matches) {
121 r = add_match(j, *match);
122 if (r < 0)
123 return r;
124 }
125
126 return 0;
127 }
128
129 static void help(void) {
130 printf("%s [OPTIONS...]\n\n"
131 "List or retrieve coredumps from the journal.\n\n"
132 "Flags:\n"
133 " -h --help Show this help\n"
134 " --version Print version string\n"
135 " --no-pager Do not pipe output into a pager\n"
136 " --no-legend Do not print the column headers.\n"
137 " -1 Show information about most recent entry only\n"
138 " -S --since=DATE Only print coredumps since the date\n"
139 " -U --until=DATE Only print coredumps until the date\n"
140 " -r --reverse Show the newest entries first\n"
141 " -F --field=FIELD List all values a certain field takes\n"
142 " -o --output=FILE Write output to FILE\n"
143 " -D --directory=DIR Use journal files from directory\n\n"
144 " -q --quiet Do not show info messages and privilege warning\n"
145 "Commands:\n"
146 " list [MATCHES...] List available coredumps (default)\n"
147 " info [MATCHES...] Show detailed information about one or more coredumps\n"
148 " dump [MATCHES...] Print first matching coredump to stdout\n"
149 " gdb [MATCHES...] Start gdb for the first matching coredump\n"
150 , program_invocation_short_name);
151 }
152
153 static int parse_argv(int argc, char *argv[]) {
154 enum {
155 ARG_VERSION = 0x100,
156 ARG_NO_PAGER,
157 ARG_NO_LEGEND,
158 };
159
160 int c, r;
161
162 static const struct option options[] = {
163 { "help", no_argument, NULL, 'h' },
164 { "version" , no_argument, NULL, ARG_VERSION },
165 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
166 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
167 { "output", required_argument, NULL, 'o' },
168 { "field", required_argument, NULL, 'F' },
169 { "directory", required_argument, NULL, 'D' },
170 { "reverse", no_argument, NULL, 'r' },
171 { "since", required_argument, NULL, 'S' },
172 { "until", required_argument, NULL, 'U' },
173 { "quiet", no_argument, NULL, 'q' },
174 {}
175 };
176
177 assert(argc >= 0);
178 assert(argv);
179
180 while ((c = getopt_long(argc, argv, "ho:F:1D:rS:U:q", options, NULL)) >= 0)
181 switch(c) {
182 case 'h':
183 arg_action = ACTION_NONE;
184 help();
185 return 0;
186
187 case ARG_VERSION:
188 arg_action = ACTION_NONE;
189 return version();
190
191 case ARG_NO_PAGER:
192 arg_no_pager = true;
193 break;
194
195 case ARG_NO_LEGEND:
196 arg_no_legend = true;
197 break;
198
199 case 'o':
200 if (arg_output) {
201 log_error("cannot set output more than once");
202 return -EINVAL;
203 }
204
205 arg_output = fopen(optarg, "we");
206 if (!arg_output)
207 return log_error_errno(errno, "writing to '%s': %m", optarg);
208
209 break;
210
211 case 'S':
212 r = parse_timestamp(optarg, &arg_since);
213 if (r < 0)
214 return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
215 break;
216
217 case 'U':
218 r = parse_timestamp(optarg, &arg_until);
219 if (r < 0)
220 return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
221 break;
222
223 case 'F':
224 if (arg_field) {
225 log_error("cannot use --field/-F more than once");
226 return -EINVAL;
227 }
228 arg_field = optarg;
229 break;
230
231 case '1':
232 arg_one = true;
233 break;
234
235 case 'D':
236 arg_directory = optarg;
237 break;
238
239 case 'r':
240 arg_reverse = true;
241 break;
242
243 case 'q':
244 arg_quiet = true;
245 break;
246
247 case '?':
248 return -EINVAL;
249
250 default:
251 assert_not_reached("Unhandled option");
252 }
253
254 if (arg_since != USEC_INFINITY && arg_until != USEC_INFINITY &&
255 arg_since > arg_until) {
256 log_error("--since= must be before --until=.");
257 return -EINVAL;
258 }
259
260 if (optind < argc) {
261 const char *cmd = argv[optind++];
262 if (streq(cmd, "list"))
263 arg_action = ACTION_LIST;
264 else if (streq(cmd, "dump"))
265 arg_action = ACTION_DUMP;
266 else if (streq(cmd, "gdb"))
267 arg_action = ACTION_GDB;
268 else if (streq(cmd, "info"))
269 arg_action = ACTION_INFO;
270 else {
271 log_error("Unknown action '%s'", cmd);
272 return -EINVAL;
273 }
274 }
275
276 if (arg_field && arg_action != ACTION_LIST) {
277 log_error("Option --field/-F only makes sense with list");
278 return -EINVAL;
279 }
280
281 if (optind < argc)
282 arg_matches = argv + optind;
283
284 return 0;
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) {
638 assert(j);
639
640 if (arg_action == ACTION_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(sd_journal *j) {
649 unsigned n_found = 0;
650 int r;
651
652 assert(j);
653
654 /* The coredumps are likely to compressed, and for just
655 * listing them we don't need to decompress them, so let's
656 * pick a fairly low data threshold here */
657 sd_journal_set_data_threshold(j, 4096);
658
659 if (arg_one) {
660 r = focus(j);
661 if (r < 0)
662 return r;
663
664 return print_entry(j, 0);
665 } else {
666 if (arg_since != USEC_INFINITY && !arg_reverse)
667 r = sd_journal_seek_realtime_usec(j, arg_since);
668 else if (arg_until != USEC_INFINITY && arg_reverse)
669 r = sd_journal_seek_realtime_usec(j, arg_until);
670 else if (arg_reverse)
671 r = sd_journal_seek_tail(j);
672 else
673 r = sd_journal_seek_head(j);
674 if (r < 0)
675 return log_error_errno(r, "Failed to seek to date: %m");
676
677 for (;;) {
678 if (!arg_reverse)
679 r = sd_journal_next(j);
680 else
681 r = sd_journal_previous(j);
682
683 if (r < 0)
684 return log_error_errno(r, "Failed to iterate through journal: %m");
685
686 if (r == 0)
687 break;
688
689 if (arg_until != USEC_INFINITY && !arg_reverse) {
690 usec_t usec;
691
692 r = sd_journal_get_realtime_usec(j, &usec);
693 if (r < 0)
694 return log_error_errno(r, "Failed to determine timestamp: %m");
695 if (usec > arg_until)
696 continue;
697 }
698
699 if (arg_since != USEC_INFINITY && arg_reverse) {
700 usec_t usec;
701
702 r = sd_journal_get_realtime_usec(j, &usec);
703 if (r < 0)
704 return log_error_errno(r, "Failed to determine timestamp: %m");
705 if (usec < arg_since)
706 continue;
707 }
708
709 r = print_entry(j, n_found++);
710 if (r < 0)
711 return r;
712 }
713
714 if (!arg_field && n_found <= 0) {
715 if (!arg_quiet)
716 log_notice("No coredumps found.");
717 return -ESRCH;
718 }
719 }
720
721 return 0;
722 }
723
724 static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) {
725 const char *data;
726 _cleanup_free_ char *filename = NULL;
727 size_t len;
728 int r, fd;
729 _cleanup_close_ int fdt = -1;
730 char *temp = NULL;
731
732 assert(!(file && path)); /* At most one can be specified */
733 assert(!!path == !!unlink_temp); /* Those must be specified together */
734
735 /* Look for a coredump on disk first. */
736 r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len);
737 if (r == 0)
738 retrieve(data, len, "COREDUMP_FILENAME", &filename);
739 else {
740 if (r != -ENOENT)
741 return log_error_errno(r, "Failed to retrieve COREDUMP_FILENAME field: %m");
742 /* Check that we can have a COREDUMP field. We still haven't set a high
743 * data threshold, so we'll get a few kilobytes at most.
744 */
745
746 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
747 if (r == -ENOENT)
748 return log_error_errno(r, "Coredump entry has no core attached (neither internally in the journal nor externally on disk).");
749 if (r < 0)
750 return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
751 }
752
753 if (filename) {
754 if (access(filename, R_OK) < 0)
755 return log_error_errno(errno, "File \"%s\" is not readable: %m", filename);
756
757 if (path && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) {
758 *path = filename;
759 filename = NULL;
760
761 return 0;
762 }
763 }
764
765 if (path) {
766 const char *vt;
767
768 /* Create a temporary file to write the uncompressed core to. */
769
770 r = var_tmp_dir(&vt);
771 if (r < 0)
772 return log_error_errno(r, "Failed to acquire temporary directory path: %m");
773
774 temp = strjoin(vt, "/coredump-XXXXXX");
775 if (!temp)
776 return log_oom();
777
778 fdt = mkostemp_safe(temp);
779 if (fdt < 0)
780 return log_error_errno(fdt, "Failed to create temporary file: %m");
781 log_debug("Created temporary file %s", temp);
782
783 fd = fdt;
784 } else {
785 /* If neither path or file are specified, we will write to stdout. Let's now check
786 * if stdout is connected to a tty. We checked that the file exists, or that the
787 * core might be stored in the journal. In this second case, if we found the entry,
788 * in all likelyhood we will be able to access the COREDUMP= field. In either case,
789 * we stop before doing any "real" work, i.e. before starting decompression or
790 * reading from the file or creating temporary files.
791 */
792 if (!file) {
793 if (on_tty())
794 return log_error_errno(ENOTTY, "Refusing to dump core to tty"
795 " (use shell redirection or specify --output).");
796 file = stdout;
797 }
798
799 fd = fileno(file);
800 }
801
802 if (filename) {
803 #if HAVE_XZ || HAVE_LZ4
804 _cleanup_close_ int fdf;
805
806 fdf = open(filename, O_RDONLY | O_CLOEXEC);
807 if (fdf < 0) {
808 r = log_error_errno(errno, "Failed to open %s: %m", filename);
809 goto error;
810 }
811
812 r = decompress_stream(filename, fdf, fd, -1);
813 if (r < 0) {
814 log_error_errno(r, "Failed to decompress %s: %m", filename);
815 goto error;
816 }
817 #else
818 log_error("Cannot decompress file. Compiled without compression support.");
819 r = -EOPNOTSUPP;
820 goto error;
821 #endif
822 } else {
823 ssize_t sz;
824
825 /* We want full data, nothing truncated. */
826 sd_journal_set_data_threshold(j, 0);
827
828 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
829 if (r < 0)
830 return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
831
832 assert(len >= 9);
833 data += 9;
834 len -= 9;
835
836 sz = write(fd, data, len);
837 if (sz < 0) {
838 r = log_error_errno(errno, "Failed to write output: %m");
839 goto error;
840 }
841 if (sz != (ssize_t) len) {
842 log_error("Short write to output.");
843 r = -EIO;
844 goto error;
845 }
846 }
847
848 if (temp) {
849 *path = temp;
850 *unlink_temp = true;
851 }
852 return 0;
853
854 error:
855 if (temp) {
856 unlink(temp);
857 log_debug("Removed temporary file %s", temp);
858 }
859 return r;
860 }
861
862 static int dump_core(sd_journal* j) {
863 int r;
864
865 assert(j);
866
867 r = focus(j);
868 if (r < 0)
869 return r;
870
871 print_info(arg_output ? stdout : stderr, j, false);
872
873 r = save_core(j, arg_output, NULL, NULL);
874 if (r < 0)
875 return r;
876
877 r = sd_journal_previous(j);
878 if (r > 0 && !arg_quiet)
879 log_notice("More than one entry matches, ignoring rest.");
880
881 return 0;
882 }
883
884 static int run_gdb(sd_journal *j) {
885 _cleanup_free_ char *exe = NULL, *path = NULL;
886 bool unlink_path = false;
887 const char *data;
888 siginfo_t st;
889 size_t len;
890 pid_t pid;
891 int r;
892
893 assert(j);
894
895 r = focus(j);
896 if (r < 0)
897 return r;
898
899 print_info(stdout, j, false);
900 fputs("\n", stdout);
901
902 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
903 if (r < 0)
904 return log_error_errno(r, "Failed to retrieve COREDUMP_EXE field: %m");
905
906 assert(len > strlen("COREDUMP_EXE="));
907 data += strlen("COREDUMP_EXE=");
908 len -= strlen("COREDUMP_EXE=");
909
910 exe = strndup(data, len);
911 if (!exe)
912 return log_oom();
913
914 if (endswith(exe, " (deleted)")) {
915 log_error("Binary already deleted.");
916 return -ENOENT;
917 }
918
919 if (!path_is_absolute(exe)) {
920 log_error("Binary is not an absolute path.");
921 return -ENOENT;
922 }
923
924 r = save_core(j, NULL, &path, &unlink_path);
925 if (r < 0)
926 return r;
927
928 /* Don't interfere with gdb and its handling of SIGINT. */
929 (void) ignore_signals(SIGINT, -1);
930
931 pid = fork();
932 if (pid < 0) {
933 r = log_error_errno(errno, "Failed to fork(): %m");
934 goto finish;
935 }
936 if (pid == 0) {
937 (void) reset_all_signal_handlers();
938 (void) reset_signal_mask();
939
940 execlp("gdb", "gdb", exe, path, NULL);
941
942 log_error_errno(errno, "Failed to invoke gdb: %m");
943 _exit(1);
944 }
945
946 r = wait_for_terminate(pid, &st);
947 if (r < 0) {
948 log_error_errno(r, "Failed to wait for gdb: %m");
949 goto finish;
950 }
951
952 r = st.si_code == CLD_EXITED ? st.si_status : 255;
953
954 finish:
955 (void) default_signals(SIGINT, -1);
956
957 if (unlink_path) {
958 log_debug("Removed temporary file %s", path);
959 unlink(path);
960 }
961
962 return r;
963 }
964
965 static int check_units_active(void) {
966 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
967 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
968 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
969 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
970 int c = 0, r;
971 const char *id, *state, *substate;
972
973 if (arg_quiet)
974 return false;
975
976 r = sd_bus_default_system(&bus);
977 if (r < 0)
978 return log_error_errno(r, "Failed to acquire bus: %m");
979
980 r = sd_bus_message_new_method_call(
981 bus,
982 &m,
983 "org.freedesktop.systemd1",
984 "/org/freedesktop/systemd1",
985 "org.freedesktop.systemd1.Manager",
986 "ListUnitsByPatterns");
987 if (r < 0)
988 return bus_log_create_error(r);
989
990 r = sd_bus_message_append_strv(m, NULL);
991 if (r < 0)
992 return bus_log_create_error(r);
993
994 r = sd_bus_message_append_strv(m, STRV_MAKE("systemd-coredump@*.service"));
995 if (r < 0)
996 return bus_log_create_error(r);
997
998 r = sd_bus_call(bus, m, SHORT_BUS_CALL_TIMEOUT_USEC, &error, &reply);
999 if (r < 0)
1000 return log_error_errno(r, "Failed to check if any systemd-coredump@.service units are running: %s",
1001 bus_error_message(&error, r));
1002
1003 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1004 if (r < 0)
1005 return bus_log_parse_error(r);
1006
1007 while ((r = sd_bus_message_read(
1008 reply, "(ssssssouso)",
1009 &id, NULL, NULL, &state, &substate,
1010 NULL, NULL, NULL, NULL, NULL)) > 0) {
1011 bool found = !STR_IN_SET(state, "inactive", "dead", "failed");
1012 log_debug("Unit %s is %s/%s, %scounting it.", id, state, substate, found ? "" : "not ");
1013 c += found;
1014 }
1015 if (r < 0)
1016 return bus_log_parse_error(r);
1017
1018 r = sd_bus_message_exit_container(reply);
1019 if (r < 0)
1020 return bus_log_parse_error(r);
1021
1022 return c;
1023 }
1024
1025 int main(int argc, char *argv[]) {
1026 _cleanup_(sd_journal_closep) sd_journal*j = NULL;
1027 int r = 0, units_active;
1028
1029 setlocale(LC_ALL, "");
1030 log_parse_environment();
1031 log_open();
1032
1033 r = parse_argv(argc, argv);
1034 if (r < 0)
1035 goto end;
1036
1037 if (arg_action == ACTION_NONE)
1038 goto end;
1039
1040 sigbus_install();
1041
1042 if (arg_directory) {
1043 r = sd_journal_open_directory(&j, arg_directory, 0);
1044 if (r < 0) {
1045 log_error_errno(r, "Failed to open journals in directory: %s: %m", arg_directory);
1046 goto end;
1047 }
1048 } else {
1049 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
1050 if (r < 0) {
1051 log_error_errno(r, "Failed to open journal: %m");
1052 goto end;
1053 }
1054 }
1055
1056 r = journal_access_check_and_warn(j, arg_quiet);
1057 if (r < 0)
1058 goto end;
1059
1060 r = add_matches(j);
1061 if (r < 0)
1062 goto end;
1063
1064 if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
1065 _cleanup_free_ char *filter;
1066
1067 filter = journal_make_match_string(j);
1068 log_debug("Journal filter: %s", filter);
1069 }
1070
1071 units_active = check_units_active(); /* error is treated the same as 0 */
1072
1073 switch(arg_action) {
1074
1075 case ACTION_LIST:
1076 case ACTION_INFO:
1077 pager_open(arg_no_pager, false);
1078 r = dump_list(j);
1079 break;
1080
1081 case ACTION_DUMP:
1082 r = dump_core(j);
1083 break;
1084
1085 case ACTION_GDB:
1086 r = run_gdb(j);
1087 break;
1088
1089 default:
1090 assert_not_reached("Shouldn't be here");
1091 }
1092
1093 if (units_active > 0)
1094 printf("%s-- Notice: %d systemd-coredump@.service %s, output may be incomplete.%s\n",
1095 ansi_highlight_red(),
1096 units_active, units_active == 1 ? "unit is running" : "units are running",
1097 ansi_normal());
1098 end:
1099 pager_close();
1100
1101 if (arg_output)
1102 fclose(arg_output);
1103
1104 return r >= 0 ? r : EXIT_FAILURE;
1105 }