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