diff options
Diffstat (limited to 'src')
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, "a); + 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, "a); - 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); } |