chiark / gitweb /
coredumpctl: fix HAVE_XZ check
[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")) {
604                 *path = filename;
605                 filename = NULL;
606
607                 return 0;
608         } else {
609                 _cleanup_close_ int fdt = -1;
610                 char *temp = NULL;
611
612                 if (fd < 0) {
613                         temp = strdup("/var/tmp/coredump-XXXXXX");
614                         if (!temp)
615                                 return log_oom();
616
617                         fdt = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
618                         if (fdt < 0) {
619                                 log_error("Failed to create temporary file: %m");
620                                 return -errno;
621                         }
622                         log_debug("Created temporary file %s", temp);
623
624                         fd = fdt;
625                 }
626
627                 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
628                 if (r == 0) {
629                         ssize_t sz;
630
631                         assert(len >= 9);
632                         data += 9;
633                         len -= 9;
634
635                         sz = write(fdt, data, len);
636                         if (sz < 0) {
637                                 log_error("Failed to write temporary file: %m");
638                                 r = -errno;
639                                 goto error;
640                         }
641                         if (sz != (ssize_t) len) {
642                                 log_error("Short write to temporary file.");
643                                 r = -EIO;
644                                 goto error;
645                         }
646                 } else if (filename) {
647 #ifdef HAVE_XZ
648                         _cleanup_close_ int fdf;
649
650                         fdf = open(filename, O_RDONLY | O_CLOEXEC);
651                         if (fdf < 0) {
652                                 log_error("Failed to open %s: %m", filename);
653                                 r = -errno;
654                                 goto error;
655                         }
656
657                         r = decompress_stream(fdf, fd, -1);
658                         if (r < 0) {
659                                 log_error("Failed to decompress %s: %s", filename, strerror(-r));
660                                 goto error;
661                         }
662 #else
663                         log_error("Cannot decompress file. Compiled without XZ support.");
664                         r = -ENOTSUP;
665                         goto error;
666 #endif
667                 } else {
668                         if (r == -ENOENT)
669                                 log_error("Coredump neither in journal file nor stored externally on disk.");
670                         else
671                                 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
672                         goto error;
673                 }
674
675                 if (temp) {
676                         *path = temp;
677                         *unlink_temp = true;
678                 }
679
680                 return 0;
681
682 error:
683                 if (temp) {
684                         unlink(temp);
685                         log_debug("Removed temporary file %s", temp);
686                 }
687                 return r;
688         }
689 }
690
691 static int dump_core(sd_journal* j) {
692         int r;
693
694         assert(j);
695
696         r = focus(j);
697         if (r < 0)
698                 return r;
699
700         print_info(output ? stdout : stderr, j, false);
701
702         if (on_tty() && !output) {
703                 log_error("Refusing to dump core to tty.");
704                 return -ENOTTY;
705         }
706
707         r = save_core(j, output ? fileno(output) : STDOUT_FILENO, NULL, NULL);
708         if (r < 0) {
709                 log_error("Coredump retrieval failed: %s", strerror(-r));
710                 return r;
711         }
712
713         r = sd_journal_previous(j);
714         if (r >= 0)
715                 log_warning("More than one entry matches, ignoring rest.");
716
717         return 0;
718 }
719
720 static int run_gdb(sd_journal *j) {
721
722         _cleanup_free_ char *exe = NULL, *coredump = NULL, *path = NULL;
723         bool unlink_path = false;
724         const char *data;
725         siginfo_t st;
726         size_t len;
727         pid_t pid;
728         int r;
729
730         assert(j);
731
732         r = focus(j);
733         if (r < 0)
734                 return r;
735
736         print_info(stdout, j, false);
737         fputs("\n", stdout);
738
739         r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
740         if (r < 0) {
741                 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r));
742                 return r;
743         }
744
745         assert(len > strlen("COREDUMP_EXE="));
746         data += strlen("COREDUMP_EXE=");
747         len -= strlen("COREDUMP_EXE=");
748
749         exe = strndup(data, len);
750         if (!exe)
751                 return log_oom();
752
753         if (endswith(exe, " (deleted)")) {
754                 log_error("Binary already deleted.");
755                 return -ENOENT;
756         }
757
758         if (!path_is_absolute(exe)) {
759                 log_error("Binary is not an absolute path.");
760                 return -ENOENT;
761         }
762
763         r = save_core(j, -1, &path, &unlink_path);
764         if (r < 0) {
765                 log_error("Failed to retrieve core: %s", strerror(-r));
766                 return r;
767         }
768
769         pid = fork();
770         if (pid < 0) {
771                 log_error("Failed to fork(): %m");
772                 r = -errno;
773                 goto finish;
774         }
775         if (pid == 0) {
776                 execlp("gdb", "gdb", exe, path, NULL);
777
778                 log_error("Failed to invoke gdb: %m");
779                 _exit(1);
780         }
781
782         r = wait_for_terminate(pid, &st);
783         if (r < 0) {
784                 log_error("Failed to wait for gdb: %m");
785                 goto finish;
786         }
787
788         r = st.si_code == CLD_EXITED ? st.si_status : 255;
789
790 finish:
791         if (unlink_path) {
792                 log_debug("Removed temporary file %s", path);
793                 unlink(path);
794         }
795
796         return r;
797 }
798
799 int main(int argc, char *argv[]) {
800         _cleanup_journal_close_ sd_journal*j = NULL;
801         const char* match;
802         Iterator it;
803         int r = 0;
804         _cleanup_set_free_free_ Set *matches = NULL;
805
806         setlocale(LC_ALL, "");
807         log_parse_environment();
808         log_open();
809
810         matches = new_matches();
811         if (!matches) {
812                 r = -ENOMEM;
813                 goto end;
814         }
815
816         r = parse_argv(argc, argv, matches);
817         if (r < 0)
818                 goto end;
819
820         if (arg_action == ACTION_NONE)
821                 goto end;
822
823         r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
824         if (r < 0) {
825                 log_error("Failed to open journal: %s", strerror(-r));
826                 goto end;
827         }
828
829         /* We want full data, nothing truncated. */
830         sd_journal_set_data_threshold(j, 0);
831
832         SET_FOREACH(match, matches, it) {
833                 r = sd_journal_add_match(j, match, strlen(match));
834                 if (r != 0) {
835                         log_error("Failed to add match '%s': %s",
836                                   match, strerror(-r));
837                         goto end;
838                 }
839         }
840
841         if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
842                 _cleanup_free_ char *filter;
843
844                 filter = journal_make_match_string(j);
845                 log_debug("Journal filter: %s", filter);
846         }
847
848         switch(arg_action) {
849
850         case ACTION_LIST:
851         case ACTION_INFO:
852                 if (!arg_no_pager)
853                         pager_open(false);
854
855                 r = dump_list(j);
856                 break;
857
858         case ACTION_DUMP:
859                 r = dump_core(j);
860                 break;
861
862         case  ACTION_GDB:
863                 r = run_gdb(j);
864                 break;
865
866         default:
867                 assert_not_reached("Shouldn't be here");
868         }
869
870 end:
871         pager_close();
872
873         if (output)
874                 fclose(output);
875
876         return r >= 0 ? r : EXIT_FAILURE;
877 }