summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TODO9
-rw-r--r--docs/AUTOMATIC_BOOT_ASSESSMENT.md20
-rw-r--r--hwdb.d/60-sensor.hwdb1
-rw-r--r--man/journalctl.xml27
-rw-r--r--man/org.freedesktop.systemd1.xml2
-rw-r--r--man/systemd-sysusers.xml13
-rw-r--r--man/systemd-tmpfiles.xml32
-rw-r--r--src/basic/user-util.c31
-rw-r--r--src/basic/user-util.h1
-rw-r--r--src/boot/bless-boot.c1
-rw-r--r--src/firstboot/firstboot.c89
-rw-r--r--src/journal/journal-qrcode.c4
-rw-r--r--src/journal/journalctl.c84
-rw-r--r--src/network/networkd-address.c2
-rw-r--r--src/network/networkd-link.c43
-rw-r--r--src/network/networkd-link.h2
-rw-r--r--src/shared/dissect-image.c77
-rw-r--r--src/shared/dissect-image.h3
-rw-r--r--src/shared/logs-show.c74
-rw-r--r--src/shared/user-record-nss.c24
-rw-r--r--src/shared/user-record.c19
-rw-r--r--src/systemctl/systemctl.c2
-rw-r--r--src/sysusers/sysusers.c38
-rw-r--r--src/test/test-nss.c39
-rw-r--r--src/test/test-seccomp.c7
-rw-r--r--src/test/test-user-util.c20
-rw-r--r--src/tmpfiles/tmpfiles.c89
-rw-r--r--units/systemd-user-sessions.service.in2
-rw-r--r--units/systemd-volatile-root.service.in2
29 files changed, 570 insertions, 187 deletions
diff --git a/TODO b/TODO
index c038a0d115..c0fbf8bc6a 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/docs/AUTOMATIC_BOOT_ASSESSMENT.md b/docs/AUTOMATIC_BOOT_ASSESSMENT.md
index 83ddf28fdd..f6d63afcdf 100644
--- a/docs/AUTOMATIC_BOOT_ASSESSMENT.md
+++ b/docs/AUTOMATIC_BOOT_ASSESSMENT.md
@@ -10,12 +10,11 @@ systemd provides support for automatically reverting back to the previous
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)
@@ -45,11 +44,10 @@ components:
* 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)
diff --git a/hwdb.d/60-sensor.hwdb b/hwdb.d/60-sensor.hwdb
index 092d356b64..36ff80fd15 100644
--- a/hwdb.d/60-sensor.hwdb
+++ b/hwdb.d/60-sensor.hwdb
@@ -111,6 +111,7 @@ sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:*pnT100CHI*
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
diff --git a/man/journalctl.xml b/man/journalctl.xml
index 07310d90a1..a03493fc35 100644
--- a/man/journalctl.xml
+++ b/man/journalctl.xml
@@ -756,17 +756,30 @@
<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>
<listitem><para>Takes a journal namespace identifier string as argument. If not specified the data
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index 6b16ae16da..8a7990ff7d 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -8778,7 +8778,7 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
<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>
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
@@ -69,6 +69,19 @@
</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
must be specified. All configuration files found in the directories listed in
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.</para></listitem>
</varlistentry>
+
<varlistentry>
<term><option>--exclude-prefix=<replaceable>path</replaceable></option></term>
<listitem><para>Ignore rules with paths that start with the
@@ -157,6 +158,16 @@
</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
<replaceable>root</replaceable> path, including config search paths.</para>
@@ -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>
diff --git a/src/basic/user-util.c b/src/basic/user-util.c
index 8115065b5e..0e96a75797 100644
--- a/src/basic/user-util.c
+++ b/src/basic/user-util.c
@@ -863,6 +863,37 @@ bool valid_gecos(const char *d) {
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. */
diff --git a/src/basic/user-util.h b/src/basic/user-util.h
index 1f267d21a3..7c142dd1e6 100644
--- a/src/basic/user-util.h
+++ b/src/basic/user-util.h
@@ -105,6 +105,7 @@ typedef enum ValidUserFlags {
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) {
diff --git a/src/boot/bless-boot.c b/src/boot/bless-boot.c
index b96e1f927f..0824266a80 100644
--- a/src/boot/bless-boot.c
+++ b/src/boot/bless-boot.c
@@ -34,6 +34,7 @@ static int help(int argc, char *argv[], void *userdata) {
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"
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/journal-qrcode.c b/src/journal/journal-qrcode.c
index dddbd7b381..8c8360853e 100644
--- a/src/journal/journal-qrcode.c
+++ b/src/journal/journal-qrcode.c
@@ -46,7 +46,7 @@ int print_qr_code(
_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;
@@ -79,7 +79,7 @@ int print_qr_code(
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]);
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 79daa43494..acdceea037 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;
}
@@ -1778,7 +1790,7 @@ static int add_syslog_identifier(sd_journal *j) {
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;
@@ -1890,52 +1902,49 @@ static int setup_keys(void) {
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);
@@ -2084,6 +2093,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 +2113,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/network/networkd-address.c b/src/network/networkd-address.c
index b09d75e615..0349ca6f93 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -487,7 +487,7 @@ static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
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;
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 0057d184f7..c3bc3415a8 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -1365,7 +1365,14 @@ static int link_request_set_addresses(Link *link) {
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;
@@ -2884,6 +2891,35 @@ static int link_drop_foreign_config(Link *link) {
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;
@@ -2896,18 +2932,19 @@ static int link_drop_config(Link *link) {
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) {
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 7f99c0f47b..ab5c3fd26a 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -75,6 +75,7 @@ typedef struct Link {
LinkAddressState address_state;
unsigned address_messages;
+ unsigned address_remove_messages;
unsigned address_label_messages;
unsigned neighbor_messages;
unsigned route_messages;
@@ -111,6 +112,7 @@ typedef struct Link {
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;
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/shared/logs-show.c b/src/shared/logs-show.c
index 899e894ab7..780ac508ce 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -168,8 +168,12 @@ typedef struct ParseFieldVec {
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;
@@ -1122,19 +1126,17 @@ static int output_cat_field(
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) {
@@ -1153,15 +1155,23 @@ static int output_cat_field(
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);
@@ -1178,20 +1188,44 @@ static int output_cat(
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;
}
diff --git a/src/shared/user-record-nss.c b/src/shared/user-record-nss.c
index f265a2af93..b27a12c55d 100644
--- a/src/shared/user-record-nss.c
+++ b/src/shared/user-record-nss.c
@@ -5,6 +5,7 @@
#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)
@@ -34,10 +35,25 @@ int nss_passwd_to_user_record(
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)
diff --git a/src/shared/user-record.c b/src/shared/user-record.c
index 801de19774..678f04e537 100644
--- a/src/shared/user-record.c
+++ b/src/shared/user-record.c
@@ -206,7 +206,6 @@ int json_dispatch_realm(const char *name, JsonVariant *variant, JsonDispatchFlag
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);
@@ -217,12 +216,20 @@ int json_dispatch_gecos(const char *name, JsonVariant *variant, JsonDispatchFlag
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;
}
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index d55a89efab..6e6e1810a0 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -7751,7 +7751,7 @@ static int systemctl_help(void) {
" -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"
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/test/test-nss.c b/src/test/test-nss.c
index 6ba0f6887e..293d7a8468 100644
--- a/src/test/test-nss.c
+++ b/src/test/test-nss.c
@@ -54,7 +54,7 @@ static const char* af_to_string(int family, char *buf, size_t buf_len) {
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;
@@ -63,6 +63,7 @@ static void* open_handle(const char* dir, const char* module, int flags) {
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());
@@ -70,10 +71,9 @@ static void* open_handle(const char* dir, const char* module, int flags) {
}
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;
@@ -147,7 +147,10 @@ static void test_gethostbyname4_r(void *handle, const char *module, const char *
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) {
@@ -197,7 +200,10 @@ static void test_gethostbyname3_r(void *handle, const char *module, const char *
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,
@@ -223,7 +229,10 @@ static void test_gethostbyname2_r(void *handle, const char *module, const char *
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",
@@ -247,7 +256,10 @@ static void test_gethostbyname_r(void *handle, const char *module, const char *n
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",
@@ -279,8 +291,10 @@ static void test_gethostbyaddr2_r(void *handle,
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);
@@ -314,8 +328,10 @@ static void test_gethostbyaddr_r(void *handle,
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);
@@ -388,14 +404,13 @@ static int make_addresses(struct local_address **addresses) {
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);
@@ -406,7 +421,7 @@ static int test_one_module(const char* dir,
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),
diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c
index eec2779a9e..cebf5a1080 100644
--- a/src/test/test-seccomp.c
+++ b/src/test/test-seccomp.c
@@ -98,9 +98,6 @@ static void test_syscall_filter_set_find(void) {
}
static void test_filter_sets(void) {
- unsigned i;
- int r;
-
log_info("/* %s */", __func__);
if (!is_seccomp_available()) {
@@ -112,7 +109,7 @@ static void test_filter_sets(void) {
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);
@@ -121,7 +118,7 @@ static void test_filter_sets(void) {
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))
diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c
index c9bff941be..306d08a282 100644
--- a/src/test/test-user-util.c
+++ b/src/test/test-user-util.c
@@ -452,6 +452,25 @@ static void test_parse_uid_range(void) {
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);
@@ -482,6 +501,7 @@ int main(int argc, char *argv[]) {
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();
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;
+
+ 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) {
@@ -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)
diff --git a/units/systemd-user-sessions.service.in b/units/systemd-user-sessions.service.in
index 6d585eb0a1..13de728b1d 100644
--- a/units/systemd-user-sessions.service.in
+++ b/units/systemd-user-sessions.service.in
@@ -10,7 +10,7 @@
[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
diff --git a/units/systemd-volatile-root.service.in b/units/systemd-volatile-root.service.in
index 8f228bce9f..a39e9a4ec2 100644
--- a/units/systemd-volatile-root.service.in
+++ b/units/systemd-volatile-root.service.in
@@ -12,7 +12,7 @@ Description=Enforce Volatile Root File Systems
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