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