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