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
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.
version of the OS or kernel in case the system consistently fails to boot. This
support is built into various of its components. When used together these
components provide a complete solution on UEFI systems, built as add-on to the
-[Boot Loader
-Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION). However, the
-different components may also be used independently, and in combination with
-other software, to implement similar schemes, for example with other boot
-loaders or for non-UEFI systems. Here's a brief overview of the complete set of
-components:
+[Boot Loader Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION).
+However, the different components may also be used independently, and in
+combination with other software, to implement similar schemes, for example with
+other boot loaders or for non-UEFI systems. Here's a brief overview of the
+complete set of components:
* The
[`systemd-boot(7)`](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)
* The `boot-complete.target` target unit (see
[`systemd.special(7)`](https://www.freedesktop.org/software/systemd/man/systemd.special.html))
- serves as a generic extension point both for units that shall be considered
- necessary to consider a boot successful on one side (example:
- `systemd-boot-check-no-failures.service` as described above), and units that
- want to act only if the boot is successful on the other (example:
- `systemd-bless-boot.service` as described above).
+ serves as a generic extension point both for units that are necessary to
+ consider a boot successful (example: `systemd-boot-check-no-failures.service`
+ as described above), and units that want to act only if the boot is
+ successful (example: `systemd-bless-boot.service` as described above).
* The
[`kernel-install(8)`](https://www.freedesktop.org/software/systemd/man/kernel-install.html)
sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:pnT300CHI*
ACCEL_MOUNT_MATRIX=0, -1, 0; 1, 0, 0; 0, 0, 1
+sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:*pnM80TA*
sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:*pnT100TA*
sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:pnT200TA*
ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
<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>
<refsect1>
<title>Scope Unit Objects</title>
- <para>All slice unit objects implement the <interfacename>org.freedesktop.systemd1.Scope</interfacename>
+ <para>All scope unit objects implement the <interfacename>org.freedesktop.systemd1.Scope</interfacename>
interface (described here) in addition to the generic
<interfacename>org.freedesktop.systemd1.Unit</interfacename> interface (see above).</para>
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
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
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
<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>
return true;
}
+char *mangle_gecos(const char *d) {
+ char *mangled;
+
+ /* Makes sure the provided string becomes valid as a GEGOS field, by dropping bad chars. glibc's
+ * putwent() only changes \n and : to spaces. We do more: replace all CC too, and remove invalid
+ * UTF-8 */
+
+ mangled = strdup(d);
+ if (!mangled)
+ return NULL;
+
+ for (char *i = mangled; *i; i++) {
+ int len;
+
+ if ((uint8_t) *i < (uint8_t) ' ' || *i == ':') {
+ *i = ' ';
+ continue;
+ }
+
+ len = utf8_encoded_valid_unichar(i, (size_t) -1);
+ if (len < 0) {
+ *i = ' ';
+ continue;
+ }
+
+ i += len - 1;
+ }
+
+ return mangled;
+}
+
bool valid_home(const char *p) {
/* Note that this function is also called by valid_shell(), any
* changes must account for that. */
bool valid_user_group_name(const char *u, ValidUserFlags flags);
bool valid_gecos(const char *d);
+char *mangle_gecos(const char *d);
bool valid_home(const char *p);
static inline bool valid_shell(const char *p) {
printf("%s [OPTIONS...] COMMAND\n"
"\n%sMark the boot process as good or bad.%s\n"
"\nCommands:\n"
+ " status Show status of current boot loader entry\n"
" good Mark this boot as good\n"
" bad Mark this boot as bad\n"
" indeterminate Undo any marking as good or bad\n"
#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"
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;
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)
_cleanup_(dlclosep) void *dl = NULL;
_cleanup_free_ char *url = NULL;
_cleanup_fclose_ FILE *f = NULL;
- size_t url_size = 0, i;
+ size_t url_size = 0;
unsigned x, y;
QRcode* qr;
int r;
fputs("fss://", f);
- for (i = 0; i < seed_size; i++) {
+ for (size_t i = 0; i < seed_size; i++) {
if (i > 0 && i % 3 == 0)
fputc('-', f);
fprintf(f, "%02x", ((uint8_t*) seed)[i]);
#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"
#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"
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;
" -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"
ARG_USER,
ARG_SYSTEM,
ARG_ROOT,
+ ARG_IMAGE,
ARG_HEADER,
ARG_FACILITY,
ARG_SETUP_KEYS,
{ "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' },
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;
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;
}
static int setup_keys(void) {
#if HAVE_GCRYPT
- size_t mpk_size, seed_size, state_size, i;
+ size_t mpk_size, seed_size, state_size;
_cleanup_(unlink_and_freep) char *k = NULL;
_cleanup_free_ char *p = NULL;
uint8_t *mpk, *seed, *state;
k = mfree(k);
+ _cleanup_free_ char *hn = NULL;
+
if (on_tty()) {
+ hn = gethostname_malloc();
+ if (hn)
+ hostname_cleanup(hn);
+
+ char tsb[FORMAT_TIMESPAN_MAX];
fprintf(stderr,
+ "\nNew keys have been generated for host %s%s" SD_ID128_FORMAT_STR ".\n"
"\n"
- "The new key pair has been generated. The %ssecret sealing key%s has been written to\n"
- "the following local file. This key file is automatically updated when the\n"
- "sealing key is advanced. It should not be used on multiple hosts.\n"
+ "The %ssecret sealing key%s has been written to the following local file.\n"
+ "This key file is automatically updated when the sealing key is advanced.\n"
+ "It should not be used on multiple hosts.\n"
"\n"
"\t%s\n"
"\n"
+ "The sealing key is automatically changed every %s.\n"
+ "\n"
"Please write down the following %ssecret verification key%s. It should be stored\n"
- "at a safe location and should not be saved locally on disk.\n"
+ "in a safe location and should not be saved locally on disk.\n"
"\n\t%s",
+ hn ?: "", hn ? "/" : "", SD_ID128_FORMAT_VAL(machine),
ansi_highlight(), ansi_normal(),
p,
+ format_timespan(tsb, sizeof(tsb), arg_interval, 0),
ansi_highlight(), ansi_normal(),
ansi_highlight_red());
fflush(stderr);
}
- for (i = 0; i < seed_size; i++) {
+
+ for (size_t i = 0; i < seed_size; i++) {
if (i > 0 && i % 3 == 0)
putchar('-');
printf("%02x", ((uint8_t*) seed)[i]);
}
-
printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
if (on_tty()) {
- _cleanup_free_ char *hn = NULL;
- char tsb[FORMAT_TIMESPAN_MAX];
-
- fprintf(stderr,
- "%s\n"
- "The sealing key is automatically changed every %s.\n",
- ansi_normal(),
- format_timespan(tsb, sizeof(tsb), arg_interval, 0));
-
- hn = gethostname_malloc();
- if (hn) {
- hostname_cleanup(hn);
- fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
- } else
- fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
-
+ fprintf(stderr, "%s", ansi_normal());
#if HAVE_QRENCODE
(void) print_qr_code(stderr,
- "\nTo transfer the verification key to your phone please scan the QR code below:\n\n",
+ "\nTo transfer the verification key to your phone scan the QR code below:\n",
seed, seed_size,
n, arg_interval,
hn, machine);
}
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;
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();
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EADDRNOTAVAIL)
log_link_message_warning_errno(link, m, r, "Could not drop address");
- else
+ else if (r >= 0)
(void) manager_rtnl_process_address(rtnl, m, link->manager);
return 1;
assert(link->network);
assert(link->state != _LINK_STATE_INVALID);
+ if (link->address_remove_messages != 0) {
+ log_link_debug(link, "Removing old addresses, new addresses will be configured later.");
+ link->request_static_addresses = true;
+ return 0;
+ }
+
/* Reset all *_configured flags we are configuring. */
+ link->request_static_addresses = false;
link->addresses_configured = false;
link->addresses_ready = false;
link->neighbors_configured = false;
return 0;
}
+static int remove_static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ assert(m);
+ assert(link);
+ assert(link->ifname);
+ assert(link->address_remove_messages > 0);
+
+ link->address_remove_messages--;
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -EADDRNOTAVAIL)
+ log_link_message_warning_errno(link, m, r, "Could not drop address");
+ else if (r >= 0)
+ (void) manager_rtnl_process_address(rtnl, m, link->manager);
+
+ if (link->address_remove_messages == 0 && link->request_static_addresses) {
+ link_set_state(link, LINK_STATE_CONFIGURING);
+ r = link_request_set_addresses(link);
+ if (r < 0)
+ link_enter_failed(link);
+ }
+
+ return 1;
+}
+
static int link_drop_config(Link *link) {
Address *address, *pool_address;
Neighbor *neighbor;
if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1 && link_ipv6ll_enabled(link))
continue;
- r = address_remove(address, link, NULL);
+ r = address_remove(address, link, remove_static_address_handler);
if (r < 0)
return r;
+ link->address_remove_messages++;
+
/* If this address came from an address pool, clean up the pool */
- LIST_FOREACH(addresses, pool_address, link->pool_addresses) {
+ LIST_FOREACH(addresses, pool_address, link->pool_addresses)
if (address_equal(address, pool_address)) {
LIST_REMOVE(addresses, link->pool_addresses, pool_address);
address_free(pool_address);
break;
}
- }
}
SET_FOREACH(neighbor, link->neighbors, i) {
LinkAddressState address_state;
unsigned address_messages;
+ unsigned address_remove_messages;
unsigned address_label_messages;
unsigned neighbor_messages;
unsigned route_messages;
sd_ipv4ll *ipv4ll;
bool ipv4ll_address_configured:1;
+ bool request_static_addresses:1;
bool addresses_configured:1;
bool addresses_ready:1;
bool neighbors_configured:1;
#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"
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",
#include "sd-id128.h"
#include "list.h"
+#include "loop-util.h"
#include "macro.h"
typedef struct DissectedImage DissectedImage;
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);
size_t *target_len;
} ParseFieldVec;
-#define PARSE_FIELD_VEC_ENTRY(_field, _target, _target_len) \
- { .field = _field, .field_len = strlen(_field), .target = _target, .target_len = _target_len }
+#define PARSE_FIELD_VEC_ENTRY(_field, _target, _target_len) { \
+ .field = _field, \
+ .field_len = strlen(_field), \
+ .target = _target, \
+ .target_len = _target_len \
+ }
static int parse_fieldv(const void *data, size_t length, const ParseFieldVec *fields, unsigned n_fields) {
unsigned i;
FILE *f,
sd_journal *j,
OutputFlags flags,
+ int prio,
const char *field,
const size_t highlight[2]) {
- const char *highlight_on, *highlight_off;
+ const char *color_on = "", *color_off = "", *highlight_on = "";
const void *data;
size_t l, fl;
int r;
- if (FLAGS_SET(flags, OUTPUT_COLOR)) {
- highlight_on = ANSI_HIGHLIGHT_RED;
- highlight_off = ANSI_NORMAL;
- } else
- highlight_on = highlight_off = "";
+ if (FLAGS_SET(flags, OUTPUT_COLOR))
+ get_log_colors(prio, &color_on, &color_off, &highlight_on);
r = sd_journal_get_data(j, field, &data, &l);
if (r == -EBADMSG) {
data = (const uint8_t*) data + fl + 1;
l -= fl + 1;
- if (highlight && FLAGS_SET(flags, OUTPUT_COLOR)) {
- assert(highlight[0] <= highlight[1]);
- assert(highlight[1] <= l);
-
- fwrite((const char*) data, 1, highlight[0], f);
- fwrite(highlight_on, 1, strlen(highlight_on), f);
- fwrite((const char*) data + highlight[0], 1, highlight[1] - highlight[0], f);
- fwrite(highlight_off, 1, strlen(highlight_off), f);
- fwrite((const char*) data + highlight[1], 1, l - highlight[1], f);
+ if (FLAGS_SET(flags, OUTPUT_COLOR)) {
+ if (highlight) {
+ assert(highlight[0] <= highlight[1]);
+ assert(highlight[1] <= l);
+
+ fputs(color_on, f);
+ fwrite((const char*) data, 1, highlight[0], f);
+ fputs(highlight_on, f);
+ fwrite((const char*) data + highlight[0], 1, highlight[1] - highlight[0], f);
+ fputs(color_on, f);
+ fwrite((const char*) data + highlight[1], 1, l - highlight[1], f);
+ fputs(color_off, f);
+ } else {
+ fputs(color_on, f);
+ fwrite((const char*) data, 1, l, f);
+ fputs(color_off, f);
+ }
} else
fwrite((const char*) data, 1, l, f);
const Set *output_fields,
const size_t highlight[2]) {
+ int r, prio = LOG_INFO;
const char *field;
Iterator iterator;
- int r;
assert(j);
assert(f);
(void) sd_journal_set_data_threshold(j, 0);
+ if (FLAGS_SET(flags, OUTPUT_COLOR)) {
+ const void *data;
+ size_t l;
+
+ /* Determine priority of this entry, so that we can color it nicely */
+
+ r = sd_journal_get_data(j, "PRIORITY", &data, &l);
+ if (r == -EBADMSG) {
+ log_debug_errno(r, "Skipping message we can't read: %m");
+ return 0;
+ }
+ if (r < 0) {
+ if (r != -ENOENT)
+ return log_error_errno(r, "Failed to get data: %m");
+
+ /* An entry without PRIORITY */
+ } else if (l == 10 && memcmp(data, "PRIORITY=", 9) == 0) {
+ char c = ((char*) data)[9];
+
+ if (c >= '0' && c <= '7')
+ prio = c - '0';
+ }
+ }
+
if (set_isempty(output_fields))
- return output_cat_field(f, j, flags, "MESSAGE", highlight);
+ return output_cat_field(f, j, flags, prio, "MESSAGE", highlight);
SET_FOREACH(field, output_fields, iterator) {
- r = output_cat_field(f, j, flags, field, streq(field, "MESSAGE") ? highlight : NULL);
+ r = output_cat_field(f, j, flags, prio, field, streq(field, "MESSAGE") ? highlight : NULL);
if (r < 0)
return r;
}
#include "libcrypt-util.h"
#include "strv.h"
#include "user-record-nss.h"
+#include "user-util.h"
#define SET_IF(field, condition, value, fallback) \
field = (condition) ? (value) : (fallback)
if (r < 0)
return r;
- r = free_and_strdup(&hr->real_name,
- streq_ptr(pwd->pw_gecos, hr->user_name) ? NULL : empty_to_null(pwd->pw_gecos));
- if (r < 0)
- return r;
+ /* Some bad NSS modules synthesize GECOS fields with embedded ":" or "\n" characters, which are not
+ * something we can output in /etc/passwd compatible format, since these are record separators
+ * there. We normally refuse that, but we need to maintain compatibility with arbitrary NSS modules,
+ * hence let's do what glibc does: mangle the data to fit the format. */
+ if (isempty(pwd->pw_gecos) || streq_ptr(pwd->pw_gecos, hr->user_name))
+ hr->real_name = mfree(hr->real_name);
+ else if (valid_gecos(pwd->pw_gecos)) {
+ r = free_and_strdup(&hr->real_name, pwd->pw_gecos);
+ if (r < 0)
+ return r;
+ } else {
+ _cleanup_free_ char *mangled = NULL;
+
+ mangled = mangle_gecos(pwd->pw_gecos);
+ if (!mangled)
+ return -ENOMEM;
+
+ free_and_replace(hr->real_name, mangled);
+ }
r = free_and_strdup(&hr->home_directory, empty_to_null(pwd->pw_dir));
if (r < 0)
int json_dispatch_gecos(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
char **s = userdata;
const char *n;
- int r;
if (json_variant_is_null(variant)) {
*s = mfree(*s);
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
n = json_variant_string(variant);
- if (!valid_gecos(n))
- return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid GECOS compatible real name.", strna(name));
+ if (valid_gecos(n)) {
+ if (free_and_strdup(s, n) < 0)
+ return json_log_oom(variant, flags);
+ } else {
+ _cleanup_free_ char *m = NULL;
- r = free_and_strdup(s, n);
- if (r < 0)
- return json_log(variant, flags, r, "Failed to allocate string: %m");
+ json_log(variant, flags|JSON_DEBUG, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid GECOS compatible string, mangling.", strna(name));
+
+ m = mangle_gecos(n);
+ if (!m)
+ return json_log_oom(variant, flags);
+
+ free_and_replace(*s, m);
+ }
return 0;
}
" -M --machine=CONTAINER Operate on a local container\n"
" -t --type=TYPE List units of a particular type\n"
" --state=STATE List units with particular LOAD or SUB or ACTIVE state\n"
- " --failed Shorcut for --state=failed\n"
+ " --failed Shortcut for --state=failed\n"
" -p --property=NAME Show only properties by this name\n"
" -P NAME Equivalent to --value --property=NAME\n"
" -a --all Show all properties/all units currently in memory,\n"
#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"
} 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;
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
" --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"
ARG_VERSION = 0x100,
ARG_CAT_CONFIG,
ARG_ROOT,
+ ARG_IMAGE,
ARG_REPLACE,
ARG_INLINE,
ARG_NO_PAGER,
{ "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 },
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"))
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;
}
}
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;
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
return buf;
}
-static void* open_handle(const char* dir, const char* module, int flags) {
+static void* open_handle(const char *dir, const char *module, int flags) {
const char *path = NULL;
void *handle;
if (!path || access(path, F_OK) < 0)
path = strjoina("libnss_", module, ".so.2");
+ log_debug("Using %s", path);
handle = dlopen(path, flags);
if (!handle)
log_error("Failed to load module %s: %s", module, dlerror());
}
static int print_gaih_addrtuples(const struct gaih_addrtuple *tuples) {
- const struct gaih_addrtuple *it;
int n = 0;
- for (it = tuples; it; it = it->next) {
+ for (const struct gaih_addrtuple *it = tuples; it; it = it->next) {
_cleanup_free_ char *a = NULL;
union in_addr_union u;
int r;
fname = strjoina("_nss_", module, "_gethostbyname4_r");
f = dlsym(handle, fname);
log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
- assert_se(f);
+ if (!f) {
+ log_info("%s not defined", fname);
+ return;
+ }
status = f(name, &pat, buffer, sizeof buffer, &errno1, &errno2, &ttl);
if (status == NSS_STATUS_SUCCESS) {
fname = strjoina("_nss_", module, "_gethostbyname3_r");
f = dlsym(handle, fname);
log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
- assert_se(f);
+ if (!f) {
+ log_info("%s not defined", fname);
+ return;
+ }
status = f(name, af, &host, buffer, sizeof buffer, &errno1, &errno2, &ttl, &canon);
log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32,
fname = strjoina("_nss_", module, "_gethostbyname2_r");
f = dlsym(handle, fname);
log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
- assert_se(f);
+ if (!f) {
+ log_info("%s not defined", fname);
+ return;
+ }
status = f(name, af, &host, buffer, sizeof buffer, &errno1, &errno2);
log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s",
fname = strjoina("_nss_", module, "_gethostbyname_r");
f = dlsym(handle, fname);
log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f);
- assert_se(f);
+ if (!f) {
+ log_info("%s not defined", fname);
+ return;
+ }
status = f(name, &host, buffer, sizeof buffer, &errno1, &errno2);
log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s",
log_full_errno(f ? LOG_DEBUG : LOG_INFO, errno,
"dlsym(0x%p, %s) → 0x%p: %m", handle, fname, f);
- if (!f)
+ if (!f) {
+ log_info("%s not defined", fname);
return;
+ }
assert_se(in_addr_to_string(af, addr, &addr_pretty) >= 0);
log_full_errno(f ? LOG_DEBUG : LOG_INFO, errno,
"dlsym(0x%p, %s) → 0x%p: %m", handle, fname, f);
- if (!f)
+ if (!f) {
+ log_info("%s not defined", fname);
return;
+ }
assert_se(in_addr_to_string(af, addr, &addr_pretty) >= 0);
return 0;
}
-static int test_one_module(const char* dir,
+static int test_one_module(const char *dir,
const char *module,
char **names,
struct local_address *addresses,
int n_addresses) {
void *handle;
char **name;
- int i;
log_info("======== %s ========", module);
STRV_FOREACH(name, names)
test_byname(handle, module, *name);
- for (i = 0; i < n_addresses; i++)
+ for (int i = 0; i < n_addresses; i++)
test_byaddr(handle, module,
&addresses[i].address,
FAMILY_ADDRESS_SIZE(addresses[i].family),
}
static void test_filter_sets(void) {
- unsigned i;
- int r;
-
log_info("/* %s */", __func__);
if (!is_seccomp_available()) {
return;
}
- for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
+ for (unsigned i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
pid_t pid;
log_info("Testing %s", syscall_filter_sets[i].name);
assert_se(pid >= 0);
if (pid == 0) { /* Child? */
- int fd;
+ int fd, r;
/* If we look at the default set (or one that includes it), allow-list instead of deny-list */
if (IN_SET(i, SYSCALL_FILTER_SET_DEFAULT, SYSCALL_FILTER_SET_SYSTEM_SERVICE))
assert_se(parse_uid_range(" 01", &a, &b) == -EINVAL && a == 4 && b == 5);
}
+static void test_mangle_gecos_one(const char *input, const char *expected) {
+ _cleanup_free_ char *p = NULL;
+
+ assert_se(p = mangle_gecos(input));
+ assert_se(streq(p, expected));
+ assert_se(valid_gecos(p));
+}
+
+static void test_mangle_gecos(void) {
+ test_mangle_gecos_one("", "");
+ test_mangle_gecos_one("root", "root");
+ test_mangle_gecos_one("wuff\nwuff", "wuff wuff");
+ test_mangle_gecos_one("wuff:wuff", "wuff wuff");
+ test_mangle_gecos_one("wuff\r\n:wuff", "wuff wuff");
+ test_mangle_gecos_one("\n--wüff-wäff-wöff::", " --wüff-wäff-wöff ");
+ test_mangle_gecos_one("\xc3\x28", " (");
+ test_mangle_gecos_one("\xe2\x28\xa1", " ( ");
+}
+
int main(int argc, char *argv[]) {
test_uid_to_name_one(0, "root");
test_uid_to_name_one(UID_NOBODY, NOBODY_USER_NAME);
test_valid_user_group_name_or_numeric_relaxed();
test_valid_user_group_name_or_numeric();
test_valid_gecos();
+ test_mangle_gecos();
test_valid_home();
test_make_salt();
#include "copy.h"
#include "def.h"
#include "dirent-util.h"
+#include "dissect-image.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#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"
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
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);
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;
" --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"
ARG_PREFIX,
ARG_EXCLUDE_PREFIX,
ARG_ROOT,
+ ARG_IMAGE,
ARG_REPLACE,
ARG_NO_PAGER,
};
{ "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 },
{}
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) {
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"))
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;
}
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;
if (DEBUG_LOGGING) {
_cleanup_free_ char *t = NULL;
+ char **i;
+
+ 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();
+ }
- t = strv_join(config_dirs, "\n\t");
- if (t)
- log_debug("Looking for configuration files in (higher priority first):\n\t%s", t);
+ log_debug("Looking for configuration files in (higher priority first):%s", t);
}
if (arg_cat_config) {
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)
[Unit]
Description=Permit User Sessions
Documentation=man:systemd-user-sessions.service(8)
-After=remote-fs.target nss-user-lookup.target network.target
+After=remote-fs.target nss-user-lookup.target network.target home.mount
[Service]
Type=oneshot
Documentation=man:systemd-volatile-root.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
-After=sysroot.mount
+After=sysroot.mount systemd-repart.service
Before=initrd-root-fs.target shutdown.target
AssertPathExists=/etc/initrd-release