coredumpctl: move verb_dump_core and save_core() to coredumpctl-core.c
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-journal.h"
|
||||
|
||||
#include "chase.h"
|
||||
#include "compress.h"
|
||||
#include "coredumpctl.h"
|
||||
#include "coredumpctl-core.h"
|
||||
#include "coredumpctl-info.h"
|
||||
#include "coredumpctl-journal.h"
|
||||
#include "coredumpctl-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "io-util.h"
|
||||
#include "log.h"
|
||||
#include "path-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
|
||||
int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) {
|
||||
const char *data;
|
||||
_cleanup_free_ char *filename = NULL;
|
||||
size_t len;
|
||||
int r, fd;
|
||||
_cleanup_close_ int fdt = -EBADF;
|
||||
char *temp = NULL;
|
||||
|
||||
assert(!(file && path)); /* At most one can be specified */
|
||||
assert(!!path == !!unlink_temp); /* Those must be specified together */
|
||||
|
||||
/* Look for a coredump on disk first. */
|
||||
r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len);
|
||||
if (r == 0) {
|
||||
_cleanup_free_ char *resolved = NULL;
|
||||
|
||||
r = retrieve(data, len, "COREDUMP_FILENAME", &filename);
|
||||
if (r < 0)
|
||||
return r;
|
||||
assert(r > 0);
|
||||
|
||||
r = chase_and_access(filename, arg_root, CHASE_PREFIX_ROOT, F_OK, &resolved);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Cannot access \"%s%s\": %m", strempty(arg_root), filename);
|
||||
|
||||
free_and_replace(filename, resolved);
|
||||
|
||||
if (path && !ENDSWITH_SET(filename, ".xz", ".lz4", ".zst")) {
|
||||
*path = TAKE_PTR(filename);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to retrieve COREDUMP_FILENAME field: %m");
|
||||
|
||||
/* Check that we can have a COREDUMP field. We still haven't set a high data threshold, so
|
||||
* we'll get a few kilobytes at most. */
|
||||
|
||||
r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
|
||||
if (r == -ENOENT)
|
||||
return log_error_errno(r, "Coredump entry has no core attached (neither internally in the journal nor externally on disk).");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
|
||||
}
|
||||
|
||||
if (path) {
|
||||
const char *vt;
|
||||
|
||||
/* Create a temporary file to write the uncompressed core to. */
|
||||
|
||||
r = var_tmp_dir(&vt);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire temporary directory path: %m");
|
||||
|
||||
temp = path_join(vt, "coredump-XXXXXX");
|
||||
if (!temp)
|
||||
return log_oom();
|
||||
|
||||
fdt = mkostemp_safe(temp);
|
||||
if (fdt < 0)
|
||||
return log_error_errno(fdt, "Failed to create temporary file: %m");
|
||||
log_debug("Created temporary file %s", temp);
|
||||
|
||||
fd = fdt;
|
||||
} else {
|
||||
/* If neither path or file are specified, we will write to stdout. Let's now check if stdout
|
||||
* is connected to a tty. We checked that the file exists, or that the core might be stored
|
||||
* in the journal. In this second case, if we found the entry, in all likelihood we will be
|
||||
* able to access the COREDUMP= field. In either case, we stop before doing any "real" work,
|
||||
* i.e. before starting decompression or reading from the file or creating temporary files. */
|
||||
if (!file) {
|
||||
if (on_tty())
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTTY),
|
||||
"Refusing to dump core to tty"
|
||||
" (use shell redirection or specify --output).");
|
||||
file = stdout;
|
||||
}
|
||||
|
||||
fd = fileno(file);
|
||||
}
|
||||
|
||||
if (filename) {
|
||||
#if HAVE_COMPRESSION
|
||||
_cleanup_close_ int fdf = -EBADF;
|
||||
|
||||
fdf = open(filename, O_RDONLY | O_CLOEXEC);
|
||||
if (fdf < 0) {
|
||||
r = log_error_errno(errno, "Failed to open %s: %m", filename);
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = decompress_stream_by_filename(filename, fdf, fd, -1);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to decompress %s: %m", filename);
|
||||
goto error;
|
||||
}
|
||||
#else
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Cannot decompress file. Compiled without compression support.");
|
||||
goto error;
|
||||
#endif
|
||||
} else {
|
||||
/* We want full data, nothing truncated. */
|
||||
sd_journal_set_data_threshold(j, 0);
|
||||
|
||||
r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
|
||||
|
||||
assert(len >= 9);
|
||||
data += 9;
|
||||
len -= 9;
|
||||
|
||||
r = loop_write(fd, data, len);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to write output: %m");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (temp) {
|
||||
*path = temp;
|
||||
*unlink_temp = true;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (temp) {
|
||||
(void) unlink(temp);
|
||||
log_debug("Removed temporary file %s", temp);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int verb_dump_core(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int r;
|
||||
|
||||
if (arg_field)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Option --field/-F only makes sense with list");
|
||||
|
||||
r = acquire_journal(&j, strv_skip(argv, 1));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = focus(j);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_output) {
|
||||
f = fopen(arg_output, "we");
|
||||
if (!f)
|
||||
return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", arg_output);
|
||||
}
|
||||
|
||||
print_info(f ? stdout : stderr, j, false);
|
||||
|
||||
r = save_core(j, f, NULL, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_journal_previous(j);
|
||||
if (r > 0 && !arg_quiet)
|
||||
log_notice("More than one entry matches, ignoring rest.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "forward.h"
|
||||
|
||||
int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp);
|
||||
|
||||
int verb_dump_core(int argc, char *argv[], uintptr_t _data, void *userdata);
|
||||
+3
-184
@@ -1,6 +1,5 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -16,9 +15,8 @@
|
||||
#include "bus-error.h"
|
||||
#include "bus-locator.h"
|
||||
#include "bus-util.h"
|
||||
#include "chase.h"
|
||||
#include "compress.h"
|
||||
#include "coredumpctl.h"
|
||||
#include "coredumpctl-core.h"
|
||||
#include "coredumpctl-info.h"
|
||||
#include "coredumpctl-journal.h"
|
||||
#include "coredumpctl-list.h"
|
||||
@@ -26,11 +24,8 @@
|
||||
#include "dissect-image.h"
|
||||
#include "dlopen-note.h"
|
||||
#include "extract-word.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "glob-util.h"
|
||||
#include "image-policy.h"
|
||||
#include "io-util.h"
|
||||
#include "log.h"
|
||||
#include "logs-show.h"
|
||||
#include "loop-util.h"
|
||||
@@ -45,9 +40,7 @@
|
||||
#include "signal-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "time-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "verbs.h"
|
||||
|
||||
#define SHORT_BUS_CALL_TIMEOUT_USEC (3 * USEC_PER_SEC)
|
||||
@@ -235,182 +228,8 @@ VERB_SCOPE(, verb_dump_list, "list", "[MATCHES…]", VERB_ANY, VERB_ANY, VERB_DE
|
||||
"List available coredumps");
|
||||
VERB_SCOPE(, verb_dump_list, "info", "[MATCHES…]", VERB_ANY, VERB_ANY, 0,
|
||||
"Show detailed information about one or more coredumps");
|
||||
|
||||
static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) {
|
||||
const char *data;
|
||||
_cleanup_free_ char *filename = NULL;
|
||||
size_t len;
|
||||
int r, fd;
|
||||
_cleanup_close_ int fdt = -EBADF;
|
||||
char *temp = NULL;
|
||||
|
||||
assert(!(file && path)); /* At most one can be specified */
|
||||
assert(!!path == !!unlink_temp); /* Those must be specified together */
|
||||
|
||||
/* Look for a coredump on disk first. */
|
||||
r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len);
|
||||
if (r == 0) {
|
||||
_cleanup_free_ char *resolved = NULL;
|
||||
|
||||
r = retrieve(data, len, "COREDUMP_FILENAME", &filename);
|
||||
if (r < 0)
|
||||
return r;
|
||||
assert(r > 0);
|
||||
|
||||
r = chase_and_access(filename, arg_root, CHASE_PREFIX_ROOT, F_OK, &resolved);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Cannot access \"%s%s\": %m", strempty(arg_root), filename);
|
||||
|
||||
free_and_replace(filename, resolved);
|
||||
|
||||
if (path && !ENDSWITH_SET(filename, ".xz", ".lz4", ".zst")) {
|
||||
*path = TAKE_PTR(filename);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to retrieve COREDUMP_FILENAME field: %m");
|
||||
/* Check that we can have a COREDUMP field. We still haven't set a high
|
||||
* data threshold, so we'll get a few kilobytes at most.
|
||||
*/
|
||||
|
||||
r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
|
||||
if (r == -ENOENT)
|
||||
return log_error_errno(r, "Coredump entry has no core attached (neither internally in the journal nor externally on disk).");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
|
||||
}
|
||||
|
||||
if (path) {
|
||||
const char *vt;
|
||||
|
||||
/* Create a temporary file to write the uncompressed core to. */
|
||||
|
||||
r = var_tmp_dir(&vt);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire temporary directory path: %m");
|
||||
|
||||
temp = path_join(vt, "coredump-XXXXXX");
|
||||
if (!temp)
|
||||
return log_oom();
|
||||
|
||||
fdt = mkostemp_safe(temp);
|
||||
if (fdt < 0)
|
||||
return log_error_errno(fdt, "Failed to create temporary file: %m");
|
||||
log_debug("Created temporary file %s", temp);
|
||||
|
||||
fd = fdt;
|
||||
} else {
|
||||
/* If neither path or file are specified, we will write to stdout. Let's now check
|
||||
* if stdout is connected to a tty. We checked that the file exists, or that the
|
||||
* core might be stored in the journal. In this second case, if we found the entry,
|
||||
* in all likelihood we will be able to access the COREDUMP= field. In either case,
|
||||
* we stop before doing any "real" work, i.e. before starting decompression or
|
||||
* reading from the file or creating temporary files.
|
||||
*/
|
||||
if (!file) {
|
||||
if (on_tty())
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTTY),
|
||||
"Refusing to dump core to tty"
|
||||
" (use shell redirection or specify --output).");
|
||||
file = stdout;
|
||||
}
|
||||
|
||||
fd = fileno(file);
|
||||
}
|
||||
|
||||
if (filename) {
|
||||
#if HAVE_COMPRESSION
|
||||
_cleanup_close_ int fdf = -EBADF;
|
||||
|
||||
fdf = open(filename, O_RDONLY | O_CLOEXEC);
|
||||
if (fdf < 0) {
|
||||
r = log_error_errno(errno, "Failed to open %s: %m", filename);
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = decompress_stream_by_filename(filename, fdf, fd, -1);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to decompress %s: %m", filename);
|
||||
goto error;
|
||||
}
|
||||
#else
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Cannot decompress file. Compiled without compression support.");
|
||||
goto error;
|
||||
#endif
|
||||
} else {
|
||||
/* We want full data, nothing truncated. */
|
||||
sd_journal_set_data_threshold(j, 0);
|
||||
|
||||
r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
|
||||
|
||||
assert(len >= 9);
|
||||
data += 9;
|
||||
len -= 9;
|
||||
|
||||
r = loop_write(fd, data, len);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to write output: %m");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (temp) {
|
||||
*path = temp;
|
||||
*unlink_temp = true;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (temp) {
|
||||
(void) unlink(temp);
|
||||
log_debug("Removed temporary file %s", temp);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
VERB(verb_dump_core, "dump", "[MATCHES…]", VERB_ANY, VERB_ANY, 0,
|
||||
"Print first matching coredump to stdout");
|
||||
static int verb_dump_core(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int r;
|
||||
|
||||
if (arg_field)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Option --field/-F only makes sense with list");
|
||||
|
||||
r = acquire_journal(&j, strv_skip(argv, 1));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = focus(j);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_output) {
|
||||
f = fopen(arg_output, "we");
|
||||
if (!f)
|
||||
return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", arg_output);
|
||||
}
|
||||
|
||||
print_info(f ? stdout : stderr, j, false);
|
||||
|
||||
r = save_core(j, f, NULL, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_journal_previous(j);
|
||||
if (r > 0 && !arg_quiet)
|
||||
log_notice("More than one entry matches, ignoring rest.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
VERB_SCOPE(, verb_dump_core, "dump", "[MATCHES…]", VERB_ANY, VERB_ANY, 0,
|
||||
"Print first matching coredump to stdout");
|
||||
|
||||
VERB(verb_run_debug, "debug", "[MATCHES…]", VERB_ANY, VERB_ANY, 0,
|
||||
"Start a debugger for the first matching coredump");
|
||||
|
||||
@@ -19,6 +19,7 @@ systemd_coredump_export_sources = files(
|
||||
)
|
||||
coredumpctl_sources = files(
|
||||
'coredumpctl.c',
|
||||
'coredumpctl-core.c',
|
||||
'coredumpctl-info.c',
|
||||
'coredumpctl-journal.c',
|
||||
'coredumpctl-list.c',
|
||||
|
||||
Reference in New Issue
Block a user