]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/coredumpctl.c
util-lib: split out allocation calls into alloc-util.[ch]
[thirdparty/systemd.git] / src / journal / coredumpctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2012 Zbigniew Jędrzejewski-Szmek
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <fcntl.h>
23 #include <getopt.h>
24 #include <locale.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #include "sd-journal.h"
30
31 #include "alloc-util.h"
32 #include "compress.h"
33 #include "fd-util.h"
34 #include "fileio.h"
35 #include "journal-internal.h"
36 #include "log.h"
37 #include "macro.h"
38 #include "pager.h"
39 #include "parse-util.h"
40 #include "path-util.h"
41 #include "process-util.h"
42 #include "set.h"
43 #include "sigbus.h"
44 #include "signal-util.h"
45 #include "string-util.h"
46 #include "terminal-util.h"
47 #include "user-util.h"
48 #include "util.h"
49
50 static enum {
51 ACTION_NONE,
52 ACTION_INFO,
53 ACTION_LIST,
54 ACTION_DUMP,
55 ACTION_GDB,
56 } arg_action = ACTION_LIST;
57 static const char* arg_field = NULL;
58 static const char *arg_directory = NULL;
59 static int arg_no_pager = false;
60 static int arg_no_legend = false;
61 static int arg_one = false;
62 static FILE* arg_output = NULL;
63
64 static Set *new_matches(void) {
65 Set *set;
66 char *tmp;
67 int r;
68
69 set = set_new(NULL);
70 if (!set) {
71 log_oom();
72 return NULL;
73 }
74
75 tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
76 if (!tmp) {
77 log_oom();
78 set_free(set);
79 return NULL;
80 }
81
82 r = set_consume(set, tmp);
83 if (r < 0) {
84 log_error_errno(r, "failed to add to set: %m");
85 set_free(set);
86 return NULL;
87 }
88
89 return set;
90 }
91
92 static int add_match(Set *set, const char *match) {
93 _cleanup_free_ char *p = NULL;
94 char *pattern = NULL;
95 const char* prefix;
96 pid_t pid;
97 int r;
98
99 if (strchr(match, '='))
100 prefix = "";
101 else if (strchr(match, '/')) {
102 r = path_make_absolute_cwd(match, &p);
103 if (r < 0)
104 goto fail;
105 match = p;
106 prefix = "COREDUMP_EXE=";
107 } else if (parse_pid(match, &pid) >= 0)
108 prefix = "COREDUMP_PID=";
109 else
110 prefix = "COREDUMP_COMM=";
111
112 pattern = strjoin(prefix, match, NULL);
113 if (!pattern) {
114 r = -ENOMEM;
115 goto fail;
116 }
117
118 log_debug("Adding pattern: %s", pattern);
119 r = set_consume(set, pattern);
120 if (r < 0)
121 goto fail;
122
123 return 0;
124 fail:
125 return log_error_errno(r, "Failed to add match: %m");
126 }
127
128 static void help(void) {
129 printf("%s [OPTIONS...]\n\n"
130 "List or retrieve coredumps from the journal.\n\n"
131 "Flags:\n"
132 " -h --help Show this help\n"
133 " --version Print version string\n"
134 " --no-pager Do not pipe output into a pager\n"
135 " --no-legend Do not print the column headers.\n"
136 " -1 Show information about most recent entry only\n"
137 " -F --field=FIELD List all values a certain field takes\n"
138 " -o --output=FILE Write output to FILE\n\n"
139 " -D --directory=DIR Use journal files from directory\n\n"
140
141 "Commands:\n"
142 " list [MATCHES...] List available coredumps (default)\n"
143 " info [MATCHES...] Show detailed information about one or more coredumps\n"
144 " dump [MATCHES...] Print first matching coredump to stdout\n"
145 " gdb [MATCHES...] Start gdb for the first matching coredump\n"
146 , program_invocation_short_name);
147 }
148
149 static int parse_argv(int argc, char *argv[], Set *matches) {
150 enum {
151 ARG_VERSION = 0x100,
152 ARG_NO_PAGER,
153 ARG_NO_LEGEND,
154 };
155
156 int r, c;
157
158 static const struct option options[] = {
159 { "help", no_argument, NULL, 'h' },
160 { "version" , no_argument, NULL, ARG_VERSION },
161 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
162 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
163 { "output", required_argument, NULL, 'o' },
164 { "field", required_argument, NULL, 'F' },
165 { "directory", required_argument, NULL, 'D' },
166 {}
167 };
168
169 assert(argc >= 0);
170 assert(argv);
171
172 while ((c = getopt_long(argc, argv, "ho:F:1D:", options, NULL)) >= 0)
173 switch(c) {
174
175 case 'h':
176 arg_action = ACTION_NONE;
177 help();
178 return 0;
179
180 case ARG_VERSION:
181 arg_action = ACTION_NONE;
182 return version();
183
184 case ARG_NO_PAGER:
185 arg_no_pager = true;
186 break;
187
188 case ARG_NO_LEGEND:
189 arg_no_legend = true;
190 break;
191
192 case 'o':
193 if (arg_output) {
194 log_error("cannot set output more than once");
195 return -EINVAL;
196 }
197
198 arg_output = fopen(optarg, "we");
199 if (!arg_output)
200 return log_error_errno(errno, "writing to '%s': %m", optarg);
201
202 break;
203
204 case 'F':
205 if (arg_field) {
206 log_error("cannot use --field/-F more than once");
207 return -EINVAL;
208 }
209 arg_field = optarg;
210 break;
211
212 case '1':
213 arg_one = true;
214 break;
215
216 case 'D':
217 arg_directory = optarg;
218 break;
219
220 case '?':
221 return -EINVAL;
222
223 default:
224 assert_not_reached("Unhandled option");
225 }
226
227 if (optind < argc) {
228 const char *cmd = argv[optind++];
229 if (streq(cmd, "list"))
230 arg_action = ACTION_LIST;
231 else if (streq(cmd, "dump"))
232 arg_action = ACTION_DUMP;
233 else if (streq(cmd, "gdb"))
234 arg_action = ACTION_GDB;
235 else if (streq(cmd, "info"))
236 arg_action = ACTION_INFO;
237 else {
238 log_error("Unknown action '%s'", cmd);
239 return -EINVAL;
240 }
241 }
242
243 if (arg_field && arg_action != ACTION_LIST) {
244 log_error("Option --field/-F only makes sense with list");
245 return -EINVAL;
246 }
247
248 while (optind < argc) {
249 r = add_match(matches, argv[optind]);
250 if (r != 0)
251 return r;
252 optind++;
253 }
254
255 return 0;
256 }
257
258 static int retrieve(const void *data,
259 size_t len,
260 const char *name,
261 char **var) {
262
263 size_t ident;
264 char *v;
265
266 ident = strlen(name) + 1; /* name + "=" */
267
268 if (len < ident)
269 return 0;
270
271 if (memcmp(data, name, ident - 1) != 0)
272 return 0;
273
274 if (((const char*) data)[ident - 1] != '=')
275 return 0;
276
277 v = strndup((const char*)data + ident, len - ident);
278 if (!v)
279 return log_oom();
280
281 free(*var);
282 *var = v;
283
284 return 0;
285 }
286
287 static void print_field(FILE* file, sd_journal *j) {
288 _cleanup_free_ char *value = NULL;
289 const void *d;
290 size_t l;
291
292 assert(file);
293 assert(j);
294
295 assert(arg_field);
296
297 SD_JOURNAL_FOREACH_DATA(j, d, l)
298 retrieve(d, l, arg_field, &value);
299
300 if (value)
301 fprintf(file, "%s\n", value);
302 }
303
304 static int print_list(FILE* file, sd_journal *j, int had_legend) {
305 _cleanup_free_ char
306 *pid = NULL, *uid = NULL, *gid = NULL,
307 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
308 *filename = NULL;
309 const void *d;
310 size_t l;
311 usec_t t;
312 char buf[FORMAT_TIMESTAMP_MAX];
313 int r;
314 bool present;
315
316 assert(file);
317 assert(j);
318
319 SD_JOURNAL_FOREACH_DATA(j, d, l) {
320 retrieve(d, l, "COREDUMP_PID", &pid);
321 retrieve(d, l, "COREDUMP_UID", &uid);
322 retrieve(d, l, "COREDUMP_GID", &gid);
323 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
324 retrieve(d, l, "COREDUMP_EXE", &exe);
325 retrieve(d, l, "COREDUMP_COMM", &comm);
326 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
327 retrieve(d, l, "COREDUMP_FILENAME", &filename);
328 }
329
330 if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename) {
331 log_warning("Empty coredump log entry");
332 return -EINVAL;
333 }
334
335 r = sd_journal_get_realtime_usec(j, &t);
336 if (r < 0)
337 return log_error_errno(r, "Failed to get realtime timestamp: %m");
338
339 format_timestamp(buf, sizeof(buf), t);
340 present = filename && access(filename, F_OK) == 0;
341
342 if (!had_legend && !arg_no_legend)
343 fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
344 FORMAT_TIMESTAMP_WIDTH, "TIME",
345 6, "PID",
346 5, "UID",
347 5, "GID",
348 3, "SIG",
349 1, "PRESENT",
350 "EXE");
351
352 fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
353 FORMAT_TIMESTAMP_WIDTH, buf,
354 6, strna(pid),
355 5, strna(uid),
356 5, strna(gid),
357 3, strna(sgnl),
358 1, present ? "*" : "",
359 strna(exe ?: (comm ?: cmdline)));
360
361 return 0;
362 }
363
364 static int print_info(FILE *file, sd_journal *j, bool need_space) {
365 _cleanup_free_ char
366 *pid = NULL, *uid = NULL, *gid = NULL,
367 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
368 *unit = NULL, *user_unit = NULL, *session = NULL,
369 *boot_id = NULL, *machine_id = NULL, *hostname = NULL,
370 *slice = NULL, *cgroup = NULL, *owner_uid = NULL,
371 *message = NULL, *timestamp = NULL, *filename = NULL;
372 const void *d;
373 size_t l;
374 int r;
375
376 assert(file);
377 assert(j);
378
379 SD_JOURNAL_FOREACH_DATA(j, d, l) {
380 retrieve(d, l, "COREDUMP_PID", &pid);
381 retrieve(d, l, "COREDUMP_UID", &uid);
382 retrieve(d, l, "COREDUMP_GID", &gid);
383 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
384 retrieve(d, l, "COREDUMP_EXE", &exe);
385 retrieve(d, l, "COREDUMP_COMM", &comm);
386 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
387 retrieve(d, l, "COREDUMP_UNIT", &unit);
388 retrieve(d, l, "COREDUMP_USER_UNIT", &user_unit);
389 retrieve(d, l, "COREDUMP_SESSION", &session);
390 retrieve(d, l, "COREDUMP_OWNER_UID", &owner_uid);
391 retrieve(d, l, "COREDUMP_SLICE", &slice);
392 retrieve(d, l, "COREDUMP_CGROUP", &cgroup);
393 retrieve(d, l, "COREDUMP_TIMESTAMP", &timestamp);
394 retrieve(d, l, "COREDUMP_FILENAME", &filename);
395 retrieve(d, l, "_BOOT_ID", &boot_id);
396 retrieve(d, l, "_MACHINE_ID", &machine_id);
397 retrieve(d, l, "_HOSTNAME", &hostname);
398 retrieve(d, l, "MESSAGE", &message);
399 }
400
401 if (need_space)
402 fputs("\n", file);
403
404 if (comm)
405 fprintf(file,
406 " PID: %s%s%s (%s)\n",
407 ansi_highlight(), strna(pid), ansi_normal(), comm);
408 else
409 fprintf(file,
410 " PID: %s%s%s\n",
411 ansi_highlight(), strna(pid), ansi_normal());
412
413 if (uid) {
414 uid_t n;
415
416 if (parse_uid(uid, &n) >= 0) {
417 _cleanup_free_ char *u = NULL;
418
419 u = uid_to_name(n);
420 fprintf(file,
421 " UID: %s (%s)\n",
422 uid, u);
423 } else {
424 fprintf(file,
425 " UID: %s\n",
426 uid);
427 }
428 }
429
430 if (gid) {
431 gid_t n;
432
433 if (parse_gid(gid, &n) >= 0) {
434 _cleanup_free_ char *g = NULL;
435
436 g = gid_to_name(n);
437 fprintf(file,
438 " GID: %s (%s)\n",
439 gid, g);
440 } else {
441 fprintf(file,
442 " GID: %s\n",
443 gid);
444 }
445 }
446
447 if (sgnl) {
448 int sig;
449
450 if (safe_atoi(sgnl, &sig) >= 0)
451 fprintf(file, " Signal: %s (%s)\n", sgnl, signal_to_string(sig));
452 else
453 fprintf(file, " Signal: %s\n", sgnl);
454 }
455
456 if (timestamp) {
457 usec_t u;
458
459 r = safe_atou64(timestamp, &u);
460 if (r >= 0) {
461 char absolute[FORMAT_TIMESTAMP_MAX], relative[FORMAT_TIMESPAN_MAX];
462
463 fprintf(file,
464 " Timestamp: %s (%s)\n",
465 format_timestamp(absolute, sizeof(absolute), u),
466 format_timestamp_relative(relative, sizeof(relative), u));
467
468 } else
469 fprintf(file, " Timestamp: %s\n", timestamp);
470 }
471
472 if (cmdline)
473 fprintf(file, " Command Line: %s\n", cmdline);
474 if (exe)
475 fprintf(file, " Executable: %s%s%s\n", ansi_highlight(), exe, ansi_normal());
476 if (cgroup)
477 fprintf(file, " Control Group: %s\n", cgroup);
478 if (unit)
479 fprintf(file, " Unit: %s\n", unit);
480 if (user_unit)
481 fprintf(file, " User Unit: %s\n", unit);
482 if (slice)
483 fprintf(file, " Slice: %s\n", slice);
484 if (session)
485 fprintf(file, " Session: %s\n", session);
486 if (owner_uid) {
487 uid_t n;
488
489 if (parse_uid(owner_uid, &n) >= 0) {
490 _cleanup_free_ char *u = NULL;
491
492 u = uid_to_name(n);
493 fprintf(file,
494 " Owner UID: %s (%s)\n",
495 owner_uid, u);
496 } else {
497 fprintf(file,
498 " Owner UID: %s\n",
499 owner_uid);
500 }
501 }
502 if (boot_id)
503 fprintf(file, " Boot ID: %s\n", boot_id);
504 if (machine_id)
505 fprintf(file, " Machine ID: %s\n", machine_id);
506 if (hostname)
507 fprintf(file, " Hostname: %s\n", hostname);
508
509 if (filename && access(filename, F_OK) == 0)
510 fprintf(file, " Coredump: %s\n", filename);
511
512 if (message) {
513 _cleanup_free_ char *m = NULL;
514
515 m = strreplace(message, "\n", "\n ");
516
517 fprintf(file, " Message: %s\n", strstrip(m ?: message));
518 }
519
520 return 0;
521 }
522
523 static int focus(sd_journal *j) {
524 int r;
525
526 r = sd_journal_seek_tail(j);
527 if (r == 0)
528 r = sd_journal_previous(j);
529 if (r < 0)
530 return log_error_errno(r, "Failed to search journal: %m");
531 if (r == 0) {
532 log_error("No match found.");
533 return -ESRCH;
534 }
535 return r;
536 }
537
538 static void print_entry(sd_journal *j, unsigned n_found) {
539 assert(j);
540
541 if (arg_action == ACTION_INFO)
542 print_info(stdout, j, n_found);
543 else if (arg_field)
544 print_field(stdout, j);
545 else
546 print_list(stdout, j, n_found);
547 }
548
549 static int dump_list(sd_journal *j) {
550 unsigned n_found = 0;
551 int r;
552
553 assert(j);
554
555 /* The coredumps are likely to compressed, and for just
556 * listing them we don't need to decompress them, so let's
557 * pick a fairly low data threshold here */
558 sd_journal_set_data_threshold(j, 4096);
559
560 if (arg_one) {
561 r = focus(j);
562 if (r < 0)
563 return r;
564
565 print_entry(j, 0);
566 } else {
567 SD_JOURNAL_FOREACH(j)
568 print_entry(j, n_found++);
569
570 if (!arg_field && n_found <= 0) {
571 log_notice("No coredumps found.");
572 return -ESRCH;
573 }
574 }
575
576 return 0;
577 }
578
579 static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) {
580 const char *data;
581 _cleanup_free_ char *filename = NULL;
582 size_t len;
583 int r;
584
585 assert((fd >= 0) != !!path);
586 assert(!!path == !!unlink_temp);
587
588 /* Prefer uncompressed file to journal (probably cached) to
589 * compressed file (probably uncached). */
590 r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len);
591 if (r < 0 && r != -ENOENT)
592 log_warning_errno(r, "Failed to retrieve COREDUMP_FILENAME: %m");
593 else if (r == 0)
594 retrieve(data, len, "COREDUMP_FILENAME", &filename);
595
596 if (filename && access(filename, R_OK) < 0) {
597 log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
598 "File %s is not readable: %m", filename);
599 filename = mfree(filename);
600 }
601
602 if (filename && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) {
603 if (path) {
604 *path = filename;
605 filename = NULL;
606 }
607
608 return 0;
609 } else {
610 _cleanup_close_ int fdt = -1;
611 char *temp = NULL;
612
613 if (fd < 0) {
614 temp = strdup("/var/tmp/coredump-XXXXXX");
615 if (!temp)
616 return log_oom();
617
618 fdt = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
619 if (fdt < 0)
620 return log_error_errno(errno, "Failed to create temporary file: %m");
621 log_debug("Created temporary file %s", temp);
622
623 fd = fdt;
624 }
625
626 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
627 if (r == 0) {
628 ssize_t sz;
629
630 assert(len >= 9);
631 data += 9;
632 len -= 9;
633
634 sz = write(fdt, data, len);
635 if (sz < 0) {
636 r = log_error_errno(errno,
637 "Failed to write temporary file: %m");
638 goto error;
639 }
640 if (sz != (ssize_t) len) {
641 log_error("Short write to temporary file.");
642 r = -EIO;
643 goto error;
644 }
645 } else if (filename) {
646 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
647 _cleanup_close_ int fdf;
648
649 fdf = open(filename, O_RDONLY | O_CLOEXEC);
650 if (fdf < 0) {
651 r = log_error_errno(errno,
652 "Failed to open %s: %m",
653 filename);
654 goto error;
655 }
656
657 r = decompress_stream(filename, fdf, fd, -1);
658 if (r < 0) {
659 log_error_errno(r, "Failed to decompress %s: %m", filename);
660 goto error;
661 }
662 #else
663 log_error("Cannot decompress file. Compiled without compression support.");
664 r = -EOPNOTSUPP;
665 goto error;
666 #endif
667 } else {
668 if (r == -ENOENT)
669 log_error("Cannot retrieve coredump from journal nor disk.");
670 else
671 log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
672 goto error;
673 }
674
675 if (temp) {
676 *path = temp;
677 *unlink_temp = true;
678 }
679
680 return 0;
681
682 error:
683 if (temp) {
684 unlink(temp);
685 log_debug("Removed temporary file %s", temp);
686 }
687 return r;
688 }
689 }
690
691 static int dump_core(sd_journal* j) {
692 int r;
693
694 assert(j);
695
696 r = focus(j);
697 if (r < 0)
698 return r;
699
700 print_info(arg_output ? stdout : stderr, j, false);
701
702 if (on_tty() && !arg_output) {
703 log_error("Refusing to dump core to tty.");
704 return -ENOTTY;
705 }
706
707 r = save_core(j, arg_output ? fileno(arg_output) : STDOUT_FILENO, NULL, NULL);
708 if (r < 0)
709 return log_error_errno(r, "Coredump retrieval failed: %m");
710
711 r = sd_journal_previous(j);
712 if (r >= 0)
713 log_warning("More than one entry matches, ignoring rest.");
714
715 return 0;
716 }
717
718 static int run_gdb(sd_journal *j) {
719 _cleanup_free_ char *exe = NULL, *path = NULL;
720 bool unlink_path = false;
721 const char *data;
722 siginfo_t st;
723 size_t len;
724 pid_t pid;
725 int r;
726
727 assert(j);
728
729 r = focus(j);
730 if (r < 0)
731 return r;
732
733 print_info(stdout, j, false);
734 fputs("\n", stdout);
735
736 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
737 if (r < 0)
738 return log_error_errno(r, "Failed to retrieve COREDUMP_EXE field: %m");
739
740 assert(len > strlen("COREDUMP_EXE="));
741 data += strlen("COREDUMP_EXE=");
742 len -= strlen("COREDUMP_EXE=");
743
744 exe = strndup(data, len);
745 if (!exe)
746 return log_oom();
747
748 if (endswith(exe, " (deleted)")) {
749 log_error("Binary already deleted.");
750 return -ENOENT;
751 }
752
753 if (!path_is_absolute(exe)) {
754 log_error("Binary is not an absolute path.");
755 return -ENOENT;
756 }
757
758 r = save_core(j, -1, &path, &unlink_path);
759 if (r < 0)
760 return log_error_errno(r, "Failed to retrieve core: %m");
761
762 pid = fork();
763 if (pid < 0) {
764 r = log_error_errno(errno, "Failed to fork(): %m");
765 goto finish;
766 }
767 if (pid == 0) {
768 (void) reset_all_signal_handlers();
769 (void) reset_signal_mask();
770
771 execlp("gdb", "gdb", exe, path, NULL);
772
773 log_error_errno(errno, "Failed to invoke gdb: %m");
774 _exit(1);
775 }
776
777 r = wait_for_terminate(pid, &st);
778 if (r < 0) {
779 log_error_errno(errno, "Failed to wait for gdb: %m");
780 goto finish;
781 }
782
783 r = st.si_code == CLD_EXITED ? st.si_status : 255;
784
785 finish:
786 if (unlink_path) {
787 log_debug("Removed temporary file %s", path);
788 unlink(path);
789 }
790
791 return r;
792 }
793
794 int main(int argc, char *argv[]) {
795 _cleanup_journal_close_ sd_journal*j = NULL;
796 const char* match;
797 Iterator it;
798 int r = 0;
799 _cleanup_set_free_free_ Set *matches = NULL;
800
801 setlocale(LC_ALL, "");
802 log_parse_environment();
803 log_open();
804
805 matches = new_matches();
806 if (!matches) {
807 r = -ENOMEM;
808 goto end;
809 }
810
811 r = parse_argv(argc, argv, matches);
812 if (r < 0)
813 goto end;
814
815 if (arg_action == ACTION_NONE)
816 goto end;
817
818 sigbus_install();
819
820 if (arg_directory) {
821 r = sd_journal_open_directory(&j, arg_directory, 0);
822 if (r < 0) {
823 log_error_errno(r, "Failed to open journals in directory: %s: %m", arg_directory);
824 goto end;
825 }
826 } else {
827 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
828 if (r < 0) {
829 log_error_errno(r, "Failed to open journal: %m");
830 goto end;
831 }
832 }
833
834 /* We want full data, nothing truncated. */
835 sd_journal_set_data_threshold(j, 0);
836
837 SET_FOREACH(match, matches, it) {
838 r = sd_journal_add_match(j, match, strlen(match));
839 if (r != 0) {
840 log_error_errno(r, "Failed to add match '%s': %m",
841 match);
842 goto end;
843 }
844 }
845
846 if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
847 _cleanup_free_ char *filter;
848
849 filter = journal_make_match_string(j);
850 log_debug("Journal filter: %s", filter);
851 }
852
853 switch(arg_action) {
854
855 case ACTION_LIST:
856 case ACTION_INFO:
857 if (!arg_no_pager)
858 pager_open(false);
859
860 r = dump_list(j);
861 break;
862
863 case ACTION_DUMP:
864 r = dump_core(j);
865 break;
866
867 case ACTION_GDB:
868 r = run_gdb(j);
869 break;
870
871 default:
872 assert_not_reached("Shouldn't be here");
873 }
874
875 end:
876 pager_close();
877
878 if (arg_output)
879 fclose(arg_output);
880
881 return r >= 0 ? r : EXIT_FAILURE;
882 }