diff --git a/TODO b/TODO
index deed465ca6..d6ae200a34 100644
--- a/TODO
+++ b/TODO
@@ -120,9 +120,9 @@ Features:
this, it's useful to have one that can dump contents of them, too.
* All tools that support --root= should also learn --image= so that they can
- operate on disk images directly. Specifically: bootctl, tmpfiles, sysusers,
- systemctl, repart, journalctl, coredumpctl. (Already done: systemd-nspawn,
- systemd-firstboot)
+ operate on disk images directly. Specifically: bootctl, systemctl,
+ coredumpctl. (Already done: systemd-nspawn, systemd-firstboot,
+ systemd-repart, systemd-tmpfiles, systemd-sysusers, journalctl)
* seccomp: by default mask x32 ABI system wide on x86-64. it's on its way out
@@ -337,9 +337,6 @@ Features:
right) become genuine first class citizens, and we gain automatic, sane JSON
output for them.
-* systemd-firstboot: teach it dissector magic, so that you can point it to some
- disk image and it will just set everything in it all behind the scenes.
-
* We should probably replace /var/log/README, /etc/rc.d/README with symlinks
that are linked to these places instead of copied. After all they are
constant vendor data.
diff --git a/man/journalctl.xml b/man/journalctl.xml
index 07310d90a1..a03493fc35 100644
--- a/man/journalctl.xml
+++ b/man/journalctl.xml
@@ -756,16 +756,29 @@
- Takes a directory path as an argument. If
- specified, journalctl will operate on journal directories and catalog file hierarchy
- underneath the specified directory instead of the root
- directory (e.g. will create
- ROOT/var/lib/systemd/catalog/database,
- and journal files under ROOT/run/journal/
- or ROOT/var/log/journal/ will be displayed).
+ Takes a directory path as an argument. If specified, journalctl
+ will operate on journal directories and catalog file hierarchy underneath the specified directory
+ instead of the root directory (e.g. will create
+ ROOT/var/lib/systemd/catalog/database, and journal
+ files under ROOT/run/journal/ or
+ ROOT/var/log/journal/ will be displayed).
+
+
+
+ Takes a path to a disk image file or block device node. If specified,
+ journalctl will operate on the file system in the indicated disk image. This is
+ similar to but operates on file systems stored in disk images or block
+ devices, thus providing an easy way to extract log data from disk images. 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/man/systemd-sysusers.xml b/man/systemd-sysusers.xml
index cc02625699..1e5853a55b 100644
--- a/man/systemd-sysusers.xml
+++ b/man/systemd-sysusers.xml
@@ -68,6 +68,19 @@
paths.
+
+
+
+ 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 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.
+
+
When this option is given, one ore more positional arguments
diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml
index 998fd0911b..4510fb120a 100644
--- a/man/systemd-tmpfiles.xml
+++ b/man/systemd-tmpfiles.xml
@@ -149,6 +149,7 @@
the specified prefix. This option can be specified multiple
times.
+
Ignore rules with paths that start with the
@@ -156,6 +157,16 @@
times.
+
+
+ A shortcut for --exclude-prefix=/dev --exclude-prefix=/proc
+ --exclude-prefix=/run --exclude-prefix=/sys, i.e. exclude the hierarchies typically backed
+ by virtual or memory file systems. This is useful in combination with , if
+ the specified directory tree contains an OS tree without these virtual/memory file systems mounted
+ in, as it is typically not desirable to create any files and directories below these subdirectories
+ if they are supposed to be overmounted during runtime.
+
+
Takes a directory path as an argument. All paths will be prefixed with the given alternate
@@ -164,7 +175,26 @@
When this option is used, the libc Name Service Switch (NSS) is bypassed for resolving users
and groups. Instead the files /etc/passwd and /etc/group
inside the alternate root are read directly. This means that users/groups not listed in these files
- will not be resolved, i.e. LDAP NIS and other complex databases are not considered.
+ will not be resolved, i.e. LDAP NIS and other complex databases are not considered.
+
+ Consider combining this with to ensure the invocation does not create files
+ or directories below mount points in the OS image operated on that are typically overmounted during
+ runtime.
+
+
+
+
+
+ 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 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.
+
+ Implies .
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index 78abcbeff6..d56de0bb25 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -19,7 +19,6 @@
#include "kbd-util.h"
#include "libcrypt-util.h"
#include "locale-util.h"
-#include "loop-util.h"
#include "main-func.h"
#include "memory-util.h"
#include "mkdir.h"
@@ -907,75 +906,6 @@ static int process_kernel_cmdline(void) {
return 0;
}
-static int setup_image(char **ret_mount_dir, LoopDevice **ret_loop_device, DecryptedImage **ret_decrypted_image) {
- DissectImageFlags f = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK;
- _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
- _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
- _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
- _cleanup_(rmdir_and_freep) char *mount_dir = NULL;
- _cleanup_free_ char *temp = NULL;
- int r;
-
- if (!arg_image) {
- *ret_mount_dir = NULL;
- *ret_decrypted_image = NULL;
- *ret_loop_device = NULL;
- return 0;
- }
-
- assert(!arg_root);
-
- r = tempfn_random_child(NULL, "firstboot", &temp);
- if (r < 0)
- return log_error_errno(r, "Failed to generate temporary mount directory: %m");
-
- r = loop_device_make_by_path(arg_image, O_RDWR, LO_FLAGS_PARTSCAN, &d);
- if (r < 0)
- return log_error_errno(r, "Failed to set up loopback device: %m");
-
- r = dissect_image_and_warn(d->fd, arg_image, NULL, 0, NULL, NULL, f, &dissected_image);
- if (r < 0)
- return r;
-
- r = dissected_image_decrypt_interactively(dissected_image, NULL, NULL, 0, NULL, NULL, NULL, 0, f, &decrypted_image);
- if (r < 0)
- return r;
-
- r = detach_mount_namespace();
- if (r < 0)
- return log_error_errno(r, "Failed to detach mount namespace: %m");
-
- mount_dir = strdup(temp);
- if (!mount_dir)
- return log_oom();
-
- r = mkdir_p(mount_dir, 0700);
- if (r < 0) {
- mount_dir = mfree(mount_dir);
- return log_error_errno(r, "Failed to create mount point: %m");
- }
-
- r = dissected_image_mount(dissected_image, mount_dir, UID_INVALID, f);
- if (r < 0)
- return log_error_errno(r, "Failed to mount image: %m");
-
- if (decrypted_image) {
- r = decrypted_image_relinquish(decrypted_image);
- if (r < 0)
- return log_error_errno(r, "Failed to relinquish DM devices: %m");
- }
-
- loop_device_relinquish(d);
-
- arg_root = TAKE_PTR(temp);
-
- *ret_mount_dir = TAKE_PTR(mount_dir);
- *ret_decrypted_image = TAKE_PTR(decrypted_image);
- *ret_loop_device = TAKE_PTR(d);
-
- return 1;
-}
-
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
@@ -1353,9 +1283,22 @@ static int run(int argc, char *argv[]) {
return 0; /* disabled */
}
- r = setup_image(&unlink_dir, &loop_device, &decrypted_image);
- if (r < 0)
- return r;
+ if (arg_image) {
+ assert(!arg_root);
+
+ r = mount_image_privately_interactively(
+ arg_image,
+ DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK,
+ &unlink_dir,
+ &loop_device,
+ &decrypted_image);
+ if (r < 0)
+ return r;
+
+ arg_root = strdup(unlink_dir);
+ if (!arg_root)
+ return log_oom();
+ }
r = process_locale();
if (r < 0)
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 79daa43494..53d2c60f91 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -31,6 +31,7 @@
#include "chattr-util.h"
#include "def.h"
#include "device-private.h"
+#include "dissect-image.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
@@ -51,6 +52,7 @@
#include "logs-show.h"
#include "memory-util.h"
#include "mkdir.h"
+#include "mount-util.h"
#include "mountpoint-util.h"
#include "nulstr-util.h"
#include "pager.h"
@@ -121,6 +123,7 @@ static bool arg_reverse = false;
static int arg_journal_type = 0;
static int arg_namespace_flags = 0;
static char *arg_root = NULL;
+static char *arg_image = NULL;
static const char *arg_machine = NULL;
static const char *arg_namespace = NULL;
static uint64_t arg_vacuum_size = 0;
@@ -375,6 +378,7 @@ static int help(void) {
" -D --directory=PATH Show journal files from directory\n"
" --file=PATH Show journal file\n"
" --root=ROOT Operate on files below a root directory\n"
+ " --image=IMAGE Operate on files in filesystem image\n"
" --namespace=NAMESPACE Show journal data from specified namespace\n"
" --interval=TIME Time interval for changing the FSS sealing key\n"
" --verify-key=KEY Specify FSS verification key\n"
@@ -422,6 +426,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_USER,
ARG_SYSTEM,
ARG_ROOT,
+ ARG_IMAGE,
ARG_HEADER,
ARG_FACILITY,
ARG_SETUP_KEYS,
@@ -478,6 +483,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "directory", required_argument, NULL, 'D' },
{ "file", required_argument, NULL, ARG_FILE },
{ "root", required_argument, NULL, ARG_ROOT },
+ { "image", required_argument, NULL, ARG_IMAGE },
{ "header", no_argument, NULL, ARG_HEADER },
{ "identifier", required_argument, NULL, 't' },
{ "priority", required_argument, NULL, 'p' },
@@ -713,7 +719,13 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_ROOT:
- r = parse_path_argument_and_warn(optarg, true, &arg_root);
+ r = parse_path_argument_and_warn(optarg, /* suppress_root= */ true, &arg_root);
+ if (r < 0)
+ return r;
+ break;
+
+ case ARG_IMAGE:
+ r = parse_path_argument_and_warn(optarg, /* suppress_root= */ false, &arg_image);
if (r < 0)
return r;
break;
@@ -1043,8 +1055,8 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_follow && !arg_no_tail && !arg_since && arg_lines == ARG_LINES_DEFAULT)
arg_lines = 10;
- if (!!arg_directory + !!arg_file + !!arg_machine + !!arg_root > 1) {
- log_error("Please specify at most one of -D/--directory=, --file=, -M/--machine=, --root.");
+ if (!!arg_directory + !!arg_file + !!arg_machine + !!arg_root + !!arg_image > 1) {
+ log_error("Please specify at most one of -D/--directory=, --file=, -M/--machine=, --root=, --image=.");
return -EINVAL;
}
@@ -2084,6 +2096,9 @@ static int wait_for_change(sd_journal *j, int poll_fd) {
}
int main(int argc, char *argv[]) {
+ _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
+ _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
+ _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
bool previous_boot_id_valid = false, first_line = true, ellipsized = false, need_seek = false;
bool use_cursor = false, after_cursor = false;
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
@@ -2101,6 +2116,24 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto finish;
+ if (arg_image) {
+ assert(!arg_root);
+
+ r = mount_image_privately_interactively(
+ arg_image,
+ DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_RELAX_VAR_CHECK|
+ (arg_action == ACTION_UPDATE_CATALOG ? DISSECT_IMAGE_FSCK : DISSECT_IMAGE_READ_ONLY),
+ &unlink_dir,
+ &loop_device,
+ &decrypted_image);
+ if (r < 0)
+ return r;
+
+ arg_root = strdup(unlink_dir);
+ if (!arg_root)
+ return log_oom();
+ }
+
signal(SIGWINCH, columns_lines_cache_reset);
sigbus_install();
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index e96658ca66..c98c84993e 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -33,8 +33,10 @@
#include "hexdecoct.h"
#include "hostname-util.h"
#include "id128-util.h"
+#include "mkdir.h"
#include "mount-util.h"
#include "mountpoint-util.h"
+#include "namespace-util.h"
#include "nulstr-util.h"
#include "os-util.h"
#include "path-util.h"
@@ -1940,9 +1942,84 @@ const char* mount_options_from_part(const MountOptions *options, unsigned int pa
LIST_FOREACH(mount_options, m, (MountOptions *)options)
if (partition_number == m->partition_number && !isempty(m->options))
return m->options;
+
return NULL;
}
+int mount_image_privately_interactively(
+ const char *image,
+ DissectImageFlags flags,
+ char **ret_directory,
+ LoopDevice **ret_loop_device,
+ DecryptedImage **ret_decrypted_image) {
+
+ _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
+ _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
+ _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
+ _cleanup_(rmdir_and_freep) char *created_dir = NULL;
+ _cleanup_free_ char *temp = NULL;
+ int r;
+
+ /* Mounts an OS image at a temporary place, inside a newly created mount namespace of our own. This
+ * is used by tools such as systemd-tmpfiles or systemd-firstboot to operate on some disk image
+ * easily. */
+
+ assert(image);
+ assert(ret_directory);
+ assert(ret_loop_device);
+ assert(ret_decrypted_image);
+
+ r = tempfn_random_child(NULL, program_invocation_short_name, &temp);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate temporary mount directory: %m");
+
+ r = loop_device_make_by_path(
+ image,
+ FLAGS_SET(flags, DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR,
+ FLAGS_SET(flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
+ &d);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set up loopback device: %m");
+
+ r = dissect_image_and_warn(d->fd, image, NULL, 0, NULL, NULL, flags, &dissected_image);
+ if (r < 0)
+ return r;
+
+ r = dissected_image_decrypt_interactively(dissected_image, NULL, NULL, 0, NULL, NULL, NULL, 0, flags, &decrypted_image);
+ if (r < 0)
+ return r;
+
+ r = detach_mount_namespace();
+ if (r < 0)
+ return log_error_errno(r, "Failed to detach mount namespace: %m");
+
+ r = mkdir_p(temp, 0700);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create mount point: %m");
+
+ created_dir = TAKE_PTR(temp);
+
+ r = dissected_image_mount(dissected_image, created_dir, UID_INVALID, flags);
+ if (r == -EUCLEAN)
+ return log_error_errno(r, "File system check on image failed: %m");
+ if (r < 0)
+ return log_error_errno(r, "Failed to mount image: %m");
+
+ if (decrypted_image) {
+ r = decrypted_image_relinquish(decrypted_image);
+ if (r < 0)
+ return log_error_errno(r, "Failed to relinquish DM devices: %m");
+ }
+
+ loop_device_relinquish(d);
+
+ *ret_directory = TAKE_PTR(created_dir);
+ *ret_loop_device = TAKE_PTR(d);
+ *ret_decrypted_image = TAKE_PTR(decrypted_image);
+
+ return 0;
+}
+
static const char *const partition_designator_table[] = {
[PARTITION_ROOT] = "root",
[PARTITION_ROOT_SECONDARY] = "root-secondary",
diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h
index 52aa377a67..7f67c8745e 100644
--- a/src/shared/dissect-image.h
+++ b/src/shared/dissect-image.h
@@ -6,6 +6,7 @@
#include "sd-id128.h"
#include "list.h"
+#include "loop-util.h"
#include "macro.h"
typedef struct DissectedImage DissectedImage;
@@ -117,3 +118,5 @@ int partition_designator_from_string(const char *name) _pure_;
int verity_metadata_load(const char *image, const char *root_hash_path, void **ret_roothash, size_t *ret_roothash_size, char **ret_verity_data, char **ret_roothashsig);
bool dissected_image_can_do_verity(const DissectedImage *image, unsigned partition_designator);
bool dissected_image_has_verity(const DissectedImage *image, unsigned partition_designator);
+
+int mount_image_privately_interactively(const char *path, DissectImageFlags flags, char **ret_directory, LoopDevice **ret_loop_device, DecryptedImage **ret_decrypted_image);
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index b5e7e08eee..383a62c598 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -7,17 +7,19 @@
#include "conf-files.h"
#include "copy.h"
#include "def.h"
+#include "dissect-image.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
#include "hashmap.h"
#include "main-func.h"
+#include "mount-util.h"
#include "pager.h"
#include "path-util.h"
#include "pretty-print.h"
-#include "set.h"
#include "selinux-util.h"
+#include "set.h"
#include "smack-util.h"
#include "specifier.h"
#include "string-util.h"
@@ -63,6 +65,7 @@ typedef struct Item {
} Item;
static char *arg_root = NULL;
+static char *arg_image = NULL;
static bool arg_cat_config = false;
static const char *arg_replace = NULL;
static bool arg_inline = false;
@@ -93,6 +96,7 @@ STATIC_DESTRUCTOR_REGISTER(database_by_groupname, hashmap_freep);
STATIC_DESTRUCTOR_REGISTER(database_groups, set_free_freep);
STATIC_DESTRUCTOR_REGISTER(uid_range, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
static int errno_is_not_exists(int code) {
/* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
@@ -1739,6 +1743,7 @@ static int help(void) {
" --version Show package version\n"
" --cat-config Show configuration files\n"
" --root=PATH Operate on an alternate filesystem root\n"
+ " --image=PATH Operate on disk image as filesystem root\n"
" --replace=PATH Treat arguments as replacement for PATH\n"
" --inline Treat arguments as configuration lines\n"
" --no-pager Do not pipe output into a pager\n"
@@ -1756,6 +1761,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_VERSION = 0x100,
ARG_CAT_CONFIG,
ARG_ROOT,
+ ARG_IMAGE,
ARG_REPLACE,
ARG_INLINE,
ARG_NO_PAGER,
@@ -1766,6 +1772,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "version", no_argument, NULL, ARG_VERSION },
{ "cat-config", no_argument, NULL, ARG_CAT_CONFIG },
{ "root", required_argument, NULL, ARG_ROOT },
+ { "image", required_argument, NULL, ARG_IMAGE },
{ "replace", required_argument, NULL, ARG_REPLACE },
{ "inline", no_argument, NULL, ARG_INLINE },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
@@ -1797,6 +1804,12 @@ static int parse_argv(int argc, char *argv[]) {
return r;
break;
+ case ARG_IMAGE:
+ r = parse_path_argument_and_warn(optarg, /* suppress_root= */ false, &arg_image);
+ if (r < 0)
+ return r;
+ break;
+
case ARG_REPLACE:
if (!path_is_absolute(optarg) ||
!endswith(optarg, ".conf"))
@@ -1829,6 +1842,9 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"When --replace= is given, some configuration items must be specified");
+ if (arg_image && arg_root)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
+
return 1;
}
@@ -1880,6 +1896,9 @@ static int read_config_files(char **args) {
}
static int run(int argc, char *argv[]) {
+ _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
+ _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
+ _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
_cleanup_close_ int lock = -1;
Iterator iterator;
Item *i;
@@ -1900,6 +1919,23 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
+ if (arg_image) {
+ assert(!arg_root);
+
+ r = mount_image_privately_interactively(
+ arg_image,
+ DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK,
+ &unlink_dir,
+ &loop_device,
+ &decrypted_image);
+ if (r < 0)
+ return r;
+
+ arg_root = strdup(unlink_dir);
+ if (!arg_root)
+ return log_oom();
+ }
+
/* If command line arguments are specified along with --replace, read all
* configuration files and insert the positional arguments at the specified
* place. Otherwise, if command line arguments are specified, execute just
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 2404e36bf2..616a54b3c3 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -26,6 +26,7 @@
#include "copy.h"
#include "def.h"
#include "dirent-util.h"
+#include "dissect-image.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
@@ -38,6 +39,7 @@
#include "macro.h"
#include "main-func.h"
#include "mkdir.h"
+#include "mount-util.h"
#include "mountpoint-util.h"
#include "offline-passwd.h"
#include "pager.h"
@@ -164,6 +166,7 @@ static PagerFlags arg_pager_flags = 0;
static char **arg_include_prefixes = NULL;
static char **arg_exclude_prefixes = NULL;
static char *arg_root = NULL;
+static char *arg_image = NULL;
static char *arg_replace = NULL;
#define MAX_DEPTH 256
@@ -177,6 +180,7 @@ STATIC_DESTRUCTOR_REGISTER(unix_sockets, set_free_freep);
STATIC_DESTRUCTOR_REGISTER(arg_include_prefixes, freep);
STATIC_DESTRUCTOR_REGISTER(arg_exclude_prefixes, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
static int specifier_machine_id_safe(char specifier, const void *data, const void *userdata, char **ret);
static int specifier_directory(char specifier, const void *data, const void *userdata, char **ret);
@@ -2884,6 +2888,27 @@ static int cat_config(char **config_dirs, char **args) {
return cat_files(NULL, files, 0);
}
+static int exclude_default_prefixes(void) {
+ int r;
+
+ /* Provide an easy way to exclude virtual/memory file systems from what we do here. Useful in
+ * combination with --root= where we probably don't want to apply stuff to these dirs as they are
+ * likely over-mounted if the root directory is actually used, and it wouldbe less than ideal to have
+ * all kinds of files created/adjusted underneath these mount points. */
+
+ r = strv_extend_strv(
+ &arg_exclude_prefixes,
+ STRV_MAKE("/dev",
+ "/proc",
+ "/run",
+ "/sys"),
+ true);
+ if (r < 0)
+ return log_oom();
+
+ return 0;
+}
+
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
@@ -2904,7 +2929,9 @@ static int help(void) {
" --boot Execute actions only safe at boot\n"
" --prefix=PATH Only apply rules with the specified prefix\n"
" --exclude-prefix=PATH Ignore rules with the specified prefix\n"
+ " -E Ignore rules prefixed with /dev, /proc, /run, /sys\n"
" --root=PATH Operate on an alternate filesystem root\n"
+ " --image=PATH Operate on disk image as filesystem root\n"
" --replace=PATH Treat arguments as replacement for PATH\n"
" --no-pager Do not pipe output into a pager\n"
"\nSee the %s for details.\n"
@@ -2928,6 +2955,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_PREFIX,
ARG_EXCLUDE_PREFIX,
ARG_ROOT,
+ ARG_IMAGE,
ARG_REPLACE,
ARG_NO_PAGER,
};
@@ -2944,6 +2972,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "prefix", required_argument, NULL, ARG_PREFIX },
{ "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX },
{ "root", required_argument, NULL, ARG_ROOT },
+ { "image", required_argument, NULL, ARG_IMAGE },
{ "replace", required_argument, NULL, ARG_REPLACE },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{}
@@ -2954,7 +2983,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hE", options, NULL)) >= 0)
switch (c) {
@@ -3004,6 +3033,21 @@ static int parse_argv(int argc, char *argv[]) {
return r;
break;
+ case ARG_IMAGE:
+ r = parse_path_argument_and_warn(optarg, /* suppress_root= */ false, &arg_image);
+ if (r < 0)
+ return r;
+
+ /* Imply -E here since it makes little sense to create files persistently in the /run mointpoint of a disk image */
+ _fallthrough_;
+
+ case 'E':
+ r = exclude_default_prefixes();
+ if (r < 0)
+ return r;
+
+ break;
+
case ARG_REPLACE:
if (!path_is_absolute(optarg) ||
!endswith(optarg, ".conf"))
@@ -3036,6 +3080,13 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"When --replace= is given, some configuration items must be specified");
+ if (arg_root && arg_user)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Combination of --user and --root= is not supported.");
+
+ if (arg_image && arg_root)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
+
return 1;
}
@@ -3211,6 +3262,9 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(item_array_hash_ops, char, string_
ItemArray, item_array_free);
static int run(int argc, char *argv[]) {
+ _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
+ _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
+ _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
_cleanup_strv_free_ char **config_dirs = NULL;
bool invalid_config = false;
Iterator iterator;
@@ -3243,10 +3297,20 @@ static int run(int argc, char *argv[]) {
if (DEBUG_LOGGING) {
_cleanup_free_ char *t = NULL;
+ char **i;
- t = strv_join(config_dirs, "\n\t");
- if (t)
- log_debug("Looking for configuration files in (higher priority first):\n\t%s", t);
+ STRV_FOREACH(i, config_dirs) {
+ _cleanup_free_ char *j = NULL;
+
+ j = path_join(arg_root, *i);
+ if (!j)
+ return log_oom();
+
+ if (!strextend(&t, "\n\t", j, NULL))
+ return log_oom();
+ }
+
+ log_debug("Looking for configuration files in (higher priority first):%s", t);
}
if (arg_cat_config) {
@@ -3261,6 +3325,23 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
+ if (arg_image) {
+ assert(!arg_root);
+
+ r = mount_image_privately_interactively(
+ arg_image,
+ DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK,
+ &unlink_dir,
+ &loop_device,
+ &decrypted_image);
+ if (r < 0)
+ return r;
+
+ arg_root = strdup(unlink_dir);
+ if (!arg_root)
+ return log_oom();
+ }
+
items = ordered_hashmap_new(&item_array_hash_ops);
globs = ordered_hashmap_new(&item_array_hash_ops);
if (!items || !globs)