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