diff --git a/man/coredumpctl.xml b/man/coredumpctl.xml index d28a177842..8002549f7d 100644 --- a/man/coredumpctl.xml +++ b/man/coredumpctl.xml @@ -248,6 +248,26 @@ + + + + Use root directory when searching for coredumps. + + + + + + + Takes a path to a disk image file or block device node. If specified, all operations + are applied to file system in the indicated disk image. This option is similar to + , but operates on file systems stored in disk images or block devices. The + disk image should either contain just a file system or a set of file systems within a GPT partition + table, following the Discoverable Partitions + Specification. For further information on supported disk images, see + systemd-nspawn1's + switch of the same name. + + diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index da0b591c48..383d5716c4 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -14,8 +14,10 @@ #include "bus-error.h" #include "bus-locator.h" #include "bus-util.h" +#include "chase-symlinks.h" #include "compress.h" #include "def.h" +#include "dissect-image.h" #include "fd-util.h" #include "format-table.h" #include "fs-util.h" @@ -25,6 +27,7 @@ #include "log.h" #include "macro.h" #include "main-func.h" +#include "mount-util.h" #include "pager.h" #include "parse-argument.h" #include "parse-util.h" @@ -49,6 +52,8 @@ static const char* arg_field = NULL; static const char *arg_debugger = NULL; static char **arg_debugger_args = NULL; static const char *arg_directory = NULL; +static char *arg_root = NULL; +static char *arg_image = NULL; static char **arg_file = NULL; static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; static PagerFlags arg_pager_flags = 0; @@ -121,6 +126,10 @@ static int acquire_journal(sd_journal **ret, char **matches) { r = sd_journal_open_directory(&j, arg_directory, 0); if (r < 0) return log_error_errno(r, "Failed to open journals in directory: %s: %m", arg_directory); + } else if (arg_root) { + r = sd_journal_open_directory(&j, arg_root, SD_JOURNAL_OS_ROOT); + if (r < 0) + return log_error_errno(r, "Failed to open journals in root directory: %s: %m", arg_root); } else if (arg_file) { r = sd_journal_open_files(&j, (const char**)arg_file, 0); if (r < 0) @@ -205,6 +214,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_JSON, ARG_DEBUGGER, ARG_FILE, + ARG_ROOT, + ARG_IMAGE, ARG_ALL, }; @@ -226,6 +237,8 @@ static int parse_argv(int argc, char *argv[]) { { "until", required_argument, NULL, 'U' }, { "quiet", no_argument, NULL, 'q' }, { "json", required_argument, NULL, ARG_JSON }, + { "root", required_argument, NULL, ARG_ROOT }, + { "image", required_argument, NULL, ARG_IMAGE }, { "all", no_argument, NULL, ARG_ALL }, {} }; @@ -316,6 +329,18 @@ static int parse_argv(int argc, char *argv[]) { arg_directory = optarg; break; + case ARG_ROOT: + r = parse_path_argument(optarg, false, &arg_root); + if (r < 0) + return r; + break; + + case ARG_IMAGE: + r = parse_path_argument(optarg, false, &arg_image); + if (r < 0) + return r; + break; + case 'r': arg_reverse = true; break; @@ -347,6 +372,9 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--since= must be before --until=."); + if ((!!arg_directory + !!arg_image + !!arg_root) > 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root=, --image= or -D/--directory=, the combination of these options is not supported."); + return 1; } @@ -467,6 +495,26 @@ error: *ret_size = UINT64_MAX; } +static int resolve_filename(const char *path, const char *root, char **ret) { + char *resolved = NULL; + int r; + + if (!path) + return 0; + + r = chase_symlinks(path, root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, NULL); + if (r < 0) + return log_error_errno(r, "Failed to resolve \"%s%s\": %m", strempty(root), path); + + free_and_replace(*ret, resolved); + + /* chase_symlinks() witth flag CHASE_NONEXISTENT + * will return 0 if the file doesn't exist and 1 if it does. + * Return that to the caller + */ + return r; +} + static int print_list(FILE* file, sd_journal *j, Table *t) { _cleanup_free_ char *mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL, @@ -515,9 +563,13 @@ static int print_list(FILE* file, sd_journal *j, Table *t) { normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR); - if (filename) + if (filename) { + r = resolve_filename(filename, arg_root, &filename); + if (r < 0) + return r; + analyze_coredump_file(filename, &present, &color, &size); - else if (coredump) + } else if (coredump) present = "journal"; else if (normal_coredump) { present = "none"; @@ -701,6 +753,10 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) { fprintf(file, " Hostname: %s\n", hostname); if (filename) { + r = resolve_filename(filename, arg_root, &filename); + if (r < 0) + return r; + const char *state = NULL, *color = NULL; uint64_t size = UINT64_MAX; @@ -915,13 +971,18 @@ static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) /* 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); - if (access(filename, R_OK) < 0) - return log_error_errno(errno, "File \"%s\" is not readable: %m", filename); + r = chase_symlinks_and_access(filename, arg_root, CHASE_PREFIX_ROOT, F_OK, &resolved, NULL); + 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); @@ -1142,6 +1203,10 @@ static int run_debug(int argc, char **argv, void *userdata) { return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Binary is not an absolute path."); + r = resolve_filename(exe, arg_root, &exe); + if (r < 0) + return r; + r = save_core(j, NULL, &path, &unlink_path); if (r < 0) return r; @@ -1150,6 +1215,24 @@ static int run_debug(int argc, char **argv, void *userdata) { if (r < 0) return log_oom(); + if (arg_root) { + if (streq(arg_debugger, "gdb")) { + const char *sysroot_cmd; + sysroot_cmd = strjoina("set sysroot ", arg_root); + + r = strv_extend_strv(&debugger_call, STRV_MAKE("-iex", sysroot_cmd), false); + if (r < 0) + return log_oom(); + } else if (streq(arg_debugger, "lldb")) { + const char *sysroot_cmd; + sysroot_cmd = strjoina("platform select --sysroot ", arg_root, " host"); + + r = strv_extend_strv(&debugger_call, STRV_MAKE("-O", sysroot_cmd), false); + if (r < 0) + return log_oom(); + } + } + /* Don't interfere with gdb and its handling of SIGINT. */ (void) ignore_signals(SIGINT); @@ -1252,6 +1335,8 @@ static int coredumpctl_main(int argc, char *argv[]) { } static int run(int argc, char *argv[]) { + _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; + _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; int r, units_active; setlocale(LC_ALL, ""); @@ -1268,6 +1353,25 @@ static int run(int argc, char *argv[]) { units_active = check_units_active(); /* error is treated the same as 0 */ + if (arg_image) { + assert(!arg_root); + + r = mount_image_privately_interactively( + arg_image, + DISSECT_IMAGE_GENERIC_ROOT | + DISSECT_IMAGE_REQUIRE_ROOT | + DISSECT_IMAGE_RELAX_VAR_CHECK | + DISSECT_IMAGE_VALIDATE_OS, + &mounted_dir, + &loop_device); + if (r < 0) + return r; + + arg_root = strdup(mounted_dir); + if (!arg_root) + return log_oom(); + } + r = coredumpctl_main(argc, argv); if (units_active > 0)