diff --git a/docs/CONTAINER_INTERFACE.md b/docs/CONTAINER_INTERFACE.md
index 460cc67d78f..0eb93edfc8e 100644
--- a/docs/CONTAINER_INTERFACE.md
+++ b/docs/CONTAINER_INTERFACE.md
@@ -230,7 +230,9 @@ care should be taken to avoid naming conflicts. `systemd` (and in particular
directory: it's used by code outside the container to insert mounts inside
it only, and is mostly an internal vehicle to achieve this. Other container
managers that want to implement similar functionality might consider using
- the same directory.
+ the same directory. Alternatively, the new mount API may be used by the
+ container manager to establish new mounts in the container without the need
+ for the `/run/host/incoming/` directory.
2. The `/run/host/inaccessible/` directory may be set up by the container
manager to include six file nodes: `reg`, `dir`, `fifo`, `sock`, `chr`,
diff --git a/man/repart.d.xml b/man/repart.d.xml
index efca8d5ca68..475d529536c 100644
--- a/man/repart.d.xml
+++ b/man/repart.d.xml
@@ -690,7 +690,7 @@
fstab 5 .
- If both bit 50 and 59 are set for a partition (i.e. the partition is marked both read-only and
+ If both bit 60 and 59 are set for a partition (i.e. the partition is marked both read-only and
marked for file system growing) the latter is typically without effect: the read-only flag takes
precedence in most tools reading these flags, and since growing the file system involves writing to
the partition it is consequently ignored.
diff --git a/man/systemd-detect-virt.xml b/man/systemd-detect-virt.xml
index e1caa4fc26d..963c91dbad9 100644
--- a/man/systemd-detect-virt.xml
+++ b/man/systemd-detect-virt.xml
@@ -62,7 +62,7 @@
- VM
+ VM
qemu
QEMU software virtualization, without KVM
@@ -217,6 +217,50 @@
WSL is categorized as a container for practical purposes.
Multiple WSL environments share the same kernel and services
should generally behave like when being run in a container.
+
+ When executed with --cvm , instead of
+ printing the virtualization technology, it will display the
+ confidential virtual machine technology, if any. The
+ following technologies are currently identified:
+
+
+ Known confidential virtualization technologies
+
+
+
+
+
+ Arch
+ ID
+ Technology
+
+
+
+
+ x86_64
+ sev
+ AMD Secure Encrypted Virtualization
+
+
+ sev-es
+ AMD Secure Encrypted Virtualization - Encrypted State
+
+
+ sev-snp
+ AMD Secure Encrypted Virtualization - Secure Nested Paging
+
+
+ tdx
+ Intel Trust Domain Extensions
+
+
+ s390x
+ protvirt
+ IBM Protected Virtualization (Secure Execution)
+
+
+
+
diff --git a/man/systemd-path.xml b/man/systemd-path.xml
index b1d566e4bf0..9d40f1b02ef 100644
--- a/man/systemd-path.xml
+++ b/man/systemd-path.xml
@@ -43,6 +43,12 @@
The variables whose name begins with search-
do not refer to individual paths, but instead to a list of
colon-separated search paths, in their order of precedence.
+
+ Note that paths which depend on environment variables are
+ computed with systemd-path 's invoked
+ environment, and not the system or user manager's environment. As
+ such, the output of systemd-path may not
+ reflect the behavior of manager processes.
diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml
index 3c06b65f935..7f97e6cbe42 100644
--- a/man/systemd-system.conf.xml
+++ b/man/systemd-system.conf.xml
@@ -455,10 +455,12 @@
ManagerEnvironment=
Takes the same arguments as DefaultEnvironment= , see above. Sets
- environment variables just for the manager process itself. In contrast to user managers, these variables
- are not inherited by processes spawned by the system manager, use DefaultEnvironment=
+ environment variables for the manager process itself. These variables are inherited by processes
+ spawned by user managers, but not the system manager - use DefaultEnvironment=
for that. Note that these variables are merged into the existing environment block. In particular, in
- case of the system manager, this includes variables set by the kernel based on the kernel command line.
+ case of the system manager, this includes variables set by the kernel based on the kernel command line.
+ As with DefaultEnvironment= , this environment block is internal, and changes are not
+ reflected in the manager's /proc/PID/environ .
Setting environment variables for the manager process may be useful to modify its behaviour.
See Known Environment Variables for a
diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml
index e9c00c935dc..b2bb5439b53 100644
--- a/man/systemd.net-naming-scheme.xml
+++ b/man/systemd.net-naming-scheme.xml
@@ -478,7 +478,8 @@
bridge as that would create naming conflict when there are more child devices on that bridge. Now,
this is relaxed and we will use slot information to generate the name based on it but only if
the PCI device has multiple functions. This is safe because distinct function number is a part of
- the device name for multifunction devices.
+ the device name for multifunction devices. Note, this is reverted in v255 .
+ See below.
@@ -521,6 +522,9 @@
Naming was changed for SR-IOV virtual device representors to enable the
change introduced in v254 by default.
+ If we detect that a PCI device associated with a slot is a PCI bridge, we no longer set
+ ID_NET_NAME_SLOT , reverting a change that was introduced in v251.
+
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 0aee0123b84..6d6f98cf1bc 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -1074,6 +1074,18 @@ Table=1234
carrier. Defaults to false. If enabled, and the IgnoreCarrierLoss= setting
is not explicitly set, then it is enabled as well.
+ With this enabled, to make the interface enter the configured state,
+ which is required to make systemd-networkd-wait-online work properly for the
+ interface, all dynamic address configuration mechanisms like DHCP= and
+ IPv6AcceptRA= (which is enabled by default in most cases) need to be disabled.
+ Also, DuplicateAddressDetection= (which is enabled by default for IPv4
+ link-local addresses and all IPv6 addresses) needs to be disabled for all static address
+ configurations. Otherwise, without carrier, the interface will be stuck in the
+ configuring state, and systemd-networkd-wait-online for the
+ interface will timeout. Also, it is recommended to set
+ RequiredForOnline=no-carrier to make
+ systemd-networkd-wait-online work for the interface.
+
@@ -1563,7 +1575,8 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix
one of predefined names default , main , and
local , and names defined in RouteTable= in
networkd.conf 5 ,
- or a number between 1 and 4294967295. Defaults to main .
+ or a number between 1 and 4294967295. Defaults to main .
+ Ignored if L3MasterDevice= is true.
diff --git a/man/systemd.service.xml b/man/systemd.service.xml
index d7a8521c58c..252762d2ca5 100644
--- a/man/systemd.service.xml
+++ b/man/systemd.service.xml
@@ -622,6 +622,12 @@
sd_notify 3 ).
+ Note that the start timeout is also applied to service reloads, regardless if implemented
+ through ExecReload= or via the reload logic enabled via Type=notify-reload .
+ If the reload does not complete within the configured time, the reload will be considered failed and
+ the service will continue running with the old configuration. This will not affect the running service,
+ but will be logged and will cause e.g. systemctl reload to fail.
+
diff --git a/meson.build b/meson.build
index d89e91c8343..fa501e37f6f 100644
--- a/meson.build
+++ b/meson.build
@@ -369,6 +369,7 @@ possible_common_cc_flags = [
'-Wstrict-aliasing=2',
'-Wstrict-prototypes',
'-Wsuggest-attribute=noreturn',
+ '-Wunterminated-string-initialization',
'-Wunused-function',
'-Wwrite-strings',
'-Wzero-length-bounds',
@@ -381,7 +382,7 @@ possible_common_cc_flags = [
'-fno-common',
'-fstack-protector',
'-fstack-protector-strong',
- '-fstrict-flex-arrays',
+ '-fstrict-flex-arrays=3',
'--param=ssp-buffer-size=4',
]
diff --git a/mkosi.extra/root/.gdbinit b/mkosi.extra/root/.gdbinit
new file mode 100644
index 00000000000..e9c9ec41445
--- /dev/null
+++ b/mkosi.extra/root/.gdbinit
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set debuginfod enabled off
+set pagination off
diff --git a/network/80-wifi-station.network.example b/network/80-wifi-station.network.example
index 160b4eb5e30..600ce4c5ea1 100644
--- a/network/80-wifi-station.network.example
+++ b/network/80-wifi-station.network.example
@@ -12,6 +12,7 @@
[Match]
Type=wlan
WLANInterfaceType=station
+SSID=*
[Network]
DHCP=yes
diff --git a/rules.d/50-udev-default.rules.in b/rules.d/50-udev-default.rules.in
index af4f5947916..8d659add0cc 100644
--- a/rules.d/50-udev-default.rules.in
+++ b/rules.d/50-udev-default.rules.in
@@ -30,6 +30,9 @@ SUBSYSTEM=="pci|usb|platform", IMPORT{builtin}="path_id"
SUBSYSTEM=="net", IMPORT{builtin}="net_driver"
+SUBSYSTEM=="ptp", ATTR{clock_name}=="KVM virtual PTP", SYMLINK+="ptp_kvm"
+SUBSYSTEM=="ptp", ATTR{clock_name}=="hyperv", SYMLINK+="ptp_hyperv"
+
ACTION!="add", GOTO="default_end"
SUBSYSTEM=="tty", KERNEL=="ptmx", GROUP="tty", MODE="0666"
@@ -119,7 +122,4 @@ KERNEL=="vhost-net", GROUP="kvm", MODE="{{DEV_KVM_MODE}}", OPTIONS+="static_node
KERNEL=="udmabuf", GROUP="kvm"
-SUBSYSTEM=="ptp", ATTR{clock_name}=="KVM virtual PTP", SYMLINK+="ptp_kvm"
-SUBSYSTEM=="ptp", ATTR{clock_name}=="hyperv", SYMLINK+="ptp_hyperv"
-
LABEL="default_end"
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 3b58db779e2..50224648d30 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -116,7 +116,9 @@ int cg_read_pidref(FILE *f, PidRef *ret, CGroupFlags flags) {
if (pid == 0)
return -EREMOTE;
- if (FLAGS_SET(flags, CGROUP_NO_PIDFD)) {
+ /* We might read kernel thread pids from cgroup.procs for which we cannot create a pidfd so
+ * catch those and don't try to create a pidfd for them. */
+ if (FLAGS_SET(flags, CGROUP_NO_PIDFD) || pid_is_kernel_thread(pid) > 0) {
*ret = PIDREF_MAKE_FROM_PID(pid);
return 1;
}
@@ -348,6 +350,12 @@ static int cg_kill_items(
if (set_get(s, PID_TO_PTR(pidref.pid)) == PID_TO_PTR(pidref.pid))
continue;
+ /* Ignore kernel threads to mimick the behavior of cgroup.kill. */
+ if (pidref_is_kernel_thread(&pidref) > 0) {
+ log_debug("Ignoring kernel thread with pid " PID_FMT " in cgroup '%s'", pidref.pid, path);
+ continue;
+ }
+
if (log_kill)
ret_log_kill = log_kill(&pidref, sig, userdata);
diff --git a/src/basic/confidential-virt.c b/src/basic/confidential-virt.c
index b6521cf5bfc..c246636c7c1 100644
--- a/src/basic/confidential-virt.c
+++ b/src/basic/confidential-virt.c
@@ -11,6 +11,7 @@
#include "confidential-virt-fundamental.h"
#include "confidential-virt.h"
#include "fd-util.h"
+#include "fileio.h"
#include "missing_threads.h"
#include "string-table.h"
#include "utf8.h"
@@ -76,7 +77,7 @@ static uint64_t msr(uint64_t index) {
return ret;
}
-static bool detect_hyperv_sev(void) {
+static bool detect_hyperv_cvm(uint32_t isoltype) {
uint32_t eax, ebx, ecx, edx, feat;
char sig[13] = {};
@@ -100,7 +101,7 @@ static bool detect_hyperv_sev(void) {
ebx = ecx = edx = 0;
cpuid(&eax, &ebx, &ecx, &edx);
- if ((ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK) == CPUID_HYPERV_ISOLATION_TYPE_SNP)
+ if ((ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK) == isoltype)
return true;
}
@@ -133,7 +134,7 @@ static ConfidentialVirtualization detect_sev(void) {
if (!(eax & EAX_SEV)) {
log_debug("No sev in CPUID, trying hyperv CPUID");
- if (detect_hyperv_sev())
+ if (detect_hyperv_cvm(CPUID_HYPERV_ISOLATION_TYPE_SNP))
return CONFIDENTIAL_VIRTUALIZATION_SEV_SNP;
log_debug("No hyperv CPUID");
@@ -171,6 +172,11 @@ static ConfidentialVirtualization detect_tdx(void) {
if (memcmp(sig, CPUID_SIG_INTEL_TDX, sizeof(sig)) == 0)
return CONFIDENTIAL_VIRTUALIZATION_TDX;
+ log_debug("No tdx in CPUID, trying hyperv CPUID");
+
+ if (detect_hyperv_cvm(CPUID_HYPERV_ISOLATION_TYPE_TDX))
+ return CONFIDENTIAL_VIRTUALIZATION_TDX;
+
return CONFIDENTIAL_VIRTUALIZATION_NONE;
}
@@ -189,40 +195,62 @@ static bool detect_hypervisor(void) {
return is_hv;
}
-ConfidentialVirtualization detect_confidential_virtualization(void) {
- static thread_local ConfidentialVirtualization cached_found = _CONFIDENTIAL_VIRTUALIZATION_INVALID;
+static ConfidentialVirtualization detect_confidential_virtualization_impl(void) {
char sig[13] = {};
- ConfidentialVirtualization cv = CONFIDENTIAL_VIRTUALIZATION_NONE;
-
- if (cached_found >= 0)
- return cached_found;
/* Skip everything on bare metal */
if (detect_hypervisor()) {
cpuid_leaf(0, sig, true);
if (memcmp(sig, CPUID_SIG_AMD, sizeof(sig)) == 0)
- cv = detect_sev();
+ return detect_sev();
else if (memcmp(sig, CPUID_SIG_INTEL, sizeof(sig)) == 0)
- cv = detect_tdx();
+ return detect_tdx();
}
- cached_found = cv;
- return cv;
+ return CONFIDENTIAL_VIRTUALIZATION_NONE;
}
+#elif defined(__s390x__)
+static ConfidentialVirtualization detect_confidential_virtualization_impl(void) {
+ _cleanup_free_ char *s = NULL;
+ size_t readsize;
+ int r;
+
+ r = read_full_virtual_file("/sys/firmware/uv/prot_virt_guest", &s, &readsize);
+ if (r < 0) {
+ log_debug_errno(r, "Unable to read /sys/firmware/uv/prot_virt_guest: %m");
+ return CONFIDENTIAL_VIRTUALIZATION_NONE;
+ }
+
+ if (readsize >= 1 && s[0] == '1')
+ return CONFIDENTIAL_VIRTUALIZATION_PROTVIRT;
+
+ return CONFIDENTIAL_VIRTUALIZATION_NONE;
+}
+
#else /* ! x86_64 */
-ConfidentialVirtualization detect_confidential_virtualization(void) {
+static ConfidentialVirtualization detect_confidential_virtualization_impl(void) {
log_debug("No confidential virtualization detection on this architecture");
return CONFIDENTIAL_VIRTUALIZATION_NONE;
}
#endif /* ! x86_64 */
+ConfidentialVirtualization detect_confidential_virtualization(void) {
+ static thread_local ConfidentialVirtualization cached_found = _CONFIDENTIAL_VIRTUALIZATION_INVALID;
+
+ if (cached_found == _CONFIDENTIAL_VIRTUALIZATION_INVALID)
+ cached_found = detect_confidential_virtualization_impl();
+
+ return cached_found;
+}
+
static const char *const confidential_virtualization_table[_CONFIDENTIAL_VIRTUALIZATION_MAX] = {
- [CONFIDENTIAL_VIRTUALIZATION_NONE] = "none",
- [CONFIDENTIAL_VIRTUALIZATION_SEV] = "sev",
- [CONFIDENTIAL_VIRTUALIZATION_SEV_ES] = "sev-es",
- [CONFIDENTIAL_VIRTUALIZATION_SEV_SNP] = "sev-snp",
- [CONFIDENTIAL_VIRTUALIZATION_TDX] = "tdx",
+ [CONFIDENTIAL_VIRTUALIZATION_NONE] = "none",
+ [CONFIDENTIAL_VIRTUALIZATION_SEV] = "sev",
+ [CONFIDENTIAL_VIRTUALIZATION_SEV_ES] = "sev-es",
+ [CONFIDENTIAL_VIRTUALIZATION_SEV_SNP] = "sev-snp",
+ [CONFIDENTIAL_VIRTUALIZATION_TDX] = "tdx",
+ [CONFIDENTIAL_VIRTUALIZATION_PROTVIRT] = "protvirt",
};
DEFINE_STRING_TABLE_LOOKUP(confidential_virtualization, ConfidentialVirtualization);
diff --git a/src/basic/confidential-virt.h b/src/basic/confidential-virt.h
index c02f3b23217..f92e3e883da 100644
--- a/src/basic/confidential-virt.h
+++ b/src/basic/confidential-virt.h
@@ -13,6 +13,7 @@ typedef enum ConfidentialVirtualization {
CONFIDENTIAL_VIRTUALIZATION_SEV_ES,
CONFIDENTIAL_VIRTUALIZATION_SEV_SNP,
CONFIDENTIAL_VIRTUALIZATION_TDX,
+ CONFIDENTIAL_VIRTUALIZATION_PROTVIRT,
_CONFIDENTIAL_VIRTUALIZATION_MAX,
_CONFIDENTIAL_VIRTUALIZATION_INVALID = -EINVAL,
diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c
index ea683eb4273..16af77d20e9 100644
--- a/src/basic/hexdecoct.c
+++ b/src/basic/hexdecoct.c
@@ -36,7 +36,7 @@ int undecchar(char c) {
}
char hexchar(int x) {
- static const char table[16] = "0123456789abcdef";
+ static const char table[] = "0123456789abcdef";
return table[x & 15];
}
@@ -168,8 +168,8 @@ int unhexmem_full(
* useful when representing NSEC3 hashes, as one can then verify the
* order of hashes directly from their representation. */
char base32hexchar(int x) {
- static const char table[32] = "0123456789"
- "ABCDEFGHIJKLMNOPQRSTUV";
+ static const char table[] = "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUV";
return table[x & 31];
}
@@ -519,9 +519,9 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l
/* https://tools.ietf.org/html/rfc4648#section-4 */
char base64char(int x) {
- static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789+/";
+ static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
return table[x & 63];
}
@@ -529,9 +529,9 @@ char base64char(int x) {
* since we don't want "/" appear in interface names (since interfaces appear in sysfs as filenames).
* See section #5 of RFC 4648. */
char urlsafe_base64char(int x) {
- static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789-_";
+ static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789-_";
return table[x & 63];
}
diff --git a/src/basic/log.c b/src/basic/log.c
index 7a443005f6f..ade6c8b0890 100644
--- a/src/basic/log.c
+++ b/src/basic/log.c
@@ -388,9 +388,10 @@ void log_forget_fds(void) {
console_fd_is_tty = -1;
}
-void log_set_max_level(int level) {
+int log_set_max_level(int level) {
assert(level == LOG_NULL || (level & LOG_PRIMASK) == level);
+ int old = log_max_level;
log_max_level = level;
/* Also propagate max log level to libc's syslog(), just in case some other component loaded into our
@@ -403,6 +404,8 @@ void log_set_max_level(int level) {
/* Ensure that our own LOG_NULL define maps sanely to the log mask */
assert_cc(LOG_UPTO(LOG_NULL) == 0);
+
+ return old;
}
void log_set_facility(int facility) {
@@ -724,7 +727,7 @@ static int write_to_journal(
if (journal_fd < 0)
return 0;
- iovec_len = MIN(6 + _log_context_num_fields * 2, IOVEC_MAX);
+ iovec_len = MIN(6 + _log_context_num_fields * 3, IOVEC_MAX);
iovec = newa(struct iovec, iovec_len);
log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object, extra_field, extra);
@@ -1072,7 +1075,7 @@ int log_struct_internal(
int r;
bool fallback = false;
- iovec_len = MIN(17 + _log_context_num_fields * 2, IOVEC_MAX);
+ iovec_len = MIN(17 + _log_context_num_fields * 3, IOVEC_MAX);
iovec = newa(struct iovec, iovec_len);
/* If the journal is available do structured logging.
@@ -1169,7 +1172,7 @@ int log_struct_iovec_internal(
struct iovec *iovec;
size_t n = 0, iovec_len;
- iovec_len = MIN(1 + n_input_iovec * 2 + _log_context_num_fields * 2, IOVEC_MAX);
+ iovec_len = MIN(1 + n_input_iovec * 2 + _log_context_num_fields * 3, IOVEC_MAX);
iovec = newa(struct iovec, iovec_len);
log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL);
diff --git a/src/basic/log.h b/src/basic/log.h
index 9008d473909..76c188dcd37 100644
--- a/src/basic/log.h
+++ b/src/basic/log.h
@@ -34,9 +34,8 @@ typedef enum LogTarget{
* used a regular log level. */
#define LOG_NULL (LOG_EMERG - 1)
-/* Note to readers: << and >> have lower precedence (are evaluated earlier) than & and | */
-#define SYNTHETIC_ERRNO(num) (1 << 30 | (num))
-#define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1)
+#define SYNTHETIC_ERRNO(num) (abs(num) | (1 << 30))
+#define IS_SYNTHETIC_ERRNO(val) (((val) >> 30) == 1)
#define ERRNO_VALUE(val) (abs(val) & ~(1 << 30))
/* The callback function to be invoked when syntax warnings are seen
@@ -56,7 +55,7 @@ int log_set_target_from_string(const char *e);
LogTarget log_get_target(void) _pure_;
void log_settle_target(void);
-void log_set_max_level(int level);
+int log_set_max_level(int level);
int log_set_max_level_from_string(const char *e);
int log_get_max_level(void) _pure_;
@@ -486,6 +485,15 @@ size_t log_context_num_contexts(void);
/* Returns the number of fields in all attached log contexts. */
size_t log_context_num_fields(void);
+static inline void _reset_log_level(int *saved_log_level) {
+ assert(saved_log_level);
+
+ log_set_max_level(*saved_log_level);
+}
+
+#define LOG_CONTEXT_SET_LOG_LEVEL(level) \
+ _cleanup_(_reset_log_level) _unused_ int _saved_log_level_ = log_set_max_level(level);
+
#define LOG_CONTEXT_PUSH(...) \
LOG_CONTEXT_PUSH_STRV(STRV_MAKE(__VA_ARGS__))
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index c047fcdfd4c..0907733c43f 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -2391,7 +2391,7 @@ static EFI_STATUS image_start(
if (err == EFI_UNSUPPORTED && entry->type == LOADER_LINUX) {
uint32_t compat_address;
- err = pe_kernel_info(loaded_image->ImageBase, &compat_address);
+ err = pe_kernel_info(loaded_image->ImageBase, &compat_address, /* ret_size_in_memory= */ NULL);
if (err != EFI_SUCCESS) {
if (err != EFI_UNSUPPORTED)
return log_error_status(err, "Error finding kernel compat entry address: %m");
diff --git a/src/boot/efi/efi-string.c b/src/boot/efi/efi-string.c
index 4144c0d4979..df71797ed0d 100644
--- a/src/boot/efi/efi-string.c
+++ b/src/boot/efi/efi-string.c
@@ -481,7 +481,7 @@ char *line_get_key_value(char *s, const char *sep, size_t *pos, char **ret_key,
}
char16_t *hexdump(const void *data, size_t size) {
- static const char hex[16] = "0123456789abcdef";
+ static const char hex[] = "0123456789abcdef";
const uint8_t *d = data;
assert(data || size == 0);
diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c
index 65bc176df77..c8e595188b6 100644
--- a/src/boot/efi/linux.c
+++ b/src/boot/efi/linux.c
@@ -98,6 +98,7 @@ EFI_STATUS linux_exec(
const void *initrd_buffer,
size_t initrd_length) {
+ size_t kernel_size_in_memory = 0;
uint32_t compat_address;
EFI_STATUS err;
@@ -105,7 +106,7 @@ EFI_STATUS linux_exec(
assert(linux_buffer && linux_length > 0);
assert(initrd_buffer || initrd_length == 0);
- err = pe_kernel_info(linux_buffer, &compat_address);
+ err = pe_kernel_info(linux_buffer, &compat_address, &kernel_size_in_memory);
#if defined(__i386__) || defined(__x86_64__)
if (err == EFI_UNSUPPORTED)
/* Kernel is too old to support LINUX_INITRD_MEDIA_GUID, try the deprecated EFI handover
@@ -116,7 +117,8 @@ EFI_STATUS linux_exec(
linux_buffer,
linux_length,
initrd_buffer,
- initrd_length);
+ initrd_length,
+ kernel_size_in_memory);
#endif
if (err != EFI_SUCCESS)
return log_error_status(err, "Bad kernel image: %m");
diff --git a/src/boot/efi/linux.h b/src/boot/efi/linux.h
index 46b5f4f4d7c..0d74c6a3a72 100644
--- a/src/boot/efi/linux.h
+++ b/src/boot/efi/linux.h
@@ -16,4 +16,5 @@ EFI_STATUS linux_exec_efi_handover(
const void *linux_buffer,
size_t linux_length,
const void *initrd_buffer,
- size_t initrd_length);
+ size_t initrd_length,
+ size_t kernel_size_in_memory);
diff --git a/src/boot/efi/linux_x86.c b/src/boot/efi/linux_x86.c
index 757902daac5..58b1b3cc8fb 100644
--- a/src/boot/efi/linux_x86.c
+++ b/src/boot/efi/linux_x86.c
@@ -7,12 +7,13 @@
* this x86 specific linux_exec function passes the initrd by setting the
* corresponding fields in the setup_header struct.
*
- * see https://docs.kernel.org/x86/boot.html
+ * see https://docs.kernel.org/arch/x86/boot.html
*/
#include "initrd.h"
#include "linux.h"
#include "macro-fundamental.h"
+#include "memory-util-fundamental.h"
#include "util.h"
#define KERNEL_SECTOR_SIZE 512u
@@ -126,7 +127,8 @@ EFI_STATUS linux_exec_efi_handover(
const void *linux_buffer,
size_t linux_length,
const void *initrd_buffer,
- size_t initrd_length) {
+ size_t initrd_length,
+ size_t kernel_size_in_memory) {
assert(parent);
assert(linux_buffer);
@@ -153,13 +155,22 @@ EFI_STATUS linux_exec_efi_handover(
FLAGS_SET(image_params->hdr.xloadflags, XLF_CAN_BE_LOADED_ABOVE_4G);
/* There is no way to pass the high bits of code32_start. Newer kernels seems to handle this
- * just fine, but older kernels will fail even if they otherwise have above 4G boot support. */
+ * just fine, but older kernels will fail even if they otherwise have above 4G boot support.
+ * A PE image's memory footprint can be larger than its file size, due to unallocated virtual
+ * memory sections. While normally all PE headers should be taken into account, this case only
+ * involves x86 Linux bzImage kernel images, for which unallocated areas are only part of the last
+ * header, so parsing SizeOfImage and zeroeing the buffer past the image size is enough. */
_cleanup_pages_ Pages linux_relocated = {};
- if (POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + linux_length > UINT32_MAX) {
+ if (POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + linux_length > UINT32_MAX || kernel_size_in_memory > linux_length) {
linux_relocated = xmalloc_pages(
- AllocateMaxAddress, EfiLoaderCode, EFI_SIZE_TO_PAGES(linux_length), UINT32_MAX);
+ AllocateMaxAddress,
+ EfiLoaderCode,
+ EFI_SIZE_TO_PAGES(kernel_size_in_memory > linux_length ? kernel_size_in_memory : linux_length),
+ UINT32_MAX);
linux_buffer = memcpy(
PHYSICAL_ADDRESS_TO_POINTER(linux_relocated.addr), linux_buffer, linux_length);
+ if (kernel_size_in_memory > linux_length)
+ memzero((uint8_t *) linux_buffer + linux_length, kernel_size_in_memory - linux_length);
}
_cleanup_pages_ Pages initrd_relocated = {};
diff --git a/src/boot/efi/pe.c b/src/boot/efi/pe.c
index 829266b7f59..fdca0c93fee 100644
--- a/src/boot/efi/pe.c
+++ b/src/boot/efi/pe.c
@@ -209,7 +209,7 @@ static uint32_t get_compatibility_entry_address(const DosFileHeader *dos, const
return 0;
}
-EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address) {
+EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address, size_t *ret_size_in_memory) {
assert(base);
assert(ret_compat_address);
@@ -221,6 +221,11 @@ EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address) {
if (!verify_pe(pe, /* allow_compatibility= */ true))
return EFI_LOAD_ERROR;
+ /* When allocating we need to also consider the virtual/uninitialized data sections, so parse it out
+ * of the SizeOfImage field in the PE header and return it */
+ if (ret_size_in_memory)
+ *ret_size_in_memory = pe->OptionalHeader.SizeOfImage;
+
/* Support for LINUX_INITRD_MEDIA_GUID was added in kernel stub 1.0. */
if (pe->OptionalHeader.MajorImageVersion < 1)
return EFI_UNSUPPORTED;
diff --git a/src/boot/efi/pe.h b/src/boot/efi/pe.h
index 7e2258fceb9..860cfe5e27b 100644
--- a/src/boot/efi/pe.h
+++ b/src/boot/efi/pe.h
@@ -16,4 +16,4 @@ EFI_STATUS pe_file_locate_sections(
size_t *offsets,
size_t *sizes);
-EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address);
+EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address, size_t *ret_size_in_memory);
diff --git a/src/boot/efi/vmm.c b/src/boot/efi/vmm.c
index bfc7acc0523..ed654f68c7b 100644
--- a/src/boot/efi/vmm.c
+++ b/src/boot/efi/vmm.c
@@ -346,7 +346,7 @@ static uint64_t msr(uint32_t index) {
return val;
}
-static bool detect_hyperv_sev(void) {
+static bool detect_hyperv_cvm(uint32_t isoltype) {
uint32_t eax, ebx, ecx, edx, feat;
char sig[13] = {};
@@ -363,7 +363,7 @@ static bool detect_hyperv_sev(void) {
if (ebx & CPUID_HYPERV_ISOLATION && !(ebx & CPUID_HYPERV_CPU_MANAGEMENT)) {
__cpuid(CPUID_HYPERV_ISOLATION_CONFIG, eax, ebx, ecx, edx);
- if ((ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK) == CPUID_HYPERV_ISOLATION_TYPE_SNP)
+ if ((ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK) == isoltype)
return true;
}
@@ -388,7 +388,7 @@ static bool detect_sev(void) {
* specific CPUID checks.
*/
if (!(eax & EAX_SEV))
- return detect_hyperv_sev();
+ return detect_hyperv_cvm(CPUID_HYPERV_ISOLATION_TYPE_SNP);
msrval = msr(MSR_AMD64_SEV);
@@ -412,6 +412,9 @@ static bool detect_tdx(void) {
if (memcmp(sig, CPUID_SIG_INTEL_TDX, sizeof(sig)) == 0)
return true;
+ if (detect_hyperv_cvm(CPUID_HYPERV_ISOLATION_TYPE_TDX))
+ return true;
+
return false;
}
#endif /* ! __i386__ && ! __x86_64__ */
diff --git a/src/core/exec-credential.c b/src/core/exec-credential.c
index 6bcfb68d8f2..50109179a15 100644
--- a/src/core/exec-credential.c
+++ b/src/core/exec-credential.c
@@ -692,8 +692,10 @@ static int acquire_credentials(
* EEXIST if the credential already exists. That's because the TPM2-based decryption is kinda
* slow and involved, hence it's nice to be able to skip that if the credential already
* exists anyway. */
- if (faccessat(dfd, sc->id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
+ if (faccessat(dfd, sc->id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) {
+ log_debug("Skipping credential with duplicated ID %s", sc->id);
continue;
+ }
if (errno != ENOENT)
return log_debug_errno(errno, "Failed to test if credential %s exists: %m", sc->id);
diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c
index 32373ed0c2d..308d332c15b 100644
--- a/src/core/exec-invoke.c
+++ b/src/core/exec-invoke.c
@@ -4279,14 +4279,6 @@ int exec_invoke(
}
}
- if (context->nice_set) {
- r = setpriority_closest(context->nice);
- if (r < 0) {
- *exit_status = EXIT_NICE;
- return log_exec_error_errno(context, params, r, "Failed to set up process scheduling priority (nice level): %m");
- }
- }
-
if (context->cpu_sched_set) {
struct sched_attr attr = {
.size = sizeof(attr),
@@ -4302,6 +4294,14 @@ int exec_invoke(
}
}
+ if (context->nice_set) {
+ r = setpriority_closest(context->nice);
+ if (r < 0) {
+ *exit_status = EXIT_NICE;
+ return log_exec_error_errno(context, params, r, "Failed to set up process scheduling priority (nice level): %m");
+ }
+ }
+
if (context->cpu_affinity_from_numa || context->cpu_set.set) {
_cleanup_(cpu_set_reset) CPUSet converted_cpu_set = {};
const CPUSet *cpu_set;
diff --git a/src/core/execute-serialize.c b/src/core/execute-serialize.c
index b1e716e8cc3..73207225126 100644
--- a/src/core/execute-serialize.c
+++ b/src/core/execute-serialize.c
@@ -431,11 +431,11 @@ static int exec_cgroup_context_serialize(const CGroupContext *c, FILE *f) {
if (r < 0)
return r;
- r = serialize_strv(f, "exec-cgroup-context-ip-ingress-filter-path=", c->ip_filters_ingress);
+ r = serialize_strv(f, "exec-cgroup-context-ip-ingress-filter-path", c->ip_filters_ingress);
if (r < 0)
return r;
- r = serialize_strv(f, "exec-cgroup-context-ip-egress-filter-path=", c->ip_filters_egress);
+ r = serialize_strv(f, "exec-cgroup-context-ip-egress-filter-path", c->ip_filters_egress);
if (r < 0)
return r;
@@ -1739,15 +1739,23 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
if (r < 0)
return r;
- r = serialize_item(f, "exec-context-working-directory", c->working_directory);
+ r = serialize_item_escaped(f, "exec-context-working-directory", c->working_directory);
if (r < 0)
return r;
- r = serialize_item(f, "exec-context-root-directory", c->root_directory);
+ r = serialize_bool_elide(f, "exec-context-working-directory-missing-ok", c->working_directory_missing_ok);
if (r < 0)
return r;
- r = serialize_item(f, "exec-context-root-image", c->root_image);
+ r = serialize_bool_elide(f, "exec-context-working-directory-home", c->working_directory_home);
+ if (r < 0)
+ return r;
+
+ r = serialize_item_escaped(f, "exec-context-root-directory", c->root_directory);
+ if (r < 0)
+ return r;
+
+ r = serialize_item_escaped(f, "exec-context-root-image", c->root_image);
if (r < 0)
return r;
@@ -1968,14 +1976,6 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
return r;
}
- r = serialize_bool_elide(f, "exec-context-working-directory-missing-ok", c->working_directory_missing_ok);
- if (r < 0)
- return r;
-
- r = serialize_bool_elide(f, "exec-context-working-directory-home", c->working_directory_home);
- if (r < 0)
- return r;
-
if (c->oom_score_adjust_set) {
r = serialize_item_format(f, "exec-context-oom-score-adjust", "%i", c->oom_score_adjust);
if (r < 0)
@@ -2611,17 +2611,29 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-context-working-directory="))) {
- r = free_and_strdup(&c->working_directory, val);
- if (r < 0)
- return r;
+ ssize_t k;
+ char *p;
+
+ k = cunescape(val, 0, &p);
+ if (k < 0)
+ return k;
+ free_and_replace(c->working_directory, p);
} else if ((val = startswith(l, "exec-context-root-directory="))) {
- r = free_and_strdup(&c->root_directory, val);
- if (r < 0)
- return r;
+ ssize_t k;
+ char *p;
+
+ k = cunescape(val, 0, &p);
+ if (k < 0)
+ return k;
+ free_and_replace(c->root_directory, p);
} else if ((val = startswith(l, "exec-context-root-image="))) {
- r = free_and_strdup(&c->root_image, val);
- if (r < 0)
- return r;
+ ssize_t k;
+ char *p;
+
+ k = cunescape(val, 0, &p);
+ if (k < 0)
+ return k;
+ free_and_replace(c->root_image, p);
} else if ((val = startswith(l, "exec-context-root-image-options="))) {
for (;;) {
_cleanup_free_ char *word = NULL, *mount_options = NULL, *partition = NULL;
diff --git a/src/core/execute.c b/src/core/execute.c
index 8dbdfcf3691..4d597bf8a60 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -377,6 +377,7 @@ int exec_spawn(Unit *unit,
assert(!params->files_env); /* We fill this field, ensure it comes NULL-initialized to us */
LOG_CONTEXT_PUSH_UNIT(unit);
+ LOG_CONTEXT_SET_LOG_LEVEL(context->log_level_max >= 0 ? context->log_level_max : log_get_max_level());
r = exec_context_load_environment(unit, context, ¶ms->files_env);
if (r < 0)
diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c
index fd45744261d..dc9c44e6d64 100644
--- a/src/core/load-dropin.c
+++ b/src/core/load-dropin.c
@@ -102,7 +102,7 @@ int unit_load_dropin(Unit *u) {
return r;
/* Load .conf dropins */
- r = unit_find_dropin_paths(u, &l);
+ r = unit_find_dropin_paths(u, /* use_unit_path_cache = */ true, &l);
if (r <= 0)
return 0;
diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h
index f0b87d3e9fb..141bc7dd0f2 100644
--- a/src/core/load-dropin.h
+++ b/src/core/load-dropin.h
@@ -6,12 +6,12 @@
/* Read service data supplementary drop-in directories */
-static inline int unit_find_dropin_paths(Unit *u, char ***paths) {
+static inline int unit_find_dropin_paths(Unit *u, bool use_unit_path_cache, char ***paths) {
assert(u);
return unit_file_find_dropin_paths(NULL,
u->manager->lookup_paths.search_path,
- u->manager->unit_path_cache,
+ use_unit_path_cache ? u->manager->unit_path_cache : NULL,
".d", ".conf",
u->id, u->aliases,
paths);
diff --git a/src/core/socket.c b/src/core/socket.c
index 9adae16b00f..25784de6ad9 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -2481,7 +2481,7 @@ static int socket_start(Unit *u) {
/* If the service is already active we cannot start the
* socket */
if (!IN_SET(service->state,
- SERVICE_DEAD, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED, SERVICE_FAILED_BEFORE_AUTO_RESTART,
+ SERVICE_DEAD, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_DEAD_RESOURCES_PINNED, SERVICE_FAILED, SERVICE_FAILED_BEFORE_AUTO_RESTART,
SERVICE_AUTO_RESTART, SERVICE_AUTO_RESTART_QUEUED))
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EBUSY),
"Socket service %s already active, refusing.", UNIT(service)->id);
@@ -3381,7 +3381,7 @@ static void socket_trigger_notify(Unit *u, Unit *other) {
return;
if (IN_SET(SERVICE(other)->state,
- SERVICE_DEAD, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED, SERVICE_FAILED_BEFORE_AUTO_RESTART,
+ SERVICE_DEAD, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_DEAD_RESOURCES_PINNED, SERVICE_FAILED, SERVICE_FAILED_BEFORE_AUTO_RESTART,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
SERVICE_AUTO_RESTART, SERVICE_AUTO_RESTART_QUEUED))
socket_enter_listening(s);
diff --git a/src/core/unit.c b/src/core/unit.c
index ac76ecd54b9..8ac9a965b62 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -3927,7 +3927,7 @@ bool unit_need_daemon_reload(Unit *u) {
if (u->load_state == UNIT_LOADED) {
_cleanup_strv_free_ char **dropins = NULL;
- (void) unit_find_dropin_paths(u, &dropins);
+ (void) unit_find_dropin_paths(u, /* use_unit_path_cache = */ false, &dropins);
if (!strv_equal(u->dropin_paths, dropins))
return true;
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index d4029272dea..a70738aa117 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -900,8 +900,7 @@ static int write_root_passwd(int rfd, int etc_fd, const char *password, const ch
_cleanup_fclose_ FILE *original = NULL, *passwd = NULL;
_cleanup_(unlink_and_freep) char *passwd_tmp = NULL;
int r;
-
- assert(password);
+ bool found = false;
r = fopen_temporary_at_label(etc_fd, "passwd", "passwd", &passwd, &passwd_tmp);
if (r < 0)
@@ -921,9 +920,11 @@ static int write_root_passwd(int rfd, int etc_fd, const char *password, const ch
while ((r = fgetpwent_sane(original, &i)) > 0) {
if (streq(i->pw_name, "root")) {
- i->pw_passwd = (char *) password;
+ if (password)
+ i->pw_passwd = (char *) password;
if (shell)
i->pw_shell = (char *) shell;
+ found = true;
}
r = putpwent_sane(i, passwd);
@@ -934,9 +935,15 @@ static int write_root_passwd(int rfd, int etc_fd, const char *password, const ch
return r;
} else {
+ r = fchmod(fileno(passwd), 0644);
+ if (r < 0)
+ return -errno;
+ }
+
+ if (!found) {
struct passwd root = {
.pw_name = (char *) "root",
- .pw_passwd = (char *) password,
+ .pw_passwd = (char *) (password ?: PASSWORD_SEE_SHADOW),
.pw_uid = 0,
.pw_gid = 0,
.pw_gecos = (char *) "Super User",
@@ -947,10 +954,6 @@ static int write_root_passwd(int rfd, int etc_fd, const char *password, const ch
if (errno != ENOENT)
return -errno;
- r = fchmod(fileno(passwd), 0644);
- if (r < 0)
- return -errno;
-
r = putpwent_sane(&root, passwd);
if (r < 0)
return r;
@@ -971,8 +974,7 @@ static int write_root_shadow(int etc_fd, const char *hashed_password) {
_cleanup_fclose_ FILE *original = NULL, *shadow = NULL;
_cleanup_(unlink_and_freep) char *shadow_tmp = NULL;
int r;
-
- assert(hashed_password);
+ bool found = false;
r = fopen_temporary_at_label(etc_fd, "shadow", "shadow", &shadow, &shadow_tmp);
if (r < 0)
@@ -992,8 +994,11 @@ static int write_root_shadow(int etc_fd, const char *hashed_password) {
while ((r = fgetspent_sane(original, &i)) > 0) {
if (streq(i->sp_namp, "root")) {
- i->sp_pwdp = (char *) hashed_password;
- i->sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
+ if (hashed_password) {
+ i->sp_pwdp = (char *) hashed_password;
+ i->sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
+ }
+ found = true;
}
r = putspent_sane(i, shadow);
@@ -1004,9 +1009,15 @@ static int write_root_shadow(int etc_fd, const char *hashed_password) {
return r;
} else {
+ r = fchmod(fileno(shadow), 0000);
+ if (r < 0)
+ return -errno;
+ }
+
+ if (!found) {
struct spwd root = {
.sp_namp = (char*) "root",
- .sp_pwdp = (char *) hashed_password,
+ .sp_pwdp = (char *) (hashed_password ?: PASSWORD_LOCKED_AND_INVALID),
.sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY),
.sp_min = -1,
.sp_max = -1,
@@ -1019,10 +1030,6 @@ static int write_root_shadow(int etc_fd, const char *hashed_password) {
if (errno != ENOENT)
return -errno;
- r = fchmod(fileno(shadow), 0000);
- if (r < 0)
- return -errno;
-
r = putspent_sane(&root, shadow);
if (r < 0)
return r;
@@ -1075,13 +1082,6 @@ static int process_root_account(int rfd) {
return 0;
}
- /* Don't create/modify passwd and shadow if not asked */
- if (!(arg_root_password || arg_prompt_root_password || arg_copy_root_password || arg_delete_root_password ||
- arg_root_shell || arg_prompt_root_shell || arg_copy_root_shell)) {
- log_debug("Initialization of root account was not requested, skipping.");
- return 0;
- }
-
r = make_lock_file_at(pfd, ETC_PASSWD_LOCK_FILENAME, LOCK_EX, &lock);
if (r < 0)
return log_error_errno(r, "Failed to take a lock on /etc/passwd: %m");
@@ -1137,10 +1137,22 @@ static int process_root_account(int rfd) {
password = PASSWORD_SEE_SHADOW;
hashed_password = _hashed_password;
- } else if (arg_delete_root_password)
- password = hashed_password = PASSWORD_NONE;
- else
- password = hashed_password = PASSWORD_LOCKED_AND_INVALID;
+ } else if (arg_delete_root_password) {
+ password = PASSWORD_SEE_SHADOW;
+ hashed_password = PASSWORD_NONE;
+ } else if (!arg_root_password && arg_prompt_root_password) {
+ /* If the user was prompted, but no password was supplied, lock the account. */
+ password = PASSWORD_SEE_SHADOW;
+ hashed_password = PASSWORD_LOCKED_AND_INVALID;
+ } else
+ /* Leave the password as is. */
+ password = hashed_password = NULL;
+
+ /* Don't create/modify passwd and shadow if there's nothing to do. */
+ if (!(password || hashed_password || arg_root_shell)) {
+ log_debug("Initialization of root account was not requested, skipping.");
+ return 0;
+ }
r = write_root_passwd(rfd, pfd, password, arg_root_shell);
if (r < 0)
diff --git a/src/fundamental/confidential-virt-fundamental.h b/src/fundamental/confidential-virt-fundamental.h
index 986923e1c2b..618b5800ea4 100644
--- a/src/fundamental/confidential-virt-fundamental.h
+++ b/src/fundamental/confidential-virt-fundamental.h
@@ -65,6 +65,7 @@
#define CPUID_HYPERV_ISOLATION_TYPE_MASK UINT32_C(0xf)
#define CPUID_HYPERV_ISOLATION_TYPE_SNP 2
+#define CPUID_HYPERV_ISOLATION_TYPE_TDX 3
#define EAX_SEV (UINT32_C(1) << 1)
#define MSR_SEV (UINT64_C(1) << 0)
diff --git a/src/import/import-raw.c b/src/import/import-raw.c
index f7ed163d864..4abc6911a14 100644
--- a/src/import/import-raw.c
+++ b/src/import/import-raw.c
@@ -408,6 +408,11 @@ static int raw_import_process(RawImport *i) {
goto finish;
}
+ if ((size_t) l > sizeof(i->buffer) - i->buffer_size) {
+ r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Read input file exceeded maximum size.");
+ goto finish;
+ }
+
i->buffer_size += l;
if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
diff --git a/src/import/import-tar.c b/src/import/import-tar.c
index 90202709ecc..0951d65e26f 100644
--- a/src/import/import-tar.c
+++ b/src/import/import-tar.c
@@ -275,6 +275,11 @@ static int tar_import_process(TarImport *i) {
goto finish;
}
+ if ((size_t) l > sizeof(i->buffer) - i->buffer_size) {
+ r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Read input file exceeded maximum size.");
+ goto finish;
+ }
+
i->buffer_size += l;
if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
diff --git a/src/journal/journald.conf b/src/journal/journald.conf
index 7b9e23205e5..ac7d3272b02 100644
--- a/src/journal/journald.conf
+++ b/src/journal/journald.conf
@@ -32,7 +32,7 @@
#RuntimeKeepFree=
#RuntimeMaxFileSize=
#RuntimeMaxFiles=100
-#MaxRetentionSec=
+#MaxRetentionSec=0
#MaxFileSec=1month
#ForwardToSyslog=no
#ForwardToKMsg=no
diff --git a/src/kernel-install/50-depmod.install b/src/kernel-install/50-depmod.install
index 88f858fed9c..08247c735b6 100755
--- a/src/kernel-install/50-depmod.install
+++ b/src/kernel-install/50-depmod.install
@@ -44,6 +44,7 @@ case "$COMMAND" in
"/lib/modules/$KERNEL_VERSION/modules.dep.bin" \
"/lib/modules/$KERNEL_VERSION/modules.devname" \
"/lib/modules/$KERNEL_VERSION/modules.softdep" \
+ "/lib/modules/$KERNEL_VERSION/modules.weakdep" \
"/lib/modules/$KERNEL_VERSION/modules.symbols" \
"/lib/modules/$KERNEL_VERSION/modules.symbols.bin"
;;
diff --git a/src/kernel-install/90-loaderentry.install.in b/src/kernel-install/90-loaderentry.install.in
index 0408530f05f..46f700b23ec 100755
--- a/src/kernel-install/90-loaderentry.install.in
+++ b/src/kernel-install/90-loaderentry.install.in
@@ -79,8 +79,10 @@ elif [ -f /etc/kernel/cmdline ]; then
BOOT_OPTIONS="$(tr -s "$IFS" ' ' "$LOADER_ENTRY" || {
echo "Error: could not create loader entry '$LOADER_ENTRY'." >&2
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index b6899df1926..25f3b1fc4f5 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -3830,7 +3830,8 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events, i
if (_unlikely_(n != sizeof(si)))
return -EIO;
- assert(SIGNAL_VALID(si.ssi_signo));
+ if (_unlikely_(!SIGNAL_VALID(si.ssi_signo)))
+ return -EIO;
if (e->signal_sources)
s = e->signal_sources[si.ssi_signo];
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 935e2093754..774c807ccb7 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -9,6 +9,7 @@
#include "alloc-util.h"
#include "audit-util.h"
+#include "bitfield.h"
#include "bootspec.h"
#include "bus-common-errors.h"
#include "bus-error.h"
@@ -3432,26 +3433,30 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error
return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS,
"The operation inhibition has been requested for is already running");
- r = bus_verify_polkit_async(
- message,
- CAP_SYS_BOOT,
- w == INHIBIT_SHUTDOWN ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") :
- w == INHIBIT_SLEEP ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep" : "org.freedesktop.login1.inhibit-delay-sleep") :
- w == INHIBIT_IDLE ? "org.freedesktop.login1.inhibit-block-idle" :
- w == INHIBIT_HANDLE_POWER_KEY ? "org.freedesktop.login1.inhibit-handle-power-key" :
- w == INHIBIT_HANDLE_SUSPEND_KEY ? "org.freedesktop.login1.inhibit-handle-suspend-key" :
- w == INHIBIT_HANDLE_REBOOT_KEY ? "org.freedesktop.login1.inhibit-handle-reboot-key" :
- w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" :
- "org.freedesktop.login1.inhibit-handle-lid-switch",
- NULL,
- false,
- UID_INVALID,
- &m->polkit_registry,
- error);
- if (r < 0)
- return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+ BIT_FOREACH(i, w) {
+ const InhibitWhat v = 1U << i;
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_BOOT,
+ v == INHIBIT_SHUTDOWN ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") :
+ v == INHIBIT_SLEEP ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep" : "org.freedesktop.login1.inhibit-delay-sleep") :
+ v == INHIBIT_IDLE ? "org.freedesktop.login1.inhibit-block-idle" :
+ v == INHIBIT_HANDLE_POWER_KEY ? "org.freedesktop.login1.inhibit-handle-power-key" :
+ v == INHIBIT_HANDLE_SUSPEND_KEY ? "org.freedesktop.login1.inhibit-handle-suspend-key" :
+ v == INHIBIT_HANDLE_REBOOT_KEY ? "org.freedesktop.login1.inhibit-handle-reboot-key" :
+ v == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" :
+ "org.freedesktop.login1.inhibit-handle-lid-switch",
+ /* details= */ NULL,
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+ }
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID, &creds);
if (r < 0)
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 4ef1be4badd..a4fa321264f 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -1526,6 +1526,13 @@ static int link_carrier_gained(Link *link) {
if (r < 0)
log_link_warning_errno(link, r, "Failed to disable carrier lost timer, ignoring: %m");
+ /* Process BindCarrier= setting specified by other interfaces. This is independent of the .network
+ * file assigned to this interface, but depends on .network files assigned to other interfaces.
+ * Hence, this can and should be called earlier. */
+ r = link_handle_bound_by_list(link);
+ if (r < 0)
+ return r;
+
/* If a wireless interface was connected to an access point, and the SSID is changed (that is,
* both previous_ssid and ssid are non-NULL), then the connected wireless network could be
* changed. So, always reconfigure the link. Which means e.g. the DHCP client will be
@@ -1559,10 +1566,6 @@ static int link_carrier_gained(Link *link) {
if (r != 0)
return r;
- r = link_handle_bound_by_list(link);
- if (r < 0)
- return r;
-
if (link->iftype == ARPHRD_CAN)
/* let's shortcut things for CAN which doesn't need most of what's done below. */
return 0;
@@ -1581,25 +1584,22 @@ static int link_carrier_gained(Link *link) {
}
static int link_carrier_lost_impl(Link *link) {
- int r, ret = 0;
+ int ret = 0;
assert(link);
link->previous_ssid = mfree(link->previous_ssid);
+ ret = link_handle_bound_by_list(link);
+
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 0;
+ return ret;
if (!link->network)
- return 0;
+ return ret;
- r = link_stop_engines(link, false);
- if (r < 0)
- ret = r;
-
- r = link_drop_managed_config(link);
- if (r < 0 && ret >= 0)
- ret = r;
+ RET_GATHER(ret, link_stop_engines(link, false));
+ RET_GATHER(ret, link_drop_managed_config(link));
return ret;
}
@@ -1620,22 +1620,17 @@ static int link_carrier_lost_handler(sd_event_source *s, uint64_t usec, void *us
static int link_carrier_lost(Link *link) {
uint16_t dhcp_mtu;
usec_t usec;
- int r;
assert(link);
- r = link_handle_bound_by_list(link);
- if (r < 0)
- return r;
-
if (link->iftype == ARPHRD_CAN)
/* let's shortcut things for CAN which doesn't need most of what's done below. */
- return 0;
+ usec = 0;
- if (!link->network)
- return 0;
+ else if (!link->network)
+ usec = 0;
- if (link->network->ignore_carrier_loss_set)
+ else if (link->network->ignore_carrier_loss_set)
/* If IgnoreCarrierLoss= is explicitly specified, then use the specified value. */
usec = link->network->ignore_carrier_loss_usec;
diff --git a/src/pcrlock/pcrlock.c b/src/pcrlock/pcrlock.c
index dde4dd93d61..ae4a357d309 100644
--- a/src/pcrlock/pcrlock.c
+++ b/src/pcrlock/pcrlock.c
@@ -1891,6 +1891,9 @@ static int event_log_map_components(EventLog *el) {
continue;
}
+ if (c->n_variants == 0)
+ log_notice("Component '%s' has no defined variants.", c->id);
+
FOREACH_ARRAY(ii, c->variants, c->n_variants) {
EventLogComponentVariant *i = *ii;
@@ -3973,6 +3976,15 @@ static int event_log_predict_pcrs(
component = ASSERT_PTR(el->components[component_index]);
+ if (component->n_variants == 0)
+ return event_log_predict_pcrs(
+ el,
+ context,
+ parent_result,
+ component_index + 1, /* Next component */
+ pcr,
+ path);
+
FOREACH_ARRAY(ii, component->variants, component->n_variants) {
_cleanup_free_ Tpm2PCRPredictionResult *result = NULL;
EventLogComponentVariant *variant = *ii;
@@ -4031,7 +4043,9 @@ static ssize_t event_log_calculate_component_combinations(EventLog *el) {
/* Overflow check */
if (c->n_variants > (size_t) (SSIZE_MAX/count))
return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "Too many component combinations.");
-
+ /* If no variant, this will lead to count being 0 and sigfpe */
+ if (c->n_variants == 0)
+ continue;
count *= c->n_variants;
}
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 75ba29c3d0b..fb07516d5df 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -1135,6 +1135,11 @@ static void resolve_service_all_complete(DnsQuery *query) {
if (r < 0)
goto finish;
+ if (isempty(type)) {
+ r = reply_method_errorf(q, BUS_ERROR_NO_SUCH_SERVICE, "'%s' does not provide valid service", dns_query_string(q));
+ goto finish;
+ }
+
r = sd_bus_message_append(
reply,
"ssst",
diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c
index 1a43d0bd498..8c1512006af 100644
--- a/src/resolve/resolved-dns-stream.c
+++ b/src/resolve/resolved-dns-stream.c
@@ -322,6 +322,12 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use
return dns_stream_complete(s, -r);
}
+ if (revents & EPOLLERR) {
+ socklen_t errlen = sizeof(r);
+ if (getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &r, &errlen) == 0)
+ return dns_stream_complete(s, r);
+ }
+
if ((revents & EPOLLOUT) &&
s->write_packet &&
s->n_written < sizeof(s->write_size) + s->write_packet->size) {
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 3f48b566b54..1354acbd0ff 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -626,7 +626,7 @@ static int on_stream_complete(DnsStream *s, int error) {
if (ERRNO_IS_DISCONNECT(error) && s->protocol != DNS_PROTOCOL_LLMNR) {
log_debug_errno(error, "Connection failure for DNS TCP stream: %m");
- if (s->transactions) {
+ if (error != ECONNRESET && s->transactions) {
DnsTransaction *t;
t = s->transactions;
diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c
index a4e2dae2454..0d5075e1e6d 100644
--- a/src/shared/base-filesystem.c
+++ b/src/shared/base-filesystem.c
@@ -56,8 +56,7 @@ static const BaseFilesystem table[] = {
/* aarch64 ELF ABI actually says dynamic loader is in /lib/, but Fedora puts it in /lib64/ anyway and
* just symlinks /lib/ld-linux-aarch64.so.1 to ../lib64/ld-linux-aarch64.so.1. For this to work
* correctly, /lib64/ must be symlinked to /usr/lib64/. */
- { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0"
- "usr/lib64\0"
+ { "lib64", 0, "usr/lib64\0"
"usr/lib\0", "ld-linux-aarch64.so.1" },
# define KNOW_LIB64_DIRS 1
#elif defined(__alpha__)
@@ -66,24 +65,20 @@ static const BaseFilesystem table[] = {
/* No /lib64 on arm. The linker is /lib/ld-linux-armhf.so.3. */
# define KNOW_LIB64_DIRS 1
#elif defined(__i386__) || defined(__x86_64__)
- { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0"
- "usr/lib64\0"
+ { "lib64", 0, "usr/lib64\0"
"usr/lib\0", "ld-linux-x86-64.so.2" },
# define KNOW_LIB64_DIRS 1
#elif defined(__ia64__)
#elif defined(__loongarch_lp64)
# define KNOW_LIB64_DIRS 1
# if defined(__loongarch_double_float)
- { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0"
- "usr/lib64\0"
+ { "lib64", 0, "usr/lib64\0"
"usr/lib\0", "ld-linux-loongarch-lp64d.so.1" },
# elif defined(__loongarch_single_float)
- { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0"
- "usr/lib64\0"
+ { "lib64", 0, "usr/lib64\0"
"usr/lib\0", "ld-linux-loongarch-lp64f.so.1" },
# elif defined(__loongarch_soft_float)
- { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0"
- "usr/lib64\0"
+ { "lib64", 0, "usr/lib64\0"
"usr/lib\0", "ld-linux-loongarch-lp64s.so.1" },
# else
# error "Unknown LoongArch ABI"
@@ -100,8 +95,7 @@ static const BaseFilesystem table[] = {
# endif
#elif defined(__powerpc__)
# if defined(__PPC64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
- { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0"
- "usr/lib64\0"
+ { "lib64", 0, "usr/lib64\0"
"usr/lib\0", "ld64.so.2" },
# define KNOW_LIB64_DIRS 1
# elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
@@ -113,16 +107,14 @@ static const BaseFilesystem table[] = {
# if __riscv_xlen == 32
# elif __riscv_xlen == 64
/* Same situation as for aarch64 */
- { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0"
- "usr/lib64\0"
+ { "lib64", 0, "usr/lib64\0"
"usr/lib\0", "ld-linux-riscv64-lp64d.so.1" },
# define KNOW_LIB64_DIRS 1
# else
# error "Unknown RISC-V ABI"
# endif
#elif defined(__s390x__)
- { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0"
- "usr/lib64\0"
+ { "lib64", 0, "usr/lib64\0"
"usr/lib\0", "ld-lsb-s390x.so.3" },
# define KNOW_LIB64_DIRS 1
#elif defined(__s390__)
diff --git a/src/shared/resize-fs.h b/src/shared/resize-fs.h
index b40943c9921..4a5ecec337f 100644
--- a/src/shared/resize-fs.h
+++ b/src/shared/resize-fs.h
@@ -9,7 +9,7 @@ int resize_fs(int fd, uint64_t sz, uint64_t *ret_size);
#define BTRFS_MINIMAL_SIZE (256U*1024U*1024U)
#define XFS_MINIMAL_SIZE (300U*1024U*1024U)
-#define EXT4_MINIMAL_SIZE (1024U*1024U)
+#define EXT4_MINIMAL_SIZE (32U*1024U*1024U)
uint64_t minimal_size_by_fs_magic(statfs_f_type_t magic);
uint64_t minimal_size_by_fs_name(const char *str);
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index 794e09ce53f..fbdf5f45781 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -1466,9 +1466,15 @@ static int process_item(Context *c, Item *i) {
case ADD_USER: {
Item *j = NULL;
- if (!i->gid_set)
+ if (!i->gid_set) {
j = ordered_hashmap_get(c->groups, i->group_name ?: i->name);
+ /* If that's not a match, also check if the group name
+ * matches a user name in the queue. */
+ if (!j && i->group_name)
+ j = ordered_hashmap_get(c->users, i->group_name);
+ }
+
if (j && j->todo_group) {
/* When a group with the target name is already in queue,
* use the information about the group and do not create
diff --git a/src/test/test-log.c b/src/test/test-log.c
index b5ba67b74b6..cc8c400cc13 100644
--- a/src/test/test-log.c
+++ b/src/test/test-log.c
@@ -13,11 +13,6 @@
#include "strv.h"
#include "tests.h"
-assert_cc(IS_SYNTHETIC_ERRNO(SYNTHETIC_ERRNO(EINVAL)));
-assert_cc(!IS_SYNTHETIC_ERRNO(EINVAL));
-assert_cc(IS_SYNTHETIC_ERRNO(SYNTHETIC_ERRNO(0)));
-assert_cc(!IS_SYNTHETIC_ERRNO(0));
-
#define X10(x) x x x x x x x x x x
#define X100(x) X10(X10(x))
#define X1000(x) X100(X10(x))
@@ -207,6 +202,15 @@ static void test_log_prefix(void) {
int main(int argc, char* argv[]) {
test_setup_logging(LOG_DEBUG);
+ assert_se(IS_SYNTHETIC_ERRNO(SYNTHETIC_ERRNO(EINVAL)));
+ assert_se(IS_SYNTHETIC_ERRNO(SYNTHETIC_ERRNO(-EINVAL)));
+ assert_cc(!IS_SYNTHETIC_ERRNO(EINVAL));
+ assert_cc(!IS_SYNTHETIC_ERRNO(-EINVAL));
+ assert_se(IS_SYNTHETIC_ERRNO(SYNTHETIC_ERRNO(0)));
+ assert_cc(!IS_SYNTHETIC_ERRNO(0));
+ assert_se(ERRNO_VALUE(EINVAL) == EINVAL);
+ assert_se(ERRNO_VALUE(SYNTHETIC_ERRNO(-EINVAL)) == EINVAL);
+
test_file();
assert_se(log_info_errno(SYNTHETIC_ERRNO(EUCLEAN), "foo") == -EUCLEAN);
diff --git a/src/ukify/test/pytest.ini b/src/ukify/test/pytest.ini
new file mode 100644
index 00000000000..a7c58bb9317
--- /dev/null
+++ b/src/ukify/test/pytest.ini
@@ -0,0 +1,2 @@
+[pytest]
+tmp_path_retention_policy = failed
diff --git a/test/test-sysusers/test-16.expected-group b/test/test-sysusers/test-16.expected-group
new file mode 100644
index 00000000000..54918e417ac
--- /dev/null
+++ b/test/test-sysusers/test-16.expected-group
@@ -0,0 +1 @@
+foo:x:SYSTEM_UGID_MAX:
diff --git a/test/test-sysusers/test-16.expected-passwd b/test/test-sysusers/test-16.expected-passwd
new file mode 100644
index 00000000000..8823813f82d
--- /dev/null
+++ b/test/test-sysusers/test-16.expected-passwd
@@ -0,0 +1,2 @@
+foo:x:SYSTEM_UGID_MAX:SYSTEM_UGID_MAX::/:NOLOGIN
+bar:x:300:SYSTEM_UGID_MAX::/:NOLOGIN
diff --git a/test/test-sysusers/test-16.input b/test/test-sysusers/test-16.input
new file mode 100644
index 00000000000..2d80d81c0c0
--- /dev/null
+++ b/test/test-sysusers/test-16.input
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Test fix for https://github.com/systemd/systemd/issues/33547.
+#
+#Type Name ID
+u foo -
+u bar 300:foo
diff --git a/test/units/TEST-04-JOURNAL.stopped-socket-activation.sh b/test/units/TEST-04-JOURNAL.stopped-socket-activation.sh
new file mode 100755
index 00000000000..083f5fa0552
--- /dev/null
+++ b/test/units/TEST-04-JOURNAL.stopped-socket-activation.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemctl stop systemd-journald.service
+systemd-cat date
+
+# shellcheck disable=SC2016
+timeout 30 bash -xec 'until test "$(systemctl show -p SubState --value systemd-journald.service)" = "running"; do sleep 1; done'
diff --git a/test/units/TEST-07-PID1.issue-31752.sh b/test/units/TEST-07-PID1.issue-31752.sh
new file mode 100755
index 00000000000..89ec07e46bf
--- /dev/null
+++ b/test/units/TEST-07-PID1.issue-31752.sh
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Make sure NeedDaemonReload= considers newly created drop-ins.
+# Issue: https://github.com/systemd/systemd/issues/31752
+
+UNIT=test-issue-31752.service
+
+cleanup() {
+ rm -rf /run/systemd/system/"$UNIT" /run/systemd/system/"$UNIT".d
+ systemctl daemon-reload
+}
+
+trap cleanup EXIT
+
+cat > /run/systemd/system/"$UNIT" < /run/systemd/system/"$UNIT".d/desc.conf <root.passwd
systemd-firstboot --root="$ROOT" --root-password-file=root.passwd
grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
-grep -q "^root:" "$ROOT/etc/shadow"
+grep -q "^root:[^!*]" "$ROOT/etc/shadow"
rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" root.passwd
-# Set the shell together with the password, as firstboot won't touch
-# /etc/passwd if it already exists
+# Make sure the root password is set if /etc/passwd and /etc/shadow exist but
+# don't have a root entry.
+touch "$ROOT/etc/passwd" "$ROOT/etc/shadow"
+systemd-firstboot --root="$ROOT" --root-password=foo
+grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
+grep -q "^root:[^!*]" "$ROOT/etc/shadow"
+rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
+# If /etc/passwd and /etc/shadow exist, they will only be updated if the shadow
+# password is !unprovisioned.
+echo "root:x:0:0:root:/root:/bin/sh" >"$ROOT/etc/passwd"
+echo "root:!test:::::::" >"$ROOT/etc/shadow"
+systemd-firstboot --root="$ROOT" --root-password=foo
+grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
+grep -q "^root:!test:" "$ROOT/etc/shadow"
+rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
+echo "root:x:0:0:root:/root:/bin/sh" >"$ROOT/etc/passwd"
+echo "root:!unprovisioned:::::::" >"$ROOT/etc/shadow"
+systemd-firstboot --root="$ROOT" --root-password=foo
+grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
+grep -q "^root:[^!*]" "$ROOT/etc/shadow"
+rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
+systemd-firstboot --root="$ROOT" --root-password-hashed="$ROOT_HASHED_PASSWORD1"
+grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
+grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow"
+rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
+systemd-firstboot --root="$ROOT" --root-shell=/bin/fooshell
+grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
+grep -q "^root:!\*:" "$ROOT/etc/shadow"
+rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
systemd-firstboot --root="$ROOT" --root-password-hashed="$ROOT_HASHED_PASSWORD1" --root-shell=/bin/fooshell
grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow"
@@ -154,8 +182,9 @@ mkdir -p "$ROOT/bin"
touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell"
# Temporarily disable pipefail to avoid `echo: write error: Broken pipe
set +o pipefail
-# We can do only limited testing here, since it's all an interactive stuff,
-# so --prompt and --prompt-root-password are skipped on purpose
+# We can do only limited testing here, since it's all an interactive stuff, so
+# --prompt is skipped on purpose and only limited --prompt-root-password
+# testing can be done.
echo -ne "\nfoo\nbar\n" | systemd-firstboot --root="$ROOT" --prompt-locale
grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
@@ -171,6 +200,11 @@ echo -ne "\nEurope/Berlin\n" | systemd-firstboot --root="$ROOT" --prompt-timezon
readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$"
echo -ne "\nfoobar\n" | systemd-firstboot --root="$ROOT" --prompt-hostname
grep -q "foobar" "$ROOT/etc/hostname"
+# With no root password provided, a locked account should be created.
+systemd-firstboot --root="$ROOT" --prompt-root-password