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