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