Merge pull request #16604 from poettering/tmpfiles-image

add --image= switch to tmpfiles, sysusers, journalctl
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2020-08-07 10:08:42 +02:00 committed by GitHub
commit 992aa67231
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 321 additions and 95 deletions

9
TODO
View File

@ -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.

View File

@ -756,16 +756,29 @@
<varlistentry>
<term><option>--root=<replaceable>ROOT</replaceable></option></term>
<listitem><para>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. <option>--update-catalog</option> will create
<filename><replaceable>ROOT</replaceable>/var/lib/systemd/catalog/database</filename>,
and journal files under <filename><replaceable>ROOT</replaceable>/run/journal/</filename>
or <filename><replaceable>ROOT</replaceable>/var/log/journal/</filename> will be displayed).
<listitem><para>Takes a directory path as an argument. If specified, <command>journalctl</command>
will operate on journal directories and catalog file hierarchy underneath the specified directory
instead of the root directory (e.g. <option>--update-catalog</option> will create
<filename><replaceable>ROOT</replaceable>/var/lib/systemd/catalog/database</filename>, and journal
files under <filename><replaceable>ROOT</replaceable>/run/journal/</filename> or
<filename><replaceable>ROOT</replaceable>/var/log/journal/</filename> will be displayed).
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--image=<replaceable>IMAGE</replaceable></option></term>
<listitem><para>Takes a path to a disk image file or block device node. If specified,
<command>journalctl</command> will operate on the file system in the indicated disk image. This is
similar to <option>--root=</option> 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 <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
Specification</ulink>. For further information on supported disk images, see
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
switch of the same name.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--namespace=<replaceable>NAMESPACE</replaceable></option></term>

View File

@ -68,6 +68,19 @@
paths. </para></listitem>
</varlistentry>
<varlistentry>
<term><option>--image=<replaceable>image</replaceable></option></term>
<listitem><para>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 <option>--root=</option>
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
<ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
Specification</ulink>. For further information on supported disk images, see
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
switch of the same name.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--replace=<replaceable>PATH</replaceable></option></term>
<listitem><para>When this option is given, one ore more positional arguments

View File

@ -149,6 +149,7 @@
the specified prefix. This option can be specified multiple
times.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--exclude-prefix=<replaceable>path</replaceable></option></term>
<listitem><para>Ignore rules with paths that start with the
@ -156,6 +157,16 @@
times.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-E</option></term>
<listitem><para>A shortcut for <literal>--exclude-prefix=/dev --exclude-prefix=/proc
--exclude-prefix=/run --exclude-prefix=/sys</literal>, i.e. exclude the hierarchies typically backed
by virtual or memory file systems. This is useful in combination with <option>--root=</option>, 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.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--root=<replaceable>root</replaceable></option></term>
<listitem><para>Takes a directory path as an argument. All paths will be prefixed with the given alternate
@ -164,7 +175,26 @@
<para>When this option is used, the libc Name Service Switch (NSS) is bypassed for resolving users
and groups. Instead the files <filename>/etc/passwd</filename> and <filename>/etc/group</filename>
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.</para></listitem>
will not be resolved, i.e. LDAP NIS and other complex databases are not considered.</para>
<para>Consider combining this with <option>-E</option> 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.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--image=<replaceable>image</replaceable></option></term>
<listitem><para>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 <option>--root=</option>
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
<ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
Specification</ulink>. For further information on supported disk images, see
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
switch of the same name.</para>
<para>Implies <option>-E</option>.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -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)

View File

@ -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();

View File

@ -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",

View File

@ -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);

View File

@ -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

View File

@ -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)