diff --git a/src/coredump/coredumpctl-core.c b/src/coredump/coredumpctl-core.c new file mode 100644 index 0000000000..4baaadd4b5 --- /dev/null +++ b/src/coredump/coredumpctl-core.c @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#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; +} diff --git a/src/coredump/coredumpctl-core.h b/src/coredump/coredumpctl-core.h new file mode 100644 index 0000000000..2742496dca --- /dev/null +++ b/src/coredump/coredumpctl-core.h @@ -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); diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 80336f1a6c..4e9c8bfd70 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -1,6 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include #include #include #include @@ -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"); diff --git a/src/coredump/meson.build b/src/coredump/meson.build index 9379affb7f..ab186fd472 100644 --- a/src/coredump/meson.build +++ b/src/coredump/meson.build @@ -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',