chiark / gitweb /
coredump: never write more than the configured processing size limit to disk
[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         if (comm)
445                 fprintf(file,
446                         "           PID: %s%s%s (%s)\n",
447                         ansi_highlight(), strna(pid), ansi_highlight_off(), comm);
448         else
449                 fprintf(file,
450                         "           PID: %s%s%s\n",
451                         ansi_highlight(), strna(pid), ansi_highlight_off());
452
453         if (uid) {
454                 uid_t n;
455
456                 if (parse_uid(uid, &n) >= 0) {
457                         _cleanup_free_ char *u = NULL;
458
459                         u = uid_to_name(n);
460                         fprintf(file,
461                                 "           UID: %s (%s)\n",
462                                 uid, u);
463                 } else {
464                         fprintf(file,
465                                 "           UID: %s\n",
466                                 uid);
467                 }
468         }
469
470         if (gid) {
471                 gid_t n;
472
473                 if (parse_gid(gid, &n) >= 0) {
474                         _cleanup_free_ char *g = NULL;
475
476                         g = gid_to_name(n);
477                         fprintf(file,
478                                 "           GID: %s (%s)\n",
479                                 gid, g);
480                 } else {
481                         fprintf(file,
482                                 "           GID: %s\n",
483                                 gid);
484                 }
485         }
486
487         if (sgnl) {
488                 int sig;
489
490                 if (safe_atoi(sgnl, &sig) >= 0)
491                         fprintf(file, "        Signal: %s (%s)\n", sgnl, signal_to_string(sig));
492                 else
493                         fprintf(file, "        Signal: %s\n", sgnl);
494         }
495
496         if (timestamp) {
497                 usec_t u;
498
499                 r = safe_atou64(timestamp, &u);
500                 if (r >= 0) {
501                         char absolute[FORMAT_TIMESTAMP_MAX], relative[FORMAT_TIMESPAN_MAX];
502
503                         fprintf(file,
504                                 "     Timestamp: %s (%s)\n",
505                                 format_timestamp(absolute, sizeof(absolute), u),
506                                 format_timestamp_relative(relative, sizeof(relative), u));
507
508                 } else
509                         fprintf(file, "     Timestamp: %s\n", timestamp);
510         }
511
512         if (cmdline)
513                 fprintf(file, "  Command Line: %s\n", cmdline);
514         if (exe)
515                 fprintf(file, "    Executable: %s%s%s\n", ansi_highlight(), exe, ansi_highlight_off());
516         if (cgroup)
517                 fprintf(file, " Control Group: %s\n", cgroup);
518         if (unit)
519                 fprintf(file, "          Unit: %s\n", unit);
520         if (user_unit)
521                 fprintf(file, "     User Unit: %s\n", unit);
522         if (slice)
523                 fprintf(file, "         Slice: %s\n", slice);
524         if (session)
525                 fprintf(file, "       Session: %s\n", session);
526         if (owner_uid) {
527                 uid_t n;
528
529                 if (parse_uid(owner_uid, &n) >= 0) {
530                         _cleanup_free_ char *u = NULL;
531
532                         u = uid_to_name(n);
533                         fprintf(file,
534                                 "     Owner UID: %s (%s)\n",
535                                 owner_uid, u);
536                 } else {
537                         fprintf(file,
538                                 "     Owner UID: %s\n",
539                                 owner_uid);
540                 }
541         }
542         if (boot_id)
543                 fprintf(file, "       Boot ID: %s\n", boot_id);
544         if (machine_id)
545                 fprintf(file, "    Machine ID: %s\n", machine_id);
546         if (hostname)
547                 fprintf(file, "      Hostname: %s\n", hostname);
548
549         if (make_coredump_path(j, &coredump) >= 0)
550                 if (access(coredump, F_OK) >= 0)
551                         fprintf(file, "      Coredump: %s\n", coredump);
552
553         if (message) {
554                 _cleanup_free_ char *m = NULL;
555
556                 m = strreplace(message, "\n", "\n                ");
557
558                 fprintf(file, "       Message: %s\n", strstrip(m ?: message));
559         }
560
561         return 0;
562 }
563
564 static int focus(sd_journal *j) {
565         int r;
566
567         r = sd_journal_seek_tail(j);
568         if (r == 0)
569                 r = sd_journal_previous(j);
570         if (r < 0) {
571                 log_error("Failed to search journal: %s", strerror(-r));
572                 return r;
573         }
574         if (r == 0) {
575                 log_error("No match found.");
576                 return -ESRCH;
577         }
578         return r;
579 }
580
581 static void print_entry(sd_journal *j, unsigned n_found) {
582         assert(j);
583
584         if (arg_action == ACTION_INFO)
585                 print_info(stdout, j, n_found);
586         else if (arg_field)
587                 print_field(stdout, j);
588         else
589                 print_list(stdout, j, n_found);
590 }
591
592 static int dump_list(sd_journal *j) {
593         unsigned n_found = 0;
594         int r;
595
596         assert(j);
597
598         /* The coredumps are likely to compressed, and for just
599          * listing them we don't need to decompress them, so let's
600          * pick a fairly low data threshold here */
601         sd_journal_set_data_threshold(j, 4096);
602
603         if (arg_one) {
604                 r = focus(j);
605                 if (r < 0)
606                         return r;
607
608                 print_entry(j, 0);
609         } else {
610                 SD_JOURNAL_FOREACH(j)
611                         print_entry(j, n_found++);
612
613                 if (!arg_field && n_found <= 0) {
614                         log_notice("No coredumps found.");
615                         return -ESRCH;
616                 }
617         }
618
619         return 0;
620 }
621
622 static int dump_core(sd_journal* j) {
623         const void *data;
624         size_t len, ret;
625         int r;
626
627         assert(j);
628
629         /* We want full data, nothing truncated. */
630         sd_journal_set_data_threshold(j, 0);
631
632         r = focus(j);
633         if (r < 0)
634                 return r;
635
636         print_info(output ? stdout : stderr, j, false);
637
638         if (on_tty() && !output) {
639                 log_error("Refusing to dump core to tty.");
640                 return -ENOTTY;
641         }
642
643         r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
644         if (r == ENOENT) {
645                 _cleanup_free_ char *fn = NULL;
646                 _cleanup_close_ int fd = -1;
647
648                 r = make_coredump_path(j, &fn);
649                 if (r < 0)
650                         return r;
651
652                 fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOCTTY);
653                 if (fd < 0) {
654                         if (errno == ENOENT)
655                                 log_error("Coredump neither in journal file nor stored externally on disk.");
656                         else
657                                 log_error("Failed to open coredump file: %s", strerror(-r));
658
659                         return -errno;
660                 }
661
662                 r = copy_bytes(fd, output ? fileno(output) : STDOUT_FILENO, (off_t) -1);
663                 if (r < 0) {
664                         log_error("Failed to stream coredump: %s", strerror(-r));
665                         return r;
666                 }
667
668         } else if (r < 0) {
669                 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
670                 return r;
671
672         } else {
673                 assert(len >= 9);
674                 data = (const uint8_t*) data + 9;
675                 len -= 9;
676
677                 ret = fwrite(data, len, 1, output ?: stdout);
678                 if (ret != 1) {
679                         log_error("Dumping coredump failed: %m (%zu)", ret);
680                         return -errno;
681                 }
682         }
683
684         r = sd_journal_previous(j);
685         if (r >= 0)
686                 log_warning("More than one entry matches, ignoring rest.");
687
688         return 0;
689 }
690
691 static int run_gdb(sd_journal *j) {
692
693         _cleanup_free_ char *exe = NULL, *coredump = NULL;
694         char temp[] = "/var/tmp/coredump-XXXXXX";
695         bool unlink_temp = false;
696         const char *path;
697         const void *data;
698         siginfo_t st;
699         size_t len;
700         pid_t pid;
701         int r;
702
703         assert(j);
704
705         sd_journal_set_data_threshold(j, 0);
706
707         r = focus(j);
708         if (r < 0)
709                 return r;
710
711         print_info(stdout, j, false);
712         fputs("\n", stdout);
713
714         r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
715         if (r < 0) {
716                 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r));
717                 return r;
718         }
719
720         assert(len >= 13);
721         data = (const uint8_t*) data + 13;
722         len -= 13;
723
724         exe = strndup(data, len);
725         if (!exe)
726                 return log_oom();
727
728         if (endswith(exe, " (deleted)")) {
729                 log_error("Binary already deleted.");
730                 return -ENOENT;
731         }
732
733         if (!path_is_absolute(exe)) {
734                 log_error("Binary is not an absolute path.");
735                 return -ENOENT;
736         }
737
738         r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
739         if (r == -ENOENT) {
740
741                 r = make_coredump_path(j, &coredump);
742                 if (r < 0)
743                         return r;
744
745                 if (access(coredump, R_OK) < 0) {
746                         if (errno == ENOENT)
747                                 log_error("Coredump neither in journal file nor stored externally on disk.");
748                         else
749                                 log_error("Failed to access coredump file: %m");
750
751                         return -errno;
752                 }
753
754                 path = coredump;
755
756         } else if (r < 0) {
757                 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
758                 return r;
759
760         } else {
761                 _cleanup_close_ int fd = -1;
762                 ssize_t sz;
763
764                 assert(len >= 9);
765                 data = (const uint8_t*) data + 9;
766                 len -= 9;
767
768                 fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
769                 if (fd < 0) {
770                         log_error("Failed to create temporary file: %m");
771                         return -errno;
772                 }
773
774                 unlink_temp = true;
775
776                 sz = write(fd, data, len);
777                 if (sz < 0) {
778                         log_error("Failed to write temporary file: %m");
779                         r = -errno;
780                         goto finish;
781                 }
782                 if (sz != (ssize_t) len) {
783                         log_error("Short write to temporary file.");
784                         r = -EIO;
785                         goto finish;
786                 }
787
788                 path = temp;
789         }
790
791         pid = fork();
792         if (pid < 0) {
793                 log_error("Failed to fork(): %m");
794                 r = -errno;
795                 goto finish;
796         }
797         if (pid == 0) {
798                 execlp("gdb", "gdb", exe, path, NULL);
799
800                 log_error("Failed to invoke gdb: %m");
801                 _exit(1);
802         }
803
804         r = wait_for_terminate(pid, &st);
805         if (r < 0) {
806                 log_error("Failed to wait for gdb: %m");
807                 goto finish;
808         }
809
810         r = st.si_code == CLD_EXITED ? st.si_status : 255;
811
812 finish:
813         if (unlink_temp)
814                 unlink(temp);
815
816         return r;
817 }
818
819 int main(int argc, char *argv[]) {
820         _cleanup_journal_close_ sd_journal*j = NULL;
821         const char* match;
822         Iterator it;
823         int r = 0;
824         _cleanup_set_free_free_ Set *matches = NULL;
825
826         setlocale(LC_ALL, "");
827         log_parse_environment();
828         log_open();
829
830         matches = new_matches();
831         if (!matches) {
832                 r = -ENOMEM;
833                 goto end;
834         }
835
836         r = parse_argv(argc, argv, matches);
837         if (r < 0)
838                 goto end;
839
840         if (arg_action == ACTION_NONE)
841                 goto end;
842
843         r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
844         if (r < 0) {
845                 log_error("Failed to open journal: %s", strerror(-r));
846                 goto end;
847         }
848
849         SET_FOREACH(match, matches, it) {
850                 r = sd_journal_add_match(j, match, strlen(match));
851                 if (r != 0) {
852                         log_error("Failed to add match '%s': %s",
853                                   match, strerror(-r));
854                         goto end;
855                 }
856         }
857
858         if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
859                 _cleanup_free_ char *filter;
860
861                 filter = journal_make_match_string(j);
862                 log_debug("Journal filter: %s", filter);
863         }
864
865         switch(arg_action) {
866
867         case ACTION_LIST:
868         case ACTION_INFO:
869                 if (!arg_no_pager)
870                         pager_open(false);
871
872                 r = dump_list(j);
873                 break;
874
875         case ACTION_DUMP:
876                 r = dump_core(j);
877                 break;
878
879         case  ACTION_GDB:
880                 r = run_gdb(j);
881                 break;
882
883         default:
884                 assert_not_reached("Shouldn't be here");
885         }
886
887 end:
888         pager_close();
889
890         if (output)
891                 fclose(output);
892
893         return r >= 0 ? r : EXIT_FAILURE;
894 }