summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backlight/backlight.c4
-rw-r--r--src/basic/env-util.c31
-rw-r--r--src/basic/env-util.h2
-rw-r--r--src/basic/filesystems-gperf.gperf1
-rw-r--r--src/basic/hashmap.h2
-rw-r--r--src/basic/linux/netfilter.h76
-rw-r--r--src/basic/meson.build2
-rw-r--r--src/basic/missing_magic.h5
-rw-r--r--src/basic/virt.c1
-rw-r--r--src/boot/bless-boot.c2
-rw-r--r--src/boot/efi/boot.c8
-rw-r--r--src/boot/efi/cpio.c2
-rw-r--r--src/busctl/busctl.c9
-rw-r--r--src/core/bpf-socket-bind.c9
-rw-r--r--src/core/bpf/socket_bind/socket-bind-api.bpf.h7
-rw-r--r--src/core/bpf/socket_bind/socket-bind.bpf.c3
-rw-r--r--src/core/core-varlink.c20
-rw-r--r--src/core/dbus-unit.c4
-rw-r--r--src/core/dbus-util.c2
-rw-r--r--src/core/dbus-util.h1
-rw-r--r--src/core/dynamic-user.c4
-rw-r--r--src/core/execute.c28
-rw-r--r--src/core/import-creds.c2
-rw-r--r--src/core/load-fragment-gperf.gperf.in4
-rw-r--r--src/core/load-fragment.c35
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/core/main.c4
-rw-r--r--src/core/manager-serialize.c11
-rw-r--r--src/core/mount.c22
-rw-r--r--src/core/service.c12
-rw-r--r--src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c2
-rw-r--r--src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c2
-rw-r--r--src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c2
-rw-r--r--src/home/homed-manager.c6
-rw-r--r--src/home/homework-fscrypt.c4
-rw-r--r--src/home/homework-quota.c2
-rw-r--r--src/import/curl-util.c7
-rw-r--r--src/journal-remote/journal-gatewayd.c1
-rw-r--r--src/journal-remote/journal-remote-main.c24
-rw-r--r--src/journal-remote/journal-remote.c6
-rw-r--r--src/journal-remote/journal-remote.h2
-rw-r--r--src/journal-remote/journal-upload.c40
-rw-r--r--src/journal-remote/journal-upload.h1
-rw-r--r--src/journal/cat.c1
-rw-r--r--src/journal/journalctl.c4
-rw-r--r--src/journal/journald-server.c2
-rw-r--r--src/libsystemd-network/icmp6-util.c4
-rw-r--r--src/libsystemd-network/test-dhcp-client.c2
-rw-r--r--src/libsystemd-network/test-dhcp-server.c4
-rw-r--r--src/libsystemd-network/test-dhcp6-client.c2
-rw-r--r--src/libsystemd-network/test-ndisc-ra.c2
-rw-r--r--src/libsystemd/sd-bus/bus-error.c6
-rw-r--r--src/libsystemd/sd-device/device-private.h5
-rw-r--r--src/libsystemd/sd-device/sd-device.c4
-rw-r--r--src/libsystemd/sd-event/sd-event.c19
-rw-r--r--src/libsystemd/sd-event/test-event.c18
-rw-r--r--src/libsystemd/sd-journal/journal-verify.c2
-rw-r--r--src/libsystemd/sd-journal/sd-journal.c8
-rw-r--r--src/login/logind-dbus.c3
-rw-r--r--src/machine/image-dbus.c79
-rw-r--r--src/machine/image-dbus.h2
-rw-r--r--src/machine/machined-dbus.c5
-rw-r--r--src/nspawn/nspawn.c2
-rw-r--r--src/portable/profile/default/service.conf2
-rw-r--r--src/portable/profile/trusted/service.conf2
-rw-r--r--src/resolve/resolved-bus.c10
-rw-r--r--src/resolve/resolved-dns-answer.c22
-rw-r--r--src/resolve/resolved-dns-answer.h17
-rw-r--r--src/resolve/resolved-dns-packet.c7
-rw-r--r--src/resolve/resolved-dns-query.c27
-rw-r--r--src/resolve/resolved-dns-rr.c17
-rw-r--r--src/resolve/resolved-dns-rr.h1
-rw-r--r--src/resolve/resolved-dns-scope.c34
-rw-r--r--src/resolve/resolved-dns-scope.h1
-rw-r--r--src/resolve/resolved-dns-stream.c45
-rw-r--r--src/resolve/resolved-dns-stream.h1
-rw-r--r--src/resolve/resolved-dns-stub.c10
-rw-r--r--src/resolve/resolved-dns-transaction.c219
-rw-r--r--src/resolve/resolved-dns-transaction.h5
-rw-r--r--src/resolve/resolved-dns-trust-anchor.c10
-rw-r--r--src/resolve/resolved-mdns.c2
-rw-r--r--src/rpm/macros.systemd.in1
-rw-r--r--src/run/run.c25
-rw-r--r--src/shared/base-filesystem.c4
-rw-r--r--src/shared/blockdev-util.c96
-rw-r--r--src/shared/bpf-dlopen.c4
-rw-r--r--src/shared/clean-ipc.c2
-rw-r--r--src/shared/conf-parser.c6
-rw-r--r--src/shared/copy.c2
-rw-r--r--src/shared/discover-image.c62
-rw-r--r--src/shared/dlfcn-util.c2
-rw-r--r--src/shared/idn-util.c5
-rw-r--r--src/shared/install.c7
-rw-r--r--src/shared/journal-importer.c14
-rw-r--r--src/shared/libcrypt-util.c2
-rw-r--r--src/shared/ptyfwd.c3
-rw-r--r--src/shared/reboot-util.c10
-rw-r--r--src/shared/reboot-util.h1
-rw-r--r--src/shared/tpm2-util.c2
-rw-r--r--src/shared/verbs.c18
-rw-r--r--src/shared/watchdog.c16
-rw-r--r--src/systemctl/systemctl-enable.c3
-rw-r--r--src/systemctl/systemctl-list-jobs.c4
-rw-r--r--src/systemctl/systemctl-logind.c2
-rw-r--r--src/systemd/sd-bus-vtable.h8
-rw-r--r--src/test/test-bpf-foreign-programs.c2
-rw-r--r--src/test/test-bpf-lsm.c2
-rw-r--r--src/test/test-namespace.c34
-rw-r--r--src/test/test-socket-bind.c2
-rw-r--r--src/tmpfiles/tmpfiles.c9
-rw-r--r--src/udev/udev-rules.c2
-rw-r--r--src/userdb/userdbctl.c6
-rw-r--r--src/userdb/userdbd-manager.c8
113 files changed, 964 insertions, 419 deletions
diff --git a/src/backlight/backlight.c b/src/backlight/backlight.c
index b6474d3..56c4663 100644
--- a/src/backlight/backlight.c
+++ b/src/backlight/backlight.c
@@ -56,6 +56,10 @@ static int has_multiple_graphics_cards(void) {
if (r < 0)
return r;
+ r = sd_device_enumerator_allow_uninitialized(e);
+ if (r < 0)
+ return r;
+
r = sd_device_enumerator_add_match_subsystem(e, "pci", /* match = */ true);
if (r < 0)
return r;
diff --git a/src/basic/env-util.c b/src/basic/env-util.c
index 55ac11a..e818cef 100644
--- a/src/basic/env-util.c
+++ b/src/basic/env-util.c
@@ -811,8 +811,8 @@ int putenv_dup(const char *assignment, bool override) {
}
int setenv_systemd_exec_pid(bool update_only) {
- char str[DECIMAL_STR_MAX(pid_t)];
const char *e;
+ int r;
/* Update $SYSTEMD_EXEC_PID=pid except when '*' is set for the variable. */
@@ -823,10 +823,9 @@ int setenv_systemd_exec_pid(bool update_only) {
if (streq_ptr(e, "*"))
return 0;
- xsprintf(str, PID_FMT, getpid_cached());
-
- if (setenv("SYSTEMD_EXEC_PID", str, 1) < 0)
- return -errno;
+ r = setenvf("SYSTEMD_EXEC_PID", /* overwrite= */ 1, PID_FMT, getpid_cached());
+ if (r < 0)
+ return r;
return 1;
}
@@ -902,3 +901,25 @@ int getenv_steal_erase(const char *name, char **ret) {
return 1;
}
+
+int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
+ _cleanup_free_ char *value = NULL;
+ va_list ap;
+ int r;
+
+ assert(name);
+
+ if (!valuef)
+ return RET_NERRNO(unsetenv(name));
+
+ va_start(ap, valuef);
+ DISABLE_WARNING_FORMAT_NONLITERAL;
+ r = vasprintf(&value, valuef, ap);
+ REENABLE_WARNING;
+ va_end(ap);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ return RET_NERRNO(setenv(name, value, overwrite));
+}
diff --git a/src/basic/env-util.h b/src/basic/env-util.h
index b927ac7..5f0ac74 100644
--- a/src/basic/env-util.h
+++ b/src/basic/env-util.h
@@ -72,3 +72,5 @@ int setenv_systemd_exec_pid(bool update_only);
int getenv_path_list(const char *name, char ***ret_paths);
int getenv_steal_erase(const char *name, char **ret);
+
+int setenvf(const char *name, bool overwrite, const char *valuef, ...) _printf_(3,4);
diff --git a/src/basic/filesystems-gperf.gperf b/src/basic/filesystems-gperf.gperf
index e8c5357..1cd66b5 100644
--- a/src/basic/filesystems-gperf.gperf
+++ b/src/basic/filesystems-gperf.gperf
@@ -91,6 +91,7 @@ ocfs2, {OCFS2_SUPER_MAGIC}
openpromfs, {OPENPROM_SUPER_MAGIC}
orangefs, {ORANGEFS_DEVREQ_MAGIC}
overlay, {OVERLAYFS_SUPER_MAGIC}
+pidfs, {PID_FS_MAGIC}
pipefs, {PIPEFS_MAGIC}
ppc-cmm, {PPC_CMM_MAGIC}
proc, {PROC_SUPER_MAGIC}
diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h
index 91b3fe8..621f0ed 100644
--- a/src/basic/hashmap.h
+++ b/src/basic/hashmap.h
@@ -40,8 +40,8 @@ typedef struct IteratedCache IteratedCache; /* Caches the iterated order of on
* by hashmap users, so the definition has to be here. Do not use its fields
* directly. */
typedef struct {
- unsigned idx; /* index of an entry to be iterated next */
const void *next_key; /* expected value of that entry's key pointer */
+ unsigned idx; /* index of an entry to be iterated next */
#if ENABLE_DEBUG_HASHMAP
unsigned put_count; /* hashmap's put_count recorded at start of iteration */
unsigned rem_count; /* hashmap's rem_count in previous iteration */
diff --git a/src/basic/linux/netfilter.h b/src/basic/linux/netfilter.h
new file mode 100644
index 0000000..30c045b
--- /dev/null
+++ b/src/basic/linux/netfilter.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_NETFILTER_H
+#define __LINUX_NETFILTER_H
+
+#include <linux/types.h>
+
+#include <linux/in.h>
+#include <linux/in6.h>
+
+/* Responses from hook functions. */
+#define NF_DROP 0
+#define NF_ACCEPT 1
+#define NF_STOLEN 2
+#define NF_QUEUE 3
+#define NF_REPEAT 4
+#define NF_STOP 5 /* Deprecated, for userspace nf_queue compatibility. */
+#define NF_MAX_VERDICT NF_STOP
+
+/* we overload the higher bits for encoding auxiliary data such as the queue
+ * number or errno values. Not nice, but better than additional function
+ * arguments. */
+#define NF_VERDICT_MASK 0x000000ff
+
+/* extra verdict flags have mask 0x0000ff00 */
+#define NF_VERDICT_FLAG_QUEUE_BYPASS 0x00008000
+
+/* queue number (NF_QUEUE) or errno (NF_DROP) */
+#define NF_VERDICT_QMASK 0xffff0000
+#define NF_VERDICT_QBITS 16
+
+#define NF_QUEUE_NR(x) ((((x) << 16) & NF_VERDICT_QMASK) | NF_QUEUE)
+
+#define NF_DROP_ERR(x) (((-x) << 16) | NF_DROP)
+
+/* only for userspace compatibility */
+
+/* NF_VERDICT_BITS should be 8 now, but userspace might break if this changes */
+#define NF_VERDICT_BITS 16
+
+enum nf_inet_hooks {
+ NF_INET_PRE_ROUTING,
+ NF_INET_LOCAL_IN,
+ NF_INET_FORWARD,
+ NF_INET_LOCAL_OUT,
+ NF_INET_POST_ROUTING,
+ NF_INET_NUMHOOKS,
+ NF_INET_INGRESS = NF_INET_NUMHOOKS,
+};
+
+enum nf_dev_hooks {
+ NF_NETDEV_INGRESS,
+ NF_NETDEV_EGRESS,
+ NF_NETDEV_NUMHOOKS
+};
+
+enum {
+ NFPROTO_UNSPEC = 0,
+ NFPROTO_INET = 1,
+ NFPROTO_IPV4 = 2,
+ NFPROTO_ARP = 3,
+ NFPROTO_NETDEV = 5,
+ NFPROTO_BRIDGE = 7,
+ NFPROTO_IPV6 = 10,
+ NFPROTO_DECNET = 12,
+ NFPROTO_NUMPROTO,
+};
+
+union nf_inet_addr {
+ __u32 all[4];
+ __be32 ip;
+ __be32 ip6[4];
+ struct in_addr in;
+ struct in6_addr in6;
+};
+
+#endif /* __LINUX_NETFILTER_H */
diff --git a/src/basic/meson.build b/src/basic/meson.build
index bfe52d5..27c4fde 100644
--- a/src/basic/meson.build
+++ b/src/basic/meson.build
@@ -389,7 +389,7 @@ filesystem_includes = ['linux/magic.h',
check_filesystems = find_program('check-filesystems.sh')
r = run_command([check_filesystems, cpp, files('filesystems-gperf.gperf')] + filesystem_includes, check: false)
if r.returncode() != 0
- error('Unknown filesystems defined in kernel headers:\n\n' + r.stdout())
+ warning('Unknown filesystems defined in kernel headers:\n\n' + r.stdout())
endif
filesystems_gperf_h = custom_target(
diff --git a/src/basic/missing_magic.h b/src/basic/missing_magic.h
index c104fcf..82ede18 100644
--- a/src/basic/missing_magic.h
+++ b/src/basic/missing_magic.h
@@ -128,6 +128,11 @@
#define DEVMEM_MAGIC 0x454d444d
#endif
+/* cb12fd8e0dabb9a1c8aef55a6a41e2c255fcdf4b (6.8) */
+#ifndef PID_FS_MAGIC
+#define PID_FS_MAGIC 0x50494446
+#endif
+
/* Not in mainline but included in Ubuntu */
#ifndef SHIFTFS_MAGIC
#define SHIFTFS_MAGIC 0x6a656a62
diff --git a/src/basic/virt.c b/src/basic/virt.c
index 6ea1854..4ff54e4 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -178,6 +178,7 @@ static Virtualization detect_vm_dmi_vendor(void) {
{ "VMW", VIRTUALIZATION_VMWARE },
{ "innotek GmbH", VIRTUALIZATION_ORACLE },
{ "VirtualBox", VIRTUALIZATION_ORACLE },
+ { "Oracle Corporation", VIRTUALIZATION_ORACLE }, /* Detect VirtualBox on some proprietary systems via the board_vendor */
{ "Xen", VIRTUALIZATION_XEN },
{ "Bochs", VIRTUALIZATION_BOCHS },
{ "Parallels", VIRTUALIZATION_PARALLELS },
diff --git a/src/boot/bless-boot.c b/src/boot/bless-boot.c
index 268651a..e863d3c 100644
--- a/src/boot/bless-boot.c
+++ b/src/boot/bless-boot.c
@@ -476,7 +476,7 @@ static int verb_set(int argc, char *argv[], void *userdata) {
/* First, fsync() the directory these files are located in */
r = fsync_parent_at(fd, skip_slash(target));
if (r < 0)
- log_debug_errno(errno, "Failed to synchronize image directory, ignoring: %m");
+ log_debug_errno(r, "Failed to synchronize image directory, ignoring: %m");
/* Secondly, syncfs() the whole file system these files are located in */
if (syncfs(fd) < 0)
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index d7b94bc..7fca854 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -2305,9 +2305,9 @@ static EFI_STATUS initrd_prepare(
assert(ret_initrd_size);
if (entry->type != LOADER_LINUX || !entry->initrd) {
- ret_options = NULL;
- ret_initrd = NULL;
- ret_initrd_size = 0;
+ *ret_options = NULL;
+ *ret_initrd = NULL;
+ *ret_initrd_size = 0;
return EFI_SUCCESS;
}
@@ -2517,7 +2517,7 @@ static EFI_STATUS secure_boot_discover_keys(Config *config, EFI_FILE *root_dir)
EFI_STATUS err;
_cleanup_(file_closep) EFI_FILE *keys_basedir = NULL;
- if (secure_boot_mode() != SECURE_BOOT_SETUP)
+ if (!IN_SET(secure_boot_mode(), SECURE_BOOT_SETUP, SECURE_BOOT_AUDIT))
return EFI_SUCCESS;
/* the lack of a 'keys' directory is not fatal and is silently ignored */
diff --git a/src/boot/efi/cpio.c b/src/boot/efi/cpio.c
index 79b5d43..34d8be4 100644
--- a/src/boot/efi/cpio.c
+++ b/src/boot/efi/cpio.c
@@ -63,7 +63,7 @@ static EFI_STATUS pack_cpio_one(
char *a;
assert(fname);
- assert(contents_size || contents_size == 0);
+ assert(contents || contents_size == 0);
assert(target_dir_prefix);
assert(inode_counter);
assert(cpio_buffer);
diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c
index ad69edf..afd5d8d 100644
--- a/src/busctl/busctl.c
+++ b/src/busctl/busctl.c
@@ -2021,6 +2021,15 @@ static int call(int argc, char **argv, void *userdata) {
if (r < 0)
return r;
+ if (!service_name_is_valid(argv[1]))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid service name: %s", argv[1]);
+ if (!object_path_is_valid(argv[2]))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid object path: %s", argv[2]);
+ if (!interface_name_is_valid(argv[3]))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid interface name: %s", argv[3]);
+ if (!member_name_is_valid(argv[4]))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid member name: %s", argv[4]);
+
r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]);
if (r < 0)
return bus_log_create_error(r);
diff --git a/src/core/bpf-socket-bind.c b/src/core/bpf-socket-bind.c
index 660ffdb..0a7ed95 100644
--- a/src/core/bpf-socket-bind.c
+++ b/src/core/bpf-socket-bind.c
@@ -32,6 +32,15 @@ static int update_rules_map(
assert(map_fd >= 0);
+ if (!head) {
+ static const struct socket_bind_rule val = {
+ .address_family = SOCKET_BIND_RULE_AF_MATCH_NOTHING,
+ };
+
+ if (sym_bpf_map_update_elem(map_fd, &i, &val, BPF_ANY) != 0)
+ return -errno;
+ }
+
LIST_FOREACH(socket_bind_items, item, head) {
struct socket_bind_rule val = {
.address_family = (uint32_t) item->address_family,
diff --git a/src/core/bpf/socket_bind/socket-bind-api.bpf.h b/src/core/bpf/socket_bind/socket-bind-api.bpf.h
index 277b9bb..4fe08f1 100644
--- a/src/core/bpf/socket_bind/socket-bind-api.bpf.h
+++ b/src/core/bpf/socket_bind/socket-bind-api.bpf.h
@@ -7,13 +7,17 @@
*/
#include <linux/types.h>
+#include <stdint.h>
/*
* Bind rule is matched with socket fields accessible to cgroup/bind{4,6} hook
* through bpf_sock_addr struct.
- * 'address_family' is expected to be one of AF_UNSPEC, AF_INET or AF_INET6.
+ * 'address_family' is expected to be one of AF_UNSPEC, AF_INET, AF_INET6 or the
+ * magic SOCKET_BIND_RULE_AF_MATCH_NOTHING.
* Matching by family is bypassed for rules with AF_UNSPEC set, which makes the
* rest of a rule applicable for both IPv4 and IPv6 addresses.
+ * If SOCKET_BIND_RULE_AF_MATCH_NOTHING is set the rule fails unconditionally
+ * and other checks are skipped.
* If matching by family is either successful or bypassed, a rule and a socket
* are matched by ip protocol.
* If 'protocol' is 0, matching is bypassed.
@@ -49,3 +53,4 @@ struct socket_bind_rule {
};
#define SOCKET_BIND_MAX_RULES 128
+#define SOCKET_BIND_RULE_AF_MATCH_NOTHING UINT32_MAX
diff --git a/src/core/bpf/socket_bind/socket-bind.bpf.c b/src/core/bpf/socket_bind/socket-bind.bpf.c
index b7972a8..da9f9d1 100644
--- a/src/core/bpf/socket_bind/socket-bind.bpf.c
+++ b/src/core/bpf/socket_bind/socket-bind.bpf.c
@@ -55,6 +55,9 @@ static __always_inline bool match(
__u32 protocol,
__u16 port,
const struct socket_bind_rule *r) {
+ if (r->address_family == SOCKET_BIND_RULE_AF_MATCH_NOTHING)
+ return false;
+
return match_af(address_family, r) &&
match_protocol(protocol, r) &&
match_user_port(port, r);
diff --git a/src/core/core-varlink.c b/src/core/core-varlink.c
index 8432715..d8b16c2 100644
--- a/src/core/core-varlink.c
+++ b/src/core/core-varlink.c
@@ -504,13 +504,21 @@ static int manager_varlink_init_system(Manager *m) {
if (!MANAGER_IS_TEST_RUN(m)) {
(void) mkdir_p_label("/run/systemd/userdb", 0755);
- r = varlink_server_listen_address(s, "/run/systemd/userdb/io.systemd.DynamicUser", 0666);
- if (r < 0)
- return log_error_errno(r, "Failed to bind to varlink socket: %m");
+ FOREACH_STRING(address, "/run/systemd/userdb/io.systemd.DynamicUser", VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM) {
+ if (MANAGER_IS_RELOADING(m)) {
+ /* If manager is reloading, we skip listening on existing addresses, since
+ * the fd should be acquired later through deserialization. */
+ if (access(address, F_OK) >= 0)
+ continue;
+ if (errno != ENOENT)
+ return log_error_errno(errno,
+ "Failed to check if varlink socket '%s' exists: %m", address);
+ }
- r = varlink_server_listen_address(s, VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM, 0666);
- if (r < 0)
- return log_error_errno(r, "Failed to bind to varlink socket: %m");
+ r = varlink_server_listen_address(s, address, 0666);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind to varlink socket '%s': %m", address);
+ }
}
r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index e2398c8..648d435 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -2159,7 +2159,7 @@ static int bus_unit_set_transient_property(
return bus_set_transient_emergency_action(u, name, &u->job_timeout_action, message, flags, error);
if (streq(name, "JobTimeoutRebootArgument"))
- return bus_set_transient_string(u, name, &u->job_timeout_reboot_arg, message, flags, error);
+ return bus_set_transient_reboot_parameter(u, name, &u->job_timeout_reboot_arg, message, flags, error);
if (streq(name, "StartLimitIntervalUSec"))
return bus_set_transient_usec(u, name, &u->start_ratelimit.interval, message, flags, error);
@@ -2183,7 +2183,7 @@ static int bus_unit_set_transient_property(
return bus_set_transient_exit_status(u, name, &u->success_action_exit_status, message, flags, error);
if (streq(name, "RebootArgument"))
- return bus_set_transient_string(u, name, &u->reboot_arg, message, flags, error);
+ return bus_set_transient_reboot_parameter(u, name, &u->reboot_arg, message, flags, error);
if (streq(name, "CollectMode"))
return bus_set_transient_collect_mode(u, name, &u->collect_mode, message, flags, error);
diff --git a/src/core/dbus-util.c b/src/core/dbus-util.c
index ea044b5..d4f4054 100644
--- a/src/core/dbus-util.c
+++ b/src/core/dbus-util.c
@@ -6,6 +6,7 @@
#include "escape.h"
#include "parse-util.h"
#include "path-util.h"
+#include "reboot-util.h"
#include "unit-printf.h"
#include "user-util.h"
#include "unit.h"
@@ -39,6 +40,7 @@ static inline bool valid_user_group_name_or_id_relaxed(const char *u) {
BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(user_relaxed, valid_user_group_name_or_id_relaxed);
BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(path, path_is_absolute);
+BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(reboot_parameter, reboot_parameter_is_valid);
int bus_set_transient_string(
Unit *u,
diff --git a/src/core/dbus-util.h b/src/core/dbus-util.h
index e12631a..19abcd1 100644
--- a/src/core/dbus-util.h
+++ b/src/core/dbus-util.h
@@ -239,6 +239,7 @@ int bus_set_transient_mode_t(Unit *u, const char *name, mode_t *p, sd_bus_messag
int bus_set_transient_unsigned(Unit *u, const char *name, unsigned *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_user_relaxed(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_path(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
+int bus_set_transient_reboot_parameter(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_string(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_bool(Unit *u, const char *name, bool *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_usec_internal(Unit *u, const char *name, usec_t *p, bool fix_0, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c
index 18b36e7..cb3af96 100644
--- a/src/core/dynamic-user.c
+++ b/src/core/dynamic-user.c
@@ -332,8 +332,10 @@ static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) {
* the lock on the socket taken. */
k = receive_one_fd_iov(d->storage_socket[0], &iov, 1, MSG_DONTWAIT, &lock_fd);
- if (k < 0)
+ if (k < 0) {
+ assert(errno_is_valid(-k));
return (int) k;
+ }
*ret_uid = uid;
*ret_lock_fd = lock_fd;
diff --git a/src/core/execute.c b/src/core/execute.c
index 2c1dda1..fc3d2ce 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -4710,12 +4710,14 @@ static int exec_child(
if (ns_type_supported(NAMESPACE_NET)) {
r = setup_shareable_ns(runtime->netns_storage_socket, CLONE_NEWNET);
- if (r == -EPERM)
- log_unit_warning_errno(unit, r,
- "PrivateNetwork=yes is configured, but network namespace setup failed, ignoring: %m");
- else if (r < 0) {
- *exit_status = EXIT_NETWORK;
- return log_unit_error_errno(unit, r, "Failed to set up network namespacing: %m");
+ if (r < 0) {
+ if (ERRNO_IS_PRIVILEGE(r))
+ log_unit_warning_errno(unit, r,
+ "PrivateNetwork=yes is configured, but network namespace setup failed, ignoring: %m");
+ else {
+ *exit_status = EXIT_NETWORK;
+ return log_unit_error_errno(unit, r, "Failed to set up network namespacing: %m");
+ }
}
} else if (context->network_namespace_path) {
*exit_status = EXIT_NETWORK;
@@ -4729,12 +4731,14 @@ static int exec_child(
if (ns_type_supported(NAMESPACE_IPC)) {
r = setup_shareable_ns(runtime->ipcns_storage_socket, CLONE_NEWIPC);
- if (r == -EPERM)
- log_unit_warning_errno(unit, r,
- "PrivateIPC=yes is configured, but IPC namespace setup failed, ignoring: %m");
- else if (r < 0) {
- *exit_status = EXIT_NAMESPACE;
- return log_unit_error_errno(unit, r, "Failed to set up IPC namespacing: %m");
+ if (r < 0) {
+ if (ERRNO_IS_PRIVILEGE(r))
+ log_unit_warning_errno(unit, r,
+ "PrivateIPC=yes is configured, but IPC namespace setup failed, ignoring: %m");
+ else {
+ *exit_status = EXIT_NAMESPACE;
+ return log_unit_error_errno(unit, r, "Failed to set up IPC namespacing: %m");
+ }
}
} else if (context->ipc_namespace_path) {
*exit_status = EXIT_NAMESPACE;
diff --git a/src/core/import-creds.c b/src/core/import-creds.c
index dab7d36..91922ab 100644
--- a/src/core/import-creds.c
+++ b/src/core/import-creds.c
@@ -512,7 +512,7 @@ static int parse_smbios_strings(ImportCredentialContext *c, const char *data, si
return log_oom();
if (!credential_name_valid(cn)) {
- log_warning("SMBIOS credential name '%s' is not valid, ignoring: %m", cn);
+ log_warning("SMBIOS credential name '%s' is not valid, ignoring.", cn);
continue;
}
diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
index 81a5971..b92c725 100644
--- a/src/core/load-fragment-gperf.gperf.in
+++ b/src/core/load-fragment-gperf.gperf.in
@@ -303,7 +303,7 @@ Unit.IgnoreOnSnapshot, config_parse_warn_compat,
Unit.JobTimeoutSec, config_parse_job_timeout_sec, 0, 0
Unit.JobRunningTimeoutSec, config_parse_job_running_timeout_sec, 0, 0
Unit.JobTimeoutAction, config_parse_emergency_action, 0, offsetof(Unit, job_timeout_action)
-Unit.JobTimeoutRebootArgument, config_parse_unit_string_printf, 0, offsetof(Unit, job_timeout_reboot_arg)
+Unit.JobTimeoutRebootArgument, config_parse_reboot_parameter, 0, offsetof(Unit, job_timeout_reboot_arg)
Unit.StartLimitIntervalSec, config_parse_sec, 0, offsetof(Unit, start_ratelimit.interval)
{# The following is a legacy alias name for compatibility #}
Unit.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_ratelimit.interval)
@@ -313,7 +313,7 @@ Unit.FailureAction, config_parse_emergency_action,
Unit.SuccessAction, config_parse_emergency_action, 0, offsetof(Unit, success_action)
Unit.FailureActionExitStatus, config_parse_exit_status, 0, offsetof(Unit, failure_action_exit_status)
Unit.SuccessActionExitStatus, config_parse_exit_status, 0, offsetof(Unit, success_action_exit_status)
-Unit.RebootArgument, config_parse_unit_string_printf, 0, offsetof(Unit, reboot_arg)
+Unit.RebootArgument, config_parse_reboot_parameter, 0, offsetof(Unit, reboot_arg)
Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, conditions)
Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, conditions)
Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, conditions)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 5f4d4b0..11166f9 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -54,6 +54,7 @@
#include "path-util.h"
#include "percent-util.h"
#include "process-util.h"
+#include "reboot-util.h"
#if HAVE_SECCOMP
#include "seccomp-util.h"
#endif
@@ -360,6 +361,40 @@ int config_parse_unit_string_printf(
return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
}
+int config_parse_reboot_parameter(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_free_ char *k = NULL;
+ const Unit *u = ASSERT_PTR(userdata);
+ int r;
+
+ assert(filename);
+ assert(line);
+ assert(rvalue);
+
+ r = unit_full_printf(u, rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
+ return 0;
+ }
+
+ if (!reboot_parameter_is_valid(k)) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid reboot parameter '%s', ignoring.", k);
+ return 0;
+ }
+
+ return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
+}
+
int config_parse_unit_strv_printf(
const char *unit,
const char *filename,
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index c57a6b2..13292db 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -21,6 +21,7 @@ void unit_dump_config_items(FILE *f);
CONFIG_PARSER_PROTOTYPE(config_parse_unit_deps);
CONFIG_PARSER_PROTOTYPE(config_parse_obsolete_unit_deps);
CONFIG_PARSER_PROTOTYPE(config_parse_unit_string_printf);
+CONFIG_PARSER_PROTOTYPE(config_parse_reboot_parameter);
CONFIG_PARSER_PROTOTYPE(config_parse_unit_strv_printf);
CONFIG_PARSER_PROTOTYPE(config_parse_unit_path_printf);
CONFIG_PARSER_PROTOTYPE(config_parse_colon_separated_paths);
diff --git a/src/core/main.c b/src/core/main.c
index 1c4b464..7b51099 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -608,7 +608,7 @@ static int parse_config_file(void) {
{ "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, &arg_cpu_affinity },
{ "Manager", "NUMAPolicy", config_parse_numa_policy, 0, &arg_numa_policy.type },
{ "Manager", "NUMAMask", config_parse_numa_mask, 0, &arg_numa_policy },
- { "Manager", "JoinControllers", config_parse_warn_compat, DISABLED_CONFIGURATION, NULL },
+ { "Manager", "JoinControllers", config_parse_warn_compat, DISABLED_LEGACY, NULL },
{ "Manager", "RuntimeWatchdogSec", config_parse_watchdog_sec, 0, &arg_runtime_watchdog },
{ "Manager", "RuntimeWatchdogPreSec", config_parse_watchdog_sec, 0, &arg_pretimeout_watchdog },
{ "Manager", "RebootWatchdogSec", config_parse_watchdog_sec, 0, &arg_reboot_watchdog },
@@ -2384,7 +2384,7 @@ static void setenv_manager_environment(void) {
r = putenv_dup(*p, true);
if (r < 0)
- log_warning_errno(errno, "Failed to setenv \"%s\", ignoring: %m", *p);
+ log_warning_errno(r, "Failed to setenv \"%s\", ignoring: %m", *p);
}
}
diff --git a/src/core/manager-serialize.c b/src/core/manager-serialize.c
index 3beb8fd..3d4f471 100644
--- a/src/core/manager-serialize.c
+++ b/src/core/manager-serialize.c
@@ -533,21 +533,12 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
return -ENOMEM;
} else if ((val = startswith(l, "varlink-server-socket-address="))) {
if (!m->varlink_server && MANAGER_IS_SYSTEM(m)) {
- _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
-
- r = manager_setup_varlink_server(m, &s);
+ r = manager_varlink_init(m);
if (r < 0) {
log_warning_errno(r, "Failed to setup varlink server, ignoring: %m");
continue;
}
- r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
- if (r < 0) {
- log_warning_errno(r, "Failed to attach varlink connection to event loop, ignoring: %m");
- continue;
- }
-
- m->varlink_server = TAKE_PTR(s);
deserialize_varlink_sockets = true;
}
diff --git a/src/core/mount.c b/src/core/mount.c
index 82ff3a7..451b429 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -1468,7 +1468,8 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
if (IN_SET(m->state, MOUNT_REMOUNTING, MOUNT_REMOUNTING_SIGKILL, MOUNT_REMOUNTING_SIGTERM))
mount_set_reload_result(m, f);
- else if (m->result == MOUNT_SUCCESS)
+ else if (m->result == MOUNT_SUCCESS && !IN_SET(m->state, MOUNT_MOUNTING, MOUNT_UNMOUNTING))
+ /* MOUNT_MOUNTING and MOUNT_UNMOUNTING states need to be patched, see below. */
m->result = f;
if (m->control_command) {
@@ -1491,11 +1492,11 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
switch (m->state) {
case MOUNT_MOUNTING:
- /* Our mount point has not appeared in mountinfo. Something went wrong. */
+ /* Our mount point has not appeared in mountinfo. Something went wrong. */
if (f == MOUNT_SUCCESS) {
- /* Either /bin/mount has an unexpected definition of success,
- * or someone raced us and we lost. */
+ /* Either /bin/mount has an unexpected definition of success, or someone raced us
+ * and we lost. */
log_unit_warning(UNIT(m), "Mount process finished, but there is no mount.");
f = MOUNT_FAILURE_PROTOCOL;
}
@@ -1513,9 +1514,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
break;
case MOUNT_UNMOUNTING:
-
if (f == MOUNT_SUCCESS && m->from_proc_self_mountinfo) {
-
/* Still a mount point? If so, let's try again. Most likely there were multiple mount points
* stacked on top of each other. We might exceed the timeout specified by the user overall,
* but we will stop as soon as any one umount times out. */
@@ -1526,15 +1525,20 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
mount_enter_unmounting(m);
} else {
log_unit_warning(u, "Mount still present after %u attempts to unmount, giving up.", m->n_retry_umount);
- mount_enter_mounted(m, f);
+ mount_enter_mounted(m, MOUNT_FAILURE_PROTOCOL);
}
+ } else if (f == MOUNT_FAILURE_EXIT_CODE && !m->from_proc_self_mountinfo) {
+ /* Hmm, umount process spawned by us failed, but the mount disappeared anyway?
+ * Maybe someone else is trying to unmount at the same time. */
+ log_unit_notice(u, "Mount disappeared even though umount process failed, continuing.");
+ mount_enter_dead(m, MOUNT_SUCCESS);
} else
mount_enter_dead_or_mounted(m, f);
break;
- case MOUNT_UNMOUNTING_SIGKILL:
case MOUNT_UNMOUNTING_SIGTERM:
+ case MOUNT_UNMOUNTING_SIGKILL:
mount_enter_dead_or_mounted(m, f);
break;
@@ -2078,7 +2082,7 @@ static int mount_process_proc_self_mountinfo(Manager *m) {
* then remove it because of an internal error. E.g., fuse.sshfs seems
* to do that when the connection fails. See #17617. To handle such the
* case, let's once set the state back to mounting. Then, the unit can
- * correctly enter the failed state later in mount_sigchld(). */
+ * correctly enter the failed state later in mount_sigchld_event(). */
mount_set_state(mount, MOUNT_MOUNTING);
break;
diff --git a/src/core/service.c b/src/core/service.c
index cdddd49..d382aad 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -3378,8 +3378,10 @@ static void service_notify_cgroup_empty_event(Unit *u) {
break;
}
- if (s->exit_type == SERVICE_EXIT_CGROUP && main_pid_good(s) <= 0)
- service_enter_start_post(s);
+ if (s->exit_type == SERVICE_EXIT_CGROUP && main_pid_good(s) <= 0) {
+ service_enter_stop_post(s, SERVICE_SUCCESS);
+ break;
+ }
_fallthrough_;
case SERVICE_START_POST:
@@ -3648,11 +3650,13 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
default:
assert_not_reached();
}
- } else if (s->exit_type == SERVICE_EXIT_CGROUP && s->state == SERVICE_START)
+ } else if (s->exit_type == SERVICE_EXIT_CGROUP && s->state == SERVICE_START &&
+ !IN_SET(s->type, SERVICE_NOTIFY, SERVICE_DBUS))
/* If a main process exits very quickly, this function might be executed
* before service_dispatch_exec_io(). Since this function disabled IO events
* to monitor the main process above, we need to update the state here too.
- * Let's consider the process is successfully launched and exited. */
+ * Let's consider the process is successfully launched and exited, but
+ * only when we're not expecting a readiness notification or dbus name. */
service_enter_start_post(s);
}
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c
index 3027804..63f9688 100644
--- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c
+++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c
@@ -34,7 +34,7 @@ _public_ int cryptsetup_token_open_pin(
const char *json;
_cleanup_(erase_and_freep) char *pin_string = NULL;
- assert(!pin || pin_size);
+ assert(pin || pin_size == 0);
assert(token >= 0);
/* This must not fail at this moment (internal error) */
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c
index c3e7fbd..63dbb19 100644
--- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c
+++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c
@@ -33,7 +33,7 @@ _public_ int cryptsetup_token_open_pin(
const char *json;
int r;
- assert(!pin || pin_size);
+ assert(pin || pin_size == 0);
assert(token >= 0);
/* This must not fail at this moment (internal error) */
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
index abe8072..3ab9cf3 100644
--- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
@@ -56,7 +56,7 @@ _public_ int cryptsetup_token_open_pin(
int r;
assert(token >= 0);
- assert(!pin || pin_size > 0);
+ assert(pin || pin_size == 0);
assert(ret_password);
assert(ret_password_len);
diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c
index 9075e3e..5084afb 100644
--- a/src/home/homed-manager.c
+++ b/src/home/homed-manager.c
@@ -891,7 +891,7 @@ static int manager_assess_image(
r = btrfs_is_subvol_fd(fd);
if (r < 0)
- return log_warning_errno(errno, "Failed to determine whether %s is a btrfs subvolume: %m", path);
+ return log_warning_errno(r, "Failed to determine whether %s is a btrfs subvolume: %m", path);
if (r > 0)
storage = USER_SUBVOLUME;
else {
@@ -1415,7 +1415,7 @@ static int manager_generate_key_pair(Manager *m) {
/* Write out public key (note that we only do that as a help to the user, we don't make use of this ever */
r = fopen_temporary("/var/lib/systemd/home/local.public", &fpublic, &temp_public);
if (r < 0)
- return log_error_errno(errno, "Failed to open key file for writing: %m");
+ return log_error_errno(r, "Failed to open key file for writing: %m");
if (PEM_write_PUBKEY(fpublic, m->private_key) <= 0)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write public key.");
@@ -1429,7 +1429,7 @@ static int manager_generate_key_pair(Manager *m) {
/* Write out the private key (this actually writes out both private and public, OpenSSL is confusing) */
r = fopen_temporary("/var/lib/systemd/home/local.private", &fprivate, &temp_private);
if (r < 0)
- return log_error_errno(errno, "Failed to open key file for writing: %m");
+ return log_error_errno(r, "Failed to open key file for writing: %m");
if (PEM_write_PrivateKey(fprivate, m->private_key, NULL, NULL, 0, NULL, 0) <= 0)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write private key pair.");
diff --git a/src/home/homework-fscrypt.c b/src/home/homework-fscrypt.c
index 5106961..4c62105 100644
--- a/src/home/homework-fscrypt.c
+++ b/src/home/homework-fscrypt.c
@@ -224,7 +224,7 @@ static int fscrypt_setup(
r = flistxattr_malloc(setup->root_fd, &xattr_buf);
if (r < 0)
- return log_error_errno(errno, "Failed to retrieve xattr list: %m");
+ return log_error_errno(r, "Failed to retrieve xattr list: %m");
NULSTR_FOREACH(xa, xattr_buf) {
_cleanup_free_ void *salt = NULL, *encrypted = NULL;
@@ -672,7 +672,7 @@ int home_passwd_fscrypt(
r = flistxattr_malloc(setup->root_fd, &xattr_buf);
if (r < 0)
- return log_error_errno(errno, "Failed to retrieve xattr list: %m");
+ return log_error_errno(r, "Failed to retrieve xattr list: %m");
NULSTR_FOREACH(xa, xattr_buf) {
const char *nr;
diff --git a/src/home/homework-quota.c b/src/home/homework-quota.c
index 574d155..c4498fa 100644
--- a/src/home/homework-quota.c
+++ b/src/home/homework-quota.c
@@ -113,7 +113,7 @@ int home_update_quota_auto(UserRecord *h, const char *path) {
r = btrfs_is_subvol(path);
if (r < 0)
- return log_error_errno(errno, "Failed to test if %s is a subvolume: %m", path);
+ return log_error_errno(r, "Failed to test if %s is a subvolume: %m", path);
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Directory %s is not a subvolume, cannot apply quota.", path);
diff --git a/src/import/curl-util.c b/src/import/curl-util.c
index 94f718d..b631f4b 100644
--- a/src/import/curl-util.c
+++ b/src/import/curl-util.c
@@ -126,6 +126,13 @@ static int curl_glue_timer_callback(CURLM *curl, long timeout_ms, void *userdata
assert(curl);
+ /* Don't configure timer anymore when the event loop is dead already. */
+ if (g->timer) {
+ sd_event *event_loop = sd_event_source_get_event(g->timer);
+ if (event_loop && sd_event_get_state(event_loop) == SD_EVENT_FINISHED)
+ return 0;
+ }
+
if (timeout_ms < 0) {
if (g->timer) {
if (sd_event_source_set_enabled(g->timer, SD_EVENT_OFF) < 0)
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index 34def46..3be488a 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -45,6 +45,7 @@ static char **arg_file = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_key_pem, erase_and_freep);
STATIC_DESTRUCTOR_REGISTER(arg_cert_pem, freep);
STATIC_DESTRUCTOR_REGISTER(arg_trust_pem, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
typedef struct RequestMeta {
sd_journal *journal;
diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c
index 3141c8a..1c993a2 100644
--- a/src/journal-remote/journal-remote-main.c
+++ b/src/journal-remote/journal-remote-main.c
@@ -107,7 +107,7 @@ static int spawn_child(const char* child, char** argv) {
r = fd_nonblock(fd[0], true);
if (r < 0)
- log_warning_errno(errno, "Failed to set child pipe to non-blocking: %m");
+ log_warning_errno(r, "Failed to set child pipe to non-blocking: %m");
return fd[0];
}
@@ -560,24 +560,6 @@ static int dispatch_http_event(sd_event_source *event,
**********************************************************************
**********************************************************************/
-static int setup_signals(RemoteServer *s) {
- int r;
-
- assert(s);
-
- assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0);
-
- r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, NULL, s);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, NULL, s);
- if (r < 0)
- return r;
-
- return 0;
-}
-
static int setup_raw_socket(RemoteServer *s, const char *address) {
int fd;
@@ -605,9 +587,9 @@ static int create_remoteserver(
if (r < 0)
return r;
- r = setup_signals(s);
+ r = sd_event_set_signal_exit(s->events, true);
if (r < 0)
- return log_error_errno(r, "Failed to set up signals: %m");
+ return log_error_errno(r, "Failed to install SIGINT/SIGTERM handlers: %m");
n = sd_listen_fds(true);
if (n < 0)
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index e8fe041..7caea19 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -362,8 +362,6 @@ void journal_remote_server_destroy(RemoteServer *s) {
writer_unref(s->_single_writer);
hashmap_free(s->writers);
- sd_event_source_unref(s->sigterm_event);
- sd_event_source_unref(s->sigint_event);
sd_event_source_unref(s->listen_event);
sd_event_unref(s->events);
@@ -497,7 +495,9 @@ static int accept_connection(
switch (socket_address_family(addr)) {
case AF_INET:
- case AF_INET6: {
+ case AF_INET6:
+ case AF_VSOCK:
+ case AF_UNIX: {
_cleanup_free_ char *a = NULL;
char *b;
diff --git a/src/journal-remote/journal-remote.h b/src/journal-remote/journal-remote.h
index facf151..53906a8 100644
--- a/src/journal-remote/journal-remote.h
+++ b/src/journal-remote/journal-remote.h
@@ -26,7 +26,7 @@ struct RemoteServer {
size_t active;
sd_event *events;
- sd_event_source *sigterm_event, *sigint_event, *listen_event;
+ sd_event_source *listen_event;
Hashmap *writers;
Writer *_single_writer;
diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c
index 06448b2..5df9f57 100644
--- a/src/journal-remote/journal-upload.c
+++ b/src/journal-remote/journal-upload.c
@@ -55,6 +55,8 @@ static int arg_follow = -1;
static const char *arg_save_state = NULL;
static usec_t arg_network_timeout_usec = USEC_INFINITY;
+STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
+
static void close_fd_input(Uploader *u);
#define SERVER_ANSWER_KEEP 2048
@@ -377,38 +379,6 @@ static int open_file_for_upload(Uploader *u, const char *filename) {
return r;
}
-static int dispatch_sigterm(sd_event_source *event,
- const struct signalfd_siginfo *si,
- void *userdata) {
- Uploader *u = ASSERT_PTR(userdata);
-
- log_received_signal(LOG_INFO, si);
-
- close_fd_input(u);
- close_journal_input(u);
-
- sd_event_exit(u->events, 0);
- return 0;
-}
-
-static int setup_signals(Uploader *u) {
- int r;
-
- assert(u);
-
- assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0);
-
- r = sd_event_add_signal(u->events, &u->sigterm_event, SIGTERM, dispatch_sigterm, u);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(u->events, &u->sigint_event, SIGINT, dispatch_sigterm, u);
- if (r < 0)
- return r;
-
- return 0;
-}
-
static int setup_uploader(Uploader *u, const char *url, const char *state_file) {
int r;
const char *host, *proto = "";
@@ -448,9 +418,9 @@ static int setup_uploader(Uploader *u, const char *url, const char *state_file)
if (r < 0)
return log_error_errno(r, "sd_event_default failed: %m");
- r = setup_signals(u);
+ r = sd_event_set_signal_exit(u->events, true);
if (r < 0)
- return log_error_errno(r, "Failed to set up signals: %m");
+ return log_error_errno(r, "Failed to install SIGINT/SIGTERM handlers: %m");
(void) sd_watchdog_enabled(false, &u->watchdog_usec);
@@ -474,8 +444,6 @@ static void destroy_uploader(Uploader *u) {
close_fd_input(u);
close_journal_input(u);
- sd_event_source_unref(u->sigterm_event);
- sd_event_source_unref(u->sigint_event);
sd_event_unref(u->events);
}
diff --git a/src/journal-remote/journal-upload.h b/src/journal-remote/journal-upload.h
index 9ff5a7b..2007864 100644
--- a/src/journal-remote/journal-upload.h
+++ b/src/journal-remote/journal-upload.h
@@ -25,7 +25,6 @@ typedef enum {
typedef struct Uploader {
sd_event *events;
- sd_event_source *sigint_event, *sigterm_event;
char *url;
CURL *easy;
diff --git a/src/journal/cat.c b/src/journal/cat.c
index 350b805..14d2083 100644
--- a/src/journal/cat.c
+++ b/src/journal/cat.c
@@ -10,6 +10,7 @@
#include "sd-journal.h"
#include "alloc-util.h"
+#include "env-util.h"
#include "fd-util.h"
#include "main-func.h"
#include "parse-argument.h"
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index d81d522..8cb96bd 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -336,7 +336,7 @@ static int help(void) {
" -u --unit=UNIT Show logs from the specified unit\n"
" --user-unit=UNIT Show logs from the specified user unit\n"
" -t --identifier=STRING Show entries with the specified syslog identifier\n"
- " -p --priority=RANGE Show entries with the specified priority\n"
+ " -p --priority=RANGE Show entries within the specified priority range\n"
" --facility=FACILITY... Show entries with the specified facilities\n"
" -g --grep=PATTERN Show entries with MESSAGE matching PATTERN\n"
" --case-sensitive[=BOOL] Force case sensitive or insensitive matching\n"
@@ -1048,7 +1048,7 @@ static int parse_argv(int argc, char *argv[]) {
arg_boot_offset = 0;
}
- if (!!arg_directory + !!arg_file + !!arg_machine + !!arg_root + !!arg_image > 1)
+ if (!!arg_directory + !!arg_file + arg_file_stdin + !!arg_machine + !!arg_root + !!arg_image > 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Please specify at most one of -D/--directory=, --file=, -M/--machine=, --root=, --image=.");
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 77aef79..31358cd 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -1016,7 +1016,7 @@ static void dispatch_message_real(
IOVEC_ADD_STRING_FIELD(iovec, n, o->slice, "OBJECT_SYSTEMD_SLICE");
IOVEC_ADD_STRING_FIELD(iovec, n, o->user_slice, "OBJECT_SYSTEMD_USER_SLICE");
- IOVEC_ADD_ID128_FIELD(iovec, n, o->invocation_id, "OBJECT_SYSTEMD_INVOCATION_ID=");
+ IOVEC_ADD_ID128_FIELD(iovec, n, o->invocation_id, "OBJECT_SYSTEMD_INVOCATION_ID");
}
assert(n <= m);
diff --git a/src/libsystemd-network/icmp6-util.c b/src/libsystemd-network/icmp6-util.c
index d3ee7c4..8ea09fd 100644
--- a/src/libsystemd-network/icmp6-util.c
+++ b/src/libsystemd-network/icmp6-util.c
@@ -154,7 +154,7 @@ int icmp6_receive(
/* This needs to be initialized with zero. See #20741. */
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int)) + /* ttl */
CMSG_SPACE_TIMEVAL) control = {};
- struct iovec iov = {};
+ struct iovec iov = { buffer, size };
union sockaddr_union sa = {};
struct msghdr msg = {
.msg_name = &sa.sa,
@@ -169,8 +169,6 @@ int icmp6_receive(
triple_timestamp t = {};
ssize_t len;
- iov = IOVEC_MAKE(buffer, size);
-
len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
if (len < 0)
return (int) len;
diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c
index 787dcf1..bf17e51 100644
--- a/src/libsystemd-network/test-dhcp-client.c
+++ b/src/libsystemd-network/test-dhcp-client.c
@@ -513,7 +513,7 @@ static void test_addr_acq(sd_event *e) {
callback_recv = test_addr_acq_recv_discover;
assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
- 2 * USEC_PER_SEC, 0,
+ 30 * USEC_PER_SEC, 0,
NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0);
res = sd_dhcp_client_start(client);
diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c
index f8a4c2c..763ecb9 100644
--- a/src/libsystemd-network/test-dhcp-server.c
+++ b/src/libsystemd-network/test-dhcp-server.c
@@ -62,7 +62,9 @@ static int test_basic(bool bind_to_interface) {
test_pool(&address_lo, 1, 0);
r = sd_dhcp_server_start(server);
- if (r == -EPERM)
+ /* skip test if running in an environment with no full networking support, CONFIG_PACKET not
+ * compiled in kernel, nor af_packet module available. */
+ if (r == -EPERM || r == -EAFNOSUPPORT)
return r;
assert_se(r >= 0);
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index 10233f5..a3a22e9 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -1003,7 +1003,7 @@ TEST(dhcp6_client) {
assert_se(sd_event_new(&e) >= 0);
assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
- 2 * USEC_PER_SEC, 0,
+ 30 * USEC_PER_SEC, 0,
NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0);
assert_se(sd_dhcp6_client_new(&client) >= 0);
diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c
index d3d96e7..16a934c 100644
--- a/src/libsystemd-network/test-ndisc-ra.c
+++ b/src/libsystemd-network/test-ndisc-ra.c
@@ -332,7 +332,7 @@ TEST(ra) {
assert_se(sd_event_source_set_io_fd_own(recv_router_advertisement, true) >= 0);
assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
- 2 * USEC_PER_SEC, 0,
+ 30 * USEC_PER_SEC, 0,
NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0);
assert_se(sd_radv_start(ra) >= 0);
diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c
index 4d687cf..cfcdb2c 100644
--- a/src/libsystemd/sd-bus/bus-error.c
+++ b/src/libsystemd/sd-bus/bus-error.c
@@ -278,14 +278,16 @@ _public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *fo
va_start(ap, format);
r = sd_bus_error_setfv(e, name, format, ap);
- assert(!name || r < 0);
+ if (name)
+ assert(r < 0);
va_end(ap);
return r;
}
r = sd_bus_error_set(e, name, NULL);
- assert(!name || r < 0);
+ if (name)
+ assert(r < 0);
return r;
}
diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h
index a59f130..7fcf715 100644
--- a/src/libsystemd/sd-device/device-private.h
+++ b/src/libsystemd/sd-device/device-private.h
@@ -19,7 +19,10 @@ int device_opendir(sd_device *device, const char *subdir, DIR **ret);
int device_get_property_bool(sd_device *device, const char *key);
int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value);
-int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value);
+int device_get_sysattr_unsigned_full(sd_device *device, const char *sysattr, unsigned base, unsigned *ret_value);
+static inline int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value) {
+ return device_get_sysattr_unsigned_full(device, sysattr, 0, ret_value);
+}
int device_get_sysattr_bool(sd_device *device, const char *sysattr);
int device_get_device_id(sd_device *device, const char **ret);
int device_get_devlink_priority(sd_device *device, int *ret);
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index f2193e2..2ac1e64 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -2368,7 +2368,7 @@ int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_valu
return v > 0;
}
-int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value) {
+int device_get_sysattr_unsigned_full(sd_device *device, const char *sysattr, unsigned base, unsigned *ret_value) {
const char *value;
int r;
@@ -2377,7 +2377,7 @@ int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned
return r;
unsigned v;
- r = safe_atou(value, &v);
+ r = safe_atou_full(value, base, &v);
if (r < 0)
return log_device_debug_errno(device, r, "Failed to parse '%s' attribute: %m", sysattr);
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index aee2ed5..6f34dd1 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -2034,7 +2034,7 @@ static int inode_data_realize_watch(sd_event *e, struct inode_data *d) {
wd = inotify_add_watch_fd(d->inotify_data->fd, d->fd, combined_mask);
if (wd < 0)
- return -errno;
+ return wd;
if (d->wd < 0) {
r = hashmap_put(d->inotify_data->wd, INT_TO_PTR(wd), d);
@@ -2255,7 +2255,7 @@ _public_ int sd_event_source_get_io_fd(sd_event_source *s) {
}
_public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
- int r;
+ int saved_fd, r;
assert_return(s, -EINVAL);
assert_return(fd >= 0, -EBADF);
@@ -2265,16 +2265,12 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
if (s->io.fd == fd)
return 0;
- if (event_source_is_offline(s)) {
- s->io.fd = fd;
- s->io.registered = false;
- } else {
- int saved_fd;
+ saved_fd = s->io.fd;
+ s->io.fd = fd;
- saved_fd = s->io.fd;
- assert(s->io.registered);
+ assert(event_source_is_offline(s) == !s->io.registered);
- s->io.fd = fd;
+ if (s->io.registered) {
s->io.registered = false;
r = source_io_register(s, s->enabled, s->io.events);
@@ -2287,6 +2283,9 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
(void) epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, saved_fd, NULL);
}
+ if (s->io.owned)
+ safe_close(saved_fd);
+
return 0;
}
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
index 33cd1c0..cf0d8c9 100644
--- a/src/libsystemd/sd-event/test-event.c
+++ b/src/libsystemd/sd-event/test-event.c
@@ -809,4 +809,22 @@ TEST(inotify_process_buffered_data) {
assert_se(sd_event_wait(e, 0) == 0);
}
+TEST(sd_event_source_set_io_fd) {
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_close_pair_ int pfd_a[2] = { -EBADF, -EBADF }, pfd_b[2] = { -EBADF, -EBADF };
+
+ assert_se(sd_event_default(&e) >= 0);
+
+ assert_se(pipe2(pfd_a, O_CLOEXEC) >= 0);
+ assert_se(pipe2(pfd_b, O_CLOEXEC) >= 0);
+
+ assert_se(sd_event_add_io(e, &s, pfd_a[0], EPOLLIN, NULL, INT_TO_PTR(-ENOANO)) >= 0);
+ assert_se(sd_event_source_set_io_fd_own(s, true) >= 0);
+ TAKE_FD(pfd_a[0]);
+
+ assert_se(sd_event_source_set_io_fd(s, pfd_b[0]) >= 0);
+ TAKE_FD(pfd_b[0]);
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);
diff --git a/src/libsystemd/sd-journal/journal-verify.c b/src/libsystemd/sd-journal/journal-verify.c
index 415fec9..e76f60d 100644
--- a/src/libsystemd/sd-journal/journal-verify.c
+++ b/src/libsystemd/sd-journal/journal-verify.c
@@ -163,7 +163,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
int r;
if (le64toh(o->data.entry_offset) == 0)
- warning(offset, "Unused data (entry_offset==0)");
+ debug(offset, "Unused data (entry_offset==0)");
if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) {
error(offset, "Bad n_entries: %"PRIu64, le64toh(o->data.n_entries));
diff --git a/src/libsystemd/sd-journal/sd-journal.c b/src/libsystemd/sd-journal/sd-journal.c
index c970876..852c373 100644
--- a/src/libsystemd/sd-journal/sd-journal.c
+++ b/src/libsystemd/sd-journal/sd-journal.c
@@ -1593,7 +1593,7 @@ static void directory_watch(sd_journal *j, Directory *m, int fd, uint32_t mask)
m->wd = inotify_add_watch_fd(j->inotify_fd, fd, mask);
if (m->wd < 0) {
- log_debug_errno(errno, "Failed to watch journal directory '%s', ignoring: %m", m->path);
+ log_debug_errno(m->wd, "Failed to watch journal directory '%s', ignoring: %m", m->path);
return;
}
@@ -2215,9 +2215,7 @@ _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id12
if (r < 0)
return r;
- if (ret_boot_id)
- *ret_boot_id = o->entry.boot_id;
- else {
+ if (!ret_boot_id) {
sd_id128_t id;
r = sd_id128_get_boot(&id);
@@ -2230,6 +2228,8 @@ _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id12
if (ret)
*ret = le64toh(o->entry.monotonic);
+ if (ret_boot_id)
+ *ret_boot_id = o->entry.boot_id;
return 0;
}
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 56aba3b..8972a53 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -2537,6 +2537,9 @@ static int method_set_reboot_parameter(
if (r < 0)
return r;
+ if (!reboot_parameter_is_valid(arg))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid reboot parameter '%s'.", arg);
+
r = detect_container();
if (r < 0)
return r;
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index 84dc95e..6acbe8d 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -133,9 +133,17 @@ int bus_image_method_rename(
if (r == 0)
return 1; /* Will call us back */
+ /* The image is cached with its name, hence it is necessary to remove from the cache before renaming. */
+ assert_se(hashmap_remove_value(m->image_cache, image->name, image));
+
r = image_rename(image, new_name);
- if (r < 0)
+ if (r < 0) {
+ image_unref(image);
return r;
+ }
+
+ /* Then save the object again in the cache. */
+ assert_se(hashmap_put(m->image_cache, image->name, image) > 0);
return sd_bus_reply_method_return(message, NULL);
}
@@ -393,30 +401,17 @@ static int image_flush_cache(sd_event_source *s, void *userdata) {
return 0;
}
-static int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
- _cleanup_free_ char *e = NULL;
- Manager *m = userdata;
- Image *image = NULL;
- const char *p;
+int manager_acquire_image(Manager *m, const char *name, Image **ret) {
int r;
- assert(bus);
- assert(path);
- assert(interface);
- assert(found);
+ assert(m);
+ assert(name);
- p = startswith(path, "/org/freedesktop/machine1/image/");
- if (!p)
+ Image *existing = hashmap_get(m->image_cache, name);
+ if (existing) {
+ if (ret)
+ *ret = existing;
return 0;
-
- e = bus_label_unescape(p);
- if (!e)
- return -ENOMEM;
-
- image = hashmap_get(m->image_cache, e);
- if (image) {
- *found = image;
- return 1;
}
if (!m->image_cache_defer_event) {
@@ -433,19 +428,49 @@ static int image_object_find(sd_bus *bus, const char *path, const char *interfac
if (r < 0)
return r;
- r = image_find(IMAGE_MACHINE, e, NULL, &image);
- if (r == -ENOENT)
- return 0;
+ _cleanup_(image_unrefp) Image *image = NULL;
+ r = image_find(IMAGE_MACHINE, name, NULL, &image);
if (r < 0)
return r;
image->userdata = m;
r = hashmap_ensure_put(&m->image_cache, &image_hash_ops, image->name, image);
- if (r < 0) {
- image_unref(image);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = image;
+
+ TAKE_PTR(image);
+ return 0;
+}
+
+static int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+ _cleanup_free_ char *e = NULL;
+ Manager *m = userdata;
+ Image *image;
+ const char *p;
+ int r;
+
+ assert(bus);
+ assert(path);
+ assert(interface);
+ assert(found);
+
+ p = startswith(path, "/org/freedesktop/machine1/image/");
+ if (!p)
+ return 0;
+
+ e = bus_label_unescape(p);
+ if (!e)
+ return -ENOMEM;
+
+ r = manager_acquire_image(m, e, &image);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
return r;
- }
*found = image;
return 1;
diff --git a/src/machine/image-dbus.h b/src/machine/image-dbus.h
index 4b00203..0c4fab1 100644
--- a/src/machine/image-dbus.h
+++ b/src/machine/image-dbus.h
@@ -2,10 +2,12 @@
#pragma once
#include "bus-object.h"
+#include "discover-image.h"
#include "machined.h"
extern const BusObjectImplementation image_object;
+int manager_acquire_image(Manager *m, const char *name, Image **ret);
char *image_bus_path(const char *name);
int bus_image_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 56dd22d..1e16046 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -541,8 +541,8 @@ static int method_get_machine_uid_shift(sd_bus_message *message, void *userdata,
}
static int redirect_method_to_image(sd_bus_message *message, Manager *m, sd_bus_error *error, sd_bus_message_handler_t method) {
- _cleanup_(image_unrefp) Image* i = NULL;
const char *name;
+ Image *i;
int r;
assert(message);
@@ -556,13 +556,12 @@ static int redirect_method_to_image(sd_bus_message *message, Manager *m, sd_bus_
if (!image_name_is_valid(name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
- r = image_find(IMAGE_MACHINE, name, NULL, &i);
+ r = manager_acquire_image(m, name, &i);
if (r == -ENOENT)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
if (r < 0)
return r;
- i->userdata = m;
return method(message, i, error);
}
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 68c5b18..b3e6867 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -2699,7 +2699,7 @@ static int setup_journal(const char *directory) {
r = mount_nofollow_verbose(LOG_DEBUG, p, q, NULL, MS_BIND, NULL);
if (r < 0)
- return log_error_errno(errno, "Failed to bind mount journal from host into guest: %m");
+ return log_error_errno(r, "Failed to bind mount journal from host into guest: %m");
return 0;
}
diff --git a/src/portable/profile/default/service.conf b/src/portable/profile/default/service.conf
index 230aa60..5c447d6 100644
--- a/src/portable/profile/default/service.conf
+++ b/src/portable/profile/default/service.conf
@@ -4,7 +4,7 @@
MountAPIVFS=yes
BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
BindReadOnlyPaths=/etc/machine-id
-BindReadOnlyPaths=/etc/resolv.conf
+BindReadOnlyPaths=-/etc/resolv.conf
BindReadOnlyPaths=/run/dbus/system_bus_socket
DynamicUser=yes
RemoveIPC=yes
diff --git a/src/portable/profile/trusted/service.conf b/src/portable/profile/trusted/service.conf
index 04deeb2..144d4f6 100644
--- a/src/portable/profile/trusted/service.conf
+++ b/src/portable/profile/trusted/service.conf
@@ -5,4 +5,4 @@ MountAPIVFS=yes
PrivateTmp=yes
BindPaths=/run
BindReadOnlyPaths=/etc/machine-id
-BindReadOnlyPaths=/etc/resolv.conf
+BindReadOnlyPaths=-/etc/resolv.conf
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 919573c..b2d8273 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -12,6 +12,7 @@
#include "missing_capability.h"
#include "resolved-bus.h"
#include "resolved-def.h"
+#include "resolved-dns-stream.h"
#include "resolved-dns-synthesize.h"
#include "resolved-dnssd-bus.h"
#include "resolved-dnssd.h"
@@ -1854,6 +1855,7 @@ static int bus_method_reset_server_features(sd_bus_message *message, void *userd
bus_client_log(message, "server feature reset");
+ (void) dns_stream_disconnect_all(m);
manager_reset_server_features(m);
return sd_bus_reply_method_return(message, NULL);
@@ -2240,9 +2242,15 @@ static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_b
if (b)
return 0;
- log_debug("Coming back from suspend, verifying all RRs...");
+ log_debug("Coming back from suspend, closing all TCP connections...");
+ (void) dns_stream_disconnect_all(m);
+
+ log_debug("Coming back from suspend, resetting all probed server features...");
+ manager_reset_server_features(m);
+ log_debug("Coming back from suspend, verifying all RRs...");
manager_verify_all(m);
+
return 0;
}
diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c
index 3d42b0d..bf023a7 100644
--- a/src/resolve/resolved-dns-answer.c
+++ b/src/resolve/resolved-dns-answer.c
@@ -181,11 +181,23 @@ int dns_answer_add(
exist = ordered_set_get(a->items, &tmp);
if (exist) {
- /* There's already an RR of the same RRset in place! Let's see if the TTLs more or less
- * match. We don't really care if they match precisely, but we do care whether one is 0 and
- * the other is not. See RFC 2181, Section 5.2. */
- if ((rr->ttl == 0) != (exist->rr->ttl == 0))
- return -EINVAL;
+ /* There's already an RR of the same RRset in place! Let's see if the TTLs more or
+ * less match. RFC 2181, Section 5.2 suggests clients should reject RRsets
+ * containing RRs with differing TTLs. We are more tolerant of this situation except
+ * if one RR has a zero TTL and the other a nonzero TTL. In mDNS, zero TTLs are
+ * special, so we must error in that case. */
+ if ((rr->ttl == 0) != (exist->rr->ttl == 0)) {
+ if ((exist->flags | flags) & DNS_ANSWER_REFUSE_TTL_NO_MATCH)
+ return log_debug_errno(
+ SYNTHETIC_ERRNO(EINVAL),
+ "Refusing to merge RRs with zero TTL and non-zero TTL: %s vs. %s",
+ dns_resource_record_to_string(rr),
+ dns_resource_record_to_string(exist->rr));
+
+ log_debug("Merging RRs with zero TTL and non-zero TTL (not RFC 2181/5.2 compliant): %s vs. %s",
+ dns_resource_record_to_string(rr),
+ dns_resource_record_to_string(exist->rr));
+ }
/* Entry already exists, keep the entry with the higher TTL. */
if (rr->ttl > exist->rr->ttl) {
diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h
index 93afea3..068803c 100644
--- a/src/resolve/resolved-dns-answer.h
+++ b/src/resolve/resolved-dns-answer.h
@@ -14,14 +14,15 @@ typedef struct DnsAnswerItem DnsAnswerItem;
* Note that we usually encode the empty DnsAnswer object as a simple NULL. */
typedef enum DnsAnswerFlags {
- DNS_ANSWER_AUTHENTICATED = 1 << 0, /* Item has been authenticated */
- DNS_ANSWER_CACHEABLE = 1 << 1, /* Item is subject to caching */
- DNS_ANSWER_SHARED_OWNER = 1 << 2, /* For mDNS: RRset may be owner by multiple peers */
- DNS_ANSWER_CACHE_FLUSH = 1 << 3, /* For mDNS: sets cache-flush bit in the rrclass of response records */
- DNS_ANSWER_GOODBYE = 1 << 4, /* For mDNS: item is subject to disappear */
- DNS_ANSWER_SECTION_ANSWER = 1 << 5, /* When parsing: RR originates from answer section */
- DNS_ANSWER_SECTION_AUTHORITY = 1 << 6, /* When parsing: RR originates from authority section */
- DNS_ANSWER_SECTION_ADDITIONAL = 1 << 7, /* When parsing: RR originates from additional section */
+ DNS_ANSWER_AUTHENTICATED = 1 << 0, /* Item has been authenticated */
+ DNS_ANSWER_CACHEABLE = 1 << 1, /* Item is subject to caching */
+ DNS_ANSWER_SHARED_OWNER = 1 << 2, /* For mDNS: RRset may be owner by multiple peers */
+ DNS_ANSWER_CACHE_FLUSH = 1 << 3, /* For mDNS: sets cache-flush bit in the rrclass of response records */
+ DNS_ANSWER_GOODBYE = 1 << 4, /* For mDNS: item is subject to disappear */
+ DNS_ANSWER_SECTION_ANSWER = 1 << 5, /* When parsing: RR originates from answer section */
+ DNS_ANSWER_SECTION_AUTHORITY = 1 << 6, /* When parsing: RR originates from authority section */
+ DNS_ANSWER_SECTION_ADDITIONAL = 1 << 7, /* When parsing: RR originates from additional section */
+ DNS_ANSWER_REFUSE_TTL_NO_MATCH = 1 << 8, /* For mDNS; refuse to merge a zero TTL RR with a nonzero TTL RR */
DNS_ANSWER_MASK_SECTIONS = DNS_ANSWER_SECTION_ANSWER|
DNS_ANSWER_SECTION_AUTHORITY|
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index ad0a34e..644f923 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -2365,8 +2365,11 @@ static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) {
} else {
DnsAnswerFlags flags = 0;
- if (p->protocol == DNS_PROTOCOL_MDNS && !cache_flush)
- flags |= DNS_ANSWER_SHARED_OWNER;
+ if (p->protocol == DNS_PROTOCOL_MDNS) {
+ flags |= DNS_ANSWER_REFUSE_TTL_NO_MATCH;
+ if (!cache_flush)
+ flags |= DNS_ANSWER_SHARED_OWNER;
+ }
/* According to RFC 4795, section 2.9. only the RRs from the Answer section shall be
* cached. Hence mark only those RRs as cacheable by default, but not the ones from
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index 7eb6b97..16334c6 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -57,6 +57,21 @@ static void dns_query_candidate_stop(DnsQueryCandidate *c) {
}
}
+static void dns_query_candidate_abandon(DnsQueryCandidate *c) {
+ DnsTransaction *t;
+
+ assert(c);
+
+ /* Abandon all the DnsTransactions attached to this query */
+
+ while ((t = set_steal_first(c->transactions))) {
+ t->wait_for_answer = true;
+ set_remove(t->notify_query_candidates, c);
+ set_remove(t->notify_query_candidates_done, c);
+ dns_transaction_gc(t);
+ }
+}
+
static DnsQueryCandidate* dns_query_candidate_unlink(DnsQueryCandidate *c) {
assert(c);
@@ -354,6 +369,16 @@ static void dns_query_stop(DnsQuery *q) {
dns_query_candidate_stop(c);
}
+static void dns_query_abandon(DnsQuery *q) {
+ assert(q);
+
+ /* Thankfully transactions have their own timeouts */
+ event_source_disable(q->timeout_event_source);
+
+ LIST_FOREACH(candidates_by_query, c, q->candidates)
+ dns_query_candidate_abandon(c);
+}
+
static void dns_query_unlink_candidates(DnsQuery *q) {
assert(q);
@@ -588,7 +613,7 @@ void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
(void) manager_monitor_send(q->manager, q->state, q->answer_rcode, q->answer_errno, q->question_idna, q->question_utf8, q->question_bypass, q->collected_questions, q->answer);
- dns_query_stop(q);
+ dns_query_abandon(q);
if (q->complete)
q->complete(q);
}
diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c
index a4379c1..c449efa 100644
--- a/src/resolve/resolved-dns-rr.c
+++ b/src/resolve/resolved-dns-rr.c
@@ -181,6 +181,23 @@ bool dns_resource_key_is_dnssd_ptr(const DnsResourceKey *key) {
dns_name_endswith(dns_resource_key_name(key), "_udp.local");
}
+bool dns_resource_key_is_dnssd_two_label_ptr(const DnsResourceKey *key) {
+ assert(key);
+
+ /* Check if this is a PTR resource key used in Service Instance
+ * Enumeration as described in RFC6763 § 4.1, excluding selective
+ * service names described in RFC6763 § 7.1. */
+
+ if (key->type != DNS_TYPE_PTR)
+ return false;
+
+ const char *name = dns_resource_key_name(key);
+ if (dns_name_parent(&name) <= 0)
+ return false;
+
+ return dns_name_equal(name, "_tcp.local") || dns_name_equal(name, "_udp.local");
+}
+
int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
int r;
diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h
index d558842..becc1c5 100644
--- a/src/resolve/resolved-dns-rr.h
+++ b/src/resolve/resolved-dns-rr.h
@@ -305,6 +305,7 @@ DnsResourceKey* dns_resource_key_unref(DnsResourceKey *key);
const char* dns_resource_key_name(const DnsResourceKey *key);
bool dns_resource_key_is_address(const DnsResourceKey *key);
bool dns_resource_key_is_dnssd_ptr(const DnsResourceKey *key);
+bool dns_resource_key_is_dnssd_two_label_ptr(const DnsResourceKey *key);
int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b);
int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, const char *search_domain);
int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsResourceKey *cname, const char *search_domain);
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index 4085b01..4780bc2 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -424,7 +424,15 @@ static int dns_scope_socket(
return r;
}
- if (ifindex != 0) {
+ bool addr_is_nonlocal = s->link &&
+ !manager_find_link_address(s->manager, sa.sa.sa_family, sockaddr_in_addr(&sa.sa)) &&
+ in_addr_is_localhost(sa.sa.sa_family, sockaddr_in_addr(&sa.sa)) == 0;
+
+ if (addr_is_nonlocal && ifindex != 0) {
+ /* As a special exception we don't use UNICAST_IF if we notice that the specified IP address
+ * is on the local host. Otherwise, destination addresses on the local host result in
+ * EHOSTUNREACH, since Linux won't send the packets out of the specified interface, but
+ * delivers them directly to the local socket. */
r = socket_set_unicast_if(fd, sa.sa.sa_family, ifindex);
if (r < 0)
return r;
@@ -463,19 +471,13 @@ static int dns_scope_socket(
else {
bool bound = false;
- /* Let's temporarily bind the socket to the specified ifindex. The kernel currently takes
- * only the SO_BINDTODEVICE/SO_BINDTOINDEX ifindex into account when making routing decisions
+ /* Let's temporarily bind the socket to the specified ifindex. Older kernels only take
+ * the SO_BINDTODEVICE/SO_BINDTOINDEX ifindex into account when making routing decisions
* in connect() — and not IP_UNICAST_IF. We don't really want any of the other semantics of
* SO_BINDTODEVICE/SO_BINDTOINDEX, hence we immediately unbind the socket after the fact
* again.
- *
- * As a special exception we don't do this if we notice that the specified IP address is on
- * the local host. SO_BINDTODEVICE in combination with destination addresses on the local
- * host result in EHOSTUNREACH, since Linux won't send the packets out of the specified
- * interface, but delivers them directly to the local socket. */
- if (s->link &&
- !manager_find_link_address(s->manager, sa.sa.sa_family, sockaddr_in_addr(&sa.sa)) &&
- in_addr_is_localhost(sa.sa.sa_family, sockaddr_in_addr(&sa.sa)) == 0) {
+ */
+ if (addr_is_nonlocal) {
r = socket_bind_to_ifindex(fd, ifindex);
if (r < 0)
return r;
@@ -594,6 +596,7 @@ DnsScopeMatch dns_scope_good_domain(
/* This returns the following return values:
*
* DNS_SCOPE_NO → This scope is not suitable for lookups of this domain, at all
+ * DNS_SCOPE_LAST_RESORT→ This scope is not suitable, unless we have no alternative
* DNS_SCOPE_MAYBE → This scope is suitable, but only if nothing else wants it
* DNS_SCOPE_YES_BASE+n → This scope is suitable, and 'n' suffix labels match
*
@@ -739,7 +742,7 @@ DnsScopeMatch dns_scope_good_domain(
if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
(s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0))
- return DNS_SCOPE_MAYBE;
+ return DNS_SCOPE_LAST_RESORT;
if ((dns_name_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */
dns_name_equal(domain, "local") == 0 && /* but not the single-label "local" name itself */
@@ -762,7 +765,7 @@ DnsScopeMatch dns_scope_good_domain(
if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
(s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0))
- return DNS_SCOPE_MAYBE;
+ return DNS_SCOPE_LAST_RESORT;
if ((dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */
!is_gateway_hostname(domain) && /* don't resolve "_gateway" with LLMNR, let local synthesizing logic handle that */
@@ -1441,9 +1444,10 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) {
continue;
}
- /* Collect service types for _services._dns-sd._udp.local RRs in a set */
+ /* Collect service types for _services._dns-sd._udp.local RRs in a set. Only two-label names
+ * (not selective names) are considered according to RFC6763 § 9. */
if (!scope->announced &&
- dns_resource_key_is_dnssd_ptr(z->rr->key)) {
+ dns_resource_key_is_dnssd_two_label_ptr(z->rr->key)) {
if (!set_contains(types, dns_resource_key_name(z->rr->key))) {
r = set_ensure_put(&types, &dns_name_hash_ops, dns_resource_key_name(z->rr->key));
if (r < 0)
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index 1f9d22b..ffa81b2 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -18,6 +18,7 @@ typedef struct DnsScope DnsScope;
typedef enum DnsScopeMatch {
DNS_SCOPE_NO,
+ DNS_SCOPE_LAST_RESORT,
DNS_SCOPE_MAYBE,
DNS_SCOPE_YES_BASE, /* Add the number of matching labels to this */
DNS_SCOPE_YES_END = DNS_SCOPE_YES_BASE + DNS_N_LABELS_MAX,
diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c
index 95fbb56..dd3da7b 100644
--- a/src/resolve/resolved-dns-stream.c
+++ b/src/resolve/resolved-dns-stream.c
@@ -195,7 +195,7 @@ static int dns_stream_identify(DnsStream *s) {
/* Make sure all packets for this connection are sent on the same interface */
r = socket_set_unicast_if(s->fd, s->local.sa.sa_family, s->ifindex);
if (r < 0)
- log_debug_errno(errno, "Failed to invoke IP_UNICAST_IF/IPV6_UNICAST_IF: %m");
+ log_debug_errno(r, "Failed to invoke IP_UNICAST_IF/IPV6_UNICAST_IF: %m");
}
s->identified = true;
@@ -454,7 +454,7 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use
if (progressed && s->timeout_event_source) {
r = sd_event_source_set_time_relative(s->timeout_event_source, DNS_STREAM_ESTABLISHED_TIMEOUT_USEC);
if (r < 0)
- log_warning_errno(errno, "Couldn't restart TCP connection timeout, ignoring: %m");
+ log_warning_errno(r, "Couldn't restart TCP connection timeout, ignoring: %m");
}
return 0;
@@ -593,3 +593,44 @@ void dns_stream_detach(DnsStream *s) {
dns_server_unref_stream(s->server);
}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ dns_stream_hash_ops,
+ void,
+ trivial_hash_func,
+ trivial_compare_func,
+ dns_stream_unref);
+
+int dns_stream_disconnect_all(Manager *m) {
+ _cleanup_(set_freep) Set *closed = NULL;
+ int r;
+
+ assert(m);
+
+ /* Terminates all TCP connections (called after system suspend for example, to speed up recovery) */
+
+ log_info("Closing all remaining TCP connections.");
+
+ bool restart;
+ do {
+ restart = false;
+
+ LIST_FOREACH(streams, s, m->dns_streams) {
+ r = set_ensure_put(&closed, &dns_stream_hash_ops, s);
+ if (r < 0)
+ return log_oom();
+ if (r > 0) {
+ /* Haven't seen this one before. Close it. */
+ dns_stream_ref(s);
+ (void) dns_stream_complete(s, ECONNRESET);
+
+ /* This might have a ripple effect, let's hence no look at the list further,
+ * but scan from the beginning again */
+ restart = true;
+ break;
+ }
+ }
+ } while (restart);
+
+ return 0;
+}
diff --git a/src/resolve/resolved-dns-stream.h b/src/resolve/resolved-dns-stream.h
index ba4a59e..912b9bf 100644
--- a/src/resolve/resolved-dns-stream.h
+++ b/src/resolve/resolved-dns-stream.h
@@ -126,3 +126,4 @@ static inline bool DNS_STREAM_QUEUED(DnsStream *s) {
}
void dns_stream_detach(DnsStream *s);
+int dns_stream_disconnect_all(Manager *m);
diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c
index 11e24b8..4e903f1 100644
--- a/src/resolve/resolved-dns-stub.c
+++ b/src/resolve/resolved-dns-stub.c
@@ -837,12 +837,20 @@ static void dns_stub_query_complete(DnsQuery *query) {
break;
case DNS_TRANSACTION_NO_SERVERS:
+ /* We're not configured to give answers for this question. Refuse it. */
+ (void) dns_stub_send_reply(q, DNS_RCODE_REFUSED);
+ break;
+
+ case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
+ /* This RR Type is not implemented */
+ (void) dns_stub_send_reply(q, DNS_RCODE_NOTIMP);
+ break;
+
case DNS_TRANSACTION_INVALID_REPLY:
case DNS_TRANSACTION_ERRNO:
case DNS_TRANSACTION_ABORTED:
case DNS_TRANSACTION_DNSSEC_FAILED:
case DNS_TRANSACTION_NO_TRUST_ANCHOR:
- case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
case DNS_TRANSACTION_NETWORK_DOWN:
case DNS_TRANSACTION_NO_SOURCE:
case DNS_TRANSACTION_STUB_LOOP:
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 21326fa..e23ec4c 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -175,6 +175,9 @@ DnsTransaction* dns_transaction_gc(DnsTransaction *t) {
if (t->block_gc > 0)
return t;
+ if (t->wait_for_answer && IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_VALIDATING))
+ return t;
+
if (set_isempty(t->notify_query_candidates) &&
set_isempty(t->notify_query_candidates_done) &&
set_isempty(t->notify_zone_items) &&
@@ -2238,7 +2241,7 @@ static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResource
return 1;
}
-static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *key) {
+static int dns_transaction_request_dnssec_rr_full(DnsTransaction *t, DnsResourceKey *key, DnsTransaction **ret) {
_cleanup_(dns_answer_unrefp) DnsAnswer *a = NULL;
DnsTransaction *aux;
int r;
@@ -2255,13 +2258,18 @@ static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *
if (r < 0)
return r;
+ if (ret)
+ *ret = NULL;
return 0;
}
/* This didn't work, ask for it via the network/cache then. */
r = dns_transaction_add_dnssec_transaction(t, key, &aux);
- if (r == -ELOOP) /* This would result in a cyclic dependency */
+ if (r == -ELOOP) { /* This would result in a cyclic dependency */
+ if (ret)
+ *ret = NULL;
return 0;
+ }
if (r < 0)
return r;
@@ -2269,11 +2277,19 @@ static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *
r = dns_transaction_go(aux);
if (r < 0)
return r;
+ if (ret)
+ *ret = aux;
}
return 1;
}
+static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *key) {
+ assert(t);
+ assert(key);
+ return dns_transaction_request_dnssec_rr_full(t, key, NULL);
+}
+
static int dns_transaction_negative_trust_anchor_lookup(DnsTransaction *t, const char *name) {
int r;
@@ -2374,6 +2390,8 @@ static bool dns_transaction_dnssec_supported_full(DnsTransaction *t) {
int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
DnsResourceRecord *rr;
+ /* Have we already requested a record that would be sufficient to validate an insecure delegation? */
+ bool chased_insecure = false;
int r;
assert(t);
@@ -2386,11 +2404,11 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
* - For RRSIG we get the matching DNSKEY
* - For DNSKEY we get the matching DS
* - For unsigned SOA/NS we get the matching DS
- * - For unsigned CNAME/DNAME/DS we get the parent SOA RR
- * - For other unsigned RRs we get the matching SOA RR
+ * - For unsigned CNAME/DNAME/DS we get the parent DS RR
+ * - For other unsigned RRs we get the matching DS RR
* - For SOA/NS queries with no matching response RR, and no NSEC/NSEC3, the DS RR
- * - For DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR
- * - For other queries with no matching response RRs, and no NSEC/NSEC3, the SOA RR
+ * - For DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's DS RR
+ * - For other queries with no matching response RRs, and no NSEC/NSEC3, the DS RR
*/
if (FLAGS_SET(t->query_flags, SD_RESOLVED_NO_VALIDATE) || t->scope->dnssec_mode == DNSSEC_NO)
@@ -2417,6 +2435,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
case DNS_TYPE_RRSIG: {
/* For each RRSIG we request the matching DNSKEY */
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *dnskey = NULL;
+ DnsTransaction *aux;
/* If this RRSIG is about a DNSKEY RR and the
* signer is the same as the owner, then we
@@ -2453,9 +2472,22 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
log_debug("Requesting DNSKEY to validate transaction %" PRIu16" (%s, RRSIG with key tag: %" PRIu16 ").",
t->id, dns_resource_key_name(rr->key), rr->rrsig.key_tag);
- r = dns_transaction_request_dnssec_rr(t, dnskey);
+ r = dns_transaction_request_dnssec_rr_full(t, dnskey, &aux);
if (r < 0)
return r;
+
+ /* If we are requesting a DNSKEY, we can anticiapte that we will want the matching DS
+ * in the near future. Let's request it in advance so we don't have to wait in the
+ * common case. */
+ if (aux) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *ds =
+ dns_resource_key_new(rr->key->class, DNS_TYPE_DS, dns_resource_key_name(dnskey));
+ if (!ds)
+ return -ENOMEM;
+ r = dns_transaction_request_dnssec_rr(t, ds);
+ if (r < 0)
+ return r;
+ }
break;
}
@@ -2522,6 +2554,10 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
return r;
if (r == 0)
continue;
+
+ /* If we were looking for the DS RR, don't request it again. */
+ if (dns_transaction_key(t)->type == DNS_TYPE_DS)
+ continue;
}
r = dnssec_has_rrsig(t->answer, rr->key);
@@ -2530,6 +2566,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
if (r > 0)
continue;
+ chased_insecure = true;
ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, dns_resource_key_name(rr->key));
if (!ds)
return -ENOMEM;
@@ -2546,11 +2583,11 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
case DNS_TYPE_DS:
case DNS_TYPE_CNAME:
case DNS_TYPE_DNAME: {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *ds = NULL;
const char *name;
/* CNAMEs and DNAMEs cannot be located at a
- * zone apex, hence ask for the parent SOA for
+ * zone apex, hence ask for the parent DS for
* unsigned CNAME/DNAME RRs, maybe that's the
* apex. But do all that only if this is
* actually a response to our original
@@ -2584,25 +2621,40 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
if (r == 0)
continue;
- soa = dns_resource_key_new(rr->key->class, DNS_TYPE_SOA, name);
- if (!soa)
+ ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, name);
+ if (!ds)
return -ENOMEM;
- log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned CNAME/DNAME/DS RRset).",
+ log_debug("Requesting parent DS to validate transaction %" PRIu16 " (%s, unsigned CNAME/DNAME/DS RRset).",
t->id, dns_resource_key_name(rr->key));
- r = dns_transaction_request_dnssec_rr(t, soa);
+ r = dns_transaction_request_dnssec_rr(t, ds);
if (r < 0)
return r;
+ if (t->scope->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE && dns_name_is_root(name)) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
+ /* We made it all the way to the root zone. If we are in allow-downgrade
+ * mode, we need to make at least one request that we can be certain should
+ * have been signed, to test for servers that are not dnssec aware. */
+ soa = dns_resource_key_new(rr->key->class, DNS_TYPE_SOA, name);
+ if (!soa)
+ return -ENOMEM;
+
+ log_debug("Requesting root zone SOA to probe dnssec support.");
+ r = dns_transaction_request_dnssec_rr(t, soa);
+ if (r < 0)
+ return r;
+ }
+
break;
}
default: {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *ds = NULL;
/* For other unsigned RRsets (including
* NSEC/NSEC3!), look for proof the zone is
- * unsigned, by requesting the SOA RR of the
+ * unsigned, by requesting the DS RR of the
* zone. However, do so only if they are
* directly relevant to our original
* question. */
@@ -2619,13 +2671,13 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
if (r > 0)
continue;
- soa = dns_resource_key_new(rr->key->class, DNS_TYPE_SOA, dns_resource_key_name(rr->key));
- if (!soa)
+ ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, dns_resource_key_name(rr->key));
+ if (!ds)
return -ENOMEM;
- log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned non-SOA/NS RRset <%s>).",
+ log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned non-SOA/NS RRset <%s>).",
t->id, dns_resource_key_name(rr->key), dns_resource_record_to_string(rr));
- r = dns_transaction_request_dnssec_rr(t, soa);
+ r = dns_transaction_request_dnssec_rr(t, ds);
if (r < 0)
return r;
break;
@@ -2640,49 +2692,38 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
if (r < 0)
return r;
if (r > 0) {
- const char *name, *signed_status;
- uint16_t type = 0;
-
- name = dns_resource_key_name(dns_transaction_key(t));
- signed_status = dns_answer_contains_nsec_or_nsec3(t->answer) ? "signed" : "unsigned";
+ const char *name = dns_resource_key_name(dns_transaction_key(t));
+ bool was_signed = dns_answer_contains_nsec_or_nsec3(t->answer);
- /* If this was a SOA or NS request, then check if there's a DS RR for the same domain. Note that this
- * could also be used as indication that we are not at a zone apex, but in real world setups there are
- * too many broken DNS servers (Hello, incapdns.net!) where non-terminal zones return NXDOMAIN even
- * though they have further children. If this was a DS request, then it's signed when the parent zone
- * is signed, hence ask the parent SOA in that case. If this was any other RR then ask for the SOA RR,
- * to see if that is signed. */
-
- if (dns_transaction_key(t)->type == DNS_TYPE_DS) {
- r = dns_name_parent(&name);
- if (r > 0) {
- type = DNS_TYPE_SOA;
- log_debug("Requesting parent SOA (%s %s) to validate transaction %" PRIu16 " (%s, %s empty DS response).",
- special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), name, t->id,
- dns_resource_key_name(dns_transaction_key(t)), signed_status);
- } else
+ /* If the response is empty, seek the DS for this name, just in case we're at a zone cut
+ * already, unless we just requested the DS, in which case we have to ask the parent to make
+ * progress.
+ *
+ * If this was an SOA or NS request, we could also skip to the parent, but in real world
+ * setups there are too many broken DNS servers (Hello, incapdns.net!) where non-terminal
+ * zones return NXDOMAIN even though they have further children. */
+
+ if (chased_insecure || was_signed)
+ /* In this case we already reqeusted what we need above. */
+ name = NULL;
+ else if (dns_transaction_key(t)->type == DNS_TYPE_DS)
+ /* If the DS response is empty, we'll walk up the dns labels requesting DS until we
+ * find a referral to the SOA or hit it anyway and get a positive DS response. */
+ if (dns_name_parent(&name) <= 0)
name = NULL;
- } else if (IN_SET(dns_transaction_key(t)->type, DNS_TYPE_SOA, DNS_TYPE_NS)) {
-
- type = DNS_TYPE_DS;
- log_debug("Requesting DS (%s %s) to validate transaction %" PRIu16 " (%s, %s empty SOA/NS response).",
- special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), name, t->id, name, signed_status);
-
- } else {
- type = DNS_TYPE_SOA;
- log_debug("Requesting SOA (%s %s) to validate transaction %" PRIu16 " (%s, %s empty non-SOA/NS/DS response).",
- special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), name, t->id, name, signed_status);
- }
-
if (name) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *ds = NULL;
+
+ log_debug("Requesting DS (%s %s) to validate transaction %" PRIu16 " (%s empty response).",
+ special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), name, t->id,
+ dns_resource_key_name(dns_transaction_key(t)));
- soa = dns_resource_key_new(dns_transaction_key(t)->class, type, name);
- if (!soa)
+ ds = dns_resource_key_new(dns_transaction_key(t)->class, DNS_TYPE_DS, name);
+ if (!ds)
return -ENOMEM;
- r = dns_transaction_request_dnssec_rr(t, soa);
+ r = dns_transaction_request_dnssec_rr(t, ds);
if (r < 0)
return r;
}
@@ -2762,7 +2803,6 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
DnsTransaction *dt;
/* For SOA or NS RRs we look for a matching DS transaction */
-
SET_FOREACH(dt, t->dnssec_transactions) {
if (dns_transaction_key(dt)->class != rr->key->class)
@@ -2770,7 +2810,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
if (dns_transaction_key(dt)->type != DNS_TYPE_DS)
continue;
- r = dns_name_equal(dns_resource_key_name(dns_transaction_key(dt)), dns_resource_key_name(rr->key));
+ r = dns_name_endswith(dns_resource_key_name(rr->key), dns_resource_key_name(dns_transaction_key(dt)));
if (r < 0)
return r;
if (r == 0)
@@ -2799,16 +2839,16 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
DnsTransaction *dt;
/*
- * CNAME/DNAME RRs cannot be located at a zone apex, hence look directly for the parent SOA.
+ * CNAME/DNAME RRs cannot be located at a zone apex, hence look directly for the parent DS.
*
- * DS RRs are signed if the parent is signed, hence also look at the parent SOA
+ * DS RRs are signed if the parent is signed, hence also look at the parent DS
*/
SET_FOREACH(dt, t->dnssec_transactions) {
if (dns_transaction_key(dt)->class != rr->key->class)
continue;
- if (dns_transaction_key(dt)->type != DNS_TYPE_SOA)
+ if (dns_transaction_key(dt)->type != DNS_TYPE_DS)
continue;
if (!parent) {
@@ -2826,13 +2866,18 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
}
}
- r = dns_name_equal(dns_resource_key_name(dns_transaction_key(dt)), parent);
+ r = dns_name_endswith(parent, dns_resource_key_name(dns_transaction_key(dt)));
if (r < 0)
return r;
if (r == 0)
continue;
- return FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
+ if (!FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED))
+ return false;
+
+ /* We expect this to be signed when the DS record exists, and don't expect it to be
+ * signed when the DS record is proven not to exist. */
+ return dns_answer_match_key(dt->answer, dns_transaction_key(dt), NULL);
}
return true;
@@ -2841,25 +2886,26 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
default: {
DnsTransaction *dt;
- /* Any other kind of RR (including DNSKEY/NSEC/NSEC3). Let's see if our SOA lookup was authenticated */
+ /* Any other kind of RR (including DNSKEY/NSEC/NSEC3). Let's see if our DS lookup was authenticated */
SET_FOREACH(dt, t->dnssec_transactions) {
-
if (dns_transaction_key(dt)->class != rr->key->class)
continue;
- if (dns_transaction_key(dt)->type != DNS_TYPE_SOA)
+ if (dns_transaction_key(dt)->type != DNS_TYPE_DS)
continue;
- r = dns_name_equal(dns_resource_key_name(dns_transaction_key(dt)), dns_resource_key_name(rr->key));
+ r = dns_name_endswith(dns_resource_key_name(rr->key), dns_resource_key_name(dns_transaction_key(dt)));
if (r < 0)
return r;
if (r == 0)
continue;
- /* We found the transaction that was supposed to find the SOA RR for us. It was
- * successful, but found no RR for us. This means we are not at a zone cut. In this
- * case, we require authentication if the SOA lookup was authenticated too. */
- return FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
+ if (!FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED))
+ return false;
+
+ /* We expect this to be signed when the DS record exists, and don't expect it to be
+ * signed when the DS record is proven not to exist. */
+ return dns_answer_match_key(dt->answer, dns_transaction_key(dt), NULL);
}
return true;
@@ -2929,7 +2975,6 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
char key_str[DNS_RESOURCE_KEY_STRING_MAX];
DnsTransaction *dt;
const char *name;
- uint16_t type = 0;
int r;
assert(t);
@@ -2964,43 +3009,37 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
name = dns_resource_key_name(dns_transaction_key(t));
- if (dns_transaction_key(t)->type == DNS_TYPE_DS) {
-
- /* We got a negative reply for this DS lookup? DS RRs are signed when their parent zone is signed,
- * hence check the parent SOA in this case. */
-
+ if (IN_SET(dns_transaction_key(t)->type, DNS_TYPE_DS, DNS_TYPE_CNAME, DNS_TYPE_DNAME)) {
+ /* We got a negative reply for this DS/CNAME/DNAME lookup? Check the parent in this case to
+ * see if this answer should have been signed. */
r = dns_name_parent(&name);
if (r < 0)
return r;
if (r == 0)
return true;
+ }
- type = DNS_TYPE_SOA;
-
- } else if (IN_SET(dns_transaction_key(t)->type, DNS_TYPE_SOA, DNS_TYPE_NS))
- /* We got a negative reply for this SOA/NS lookup? If so, check if there's a DS RR for this */
- type = DNS_TYPE_DS;
- else
- /* For all other negative replies, check for the SOA lookup */
- type = DNS_TYPE_SOA;
-
- /* For all other RRs we check the SOA on the same level to see
+ /* For all other RRs we check the DS on the same level to see
* if it's signed. */
SET_FOREACH(dt, t->dnssec_transactions) {
-
if (dns_transaction_key(dt)->class != dns_transaction_key(t)->class)
continue;
- if (dns_transaction_key(dt)->type != type)
+ if (dns_transaction_key(dt)->type != DNS_TYPE_DS)
continue;
- r = dns_name_equal(dns_resource_key_name(dns_transaction_key(dt)), name);
+ r = dns_name_endswith(name, dns_resource_key_name(dns_transaction_key(dt)));
if (r < 0)
return r;
if (r == 0)
continue;
- return FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
+ if (!FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED))
+ return false;
+
+ /* We expect this to be signed when the DS record exists, and don't expect it to be signed
+ * when the DS record is proven not to exist. */
+ return dns_answer_match_key(dt->answer, dns_transaction_key(dt), NULL);
}
/* If in doubt, require NSEC/NSEC3 */
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index 5a0e357..2710e1b 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -135,6 +135,11 @@ struct DnsTransaction {
unsigned block_gc;
+ /* Set when we're willing to let this transaction live beyond it's usefulness for the original query,
+ * for caching purposes. This blocks gc while there is still a chance we might still receive an
+ * answer. */
+ bool wait_for_answer;
+
LIST_FIELDS(DnsTransaction, transactions_by_scope);
LIST_FIELDS(DnsTransaction, transactions_by_stream);
LIST_FIELDS(DnsTransaction, transactions_by_key);
diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c
index 69a484d..6409615 100644
--- a/src/resolve/resolved-dns-trust-anchor.c
+++ b/src/resolve/resolved-dns-trust-anchor.c
@@ -163,7 +163,15 @@ static int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor *d) {
"private\0"
/* Defined by RFC 8375. The most official choice. */
- "home.arpa\0";
+ "home.arpa\0"
+
+ /* RFC 8880 says because the 'ipv4only.arpa' zone has to
+ * be an insecure delegation, DNSSEC cannot be used to
+ * protect these answers from tampering by malicious
+ * devices on the path */
+ "ipv4only.arpa\0"
+ "170.0.0.192.in-addr.arpa\0"
+ "171.0.0.192.in-addr.arpa\0";
const char *name;
int r;
diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c
index f5f1560..4fd47fc 100644
--- a/src/resolve/resolved-mdns.c
+++ b/src/resolve/resolved-mdns.c
@@ -315,7 +315,7 @@ static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) {
}
DNS_ANSWER_FOREACH_ITEM(item, answer) {
- DnsAnswerFlags flags = item->flags;
+ DnsAnswerFlags flags = item->flags | DNS_ANSWER_REFUSE_TTL_NO_MATCH;
/* The cache-flush bit must not be set in legacy unicast responses.
* See section 6.7 of RFC 6762. */
if (legacy_query)
diff --git a/src/rpm/macros.systemd.in b/src/rpm/macros.systemd.in
index 8880078..238e732 100644
--- a/src/rpm/macros.systemd.in
+++ b/src/rpm/macros.systemd.in
@@ -13,6 +13,7 @@
%_udevhwdbdir {{UDEV_HWDB_DIR}}
%_udevrulesdir {{UDEV_RULES_DIR}}
%_journalcatalogdir {{SYSTEMD_CATALOG_DIR}}
+%_kernel_install_dir {{KERNEL_INSTALL_DIR}}
%_binfmtdir {{BINFMT_DIR}}
%_sysctldir {{SYSCTL_DIR}}
%_sysusersdir {{SYSUSERS_DIR}}
diff --git a/src/run/run.c b/src/run/run.c
index 8e4b0ec..c792807 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -772,11 +772,17 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
}
if (pty_path) {
+ _cleanup_close_ int pty_slave = -EBADF;
+
+ pty_slave = open_terminal(pty_path, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (pty_slave < 0)
+ return pty_slave;
+
r = sd_bus_message_append(m,
"(sv)(sv)(sv)(sv)",
- "StandardInput", "s", "tty",
- "StandardOutput", "s", "tty",
- "StandardError", "s", "tty",
+ "StandardInputFileDescriptor", "h", pty_slave,
+ "StandardOutputFileDescriptor", "h", pty_slave,
+ "StandardErrorFileDescriptor", "h", pty_slave,
"TTYPath", "s", pty_path);
if (r < 0)
return bus_log_create_error(r);
@@ -1027,7 +1033,7 @@ static void run_context_check_done(RunContext *c) {
else
done = true;
- if (c->forward && done) /* If the service is gone, it's time to drain the output */
+ if (c->forward && !pty_forward_is_done(c->forward) && done) /* If the service is gone, it's time to drain the output */
done = pty_forward_drain(c->forward);
if (done)
@@ -1095,11 +1101,18 @@ static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error
}
static int pty_forward_handler(PTYForward *f, int rcode, void *userdata) {
- RunContext *c = userdata;
+ RunContext *c = ASSERT_PTR(userdata);
assert(f);
- if (rcode < 0) {
+ if (rcode == -ECANCELED) {
+ log_debug_errno(rcode, "PTY forwarder disconnected.");
+ if (!arg_wait)
+ return sd_event_exit(c->event, EXIT_SUCCESS);
+
+ /* If --wait is specified, we'll only exit the pty forwarding, but will continue to wait
+ * for the service to end. If the user hits ^C we'll exit too. */
+ } else if (rcode < 0) {
sd_event_exit(c->event, EXIT_FAILURE);
return log_error_errno(rcode, "Error on PTY forwarding logic: %m");
}
diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c
index 47a766e..950ed1e 100644
--- a/src/shared/base-filesystem.c
+++ b/src/shared/base-filesystem.c
@@ -107,12 +107,12 @@ static const BaseFilesystem table[] = {
# else
# error "Unknown RISC-V ABI"
# endif
-#elif defined(__s390__)
- /* s390-linux-gnu */
#elif defined(__s390x__)
{ "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0"
"usr/lib64\0", "ld-lsb-s390x.so.3" },
# define KNOW_LIB64_DIRS 1
+#elif defined(__s390__)
+ /* s390-linux-gnu */
#elif defined(__sparc__)
#endif
/* gcc doesn't allow pragma to be used within constructs, hence log about this separately below */
diff --git a/src/shared/blockdev-util.c b/src/shared/blockdev-util.c
index 72fad16..3be617b 100644
--- a/src/shared/blockdev-util.c
+++ b/src/shared/blockdev-util.c
@@ -11,6 +11,7 @@
#include "alloc-util.h"
#include "blockdev-util.h"
#include "btrfs-util.h"
+#include "device-private.h"
#include "device-util.h"
#include "devnum-util.h"
#include "dirent-util.h"
@@ -397,24 +398,74 @@ int lock_whole_block_device(dev_t devt, int operation) {
}
int blockdev_partscan_enabled(int fd) {
- _cleanup_free_ char *p = NULL, *buf = NULL;
- unsigned long long ull;
- struct stat st;
- int r;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ unsigned capability;
+ int r, ext_range;
+
+ /* Checks if partition scanning is correctly enabled on the block device.
+ *
+ * The 'GENHD_FL_NO_PART_SCAN' flag was introduced by
+ * https://github.com/torvalds/linux/commit/d27769ec3df1a8de9ca450d2dcd72d1ab259ba32 (v3.2).
+ * But at that time, the flag is also effectively implied when 'minors' element of 'struct gendisk'
+ * is 1, which can be check with 'ext_range' sysfs attribute. Explicit flag ('GENHD_FL_NO_PART_SCAN')
+ * can be obtained from 'capability' sysattr.
+ *
+ * With https://github.com/torvalds/linux/commit/46e7eac647b34ed4106a8262f8bedbb90801fadd (v5.17),
+ * the flag is renamed to GENHD_FL_NO_PART.
+ *
+ * With https://github.com/torvalds/linux/commit/1ebe2e5f9d68e94c524aba876f27b945669a7879 (v5.17),
+ * we can check the flag from 'ext_range' sysfs attribute directly.
+ *
+ * With https://github.com/torvalds/linux/commit/430cc5d3ab4d0ba0bd011cfbb0035e46ba92920c (v5.17),
+ * the value of GENHD_FL_NO_PART is changed from 0x0200 to 0x0004. 💣💣💣
+ * Note, the new value was used by the GENHD_FL_MEDIA_CHANGE_NOTIFY flag, which was introduced by
+ * 86ce18d7b7925bfd6b64c061828ca2a857ee83b8 (v2.6.22), and removed by
+ * 9243c6f3e012a92dd900d97ef45efaf8a8edc448 (v5.7). If we believe the commit message of
+ * e81cd5a983bb35dabd38ee472cf3fea1c63e0f23, the flag was never used. So, fortunately, we can use
+ * both the new and old values safely.
+ *
+ * With https://github.com/torvalds/linux/commit/b9684a71fca793213378dd410cd11675d973eaa1 (v5.19),
+ * another flag GD_SUPPRESS_PART_SCAN is introduced for loopback block device, and partition scanning
+ * is done only when both GENHD_FL_NO_PART and GD_SUPPRESS_PART_SCAN are not set. Before the commit,
+ * LO_FLAGS_PARTSCAN flag was directly tied with GENHD_FL_NO_PART. But with this change now it is
+ * tied with GD_SUPPRESS_PART_SCAN. So, LO_FLAGS_PARTSCAN cannot be obtained from 'ext_range'
+ * sysattr, which corresponds to GENHD_FL_NO_PART, and we need to read 'loop/partscan'. 💣💣💣
+ *
+ * With https://github.com/torvalds/linux/commit/73a166d9749230d598320fdae3b687cdc0e2e205 (v6.3),
+ * the GD_SUPPRESS_PART_SCAN flag is also introduced for userspace block device (ublk). Though, not
+ * sure if we should support the device...
+ *
+ * With https://github.com/torvalds/linux/commit/e81cd5a983bb35dabd38ee472cf3fea1c63e0f23 (v6.3),
+ * the 'capability' sysfs attribute is deprecated, hence we cannot check flags from it. 💣💣💣
+ *
+ * With https://github.com/torvalds/linux/commit/a4217c6740dc64a3eb6815868a9260825e8c68c6 (v6.10,
+ * backported to v6.9), the partscan status is directly exposed as 'partscan' sysattr.
+ *
+ * To support both old and new kernels, we need to do the following:
+ * 1) check 'partscan' sysfs attribute where the information is made directly available,
+ * 2) check 'loop/partscan' sysfs attribute for loopback block devices, and if '0' we can conclude
+ * partition scanning is disabled,
+ * 3) check 'ext_range' sysfs attribute, and if '1' we can conclude partition scanning is disabled,
+ * 4) otherwise check 'capability' sysfs attribute for ancient version. */
- /* Checks if partition scanning is correctly enabled on the block device */
+ assert(fd >= 0);
- if (fstat(fd, &st) < 0)
- return -errno;
+ r = block_device_new_from_fd(fd, 0, &dev);
+ if (r < 0)
+ return r;
- if (!S_ISBLK(st.st_mode))
- return -ENOTBLK;
+ /* For v6.10 or newer. */
+ r = device_get_sysattr_bool(dev, "partscan");
+ if (r != -ENOENT)
+ return r;
- if (asprintf(&p, "/sys/dev/block/%u:%u/capability", major(st.st_rdev), minor(st.st_rdev)) < 0)
- return -ENOMEM;
+ /* For loopback block device, especially for v5.19 or newer. Even if this is enabled, we also need to
+ * check GENHD_FL_NO_PART flag through 'ext_range' and 'capability' sysfs attributes below. */
+ if (device_get_sysattr_bool(dev, "loop/partscan") == 0)
+ return false;
- r = read_one_line_file(p, &buf);
- if (r == -ENOENT) /* If the capability file doesn't exist then we are most likely looking at a
+ r = device_get_sysattr_int(dev, "ext_range", &ext_range);
+ if (r == -ENOENT) /* If the ext_range file doesn't exist then we are most likely looking at a
* partition block device, not the whole block device. And that means we have no
* partition scanning on for it (we do for its parent, but not for the partition
* itself). */
@@ -422,15 +473,24 @@ int blockdev_partscan_enabled(int fd) {
if (r < 0)
return r;
- r = safe_atollu_full(buf, 16, &ull);
+ if (ext_range <= 1) /* The valus should be always positive, but the kernel uses '%d' for the
+ * attribute. Let's gracefully handle zero or negative. */
+ return false;
+
+ r = device_get_sysattr_unsigned_full(dev, "capability", 16, &capability);
+ if (r == -ENOENT)
+ return false;
if (r < 0)
return r;
-#ifndef GENHD_FL_NO_PART_SCAN
-#define GENHD_FL_NO_PART_SCAN (0x0200)
-#endif
+#define GENHD_FL_NO_PART_OLD 0x0200
+#define GENHD_FL_NO_PART_NEW 0x0004
+ /* If one of the NO_PART flags is set, part scanning is definitely off. */
+ if ((capability & (GENHD_FL_NO_PART_OLD | GENHD_FL_NO_PART_NEW)) != 0)
+ return false;
- return !FLAGS_SET(ull, GENHD_FL_NO_PART_SCAN);
+ /* Otherwise, assume part scanning is on, we have no further checks available. Assume the best. */
+ return true;
}
static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) {
diff --git a/src/shared/bpf-dlopen.c b/src/shared/bpf-dlopen.c
index 2556053..808f8c0 100644
--- a/src/shared/bpf-dlopen.c
+++ b/src/shared/bpf-dlopen.c
@@ -60,11 +60,15 @@ int dlopen_bpf(void) {
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"neither libbpf.so.1 nor libbpf.so.0 are installed: %s", dlerror());
+ log_debug("Loaded 'libbpf.so.0' via dlopen()");
+
/* symbols deprecated in 1.0 we use as compat */
r = dlsym_many_or_warn(dl, LOG_DEBUG,
DLSYM_ARG(bpf_create_map),
DLSYM_ARG(bpf_probe_prog_type));
} else {
+ log_debug("Loaded 'libbpf.so.1' via dlopen()");
+
/* symbols available from 0.7.0 */
r = dlsym_many_or_warn(dl, LOG_DEBUG,
DLSYM_ARG(bpf_map_create),
diff --git a/src/shared/clean-ipc.c b/src/shared/clean-ipc.c
index bbb343f..1e90cc2 100644
--- a/src/shared/clean-ipc.c
+++ b/src/shared/clean-ipc.c
@@ -58,7 +58,7 @@ static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid, bool rm) {
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
- return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
+ return log_warning_errno(r, "Failed to read /proc/sysvipc/shm: %m");
if (r == 0)
break;
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index 327dc38..55301af 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -155,7 +155,11 @@ static int next_assignment(
/* Warn about unknown non-extension fields. */
if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(lvalue, "X-"))
log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Unknown key name '%s' in section '%s', ignoring.", lvalue, section);
+ "Unknown key '%s'%s%s%s, ignoring.",
+ lvalue,
+ section ? " in section [" : "",
+ strempty(section),
+ section ? "]" : "");
return 0;
}
diff --git a/src/shared/copy.c b/src/shared/copy.c
index 9b78b9e..64c61e1 100644
--- a/src/shared/copy.c
+++ b/src/shared/copy.c
@@ -304,7 +304,7 @@ int copy_bytes_full(
if (try_cfr) {
n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u);
if (n < 0) {
- if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF))
+ if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF, -EOPNOTSUPP))
return n;
try_cfr = false;
diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c
index 0350dd1..11729d0 100644
--- a/src/shared/discover-image.c
+++ b/src/shared/discover-image.c
@@ -202,6 +202,44 @@ static int extract_pretty(const char *path, const char *suffix, char **ret) {
return 0;
}
+static int image_update_quota(Image *i, int fd) {
+ _cleanup_close_ int fd_close = -EBADF;
+ int r;
+
+ assert(i);
+
+ if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
+ return -EROFS;
+
+ if (i->type != IMAGE_SUBVOLUME)
+ return -EOPNOTSUPP;
+
+ if (fd < 0) {
+ fd_close = open(i->path, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
+ if (fd_close < 0)
+ return -errno;
+ fd = fd_close;
+ }
+
+ r = btrfs_quota_scan_ongoing(fd);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 0;
+
+ BtrfsQuotaInfo quota;
+ r = btrfs_subvol_get_subtree_quota_fd(fd, 0, &quota);
+ if (r < 0)
+ return r;
+
+ i->usage = quota.referenced;
+ i->usage_exclusive = quota.exclusive;
+ i->limit = quota.referenced_max;
+ i->limit_exclusive = quota.exclusive_max;
+
+ return 1;
+}
+
static int image_make(
const char *pretty,
int dfd,
@@ -288,19 +326,7 @@ static int image_make(
if (r < 0)
return r;
- if (btrfs_quota_scan_ongoing(fd) == 0) {
- BtrfsQuotaInfo quota;
-
- r = btrfs_subvol_get_subtree_quota_fd(fd, 0, &quota);
- if (r >= 0) {
- (*ret)->usage = quota.referenced;
- (*ret)->usage_exclusive = quota.exclusive;
-
- (*ret)->limit = quota.referenced_max;
- (*ret)->limit_exclusive = quota.exclusive_max;
- }
- }
-
+ (void) image_update_quota(*ret, fd);
return 0;
}
}
@@ -1002,6 +1028,7 @@ int image_read_only(Image *i, bool b) {
return -EOPNOTSUPP;
}
+ i->read_only = b;
return 0;
}
@@ -1100,6 +1127,8 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile
}
int image_set_limit(Image *i, uint64_t referenced_max) {
+ int r;
+
assert(i);
if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
@@ -1115,7 +1144,12 @@ int image_set_limit(Image *i, uint64_t referenced_max) {
(void) btrfs_qgroup_set_limit(i->path, 0, referenced_max);
(void) btrfs_subvol_auto_qgroup(i->path, 0, true);
- return btrfs_subvol_set_subtree_quota_limit(i->path, 0, referenced_max);
+ r = btrfs_subvol_set_subtree_quota_limit(i->path, 0, referenced_max);
+ if (r < 0)
+ return r;
+
+ (void) image_update_quota(i, -EBADF);
+ return 0;
}
int image_read_metadata(Image *i) {
diff --git a/src/shared/dlfcn-util.c b/src/shared/dlfcn-util.c
index a321df3..8022f55 100644
--- a/src/shared/dlfcn-util.c
+++ b/src/shared/dlfcn-util.c
@@ -49,6 +49,8 @@ int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, int log_l
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"%s is not installed: %s", filename, dlerror());
+ log_debug("Loaded '%s' via dlopen()", filename);
+
va_list ap;
va_start(ap, log_level);
r = dlsym_many_or_warnv(dl, log_level, ap);
diff --git a/src/shared/idn-util.c b/src/shared/idn-util.c
index d4108d0..d9e0cca 100644
--- a/src/shared/idn-util.c
+++ b/src/shared/idn-util.c
@@ -50,7 +50,10 @@ int dlopen_idn(void) {
if (!dl)
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"libidn support is not installed: %s", dlerror());
- }
+ log_debug("Loaded 'libidn.so.11' via dlopen()");
+ } else
+ log_debug("Loaded 'libidn.so.12' via dlopen()");
+
r = dlsym_many_or_warn(
dl,
diff --git a/src/shared/install.c b/src/shared/install.c
index 84d29dc..af3e8a3 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -342,9 +342,12 @@ void install_changes_dump(int r, const char *verb, const InstallChange *changes,
assert(verb || r >= 0);
for (size_t i = 0; i < n_changes; i++) {
- if (changes[i].type < 0)
- assert(verb);
assert(changes[i].path);
+ /* This tries to tell the compiler that it's safe to use 'verb' in a string format if there
+ * was an error, but the compiler doesn't care and fails anyway, so strna(verb) is used
+ * too. */
+ assert(verb || changes[i].type >= 0);
+ verb = strna(verb);
/* When making changes here, make sure to also change install_error() in dbus-manager.c. */
diff --git a/src/shared/journal-importer.c b/src/shared/journal-importer.c
index d9eabec..1914e74 100644
--- a/src/shared/journal-importer.c
+++ b/src/shared/journal-importer.c
@@ -92,7 +92,12 @@ static int get_line(JournalImporter *imp, char **line, size_t *size) {
imp->buf + imp->filled,
MALLOC_SIZEOF_SAFE(imp->buf) - imp->filled);
if (n < 0) {
- if (errno != EAGAIN)
+ if (ERRNO_IS_DISCONNECT(errno)) {
+ log_debug_errno(errno, "Got disconnect for importer %s.", strna(imp->name));
+ return 0;
+ }
+
+ if (!ERRNO_IS_TRANSIENT(errno))
log_error_errno(errno, "read(%d, ..., %zu): %m",
imp->fd,
MALLOC_SIZEOF_SAFE(imp->buf) - imp->filled);
@@ -133,7 +138,12 @@ static int fill_fixed_size(JournalImporter *imp, void **data, size_t size) {
n = read(imp->fd, imp->buf + imp->filled,
MALLOC_SIZEOF_SAFE(imp->buf) - imp->filled);
if (n < 0) {
- if (errno != EAGAIN)
+ if (ERRNO_IS_DISCONNECT(errno)) {
+ log_debug_errno(errno, "Got disconnect for importer %s.", strna(imp->name));
+ return 0;
+ }
+
+ if (!ERRNO_IS_TRANSIENT(errno))
log_error_errno(errno, "read(%d, ..., %zu): %m", imp->fd,
MALLOC_SIZEOF_SAFE(imp->buf) - imp->filled);
return -errno;
diff --git a/src/shared/libcrypt-util.c b/src/shared/libcrypt-util.c
index 81e6f17..5ccf75a 100644
--- a/src/shared/libcrypt-util.c
+++ b/src/shared/libcrypt-util.c
@@ -114,7 +114,7 @@ static char* systemd_crypt_ra(const char *phrase, const char *setting, void **da
if (!*data) {
*data = new0(struct crypt_data, 1);
if (!*data) {
- errno = -ENOMEM;
+ errno = ENOMEM;
return NULL;
}
diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c
index 6ffe86e..21cfe1d 100644
--- a/src/shared/ptyfwd.c
+++ b/src/shared/ptyfwd.c
@@ -406,6 +406,9 @@ int pty_forward_new(
struct winsize ws;
int r;
+ assert(master >= 0);
+ assert(ret);
+
f = new(PTYForward, 1);
if (!f)
return -ENOMEM;
diff --git a/src/shared/reboot-util.c b/src/shared/reboot-util.c
index 756f9d3..618658a 100644
--- a/src/shared/reboot-util.c
+++ b/src/shared/reboot-util.c
@@ -11,8 +11,15 @@
#include "reboot-util.h"
#include "string-util.h"
#include "umask-util.h"
+#include "utf8.h"
#include "virt.h"
+bool reboot_parameter_is_valid(const char *parameter) {
+ assert(parameter);
+
+ return ascii_is_valid(parameter) && strlen(parameter) <= NAME_MAX;
+}
+
int update_reboot_parameter_and_warn(const char *parameter, bool keep) {
int r;
@@ -30,6 +37,9 @@ int update_reboot_parameter_and_warn(const char *parameter, bool keep) {
return 0;
}
+ if (!reboot_parameter_is_valid(parameter))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid reboot parameter '%s'.", parameter);
+
RUN_WITH_UMASK(0022) {
r = write_string_file("/run/systemd/reboot-param", parameter,
WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
diff --git a/src/shared/reboot-util.h b/src/shared/reboot-util.h
index bbca8b8..137f237 100644
--- a/src/shared/reboot-util.h
+++ b/src/shared/reboot-util.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+bool reboot_parameter_is_valid(const char *parameter);
int update_reboot_parameter_and_warn(const char *parameter, bool keep);
typedef enum RebootFlags {
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index dd8b825..04794c5 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -199,6 +199,8 @@ int tpm2_context_init(const char *device, struct tpm2_context *ret) {
if (!dl)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to load %s: %s", fn, dlerror());
+ log_debug("Loaded '%s' via dlopen()", fn);
+
func = dlsym(dl, TSS2_TCTI_INFO_SYMBOL);
if (!func)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
diff --git a/src/shared/verbs.c b/src/shared/verbs.c
index 2d19172..2973fe3 100644
--- a/src/shared/verbs.c
+++ b/src/shared/verbs.c
@@ -13,22 +13,21 @@
#include "verbs.h"
#include "virt.h"
-/* Wraps running_in_chroot() which is used in various places, but also adds an environment variable check so external
- * processes can reliably force this on.
- */
+/* Wraps running_in_chroot() which is used in various places, but also adds an environment variable check
+ * so external processes can reliably force this on. */
bool running_in_chroot_or_offline(void) {
int r;
- /* Added to support use cases like rpm-ostree, where from %post scripts we only want to execute "preset", but
- * not "start"/"restart" for example.
+ /* Added to support use cases like rpm-ostree, where from %post scripts we only want to execute "preset",
+ * but not "start"/"restart" for example.
*
* See docs/ENVIRONMENT.md for docs.
*/
r = getenv_bool("SYSTEMD_OFFLINE");
- if (r < 0 && r != -ENXIO)
- log_debug_errno(r, "Failed to parse $SYSTEMD_OFFLINE: %m");
- else if (r >= 0)
+ if (r >= 0)
return r > 0;
+ if (r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $SYSTEMD_OFFLINE, ignoring: %m");
/* We've had this condition check for a long time which basically checks for legacy chroot case like Fedora's
* "mock", which is used for package builds. We don't want to try to start systemd services there, since
@@ -40,8 +39,7 @@ bool running_in_chroot_or_offline(void) {
*/
r = running_in_chroot();
if (r < 0)
- log_debug_errno(r, "running_in_chroot(): %m");
-
+ log_debug_errno(r, "Failed to check if we're running in chroot, assuming not: %m");
return r > 0;
}
diff --git a/src/shared/watchdog.c b/src/shared/watchdog.c
index 8871476..1b19fc4 100644
--- a/src/shared/watchdog.c
+++ b/src/shared/watchdog.c
@@ -95,7 +95,7 @@ static int set_pretimeout_governor(const char *governor) {
governor,
WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE);
if (r < 0)
- return log_error_errno(r, "Failed to set pretimeout_governor to '%s': %m", governor);
+ return log_error_errno(r, "Failed to set watchdog pretimeout_governor to '%s': %m", governor);
return r;
}
@@ -157,7 +157,7 @@ static int watchdog_read_pretimeout(void) {
if (ioctl(watchdog_fd, WDIOC_GETPRETIMEOUT, &sec) < 0) {
watchdog_pretimeout = 0;
- return log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno, "Failed to get pretimeout value, ignoring: %m");
+ return log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno, "Failed to get watchdog pretimeout value, ignoring: %m");
}
watchdog_pretimeout = sec * USEC_PER_SEC;
@@ -181,7 +181,7 @@ static int watchdog_set_pretimeout(void) {
return 0;
}
- return log_error_errno(errno, "Failed to set pretimeout to %s: %m", FORMAT_TIMESPAN(sec, USEC_PER_SEC));
+ return log_error_errno(errno, "Failed to set watchdog pretimeout to %s: %m", FORMAT_TIMESPAN(sec, USEC_PER_SEC));
}
/* The set ioctl does not return the actual value set so get it now. */
@@ -274,10 +274,10 @@ static int update_timeout(void) {
r = watchdog_set_timeout();
if (r < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(r))
- return log_error_errno(r, "Failed to set timeout to %s: %m",
+ return log_error_errno(r, "Failed to set watchdog hardware timeout to %s: %m",
FORMAT_TIMESPAN(watchdog_timeout, 0));
- log_info("Modifying watchdog timeout is not supported, reusing the programmed timeout.");
+ log_info("Modifying watchdog hardware timeout is not supported, reusing the programmed timeout.");
watchdog_timeout = USEC_INFINITY;
}
}
@@ -286,8 +286,8 @@ static int update_timeout(void) {
r = watchdog_read_timeout();
if (r < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(r))
- return log_error_errno(r, "Failed to query watchdog HW timeout: %m");
- log_info("Reading watchdog timeout is not supported, reusing the configured timeout.");
+ return log_error_errno(r, "Failed to query watchdog hardware timeout: %m");
+ log_info("Reading watchdog hardware timeout is not supported, reusing the configured timeout.");
watchdog_timeout = previous_timeout;
}
}
@@ -302,7 +302,7 @@ static int update_timeout(void) {
if (r < 0)
return r;
- log_info("Watchdog running with a timeout of %s.", FORMAT_TIMESPAN(watchdog_timeout, 0));
+ log_info("Watchdog running with a hardware timeout of %s.", FORMAT_TIMESPAN(watchdog_timeout, 0));
return watchdog_ping_now();
}
diff --git a/src/systemctl/systemctl-enable.c b/src/systemctl/systemctl-enable.c
index 5be4c0c..73befeb 100644
--- a/src/systemctl/systemctl-enable.c
+++ b/src/systemctl/systemctl-enable.c
@@ -73,7 +73,8 @@ int verb_enable(int argc, char *argv[], void *userdata) {
if (!argv[1])
return 0;
- r = mangle_names("to enable", strv_skip(argv, 1), &names);
+ const char *operation = strjoina("to ", verb);
+ r = mangle_names(operation, strv_skip(argv, 1), &names);
if (r < 0)
return r;
diff --git a/src/systemctl/systemctl-list-jobs.c b/src/systemctl/systemctl-list-jobs.c
index a752173..fcfe2ac 100644
--- a/src/systemctl/systemctl-list-jobs.c
+++ b/src/systemctl/systemctl-list-jobs.c
@@ -102,9 +102,9 @@ static int output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned n
return table_log_add_error(r);
if (arg_jobs_after)
- output_waiting_jobs(bus, table, j->id, "GetJobAfter", "\twaiting for job");
+ output_waiting_jobs(bus, table, j->id, "GetJobAfter", "\tblocking job");
if (arg_jobs_before)
- output_waiting_jobs(bus, table, j->id, "GetJobBefore", "\tblocking job");
+ output_waiting_jobs(bus, table, j->id, "GetJobBefore", "\twaiting for job");
}
r = table_print(table, NULL);
diff --git a/src/systemctl/systemctl-logind.c b/src/systemctl/systemctl-logind.c
index 1c3b68f..781cb37 100644
--- a/src/systemctl/systemctl-logind.c
+++ b/src/systemctl/systemctl-logind.c
@@ -363,7 +363,7 @@ int logind_show_shutdown(void) {
return r;
if (isempty(action))
- return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "No scheduled shutdown.");
+ return log_full_errno(arg_quiet ? LOG_DEBUG : LOG_ERR, SYNTHETIC_ERRNO(ENODATA), "No scheduled shutdown.");
if (STR_IN_SET(action, "halt", "poweroff", "exit"))
action = "Shutdown";
diff --git a/src/systemd/sd-bus-vtable.h b/src/systemd/sd-bus-vtable.h
index 5e80ea8..d06c5c3 100644
--- a/src/systemd/sd-bus-vtable.h
+++ b/src/systemd/sd-bus-vtable.h
@@ -208,6 +208,7 @@ struct sd_bus_vtable {
_41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
NAME, ...) NAME
+#define _SD_VARARGS_FOREACH_EVEN_00(FN)
#define _SD_VARARGS_FOREACH_EVEN_01(FN, X) FN(X)
#define _SD_VARARGS_FOREACH_EVEN_02(FN, X, Y) FN(X)
#define _SD_VARARGS_FOREACH_EVEN_04(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_02(FN, __VA_ARGS__)
@@ -261,9 +262,11 @@ struct sd_bus_vtable {
_SD_VARARGS_FOREACH_EVEN_08, _SD_VARARGS_FOREACH_EVEN_07, \
_SD_VARARGS_FOREACH_EVEN_06, _SD_VARARGS_FOREACH_EVEN_05, \
_SD_VARARGS_FOREACH_EVEN_04, _SD_VARARGS_FOREACH_EVEN_03, \
- _SD_VARARGS_FOREACH_EVEN_02, _SD_VARARGS_FOREACH_EVEN_01) \
+ _SD_VARARGS_FOREACH_EVEN_02, _SD_VARARGS_FOREACH_EVEN_01, \
+ _SD_VARARGS_FOREACH_EVEN_00) \
(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_ODD_00(FN)
#define _SD_VARARGS_FOREACH_ODD_01(FN, X)
#define _SD_VARARGS_FOREACH_ODD_02(FN, X, Y) FN(Y)
#define _SD_VARARGS_FOREACH_ODD_04(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_02(FN, __VA_ARGS__)
@@ -317,7 +320,8 @@ struct sd_bus_vtable {
_SD_VARARGS_FOREACH_ODD_08, _SD_VARARGS_FOREACH_ODD_07, \
_SD_VARARGS_FOREACH_ODD_06, _SD_VARARGS_FOREACH_ODD_05, \
_SD_VARARGS_FOREACH_ODD_04, _SD_VARARGS_FOREACH_ODD_03, \
- _SD_VARARGS_FOREACH_ODD_02, _SD_VARARGS_FOREACH_ODD_01) \
+ _SD_VARARGS_FOREACH_ODD_02, _SD_VARARGS_FOREACH_ODD_01, \
+ _SD_VARARGS_FOREACH_ODD_00) \
(FN, __VA_ARGS__)
#define SD_BUS_ARGS(...) __VA_ARGS__
diff --git a/src/test/test-bpf-foreign-programs.c b/src/test/test-bpf-foreign-programs.c
index 0445c39..41cd19a 100644
--- a/src/test/test-bpf-foreign-programs.c
+++ b/src/test/test-bpf-foreign-programs.c
@@ -253,7 +253,7 @@ static int test_bpf_cgroup_programs(Manager *m, const char *unit_name, const Tes
while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED)) {
r = sd_event_run(m->event, UINT64_MAX);
if (r < 0)
- return log_error_errno(errno, "Event run failed %m");
+ return log_error_errno(r, "Event run failed %m");
}
cld_code = SERVICE(u)->exec_command[SERVICE_EXEC_START]->exec_status.code;
diff --git a/src/test/test-bpf-lsm.c b/src/test/test-bpf-lsm.c
index e1df62f..54aa177 100644
--- a/src/test/test-bpf-lsm.c
+++ b/src/test/test-bpf-lsm.c
@@ -46,7 +46,7 @@ static int test_restrict_filesystems(Manager *m, const char *unit_name, const ch
while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED)) {
r = sd_event_run(m->event, UINT64_MAX);
if (r < 0)
- return log_error_errno(errno, "Event run failed %m");
+ return log_error_errno(r, "Event run failed %m");
}
cld_code = SERVICE(u)->exec_command[SERVICE_EXEC_START]->exec_status.code;
diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c
index 7084e70..6974ad8 100644
--- a/src/test/test-namespace.c
+++ b/src/test/test-namespace.c
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
+#include <sysexits.h>
#include <sys/socket.h>
#include <sys/stat.h>
@@ -85,6 +86,7 @@ TEST(tmpdir) {
static void test_shareable_ns(unsigned long nsflag) {
_cleanup_close_pair_ int s[2] = { -1, -1 };
+ bool permission_denied = false;
pid_t pid1, pid2, pid3;
int r, n = 0;
siginfo_t si;
@@ -101,8 +103,8 @@ static void test_shareable_ns(unsigned long nsflag) {
if (pid1 == 0) {
r = setup_shareable_ns(s, nsflag);
- assert_se(r >= 0);
- _exit(r);
+ assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r));
+ _exit(r >= 0 ? r : EX_NOPERM);
}
pid2 = fork();
@@ -110,8 +112,8 @@ static void test_shareable_ns(unsigned long nsflag) {
if (pid2 == 0) {
r = setup_shareable_ns(s, nsflag);
- assert_se(r >= 0);
- exit(r);
+ assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r));
+ _exit(r >= 0 ? r : EX_NOPERM);
}
pid3 = fork();
@@ -119,24 +121,38 @@ static void test_shareable_ns(unsigned long nsflag) {
if (pid3 == 0) {
r = setup_shareable_ns(s, nsflag);
- assert_se(r >= 0);
- exit(r);
+ assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r));
+ _exit(r >= 0 ? r : EX_NOPERM);
}
r = wait_for_terminate(pid1, &si);
assert_se(r >= 0);
assert_se(si.si_code == CLD_EXITED);
- n += si.si_status;
+ if (si.si_status == EX_NOPERM)
+ permission_denied = true;
+ else
+ n += si.si_status;
r = wait_for_terminate(pid2, &si);
assert_se(r >= 0);
assert_se(si.si_code == CLD_EXITED);
- n += si.si_status;
+ if (si.si_status == EX_NOPERM)
+ permission_denied = true;
+ else
+ n += si.si_status;
r = wait_for_terminate(pid3, &si);
assert_se(r >= 0);
assert_se(si.si_code == CLD_EXITED);
- n += si.si_status;
+ if (si.si_status == EX_NOPERM)
+ permission_denied = true;
+ else
+ n += si.si_status;
+
+ /* LSMs can cause setup_shareable_ns() to fail with permission denied, do not fail the test in that
+ * case (e.g.: LXC with AppArmor on kernel < v6.2). */
+ if (permission_denied)
+ return (void) log_tests_skipped("insufficient privileges");
assert_se(n == 1);
}
diff --git a/src/test/test-socket-bind.c b/src/test/test-socket-bind.c
index a9ef940..677f967 100644
--- a/src/test/test-socket-bind.c
+++ b/src/test/test-socket-bind.c
@@ -83,7 +83,7 @@ static int test_socket_bind(
while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED)) {
r = sd_event_run(m->event, UINT64_MAX);
if (r < 0)
- return log_error_errno(errno, "Event run failed %m");
+ return log_error_errno(r, "Event run failed %m");
}
cld_code = SERVICE(u)->exec_command[SERVICE_EXEC_START]->exec_status.code;
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 281284c..b44e572 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -2134,7 +2134,7 @@ static int item_do(
fdaction_t action) {
struct stat st;
- int r = 0, q;
+ int r = 0, q = 0;
assert(i);
assert(path);
@@ -2168,9 +2168,10 @@ static int item_do(
continue;
de_fd = openat(fd, de->d_name, O_NOFOLLOW|O_CLOEXEC|O_PATH);
- if (de_fd < 0)
- q = log_error_errno(errno, "Failed to open() file '%s': %m", de->d_name);
- else {
+ if (de_fd < 0) {
+ if (errno != ENOENT)
+ q = log_error_errno(errno, "Failed to open file '%s': %m", de->d_name);
+ } else {
_cleanup_free_ char *de_path = NULL;
de_path = path_join(path, de->d_name);
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index 8ee7d67..9b8309f 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -1196,7 +1196,7 @@ int udev_rules_parse_file(UdevRules *rules, const char *filename) {
r = hashmap_put_stats_by_path(&rules->stats_by_path, filename, &st);
if (r < 0)
- return log_warning_errno(errno, "Failed to save stat for %s, ignoring: %m", filename);
+ return log_warning_errno(r, "Failed to save stat for %s, ignoring: %m", filename);
(void) fd_warn_permissions(filename, fileno(f));
diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c
index e762fca..3aad352 100644
--- a/src/userdb/userdbctl.c
+++ b/src/userdb/userdbctl.c
@@ -309,6 +309,9 @@ static int table_add_uid_map(
assert(table);
assert(add_unavailable);
+ if (!p)
+ return 0;
+
for (size_t i = 0; p && i < p->n_entries; i++) {
UidRangeEntry *x = p->entries + i;
@@ -533,7 +536,8 @@ static int table_add_gid_boundaries(Table *table, const UidRange *p) {
for (size_t i = 0; i < ELEMENTSOF(uid_range_table); i++) {
_cleanup_free_ char *name = NULL, *comment = NULL;
- if (!uid_range_covers(p, uid_range_table[i].first, uid_range_table[i].last))
+ if (!uid_range_covers(p, uid_range_table[i].first,
+ uid_range_table[i].last - uid_range_table[i].first + 1))
continue;
name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN),
diff --git a/src/userdb/userdbd-manager.c b/src/userdb/userdbd-manager.c
index f0589ad..27db358 100644
--- a/src/userdb/userdbd-manager.c
+++ b/src/userdb/userdbd-manager.c
@@ -4,6 +4,7 @@
#include "sd-daemon.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "mkdir.h"
@@ -158,7 +159,6 @@ static int start_one_worker(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to fork new worker child: %m");
if (r == 0) {
- char pids[DECIMAL_STR_MAX(pid_t)];
/* Child */
log_close();
@@ -186,9 +186,9 @@ static int start_one_worker(Manager *m) {
safe_close(m->listen_fd);
}
- xsprintf(pids, PID_FMT, pid);
- if (setenv("LISTEN_PID", pids, 1) < 0) {
- log_error_errno(errno, "Failed to set $LISTEN_PID: %m");
+ r = setenvf("LISTEN_PID", /* overwrite= */ true, PID_FMT, pid);
+ if (r < 0) {
+ log_error_errno(r, "Failed to set $LISTEN_PID: %m");
_exit(EXIT_FAILURE);
}