chiark / gitweb /
journal: add LZ4 as optional compressor
[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_debug("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("Coredump neither in journal file nor stored externally on 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 }