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