chiark / gitweb /
2bc9021022bbfbfba56cd2c16d4accc3d8920de0
[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 help(void) {
85
86         printf("%s [OPTIONS...]\n\n"
87                "List or retrieve coredumps from the journal.\n\n"
88                "Flags:\n"
89                "  -h --help          Show this help\n"
90                "     --version       Print version string\n"
91                "     --no-pager      Do not pipe output into a pager\n"
92                "     --no-legend     Do not print the column headers.\n"
93                "  -1                 Show information about most recent entry only\n"
94                "  -F --field=FIELD   List all values a certain field takes\n"
95                "  -o --output=FILE   Write output to FILE\n\n"
96
97                "Commands:\n"
98                "  list [MATCHES...]  List available coredumps (default)\n"
99                "  info [MATCHES...]  Show detailed information about one or more coredumps\n"
100                "  dump [MATCHES...]  Print first matching coredump to stdout\n"
101                "  gdb [MATCHES...]   Start gdb for the first matching coredump\n"
102                , program_invocation_short_name);
103
104         return 0;
105 }
106
107 static int add_match(Set *set, const char *match) {
108         int r = -ENOMEM;
109         unsigned pid;
110         const char* prefix;
111         char *pattern = NULL;
112         _cleanup_free_ char *p = NULL;
113
114         if (strchr(match, '='))
115                 prefix = "";
116         else if (strchr(match, '/')) {
117                 p = path_make_absolute_cwd(match);
118                 if (!p)
119                         goto fail;
120
121                 match = p;
122                 prefix = "COREDUMP_EXE=";
123         }
124         else if (safe_atou(match, &pid) == 0)
125                 prefix = "COREDUMP_PID=";
126         else
127                 prefix = "COREDUMP_COMM=";
128
129         pattern = strjoin(prefix, match, NULL);
130         if (!pattern)
131                 goto fail;
132
133         log_debug("Adding pattern: %s", pattern);
134         r = set_consume(set, pattern);
135         if (r < 0) {
136                 log_error("Failed to add pattern '%s': %s",
137                           pattern, strerror(-r));
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 static void print_field(FILE* file, sd_journal *j) {
284         _cleanup_free_ char *value = NULL;
285         const void *d;
286         size_t l;
287
288         assert(file);
289         assert(j);
290
291         assert(arg_field);
292
293         SD_JOURNAL_FOREACH_DATA(j, d, l)
294                 retrieve(d, l, arg_field, &value);
295
296         if (value)
297                 fprintf(file, "%s\n", value);
298 }
299
300 static int print_list(FILE* file, sd_journal *j, int had_legend) {
301         _cleanup_free_ char
302                 *pid = NULL, *uid = NULL, *gid = NULL,
303                 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
304                 *filename = NULL;
305         const void *d;
306         size_t l;
307         usec_t t;
308         char buf[FORMAT_TIMESTAMP_MAX];
309         int r;
310         bool present;
311
312         assert(file);
313         assert(j);
314
315         SD_JOURNAL_FOREACH_DATA(j, d, l) {
316                 retrieve(d, l, "COREDUMP_PID", &pid);
317                 retrieve(d, l, "COREDUMP_UID", &uid);
318                 retrieve(d, l, "COREDUMP_GID", &gid);
319                 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
320                 retrieve(d, l, "COREDUMP_EXE", &exe);
321                 retrieve(d, l, "COREDUMP_COMM", &comm);
322                 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
323                 retrieve(d, l, "COREDUMP_FILENAME", &filename);
324         }
325
326         if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename) {
327                 log_warning("Empty coredump log entry");
328                 return -EINVAL;
329         }
330
331         r = sd_journal_get_realtime_usec(j, &t);
332         if (r < 0) {
333                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
334                 return r;
335         }
336
337         format_timestamp(buf, sizeof(buf), t);
338         present = filename && access(filename, F_OK) == 0;
339
340         if (!had_legend && !arg_no_legend)
341                 fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
342                         FORMAT_TIMESTAMP_WIDTH, "TIME",
343                         6, "PID",
344                         5, "UID",
345                         5, "GID",
346                         3, "SIG",
347                         1, "PRESENT",
348                            "EXE");
349
350         fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
351                 FORMAT_TIMESTAMP_WIDTH, buf,
352                 6, strna(pid),
353                 5, strna(uid),
354                 5, strna(gid),
355                 3, strna(sgnl),
356                 1, present ? "*" : "",
357                 strna(exe ?: (comm ?: cmdline)));
358
359         return 0;
360 }
361
362 static int print_info(FILE *file, sd_journal *j, bool need_space) {
363         _cleanup_free_ char
364                 *pid = NULL, *uid = NULL, *gid = NULL,
365                 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
366                 *unit = NULL, *user_unit = NULL, *session = NULL,
367                 *boot_id = NULL, *machine_id = NULL, *hostname = NULL,
368                 *slice = NULL, *cgroup = NULL, *owner_uid = NULL,
369                 *message = NULL, *timestamp = NULL, *filename = NULL;
370         const void *d;
371         size_t l;
372         int r;
373
374         assert(file);
375         assert(j);
376
377         SD_JOURNAL_FOREACH_DATA(j, d, l) {
378                 retrieve(d, l, "COREDUMP_PID", &pid);
379                 retrieve(d, l, "COREDUMP_UID", &uid);
380                 retrieve(d, l, "COREDUMP_GID", &gid);
381                 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
382                 retrieve(d, l, "COREDUMP_EXE", &exe);
383                 retrieve(d, l, "COREDUMP_COMM", &comm);
384                 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
385                 retrieve(d, l, "COREDUMP_UNIT", &unit);
386                 retrieve(d, l, "COREDUMP_USER_UNIT", &user_unit);
387                 retrieve(d, l, "COREDUMP_SESSION", &session);
388                 retrieve(d, l, "COREDUMP_OWNER_UID", &owner_uid);
389                 retrieve(d, l, "COREDUMP_SLICE", &slice);
390                 retrieve(d, l, "COREDUMP_CGROUP", &cgroup);
391                 retrieve(d, l, "COREDUMP_TIMESTAMP", &timestamp);
392                 retrieve(d, l, "COREDUMP_FILENAME", &filename);
393                 retrieve(d, l, "_BOOT_ID", &boot_id);
394                 retrieve(d, l, "_MACHINE_ID", &machine_id);
395                 retrieve(d, l, "_HOSTNAME", &hostname);
396                 retrieve(d, l, "MESSAGE", &message);
397         }
398
399         if (need_space)
400                 fputs("\n", file);
401
402         if (comm)
403                 fprintf(file,
404                         "           PID: %s%s%s (%s)\n",
405                         ansi_highlight(), strna(pid), ansi_highlight_off(), comm);
406         else
407                 fprintf(file,
408                         "           PID: %s%s%s\n",
409                         ansi_highlight(), strna(pid), ansi_highlight_off());
410
411         if (uid) {
412                 uid_t n;
413
414                 if (parse_uid(uid, &n) >= 0) {
415                         _cleanup_free_ char *u = NULL;
416
417                         u = uid_to_name(n);
418                         fprintf(file,
419                                 "           UID: %s (%s)\n",
420                                 uid, u);
421                 } else {
422                         fprintf(file,
423                                 "           UID: %s\n",
424                                 uid);
425                 }
426         }
427
428         if (gid) {
429                 gid_t n;
430
431                 if (parse_gid(gid, &n) >= 0) {
432                         _cleanup_free_ char *g = NULL;
433
434                         g = gid_to_name(n);
435                         fprintf(file,
436                                 "           GID: %s (%s)\n",
437                                 gid, g);
438                 } else {
439                         fprintf(file,
440                                 "           GID: %s\n",
441                                 gid);
442                 }
443         }
444
445         if (sgnl) {
446                 int sig;
447
448                 if (safe_atoi(sgnl, &sig) >= 0)
449                         fprintf(file, "        Signal: %s (%s)\n", sgnl, signal_to_string(sig));
450                 else
451                         fprintf(file, "        Signal: %s\n", sgnl);
452         }
453
454         if (timestamp) {
455                 usec_t u;
456
457                 r = safe_atou64(timestamp, &u);
458                 if (r >= 0) {
459                         char absolute[FORMAT_TIMESTAMP_MAX], relative[FORMAT_TIMESPAN_MAX];
460
461                         fprintf(file,
462                                 "     Timestamp: %s (%s)\n",
463                                 format_timestamp(absolute, sizeof(absolute), u),
464                                 format_timestamp_relative(relative, sizeof(relative), u));
465
466                 } else
467                         fprintf(file, "     Timestamp: %s\n", timestamp);
468         }
469
470         if (cmdline)
471                 fprintf(file, "  Command Line: %s\n", cmdline);
472         if (exe)
473                 fprintf(file, "    Executable: %s%s%s\n", ansi_highlight(), exe, ansi_highlight_off());
474         if (cgroup)
475                 fprintf(file, " Control Group: %s\n", cgroup);
476         if (unit)
477                 fprintf(file, "          Unit: %s\n", unit);
478         if (user_unit)
479                 fprintf(file, "     User Unit: %s\n", unit);
480         if (slice)
481                 fprintf(file, "         Slice: %s\n", slice);
482         if (session)
483                 fprintf(file, "       Session: %s\n", session);
484         if (owner_uid) {
485                 uid_t n;
486
487                 if (parse_uid(owner_uid, &n) >= 0) {
488                         _cleanup_free_ char *u = NULL;
489
490                         u = uid_to_name(n);
491                         fprintf(file,
492                                 "     Owner UID: %s (%s)\n",
493                                 owner_uid, u);
494                 } else {
495                         fprintf(file,
496                                 "     Owner UID: %s\n",
497                                 owner_uid);
498                 }
499         }
500         if (boot_id)
501                 fprintf(file, "       Boot ID: %s\n", boot_id);
502         if (machine_id)
503                 fprintf(file, "    Machine ID: %s\n", machine_id);
504         if (hostname)
505                 fprintf(file, "      Hostname: %s\n", hostname);
506
507         if (filename && access(filename, F_OK) == 0)
508                 fprintf(file, "      Coredump: %s\n", filename);
509
510         if (message) {
511                 _cleanup_free_ char *m = NULL;
512
513                 m = strreplace(message, "\n", "\n                ");
514
515                 fprintf(file, "       Message: %s\n", strstrip(m ?: message));
516         }
517
518         return 0;
519 }
520
521 static int focus(sd_journal *j) {
522         int r;
523
524         r = sd_journal_seek_tail(j);
525         if (r == 0)
526                 r = sd_journal_previous(j);
527         if (r < 0) {
528                 log_error("Failed to search journal: %s", strerror(-r));
529                 return r;
530         }
531         if (r == 0) {
532                 log_error("No match found.");
533                 return -ESRCH;
534         }
535         return r;
536 }
537
538 static void print_entry(sd_journal *j, unsigned n_found) {
539         assert(j);
540
541         if (arg_action == ACTION_INFO)
542                 print_info(stdout, j, n_found);
543         else if (arg_field)
544                 print_field(stdout, j);
545         else
546                 print_list(stdout, j, n_found);
547 }
548
549 static int dump_list(sd_journal *j) {
550         unsigned n_found = 0;
551         int r;
552
553         assert(j);
554
555         /* The coredumps are likely to compressed, and for just
556          * listing them we don't need to decompress them, so let's
557          * pick a fairly low data threshold here */
558         sd_journal_set_data_threshold(j, 4096);
559
560         if (arg_one) {
561                 r = focus(j);
562                 if (r < 0)
563                         return r;
564
565                 print_entry(j, 0);
566         } else {
567                 SD_JOURNAL_FOREACH(j)
568                         print_entry(j, n_found++);
569
570                 if (!arg_field && n_found <= 0) {
571                         log_notice("No coredumps found.");
572                         return -ESRCH;
573                 }
574         }
575
576         return 0;
577 }
578
579 static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) {
580         const char *data;
581         _cleanup_free_ char *filename = NULL;
582         size_t len;
583         int r;
584
585         assert((fd >= 0) != !!path);
586         assert(!!path == !!unlink_temp);
587
588         /* Prefer uncompressed file to journal (probably cached) to
589          * compressed file (probably uncached). */
590         r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len);
591         if (r < 0 && r != -ENOENT)
592                 log_warning("Failed to retrieve COREDUMP_FILENAME: %s", strerror(-r));
593         else if (r == 0)
594                 retrieve(data, len, "COREDUMP_FILENAME", &filename);
595
596         if (filename && access(filename, R_OK) < 0) {
597                 log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
598                          "File %s is not readable: %m", filename);
599                 free(filename);
600                 filename = NULL;
601         }
602
603         if (filename && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) {
604                 if (path) {
605                         *path = filename;
606                         filename = NULL;
607                 }
608
609                 return 0;
610         } else {
611                 _cleanup_close_ int fdt = -1;
612                 char *temp = NULL;
613
614                 if (fd < 0) {
615                         temp = strdup("/var/tmp/coredump-XXXXXX");
616                         if (!temp)
617                                 return log_oom();
618
619                         fdt = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
620                         if (fdt < 0) {
621                                 log_error("Failed to create temporary file: %m");
622                                 return -errno;
623                         }
624                         log_debug("Created temporary file %s", temp);
625
626                         fd = fdt;
627                 }
628
629                 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
630                 if (r == 0) {
631                         ssize_t sz;
632
633                         assert(len >= 9);
634                         data += 9;
635                         len -= 9;
636
637                         sz = write(fdt, data, len);
638                         if (sz < 0) {
639                                 log_error("Failed to write temporary file: %m");
640                                 r = -errno;
641                                 goto error;
642                         }
643                         if (sz != (ssize_t) len) {
644                                 log_error("Short write to temporary file.");
645                                 r = -EIO;
646                                 goto error;
647                         }
648                 } else if (filename) {
649 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
650                         _cleanup_close_ int fdf;
651
652                         fdf = open(filename, O_RDONLY | O_CLOEXEC);
653                         if (fdf < 0) {
654                                 log_error("Failed to open %s: %m", filename);
655                                 r = -errno;
656                                 goto error;
657                         }
658
659                         r = decompress_stream(filename, fdf, fd, -1);
660                         if (r < 0) {
661                                 log_error("Failed to decompress %s: %s", filename, strerror(-r));
662                                 goto error;
663                         }
664 #else
665                         log_error("Cannot decompress file. Compiled without compression support.");
666                         r = -ENOTSUP;
667                         goto error;
668 #endif
669                 } else {
670                         if (r == -ENOENT)
671                                 log_error("Cannot retrieve coredump from journal nor disk.");
672                         else
673                                 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
674                         goto error;
675                 }
676
677                 if (temp) {
678                         *path = temp;
679                         *unlink_temp = true;
680                 }
681
682                 return 0;
683
684 error:
685                 if (temp) {
686                         unlink(temp);
687                         log_debug("Removed temporary file %s", temp);
688                 }
689                 return r;
690         }
691 }
692
693 static int dump_core(sd_journal* j) {
694         int r;
695
696         assert(j);
697
698         r = focus(j);
699         if (r < 0)
700                 return r;
701
702         print_info(output ? stdout : stderr, j, false);
703
704         if (on_tty() && !output) {
705                 log_error("Refusing to dump core to tty.");
706                 return -ENOTTY;
707         }
708
709         r = save_core(j, output ? fileno(output) : STDOUT_FILENO, NULL, NULL);
710         if (r < 0) {
711                 log_error("Coredump retrieval failed: %s", strerror(-r));
712                 return r;
713         }
714
715         r = sd_journal_previous(j);
716         if (r >= 0)
717                 log_warning("More than one entry matches, ignoring rest.");
718
719         return 0;
720 }
721
722 static int run_gdb(sd_journal *j) {
723         _cleanup_free_ char *exe = NULL, *path = NULL;
724         bool unlink_path = false;
725         const char *data;
726         siginfo_t st;
727         size_t len;
728         pid_t pid;
729         int r;
730
731         assert(j);
732
733         r = focus(j);
734         if (r < 0)
735                 return r;
736
737         print_info(stdout, j, false);
738         fputs("\n", stdout);
739
740         r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
741         if (r < 0) {
742                 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r));
743                 return r;
744         }
745
746         assert(len > strlen("COREDUMP_EXE="));
747         data += strlen("COREDUMP_EXE=");
748         len -= strlen("COREDUMP_EXE=");
749
750         exe = strndup(data, len);
751         if (!exe)
752                 return log_oom();
753
754         if (endswith(exe, " (deleted)")) {
755                 log_error("Binary already deleted.");
756                 return -ENOENT;
757         }
758
759         if (!path_is_absolute(exe)) {
760                 log_error("Binary is not an absolute path.");
761                 return -ENOENT;
762         }
763
764         r = save_core(j, -1, &path, &unlink_path);
765         if (r < 0) {
766                 log_error("Failed to retrieve core: %s", strerror(-r));
767                 return r;
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_path) {
793                 log_debug("Removed temporary file %s", path);
794                 unlink(path);
795         }
796
797         return r;
798 }
799
800 int main(int argc, char *argv[]) {
801         _cleanup_journal_close_ sd_journal*j = NULL;
802         const char* match;
803         Iterator it;
804         int r = 0;
805         _cleanup_set_free_free_ Set *matches = NULL;
806
807         setlocale(LC_ALL, "");
808         log_parse_environment();
809         log_open();
810
811         matches = new_matches();
812         if (!matches) {
813                 r = -ENOMEM;
814                 goto end;
815         }
816
817         r = parse_argv(argc, argv, matches);
818         if (r < 0)
819                 goto end;
820
821         if (arg_action == ACTION_NONE)
822                 goto end;
823
824         r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
825         if (r < 0) {
826                 log_error("Failed to open journal: %s", strerror(-r));
827                 goto end;
828         }
829
830         /* We want full data, nothing truncated. */
831         sd_journal_set_data_threshold(j, 0);
832
833         SET_FOREACH(match, matches, it) {
834                 r = sd_journal_add_match(j, match, strlen(match));
835                 if (r != 0) {
836                         log_error("Failed to add match '%s': %s",
837                                   match, strerror(-r));
838                         goto end;
839                 }
840         }
841
842         if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
843                 _cleanup_free_ char *filter;
844
845                 filter = journal_make_match_string(j);
846                 log_debug("Journal filter: %s", filter);
847         }
848
849         switch(arg_action) {
850
851         case ACTION_LIST:
852         case ACTION_INFO:
853                 if (!arg_no_pager)
854                         pager_open(false);
855
856                 r = dump_list(j);
857                 break;
858
859         case ACTION_DUMP:
860                 r = dump_core(j);
861                 break;
862
863         case  ACTION_GDB:
864                 r = run_gdb(j);
865                 break;
866
867         default:
868                 assert_not_reached("Shouldn't be here");
869         }
870
871 end:
872         pager_close();
873
874         if (output)
875                 fclose(output);
876
877         return r >= 0 ? r : EXIT_FAILURE;
878 }