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