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