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