diff --git a/.github/workflows/mkosi.yml b/.github/workflows/mkosi.yml index 2a1163aa0..523415517 100644 --- a/.github/workflows/mkosi.yml +++ b/.github/workflows/mkosi.yml @@ -54,6 +54,13 @@ jobs: - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b - uses: systemd/mkosi@792cbc60eb2dc4a58d66bb3c212bf92f8d50f6ea + # FIXME: temporary workaround for a file conflict between systemd (C9S) and + # systemd-boot (EPEL9). Drop this once systemd in C9S is updated to v252 + # (should be done by the end of 2022). + - name: Fix C9S/EPEL9 + if: ${{ matrix.release == '9-stream' }} + run: sudo sed -i '/add_packages/s/systemd-boot/systemd/g' /usr/local/lib/python3.10/dist-packages/mkosi/__init__.py + - name: Install run: sudo apt-get update && sudo apt-get install --no-install-recommends python3-pexpect python3-jinja2 diff --git a/.packit.yml b/.packit.yml index a7502b25b..1b49ddf28 100644 --- a/.packit.yml +++ b/.packit.yml @@ -4,8 +4,8 @@ # Docs: https://packit.dev/docs/ specfile_path: .packit_rpm/systemd.spec -synced_files: - - .packit.yaml +files_to_sync: + - .packit.yml - src: .packit_rpm/systemd.spec dest: systemd.spec upstream_package_name: systemd @@ -32,14 +32,15 @@ actions: # [0] https://github.com/mesonbuild/meson/issues/7360 # [1] https://github.com/systemd/systemd/pull/18908#issuecomment-792250110 - 'sed -i "/^CONFIGURE_OPTS=(/a--werror" .packit_rpm/systemd.spec' + # Ignore unpackages standalone binaries + - "sed -i 's/assert False,.*/pass/' .packit_rpm/split-files.py" jobs: - job: copr_build trigger: pull_request - metadata: - targets: - - fedora-rawhide-aarch64 - - fedora-rawhide-i386 - - fedora-rawhide-ppc64le - - fedora-rawhide-s390x - - fedora-rawhide-x86_64 + targets: + - fedora-rawhide-aarch64 + - fedora-rawhide-i386 + - fedora-rawhide-ppc64le + - fedora-rawhide-s390x + - fedora-rawhide-x86_64 diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index ab3add603..1e7a75a36 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -268,6 +268,13 @@ All tools: it is either set to `system` or `user` depending on whether the NSS/PAM module is called by systemd in `--system` or `--user` mode. +* `$SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST` — can be set to override the mount + units burst rate limit for parsing `/proc/self/mountinfo`. On a system with + few resources but many mounts the rate limit may be hit, which will cause the + processing of mount units to stall. The burst limit may be adjusted when the + default is not appropriate for a given system. Defaults to `5`, accepts + positive integers. + `systemd-remount-fs`: * `$SYSTEMD_REMOUNT_ROOT_RW=1` — if set and no entry for the root directory diff --git a/mkosi.default.d/debian/10-mkosi.debian b/mkosi.default.d/debian/10-mkosi.debian index 3eea0c74b..c21fe77c2 100644 --- a/mkosi.default.d/debian/10-mkosi.debian +++ b/mkosi.default.d/debian/10-mkosi.debian @@ -13,7 +13,7 @@ Packages= fdisk iproute2 isc-dhcp-server - libbpf0 + libbpf1 libfido2-1 libglib2.0-0 libgnutls30 diff --git a/rules.d/60-evdev.rules b/rules.d/60-evdev.rules index 055a62a12..15483101e 100644 --- a/rules.d/60-evdev.rules +++ b/rules.d/60-evdev.rules @@ -3,23 +3,28 @@ ACTION=="remove", GOTO="evdev_end" KERNEL!="event*", GOTO="evdev_end" -# skip later rules when we find something for this input device -IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=evdev:", \ - IMPORT{builtin}="keyboard", GOTO="evdev_end" +# Execute the match patterns below, from least-to-most specific. + +# Device matching the modalias string (bustype, vendor, product, version, other properties) +IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=evdev:", + ENV{.HAVE_HWDB_PROPERTIES}="1" # AT keyboard matching by the machine's DMI data DRIVERS=="atkbd", \ IMPORT{builtin}="hwdb 'evdev:atkbd:$attr{[dmi/id]modalias}'", \ - IMPORT{builtin}="keyboard", GOTO="evdev_end" + ENV{.HAVE_HWDB_PROPERTIES}="1" -# device matching the input device name + properties + the machine's DMI data -KERNELS=="input*", \ - IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:phys:$attr{phys}:ev:$attr{capabilities/ev}:$attr{[dmi/id]modalias}'", \ - IMPORT{builtin}="keyboard", GOTO="evdev_end" - -# device matching the input device name and the machine's DMI data +# Device matching the input device name and the machine's DMI data KERNELS=="input*", \ IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'", \ - IMPORT{builtin}="keyboard", GOTO="evdev_end" + ENV{.HAVE_HWDB_PROPERTIES}="1" + +# Device matching the input device name + properties + the machine's DMI data +KERNELS=="input*", \ + IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:phys:$attr{phys}:ev:$attr{capabilities/ev}:$attr{[dmi/id]modalias}'", \ + ENV{.HAVE_HWDB_PROPERTIES}="1" + +ENV{.HAVE_HWDB_PROPERTIES}=="1", \ + IMPORT{builtin}="keyboard" LABEL="evdev_end" diff --git a/src/basic/glyph-util.c b/src/basic/glyph-util.c index 67f2270da..7e26edb9c 100644 --- a/src/basic/glyph-util.c +++ b/src/basic/glyph-util.c @@ -9,15 +9,15 @@ bool emoji_enabled(void) { static int cached_emoji_enabled = -1; if (cached_emoji_enabled < 0) { - int val; + int val = getenv_bool("SYSTEMD_EMOJI"); + if (val >= 0) + return (cached_emoji_enabled = val); - val = getenv_bool("SYSTEMD_EMOJI"); - if (val < 0) - cached_emoji_enabled = - is_locale_utf8() && - !STRPTR_IN_SET(getenv("TERM"), "dumb", "linux"); - else - cached_emoji_enabled = val; + const char *term = getenv("TERM"); + if (!term || STR_IN_SET(term, "dumb", "linux")) + return (cached_emoji_enabled = false); + + cached_emoji_enabled = is_locale_utf8(); } return cached_emoji_enabled; diff --git a/src/basic/io-util.h b/src/basic/io-util.h index 39728e06b..3afb13426 100644 --- a/src/basic/io-util.h +++ b/src/basic/io-util.h @@ -91,7 +91,16 @@ struct iovec_wrapper *iovw_new(void); struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw); struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw); void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors); + int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len); +static inline int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) { + /* Move data into iovw or free on error */ + int r = iovw_put(iovw, data, len); + if (r < 0) + free(data); + return r; +} + int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value); int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value); void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new); diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 7da48b4ca..7916587b4 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -179,7 +179,7 @@ static int load_etc_machine_id(void) { int r; r = sd_id128_get_machine(&arg_machine_id); - if (IN_SET(r, -ENOENT, -ENOMEDIUM)) /* Not set or empty */ + if (IN_SET(r, -ENOENT, -ENOMEDIUM, -ENOPKG)) /* Not set or empty */ return 0; if (r < 0) return log_error_errno(r, "Failed to get machine-id: %m"); diff --git a/src/core/cgroup.c b/src/core/cgroup.c index c44966839..4c0a821d9 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -4158,7 +4158,7 @@ int compare_job_priority(const void *a, const void *b) { int unit_cgroup_freezer_action(Unit *u, FreezerAction action) { _cleanup_free_ char *path = NULL; FreezerState target, kernel = _FREEZER_STATE_INVALID; - int r; + int r, ret; assert(u); assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW)); @@ -4166,9 +4166,23 @@ int unit_cgroup_freezer_action(Unit *u, FreezerAction action) { if (!cg_freezer_supported()) return 0; + /* Ignore all requests to thaw init.scope or -.slice and reject all requests to freeze them */ + if (unit_has_name(u, SPECIAL_ROOT_SLICE) || unit_has_name(u, SPECIAL_INIT_SCOPE)) + return action == FREEZER_FREEZE ? -EPERM : 0; + if (!u->cgroup_realized) return -EBUSY; + if (action == FREEZER_THAW) { + Unit *slice = UNIT_GET_SLICE(u); + + if (slice) { + r = unit_cgroup_freezer_action(slice, FREEZER_THAW); + if (r < 0) + return log_unit_error_errno(u, r, "Failed to thaw slice %s of unit: %m", slice->id); + } + } + target = action == FREEZER_FREEZE ? FREEZER_FROZEN : FREEZER_RUNNING; r = unit_freezer_state_kernel(u, &kernel); @@ -4177,8 +4191,11 @@ int unit_cgroup_freezer_action(Unit *u, FreezerAction action) { if (target == kernel) { u->freezer_state = target; - return 0; - } + if (action == FREEZER_FREEZE) + return 0; + ret = 0; + } else + ret = 1; r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.freeze", &path); if (r < 0) @@ -4186,16 +4203,18 @@ int unit_cgroup_freezer_action(Unit *u, FreezerAction action) { log_unit_debug(u, "%s unit.", action == FREEZER_FREEZE ? "Freezing" : "Thawing"); - if (action == FREEZER_FREEZE) - u->freezer_state = FREEZER_FREEZING; - else - u->freezer_state = FREEZER_THAWING; + if (target != kernel) { + if (action == FREEZER_FREEZE) + u->freezer_state = FREEZER_FREEZING; + else + u->freezer_state = FREEZER_THAWING; + } r = write_string_file(path, one_zero(action == FREEZER_FREEZE), WRITE_STRING_FILE_DISABLE_BUFFER); if (r < 0) return r; - return 1; + return ret; } int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name) { diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 19a71b6cb..0702373ab 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -782,14 +782,15 @@ static int bus_unit_method_freezer_generic(sd_bus_message *message, void *userda if (r == 0) reply_no_delay = true; - assert(!u->pending_freezer_message); + if (u->pending_freezer_message) { + bus_unit_send_pending_freezer_message(u, true); + assert(!u->pending_freezer_message); + } - r = sd_bus_message_new_method_return(message, &u->pending_freezer_message); - if (r < 0) - return r; + u->pending_freezer_message = sd_bus_message_ref(message); if (reply_no_delay) { - r = bus_unit_send_pending_freezer_message(u); + r = bus_unit_send_pending_freezer_message(u, false); if (r < 0) return r; } @@ -1661,7 +1662,8 @@ void bus_unit_send_pending_change_signal(Unit *u, bool including_new) { bus_unit_send_change_signal(u); } -int bus_unit_send_pending_freezer_message(Unit *u) { +int bus_unit_send_pending_freezer_message(Unit *u, bool cancelled) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; int r; assert(u); @@ -1669,7 +1671,18 @@ int bus_unit_send_pending_freezer_message(Unit *u) { if (!u->pending_freezer_message) return 0; - r = sd_bus_send(NULL, u->pending_freezer_message, NULL); + if (cancelled) + r = sd_bus_message_new_method_error( + u->pending_freezer_message, + &reply, + &SD_BUS_ERROR_MAKE_CONST( + BUS_ERROR_FREEZE_CANCELLED, "Freeze operation aborted")); + else + r = sd_bus_message_new_method_return(u->pending_freezer_message, &reply); + if (r < 0) + return r; + + r = sd_bus_send(NULL, reply, NULL); if (r < 0) log_warning_errno(r, "Failed to send queued message, ignoring: %m"); diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h index 643edcd87..6b7828e4b 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -10,7 +10,7 @@ extern const sd_bus_vtable bus_unit_cgroup_vtable[]; void bus_unit_send_change_signal(Unit *u); void bus_unit_send_pending_change_signal(Unit *u, bool including_new); -int bus_unit_send_pending_freezer_message(Unit *u); +int bus_unit_send_pending_freezer_message(Unit *u, bool cancelled); void bus_unit_send_removed_signal(Unit *u); int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error); diff --git a/src/core/execute.c b/src/core/execute.c index b5b7de6d2..6c3fbc2ff 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2662,6 +2662,7 @@ static int load_credential( assert(id); assert(path); assert(unit); + assert(read_dfd >= 0 || read_dfd == AT_FDCWD); assert(write_dfd >= 0); assert(left); @@ -2888,7 +2889,7 @@ static int acquire_credentials( lc->path, lc->encrypted, unit, - -1, + AT_FDCWD, dfd, uid, ownership_ok, diff --git a/src/core/mount.c b/src/core/mount.c index 5e8a6ead6..dea7cd9d3 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -1908,6 +1908,7 @@ static void mount_enumerate(Manager *m) { mnt_init_debug(0); if (!m->mount_monitor) { + unsigned mount_rate_limit_burst = 5; int fd; m->mount_monitor = mnt_new_monitor(); @@ -1947,7 +1948,15 @@ static void mount_enumerate(Manager *m) { goto fail; } - r = sd_event_source_set_ratelimit(m->mount_event_source, 1 * USEC_PER_SEC, 5); + /* Let users override the default (5 in 1s), as it stalls the boot sequence on busy systems. */ + const char *e = secure_getenv("SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST"); + if (e) { + r = safe_atou(e, &mount_rate_limit_burst); + if (r < 0) + log_debug("Invalid value in $SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST, ignoring: %s", e); + } + + r = sd_event_source_set_ratelimit(m->mount_event_source, 1 * USEC_PER_SEC, mount_rate_limit_burst); if (r < 0) { log_error_errno(r, "Failed to enable rate limit for mount events: %m"); goto fail; diff --git a/src/core/unit.c b/src/core/unit.c index d08c73613..bed5544cc 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -937,29 +937,17 @@ static int unit_reserve_dependencies(Unit *u, Unit *other) { return 0; } -static void unit_maybe_warn_about_dependency( - Unit *u, - const char *other_id, - UnitDependency dependency) { - - assert(u); - +static bool unit_should_warn_about_dependency(UnitDependency dependency) { /* Only warn about some unit types */ - if (!IN_SET(dependency, - UNIT_CONFLICTS, - UNIT_CONFLICTED_BY, - UNIT_BEFORE, - UNIT_AFTER, - UNIT_ON_SUCCESS, - UNIT_ON_FAILURE, - UNIT_TRIGGERS, - UNIT_TRIGGERED_BY)) - return; - - if (streq_ptr(u->id, other_id)) - log_unit_warning(u, "Dependency %s=%s dropped", unit_dependency_to_string(dependency), u->id); - else - log_unit_warning(u, "Dependency %s=%s dropped, merged into %s", unit_dependency_to_string(dependency), strna(other_id), u->id); + return IN_SET(dependency, + UNIT_CONFLICTS, + UNIT_CONFLICTED_BY, + UNIT_BEFORE, + UNIT_AFTER, + UNIT_ON_SUCCESS, + UNIT_ON_FAILURE, + UNIT_TRIGGERS, + UNIT_TRIGGERED_BY); } static int unit_per_dependency_type_hashmap_update( @@ -1044,11 +1032,10 @@ static int unit_add_dependency_hashmap( return unit_per_dependency_type_hashmap_update(per_type, other, origin_mask, destination_mask); } -static void unit_merge_dependencies( - Unit *u, - Unit *other) { - - int r; +static void unit_merge_dependencies(Unit *u, Unit *other) { + Hashmap *deps; + void *dt; /* Actually of type UnitDependency, except that we don't bother casting it here, + * since the hashmaps all want it as void pointer. */ assert(u); assert(other); @@ -1056,18 +1043,29 @@ static void unit_merge_dependencies( if (u == other) return; + /* First, remove dependency to other. */ + HASHMAP_FOREACH_KEY(deps, dt, u->dependencies) { + if (hashmap_remove(deps, other) && unit_should_warn_about_dependency(UNIT_DEPENDENCY_FROM_PTR(dt))) + log_unit_warning(u, "Dependency %s=%s is dropped, as %s is merged into %s.", + unit_dependency_to_string(UNIT_DEPENDENCY_FROM_PTR(dt)), + other->id, other->id, u->id); + + if (hashmap_isempty(deps)) + hashmap_free(hashmap_remove(u->dependencies, dt)); + } + for (;;) { _cleanup_(hashmap_freep) Hashmap *other_deps = NULL; UnitDependencyInfo di_back; Unit *back; - void *dt; /* Actually of type UnitDependency, except that we don't bother casting it here, - * since the hashmaps all want it as void pointer. */ /* Let's focus on one dependency type at a time, that 'other' has defined. */ other_deps = hashmap_steal_first_key_and_value(other->dependencies, &dt); if (!other_deps) break; /* done! */ + deps = hashmap_get(u->dependencies, dt); + /* Now iterate through all dependencies of this dependency type, of 'other'. We refer to the * referenced units as 'back'. */ HASHMAP_FOREACH_KEY(di_back.data, back, other_deps) { @@ -1077,7 +1075,12 @@ static void unit_merge_dependencies( if (back == u) { /* This is a dependency pointing back to the unit we want to merge with? * Suppress it (but warn) */ - unit_maybe_warn_about_dependency(u, other->id, UNIT_DEPENDENCY_FROM_PTR(dt)); + if (unit_should_warn_about_dependency(UNIT_DEPENDENCY_FROM_PTR(dt))) + log_unit_warning(u, "Dependency %s=%s in %s is dropped, as %s is merged into %s.", + unit_dependency_to_string(UNIT_DEPENDENCY_FROM_PTR(dt)), + u->id, other->id, other->id, u->id); + + hashmap_remove(other_deps, back); continue; } @@ -1096,41 +1099,21 @@ static void unit_merge_dependencies( di_move.origin_mask, di_move.destination_mask) >= 0); } - } - - /* Now all references towards 'other' of the current type 'dt' are corrected to point to - * 'u'. Lets's now move the deps of type 'dt' from 'other' to 'u'. First, let's try to move - * them per type wholesale. */ - r = hashmap_put(u->dependencies, dt, other_deps); - if (r == -EEXIST) { - Hashmap *deps; /* The target unit already has dependencies of this type, let's then merge this individually. */ - - assert_se(deps = hashmap_get(u->dependencies, dt)); - - for (;;) { - UnitDependencyInfo di_move; - - /* Get first dep */ - di_move.data = hashmap_steal_first_key_and_value(other_deps, (void**) &back); - if (!di_move.data) - break; /* done */ - if (back == u) { - /* Would point back to us, ignore */ - unit_maybe_warn_about_dependency(u, other->id, UNIT_DEPENDENCY_FROM_PTR(dt)); - continue; - } - - assert_se(unit_per_dependency_type_hashmap_update(deps, back, di_move.origin_mask, di_move.destination_mask) >= 0); - } - } else { - assert_se(r >= 0); - TAKE_PTR(other_deps); - - if (hashmap_remove(other_deps, u)) - unit_maybe_warn_about_dependency(u, other->id, UNIT_DEPENDENCY_FROM_PTR(dt)); + if (deps) + assert_se(unit_per_dependency_type_hashmap_update( + deps, + back, + di_back.origin_mask, + di_back.destination_mask) >= 0); } + + /* Now all references towards 'other' of the current type 'dt' are corrected to point to 'u'. + * Lets's now move the deps of type 'dt' from 'other' to 'u'. If the unit does not have + * dependencies of this type, let's move them per type wholesale. */ + if (!deps) + assert_se(hashmap_put(u->dependencies, dt, TAKE_PTR(other_deps)) >= 0); } other->dependencies = hashmap_free(other->dependencies); @@ -1176,11 +1159,6 @@ int unit_merge(Unit *u, Unit *other) { if (r < 0) return r; - /* Merge names */ - r = unit_merge_names(u, other); - if (r < 0) - return r; - /* Redirect all references */ while (other->refs_by_target) unit_ref_set(other->refs_by_target, other->refs_by_target->source, u); @@ -1188,6 +1166,11 @@ int unit_merge(Unit *u, Unit *other) { /* Merge dependencies */ unit_merge_dependencies(u, other); + /* Merge names. It is better to do that after merging deps, otherwise the log message contains n/a. */ + r = unit_merge_names(u, other); + if (r < 0) + return r; + other->load_state = UNIT_MERGED; other->merged_into = u; @@ -3066,7 +3049,6 @@ int unit_add_dependency( [UNIT_IN_SLICE] = UNIT_SLICE_OF, [UNIT_SLICE_OF] = UNIT_IN_SLICE, }; - Unit *original_u = u, *original_other = other; UnitDependencyAtom a; int r; @@ -3085,7 +3067,9 @@ int unit_add_dependency( /* We won't allow dependencies on ourselves. We will not consider them an error however. */ if (u == other) { - unit_maybe_warn_about_dependency(original_u, original_other->id, d); + if (unit_should_warn_about_dependency(d)) + log_unit_warning(u, "Dependency %s=%s is dropped.", + unit_dependency_to_string(d), u->id); return 0; } @@ -5811,7 +5795,7 @@ void unit_frozen(Unit *u) { u->freezer_state = FREEZER_FROZEN; - bus_unit_send_pending_freezer_message(u); + bus_unit_send_pending_freezer_message(u, false); } void unit_thawed(Unit *u) { @@ -5819,7 +5803,7 @@ void unit_thawed(Unit *u) { u->freezer_state = FREEZER_RUNNING; - bus_unit_send_pending_freezer_message(u); + bus_unit_send_pending_freezer_message(u, false); } static int unit_freezer_action(Unit *u, FreezerAction action) { @@ -5844,7 +5828,8 @@ static int unit_freezer_action(Unit *u, FreezerAction action) { if (s != UNIT_ACTIVE) return -EHOSTDOWN; - if (IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING)) + if ((IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING) && action == FREEZER_FREEZE) || + (u->freezer_state == FREEZER_THAWING && action == FREEZER_THAW)) return -EALREADY; r = method(u); diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 98e749281..ea3d8c415 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -106,24 +107,27 @@ enum { META_EXE = _META_MANDATORY_MAX, META_UNIT, + META_PROC_AUXV, _META_MAX }; static const char * const meta_field_names[_META_MAX] = { - [META_ARGV_PID] = "COREDUMP_PID=", - [META_ARGV_UID] = "COREDUMP_UID=", - [META_ARGV_GID] = "COREDUMP_GID=", - [META_ARGV_SIGNAL] = "COREDUMP_SIGNAL=", - [META_ARGV_TIMESTAMP] = "COREDUMP_TIMESTAMP=", - [META_ARGV_RLIMIT] = "COREDUMP_RLIMIT=", - [META_ARGV_HOSTNAME] = "COREDUMP_HOSTNAME=", - [META_COMM] = "COREDUMP_COMM=", - [META_EXE] = "COREDUMP_EXE=", - [META_UNIT] = "COREDUMP_UNIT=", + [META_ARGV_PID] = "COREDUMP_PID=", + [META_ARGV_UID] = "COREDUMP_UID=", + [META_ARGV_GID] = "COREDUMP_GID=", + [META_ARGV_SIGNAL] = "COREDUMP_SIGNAL=", + [META_ARGV_TIMESTAMP] = "COREDUMP_TIMESTAMP=", + [META_ARGV_RLIMIT] = "COREDUMP_RLIMIT=", + [META_ARGV_HOSTNAME] = "COREDUMP_HOSTNAME=", + [META_COMM] = "COREDUMP_COMM=", + [META_EXE] = "COREDUMP_EXE=", + [META_UNIT] = "COREDUMP_UNIT=", + [META_PROC_AUXV] = "COREDUMP_PROC_AUXV=", }; typedef struct Context { const char *meta[_META_MAX]; + size_t meta_size[_META_MAX]; pid_t pid; bool is_pid1; bool is_journald; @@ -138,9 +142,9 @@ typedef enum CoredumpStorage { } CoredumpStorage; static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = { - [COREDUMP_STORAGE_NONE] = "none", + [COREDUMP_STORAGE_NONE] = "none", [COREDUMP_STORAGE_EXTERNAL] = "external", - [COREDUMP_STORAGE_JOURNAL] = "journal", + [COREDUMP_STORAGE_JOURNAL] = "journal", }; DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage); @@ -156,13 +160,13 @@ static uint64_t arg_max_use = UINT64_MAX; static int parse_config(void) { static const ConfigTableItem items[] = { - { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage }, - { "Coredump", "Compress", config_parse_bool, 0, &arg_compress }, - { "Coredump", "ProcessSizeMax", config_parse_iec_uint64, 0, &arg_process_size_max }, - { "Coredump", "ExternalSizeMax", config_parse_iec_uint64_infinity, 0, &arg_external_size_max }, - { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max }, - { "Coredump", "KeepFree", config_parse_iec_uint64, 0, &arg_keep_free }, - { "Coredump", "MaxUse", config_parse_iec_uint64, 0, &arg_max_use }, + { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage }, + { "Coredump", "Compress", config_parse_bool, 0, &arg_compress }, + { "Coredump", "ProcessSizeMax", config_parse_iec_uint64, 0, &arg_process_size_max }, + { "Coredump", "ExternalSizeMax", config_parse_iec_uint64_infinity, 0, &arg_external_size_max }, + { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max }, + { "Coredump", "KeepFree", config_parse_iec_uint64, 0, &arg_keep_free }, + { "Coredump", "MaxUse", config_parse_iec_uint64, 0, &arg_max_use }, {} }; @@ -185,13 +189,16 @@ static uint64_t storage_size_max(void) { return 0; } -static int fix_acl(int fd, uid_t uid) { +static int fix_acl(int fd, uid_t uid, bool allow_user) { + assert(fd >= 0); + assert(uid_is_valid(uid)); #if HAVE_ACL int r; - assert(fd >= 0); - assert(uid_is_valid(uid)); + /* We don't allow users to read coredumps if the uid or capabilities were changed. */ + if (!allow_user) + return 0; if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY) return 0; @@ -208,15 +215,15 @@ static int fix_acl(int fd, uid_t uid) { static int fix_xattr(int fd, const Context *context) { static const char * const xattrs[_META_MAX] = { - [META_ARGV_PID] = "user.coredump.pid", - [META_ARGV_UID] = "user.coredump.uid", - [META_ARGV_GID] = "user.coredump.gid", - [META_ARGV_SIGNAL] = "user.coredump.signal", - [META_ARGV_TIMESTAMP] = "user.coredump.timestamp", - [META_ARGV_RLIMIT] = "user.coredump.rlimit", - [META_ARGV_HOSTNAME] = "user.coredump.hostname", - [META_COMM] = "user.coredump.comm", - [META_EXE] = "user.coredump.exe", + [META_ARGV_PID] = "user.coredump.pid", + [META_ARGV_UID] = "user.coredump.uid", + [META_ARGV_GID] = "user.coredump.gid", + [META_ARGV_SIGNAL] = "user.coredump.signal", + [META_ARGV_TIMESTAMP] = "user.coredump.timestamp", + [META_ARGV_RLIMIT] = "user.coredump.rlimit", + [META_ARGV_HOSTNAME] = "user.coredump.hostname", + [META_COMM] = "user.coredump.comm", + [META_EXE] = "user.coredump.exe", }; int r = 0; @@ -251,7 +258,8 @@ static int fix_permissions( const char *filename, const char *target, const Context *context, - uid_t uid) { + uid_t uid, + bool allow_user) { int r; @@ -261,7 +269,7 @@ static int fix_permissions( /* Ignore errors on these */ (void) fchmod(fd, 0640); - (void) fix_acl(fd, uid); + (void) fix_acl(fd, uid, allow_user); (void) fix_xattr(fd, context); r = fsync_full(fd); @@ -331,6 +339,153 @@ static int make_filename(const Context *context, char **ret) { return 0; } +static int parse_auxv64( + const uint64_t *auxv, + size_t size_bytes, + int *at_secure, + uid_t *uid, + uid_t *euid, + gid_t *gid, + gid_t *egid) { + + assert(auxv || size_bytes == 0); + + if (size_bytes % (2 * sizeof(uint64_t)) != 0) + return log_warning_errno(SYNTHETIC_ERRNO(EIO), "Incomplete auxv structure (%zu bytes).", size_bytes); + + size_t words = size_bytes / sizeof(uint64_t); + + /* Note that we set output variables even on error. */ + + for (size_t i = 0; i + 1 < words; i += 2) + switch (auxv[i]) { + case AT_SECURE: + *at_secure = auxv[i + 1] != 0; + break; + case AT_UID: + *uid = auxv[i + 1]; + break; + case AT_EUID: + *euid = auxv[i + 1]; + break; + case AT_GID: + *gid = auxv[i + 1]; + break; + case AT_EGID: + *egid = auxv[i + 1]; + break; + case AT_NULL: + if (auxv[i + 1] != 0) + goto error; + return 0; + } + error: + return log_warning_errno(SYNTHETIC_ERRNO(ENODATA), + "AT_NULL terminator not found, cannot parse auxv structure."); +} + +static int parse_auxv32( + const uint32_t *auxv, + size_t size_bytes, + int *at_secure, + uid_t *uid, + uid_t *euid, + gid_t *gid, + gid_t *egid) { + + assert(auxv || size_bytes == 0); + + size_t words = size_bytes / sizeof(uint32_t); + + if (size_bytes % (2 * sizeof(uint32_t)) != 0) + return log_warning_errno(SYNTHETIC_ERRNO(EIO), "Incomplete auxv structure (%zu bytes).", size_bytes); + + /* Note that we set output variables even on error. */ + + for (size_t i = 0; i + 1 < words; i += 2) + switch (auxv[i]) { + case AT_SECURE: + *at_secure = auxv[i + 1] != 0; + break; + case AT_UID: + *uid = auxv[i + 1]; + break; + case AT_EUID: + *euid = auxv[i + 1]; + break; + case AT_GID: + *gid = auxv[i + 1]; + break; + case AT_EGID: + *egid = auxv[i + 1]; + break; + case AT_NULL: + if (auxv[i + 1] != 0) + goto error; + return 0; + } + error: + return log_warning_errno(SYNTHETIC_ERRNO(ENODATA), + "AT_NULL terminator not found, cannot parse auxv structure."); +} + +static int grant_user_access(int core_fd, const Context *context) { + int at_secure = -1; + uid_t uid = UID_INVALID, euid = UID_INVALID; + uid_t gid = GID_INVALID, egid = GID_INVALID; + int r; + + assert(core_fd >= 0); + assert(context); + + if (!context->meta[META_PROC_AUXV]) + return log_warning_errno(SYNTHETIC_ERRNO(ENODATA), "No auxv data, not adjusting permissions."); + + uint8_t elf[EI_NIDENT]; + errno = 0; + if (pread(core_fd, &elf, sizeof(elf), 0) != sizeof(elf)) + return log_warning_errno(errno_or_else(EIO), + "Failed to pread from coredump fd: %s", STRERROR_OR_EOF(errno)); + + if (elf[EI_MAG0] != ELFMAG0 || + elf[EI_MAG1] != ELFMAG1 || + elf[EI_MAG2] != ELFMAG2 || + elf[EI_MAG3] != ELFMAG3 || + elf[EI_VERSION] != EV_CURRENT) + return log_info_errno(SYNTHETIC_ERRNO(EUCLEAN), + "Core file does not have ELF header, not adjusting permissions."); + if (!IN_SET(elf[EI_CLASS], ELFCLASS32, ELFCLASS64) || + !IN_SET(elf[EI_DATA], ELFDATA2LSB, ELFDATA2MSB)) + return log_info_errno(SYNTHETIC_ERRNO(EUCLEAN), + "Core file has strange ELF class, not adjusting permissions."); + + if ((elf[EI_DATA] == ELFDATA2LSB) != (__BYTE_ORDER == __LITTLE_ENDIAN)) + return log_info_errno(SYNTHETIC_ERRNO(EUCLEAN), + "Core file has non-native endianness, not adjusting permissions."); + + if (elf[EI_CLASS] == ELFCLASS64) + r = parse_auxv64((const uint64_t*) context->meta[META_PROC_AUXV], + context->meta_size[META_PROC_AUXV], + &at_secure, &uid, &euid, &gid, &egid); + else + r = parse_auxv32((const uint32_t*) context->meta[META_PROC_AUXV], + context->meta_size[META_PROC_AUXV], + &at_secure, &uid, &euid, &gid, &egid); + if (r < 0) + return r; + + /* We allow access if we got all the data and at_secure is not set and + * the uid/gid matches euid/egid. */ + bool ret = + at_secure == 0 && + uid != UID_INVALID && euid != UID_INVALID && uid == euid && + gid != GID_INVALID && egid != GID_INVALID && gid == egid; + log_debug("Will %s access (uid="UID_FMT " euid="UID_FMT " gid="GID_FMT " egid="GID_FMT " at_secure=%s)", + ret ? "permit" : "restrict", + uid, euid, gid, egid, yes_no(at_secure)); + return ret; +} + static int save_external_coredump( const Context *context, int input_fd, @@ -453,6 +608,8 @@ static int save_external_coredump( context->meta[META_ARGV_PID], context->meta[META_COMM]); truncated = r == 1; + bool allow_user = grant_user_access(fd, context) > 0; + #if HAVE_COMPRESSION if (arg_compress) { _cleanup_(unlink_and_freep) char *tmp_compressed = NULL; @@ -490,7 +647,7 @@ static int save_external_coredump( uncompressed_size += partial_uncompressed_size; } - r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid); + r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid, allow_user); if (r < 0) return r; @@ -517,7 +674,7 @@ static int save_external_coredump( "SIZE_LIMIT=%"PRIu64, max_size, "MESSAGE_ID=" SD_MESSAGE_TRUNCATED_CORE_STR); - r = fix_permissions(fd, tmp, fn, context, uid); + r = fix_permissions(fd, tmp, fn, context, uid, allow_user); if (r < 0) return log_error_errno(r, "Failed to fix permissions and finalize coredump %s into %s: %m", coredump_tmpfile_name(tmp), fn); @@ -765,7 +922,7 @@ static int change_uid_gid(const Context *context) { } static int submit_coredump( - Context *context, + const Context *context, struct iovec_wrapper *iovw, int input_fd) { @@ -944,16 +1101,15 @@ static int save_context(Context *context, const struct iovec_wrapper *iovw) { struct iovec *iovec = iovw->iovec + n; for (size_t i = 0; i < ELEMENTSOF(meta_field_names); i++) { - char *p; - /* Note that these strings are NUL terminated, because we made sure that a * trailing NUL byte is in the buffer, though not included in the iov_len * count (see process_socket() and gather_pid_metadata_*()) */ assert(((char*) iovec->iov_base)[iovec->iov_len] == 0); - p = startswith(iovec->iov_base, meta_field_names[i]); + const char *p = startswith(iovec->iov_base, meta_field_names[i]); if (p) { context->meta[i] = p; + context->meta_size[i] = iovec->iov_len - strlen(meta_field_names[i]); break; } } @@ -1190,6 +1346,7 @@ static int gather_pid_metadata(struct iovec_wrapper *iovw, Context *context) { uid_t owner_uid; pid_t pid; char *t; + size_t size; const char *p; int r; @@ -1254,13 +1411,26 @@ static int gather_pid_metadata(struct iovec_wrapper *iovw, Context *context) { (void) iovw_put_string_field_free(iovw, "COREDUMP_PROC_LIMITS=", t); p = procfs_file_alloca(pid, "cgroup"); - if (read_full_virtual_file(p, &t, NULL) >=0) + if (read_full_virtual_file(p, &t, NULL) >= 0) (void) iovw_put_string_field_free(iovw, "COREDUMP_PROC_CGROUP=", t); p = procfs_file_alloca(pid, "mountinfo"); - if (read_full_virtual_file(p, &t, NULL) >=0) + if (read_full_virtual_file(p, &t, NULL) >= 0) (void) iovw_put_string_field_free(iovw, "COREDUMP_PROC_MOUNTINFO=", t); + /* We attach /proc/auxv here. ELF coredumps also contain a note for this (NT_AUXV), see elf(5). */ + p = procfs_file_alloca(pid, "auxv"); + if (read_full_virtual_file(p, &t, &size) >= 0) { + char *buf = malloc(strlen("COREDUMP_PROC_AUXV=") + size + 1); + if (buf) { + /* Add a dummy terminator to make save_context() happy. */ + *((uint8_t*) mempcpy(stpcpy(buf, "COREDUMP_PROC_AUXV="), t, size)) = '\0'; + (void) iovw_consume(iovw, buf, size + strlen("COREDUMP_PROC_AUXV=")); + } + + free(t); + } + if (get_process_cwd(pid, &t) >= 0) (void) iovw_put_string_field_free(iovw, "COREDUMP_CWD=", t); diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 30ea08702..52db7c03a 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -18,6 +18,7 @@ #include "compress.h" #include "def.h" #include "dissect-image.h" +#include "escape.h" #include "fd-util.h" #include "format-table.h" #include "fs-util.h" @@ -790,9 +791,10 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; r = json_parse(pkgmeta_json, 0, &v, NULL, NULL); - if (r < 0) - log_warning_errno(r, "json_parse on %s failed, ignoring: %m", pkgmeta_json); - else { + if (r < 0) { + _cleanup_free_ char *esc = cescape(pkgmeta_json); + log_warning_errno(r, "json_parse on \"%s\" failed, ignoring: %m", strnull(esc)); + } else { const char *module_name; JsonVariant *module_json; diff --git a/src/fundamental/string-util-fundamental.h b/src/fundamental/string-util-fundamental.h index ecf32e519..d82314788 100644 --- a/src/fundamental/string-util-fundamental.h +++ b/src/fundamental/string-util-fundamental.h @@ -110,6 +110,10 @@ static inline bool ascii_isdigit(sd_char a) { return a >= '0' && a <= '9'; } +static inline bool ascii_ishex(sd_char a) { + return ascii_isdigit(a) || (a >= 'a' && a <= 'f') || (a >= 'A' && a <= 'F'); +} + static inline bool ascii_isalpha(sd_char a) { /* A pure ASCII, locale independent version of isalpha() */ return (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z'); diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index d4a1fb689..40d6abc41 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -31,6 +31,7 @@ #define BUS_ERROR_NOTHING_TO_CLEAN "org.freedesktop.systemd1.NothingToClean" #define BUS_ERROR_UNIT_BUSY "org.freedesktop.systemd1.UnitBusy" #define BUS_ERROR_UNIT_INACTIVE "org.freedesktop.systemd1.UnitInactive" +#define BUS_ERROR_FREEZE_CANCELLED "org.freedesktop.systemd1.FreezeCancelled" #define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine" #define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage" diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index f2e142457..ff5d06565 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -2333,9 +2333,14 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, sysattr, value, ret_value ? "" : ", ignoring"); if (ret_value) return r; - } else if (ret_value) - *ret_value = TAKE_PTR(value); + return 0; + } + + if (ret_value) + *ret_value = value; + + TAKE_PTR(value); return 0; } diff --git a/src/libsystemd/sd-id128/id128-util.c b/src/libsystemd/sd-id128/id128-util.c index 4f52c14f6..2cf884898 100644 --- a/src/libsystemd/sd-id128/id128-util.c +++ b/src/libsystemd/sd-id128/id128-util.c @@ -13,59 +13,48 @@ #include "sync-util.h" bool id128_is_valid(const char *s) { - size_t i, l; + size_t l; assert(s); l = strlen(s); - if (l == 32) { + if (l == SD_ID128_STRING_MAX - 1) /* Plain formatted 128bit hex string */ + return in_charset(s, HEXDIGITS); - for (i = 0; i < l; i++) { - char c = s[i]; - - if (!ascii_isdigit(c) && - !(c >= 'a' && c <= 'f') && - !(c >= 'A' && c <= 'F')) - return false; - } - - } else if (l == 36) { - + if (l == SD_ID128_UUID_STRING_MAX - 1) { /* Formatted UUID */ - - for (i = 0; i < l; i++) { + for (size_t i = 0; i < l; i++) { char c = s[i]; if (IN_SET(i, 8, 13, 18, 23)) { if (c != '-') return false; - } else { - if (!ascii_isdigit(c) && - !(c >= 'a' && c <= 'f') && - !(c >= 'A' && c <= 'F')) - return false; - } + } else if (!ascii_ishex(c)) + return false; } + return true; + } - } else - return false; - - return true; + return false; } -int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) { - char buffer[36 + 2]; +int id128_read_fd(int fd, Id128FormatFlag f, sd_id128_t *ret) { + char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */ ssize_t l; assert(fd >= 0); - assert(f < _ID128_FORMAT_MAX); /* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both * optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they * aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you - * accept". */ + * accept". + * + * This returns the following: + * -ENOMEDIUM: an empty string, + * -ENOPKG: "uninitialized" or "uninitialized\n", + * -EINVAL: other invalid strings. */ l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */ if (l < 0) @@ -75,33 +64,32 @@ int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) { switch (l) { - case 13: - case 14: - /* Treat an "uninitialized" id file like an empty one */ - return f == ID128_PLAIN_OR_UNINIT && strneq(buffer, "uninitialized\n", l) ? -ENOMEDIUM : -EINVAL; + case STRLEN("uninitialized"): + case STRLEN("uninitialized\n"): + return strneq(buffer, "uninitialized\n", l) ? -ENOPKG : -EINVAL; - case 33: /* plain UUID with trailing newline */ - if (buffer[32] != '\n') + case SD_ID128_STRING_MAX: /* plain UUID with trailing newline */ + if (buffer[SD_ID128_STRING_MAX-1] != '\n') return -EINVAL; _fallthrough_; - case 32: /* plain UUID without trailing newline */ - if (f == ID128_UUID) + case SD_ID128_STRING_MAX-1: /* plain UUID without trailing newline */ + if (!FLAGS_SET(f, ID128_FORMAT_PLAIN)) return -EINVAL; - buffer[32] = 0; + buffer[SD_ID128_STRING_MAX-1] = 0; break; - case 37: /* RFC UUID with trailing newline */ - if (buffer[36] != '\n') + case SD_ID128_UUID_STRING_MAX: /* RFC UUID with trailing newline */ + if (buffer[SD_ID128_UUID_STRING_MAX-1] != '\n') return -EINVAL; _fallthrough_; - case 36: /* RFC UUID without trailing newline */ - if (IN_SET(f, ID128_PLAIN, ID128_PLAIN_OR_UNINIT)) + case SD_ID128_UUID_STRING_MAX-1: /* RFC UUID without trailing newline */ + if (!FLAGS_SET(f, ID128_FORMAT_UUID)) return -EINVAL; - buffer[36] = 0; + buffer[SD_ID128_UUID_STRING_MAX-1] = 0; break; default: @@ -111,7 +99,7 @@ int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) { return sd_id128_from_string(buffer, ret); } -int id128_read(const char *p, Id128Format f, sd_id128_t *ret) { +int id128_read(const char *p, Id128FormatFlag f, sd_id128_t *ret) { _cleanup_close_ int fd = -1; fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY); @@ -121,24 +109,23 @@ int id128_read(const char *p, Id128Format f, sd_id128_t *ret) { return id128_read_fd(fd, f, ret); } -int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) { - char buffer[36 + 2]; +int id128_write_fd(int fd, Id128FormatFlag f, sd_id128_t id, bool do_sync) { + char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */ size_t sz; int r; assert(fd >= 0); - assert(f < _ID128_FORMAT_MAX); + assert(IN_SET((f & ID128_FORMAT_ANY), ID128_FORMAT_PLAIN, ID128_FORMAT_UUID)); - if (f != ID128_UUID) { + if (FLAGS_SET(f, ID128_FORMAT_PLAIN)) { assert_se(sd_id128_to_string(id, buffer)); - buffer[SD_ID128_STRING_MAX - 1] = '\n'; sz = SD_ID128_STRING_MAX; } else { assert_se(sd_id128_to_uuid_string(id, buffer)); - buffer[SD_ID128_UUID_STRING_MAX - 1] = '\n'; sz = SD_ID128_UUID_STRING_MAX; } + buffer[sz - 1] = '\n'; r = loop_write(fd, buffer, sz, false); if (r < 0) return r; @@ -152,7 +139,7 @@ int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) { return 0; } -int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) { +int id128_write(const char *p, Id128FormatFlag f, sd_id128_t id, bool do_sync) { _cleanup_close_ int fd = -1; fd = open(p, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, 0444); @@ -194,9 +181,9 @@ int id128_get_product(sd_id128_t *ret) { /* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is * particularly relevant in VM environments, where VM managers typically place a VM uuid there. */ - r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid); + r = id128_read("/sys/class/dmi/id/product_uuid", ID128_FORMAT_UUID, &uuid); if (r == -ENOENT) - r = id128_read("/proc/device-tree/vm,uuid", ID128_UUID, &uuid); + r = id128_read("/proc/device-tree/vm,uuid", ID128_FORMAT_UUID, &uuid); if (r < 0) return r; diff --git a/src/libsystemd/sd-id128/id128-util.h b/src/libsystemd/sd-id128/id128-util.h index 17b180c10..bb237cb6c 100644 --- a/src/libsystemd/sd-id128/id128-util.h +++ b/src/libsystemd/sd-id128/id128-util.h @@ -10,22 +10,17 @@ bool id128_is_valid(const char *s) _pure_; -typedef enum Id128Format { - ID128_ANY, - ID128_PLAIN, /* formatted as 32 hex chars as-is */ - ID128_PLAIN_OR_UNINIT, /* formatted as 32 hex chars as-is; allow special "uninitialized" - * value when reading from file (id128_read() and id128_read_fd()). - * - * This format should be used when reading a machine-id file. */ - ID128_UUID, /* formatted as 36 character uuid string */ - _ID128_FORMAT_MAX, -} Id128Format; +typedef enum Id128FormatFlag { + ID128_FORMAT_PLAIN = 1 << 0, /* formatted as 32 hex chars as-is */ + ID128_FORMAT_UUID = 1 << 1, /* formatted as 36 character uuid string */ + ID128_FORMAT_ANY = ID128_FORMAT_PLAIN | ID128_FORMAT_UUID, +} Id128FormatFlag; -int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret); -int id128_read(const char *p, Id128Format f, sd_id128_t *ret); +int id128_read_fd(int fd, Id128FormatFlag f, sd_id128_t *ret); +int id128_read(const char *p, Id128FormatFlag f, sd_id128_t *ret); -int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync); -int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync); +int id128_write_fd(int fd, Id128FormatFlag f, sd_id128_t id, bool do_sync); +int id128_write(const char *p, Id128FormatFlag f, sd_id128_t id, bool do_sync); void id128_hash_func(const sd_id128_t *p, struct siphash *state); int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_; diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c index 709c8ffb5..5e9ec2b5f 100644 --- a/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libsystemd/sd-id128/sd-id128.c @@ -19,14 +19,17 @@ #include "util.h" _public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) { + size_t k = 0; + assert_return(s, NULL); - for (size_t n = 0; n < 16; n++) { - s[n*2] = hexchar(id.bytes[n] >> 4); - s[n*2+1] = hexchar(id.bytes[n] & 0xF); + for (size_t n = 0; n < sizeof(sd_id128_t); n++) { + s[k++] = hexchar(id.bytes[n] >> 4); + s[k++] = hexchar(id.bytes[n] & 0xF); } - s[SD_ID128_STRING_MAX-1] = 0; + assert(k == SD_ID128_STRING_MAX - 1); + s[k] = 0; return s; } @@ -38,7 +41,7 @@ _public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */ - for (size_t n = 0; n < 16; n++) { + for (size_t n = 0; n < sizeof(sd_id128_t); n++) { if (IN_SET(n, 4, 6, 8, 10)) s[k++] = '-'; @@ -53,14 +56,14 @@ _public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD return s; } -_public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) { - unsigned n, i; +_public_ int sd_id128_from_string(const char *s, sd_id128_t *ret) { + size_t n, i; sd_id128_t t; bool is_guid = false; assert_return(s, -EINVAL); - for (n = 0, i = 0; n < 16;) { + for (n = 0, i = 0; n < sizeof(sd_id128_t);) { int a, b; if (s[i] == '-') { @@ -90,7 +93,7 @@ _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) { t.bytes[n++] = (a << 4) | b; } - if (i != (is_guid ? 36 : 32)) + if (i != (is_guid ? SD_ID128_UUID_STRING_MAX : SD_ID128_STRING_MAX) - 1) return -EINVAL; if (s[i] != 0) @@ -124,7 +127,7 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) { assert_return(ret, -EINVAL); if (sd_id128_is_null(saved_machine_id)) { - r = id128_read("/etc/machine-id", ID128_PLAIN, &saved_machine_id); + r = id128_read("/etc/machine-id", ID128_FORMAT_PLAIN, &saved_machine_id); if (r < 0) return r; @@ -143,7 +146,7 @@ _public_ int sd_id128_get_boot(sd_id128_t *ret) { assert_return(ret, -EINVAL); if (sd_id128_is_null(saved_boot_id)) { - r = id128_read("/proc/sys/kernel/random/boot_id", ID128_UUID, &saved_boot_id); + r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID, &saved_boot_id); if (r < 0) return r; } diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c index c1ec6bb1d..5c414afca 100644 --- a/src/libsystemd/sd-journal/journal-file.c +++ b/src/libsystemd/sd-journal/journal-file.c @@ -370,7 +370,7 @@ static int journal_file_refresh_header(JournalFile *f) { assert(f->header); r = sd_id128_get_machine(&f->header->machine_id); - if (IN_SET(r, -ENOENT, -ENOMEDIUM)) + if (IN_SET(r, -ENOENT, -ENOMEDIUM, -ENOPKG)) /* We don't have a machine-id, let's continue without */ zero(f->header->machine_id); else if (r < 0) diff --git a/src/machine-id-setup/machine-id-setup-main.c b/src/machine-id-setup/machine-id-setup-main.c index 8a3b1efb4..b595b2ab3 100644 --- a/src/machine-id-setup/machine-id-setup-main.c +++ b/src/machine-id-setup/machine-id-setup-main.c @@ -164,7 +164,7 @@ static int run(int argc, char *argv[]) { return r; etc_machine_id = prefix_roota(arg_root, "/etc/machine-id"); - r = id128_read(etc_machine_id, ID128_PLAIN, &id); + r = id128_read(etc_machine_id, ID128_FORMAT_PLAIN, &id); if (r < 0) return log_error_errno(r, "Failed to read machine ID back: %m"); } else { diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 130cc3f7d..63fca75ad 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -2195,7 +2195,7 @@ static int setup_boot_id(void) { if (r < 0) return log_error_errno(r, "Failed to generate random boot id: %m"); - r = id128_write(path, ID128_UUID, rnd, false); + r = id128_write(path, ID128_FORMAT_UUID, rnd, false); if (r < 0) return log_error_errno(r, "Failed to write boot id: %m"); @@ -2821,9 +2821,9 @@ static int setup_machine_id(const char *directory) { etc_machine_id = prefix_roota(directory, "/etc/machine-id"); - r = id128_read(etc_machine_id, ID128_PLAIN_OR_UNINIT, &id); + r = id128_read(etc_machine_id, ID128_FORMAT_PLAIN, &id); if (r < 0) { - if (!IN_SET(r, -ENOENT, -ENOMEDIUM)) /* If the file is missing or empty, we don't mind */ + if (!IN_SET(r, -ENOENT, -ENOMEDIUM, -ENOPKG)) /* If the file is missing, empty, or uninitialized, we don't mind */ return log_error_errno(r, "Failed to read machine ID from container image: %m"); if (sd_id128_is_null(arg_uuid)) { diff --git a/src/partition/repart.c b/src/partition/repart.c index c4ca9840c..a15ed5b13 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -4473,8 +4473,8 @@ static int context_read_seed(Context *context, const char *root) { else if (fd < 0) return log_error_errno(fd, "Failed to determine machine ID of image: %m"); else { - r = id128_read_fd(fd, ID128_PLAIN_OR_UNINIT, &context->seed); - if (r == -ENOMEDIUM) + r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &context->seed); + if (IN_SET(r, -ENOMEDIUM, -ENOPKG)) log_info("No machine ID set, using randomized partition UUIDs."); else if (r < 0) return log_error_errno(r, "Failed to parse machine ID of image: %m"); diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index a4ba6bdc6..5f890f950 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -1768,36 +1768,30 @@ int dns_resource_record_get_cname_target(DnsResourceKey *key, DnsResourceRecord return 0; } -DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) { - DnsTxtItem *n; +DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *first) { + LIST_FOREACH(items, i, first) + free(i); - if (!i) - return NULL; - - n = i->items_next; - - free(i); - return dns_txt_item_free_all(n); + return NULL; } bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) { + DnsTxtItem *bb = b; if (a == b) return true; - if (!a != !b) - return false; + LIST_FOREACH(items, aa, a) { + if (!bb) + return false; - if (!a) - return true; + if (memcmp_nn(aa->data, aa->length, bb->data, bb->length) != 0) + return false; - if (a->length != b->length) - return false; + bb = bb->items_next; + } - if (memcmp(a->data, b->data, a->length) != 0) - return false; - - return dns_txt_item_equal(a->items_next, b->items_next); + return !bb; } DnsTxtItem *dns_txt_item_copy(DnsTxtItem *first) { diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index 5d740de26..021132b0f 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -1153,7 +1153,7 @@ int image_read_metadata(Image *i) { if (fd < 0) log_debug_errno(errno, "Failed to open %s: %m", path); else { - r = id128_read_fd(fd, ID128_PLAIN, &machine_id); + r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &machine_id); if (r < 0) log_debug_errno(r, "Image %s contains invalid machine ID.", i->name); } diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 4d45ed4ae..0f1b7281b 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -74,9 +74,15 @@ /* how many times to wait for the device nodes to appear */ #define N_DEVICE_NODE_LIST_ATTEMPTS 10 -int probe_filesystem_full(int fd, const char *path, char **ret_fstype) { +int probe_filesystem_full( + int fd, + const char *path, + uint64_t offset, + uint64_t size, + char **ret_fstype) { + /* Try to find device content type and return it in *ret_fstype. If nothing is found, - * 0/NULL will be returned. -EUCLEAN will be returned for ambiguous results, and an + * 0/NULL will be returned. -EUCLEAN will be returned for ambiguous results, and a * different error otherwise. */ #if HAVE_BLKID @@ -105,12 +111,19 @@ int probe_filesystem_full(int fd, const char *path, char **ret_fstype) { path = path_by_fd; } + if (size == 0) /* empty size? nothing found! */ + goto not_found; + b = blkid_new_probe(); if (!b) return -ENOMEM; errno = 0; - r = blkid_probe_set_device(b, fd, 0, 0); + r = blkid_probe_set_device( + b, + fd, + offset, + size == UINT64_MAX ? 0 : size); /* when blkid sees size=0 it understands "everything". We prefer using UINT64_MAX for that */ if (r != 0) return errno_or_else(ENOMEM); @@ -152,7 +165,7 @@ not_found: } #if HAVE_BLKID -static int dissected_image_probe_filesystem(DissectedImage *m) { +static int dissected_image_probe_filesystems(DissectedImage *m, int fd) { int r; assert(m); @@ -165,9 +178,14 @@ static int dissected_image_probe_filesystem(DissectedImage *m) { if (!p->found) continue; - if (!p->fstype && p->mount_node_fd >= 0 && !p->decrypted_node) { - r = probe_filesystem_full(p->mount_node_fd, p->node, &p->fstype); - if (r < 0 && r != -EUCLEAN) + if (!p->fstype) { + /* If we have an fd referring to the partition block device, use that. Otherwise go + * via the whole block device or backing regular file, and read via offset. */ + if (p->mount_node_fd >= 0) + r = probe_filesystem_full(p->mount_node_fd, p->node, 0, UINT64_MAX, &p->fstype); + else + r = probe_filesystem_full(fd, p->node, p->offset, p->size, &p->fstype); + if (r < 0) return r; } @@ -1217,6 +1235,10 @@ static int dissect_image( } } + r = dissected_image_probe_filesystems(m, fd); + if (r < 0) + return r; + return 0; } #endif @@ -2339,7 +2361,7 @@ int dissected_image_decrypt( } if (!p->decrypted_fstype && p->mount_node_fd >= 0 && p->decrypted_node) { - r = probe_filesystem_full(p->mount_node_fd, p->decrypted_node, &p->decrypted_fstype); + r = probe_filesystem_full(p->mount_node_fd, p->decrypted_node, 0, UINT64_MAX, &p->decrypted_fstype); if (r < 0 && r != -EUCLEAN) return r; } @@ -3041,10 +3063,6 @@ int dissect_loop_device( if (r < 0) return r; - r = dissected_image_probe_filesystem(m); - if (r < 0) - return r; - *ret = TAKE_PTR(m); return 0; #else diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 631d4c7a0..1fd03ee91 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -271,9 +271,9 @@ MountOptions* mount_options_free_all(MountOptions *options); DEFINE_TRIVIAL_CLEANUP_FUNC(MountOptions*, mount_options_free_all); const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator); -int probe_filesystem_full(int fd, const char *path, char **ret_fstype); +int probe_filesystem_full(int fd, const char *path, uint64_t offset, uint64_t size, char **ret_fstype); static inline int probe_filesystem(const char *path, char **ret_fstype) { - return probe_filesystem_full(-1, path, ret_fstype); + return probe_filesystem_full(-1, path, 0, UINT64_MAX, ret_fstype); } int dissect_image_file( const char *path, diff --git a/src/shared/elf-util.c b/src/shared/elf-util.c index 181735409..294504e58 100644 --- a/src/shared/elf-util.c +++ b/src/shared/elf-util.c @@ -15,6 +15,7 @@ #include "dlfcn-util.h" #include "elf-util.h" #include "errno-util.h" +#include "escape.h" #include "fileio.h" #include "fd-util.h" #include "format-util.h" @@ -397,8 +398,10 @@ static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *e } r = json_parse(payload, 0, &v, NULL, NULL); - if (r < 0) - return log_error_errno(r, "json_parse on %s failed: %m", payload); + if (r < 0) { + _cleanup_free_ char *esc = cescape(payload); + return log_error_errno(r, "json_parse on \"%s\" failed: %m", strnull(esc)); + } /* If we have a build-id, merge it in the same JSON object so that it appears all * nicely together in the logs/metadata. */ diff --git a/src/shared/machine-id-setup.c b/src/shared/machine-id-setup.c index df4ac419c..787c0765d 100644 --- a/src/shared/machine-id-setup.c +++ b/src/shared/machine-id-setup.c @@ -38,7 +38,7 @@ static int generate_machine_id(const char *root, sd_id128_t *ret) { dbus_machine_id = prefix_roota(root, "/var/lib/dbus/machine-id"); fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); if (fd >= 0) { - if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0) { + if (id128_read_fd(fd, ID128_FORMAT_PLAIN, ret) >= 0) { log_info("Initializing machine ID from D-Bus machine ID."); return 0; } @@ -123,7 +123,7 @@ int machine_id_setup(const char *root, bool force_transient, sd_id128_t machine_ if (sd_id128_is_null(machine_id)) { /* Try to read any existing machine ID */ - if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0) + if (id128_read_fd(fd, ID128_FORMAT_PLAIN, ret) >= 0) return 0; /* Hmm, so, the id currently stored is not useful, then let's generate one */ @@ -152,7 +152,7 @@ int machine_id_setup(const char *root, bool force_transient, sd_id128_t machine_ if (r < 0) return log_error_errno(r, "Failed to sync %s: %m", etc_machine_id); } else { - r = id128_write_fd(fd, ID128_PLAIN, machine_id, true); + r = id128_write_fd(fd, ID128_FORMAT_PLAIN, machine_id, true); if (r < 0) return log_error_errno(r, "Failed to write %s: %m", etc_machine_id); else @@ -168,7 +168,7 @@ int machine_id_setup(const char *root, bool force_transient, sd_id128_t machine_ run_machine_id = prefix_roota(root, "/run/machine-id"); RUN_WITH_UMASK(0022) - r = id128_write(run_machine_id, ID128_PLAIN, machine_id, false); + r = id128_write(run_machine_id, ID128_FORMAT_PLAIN, machine_id, false); if (r < 0) { (void) unlink(run_machine_id); return log_error_errno(r, "Cannot write %s: %m", run_machine_id); @@ -240,7 +240,7 @@ int machine_id_commit(const char *root) { "%s is not on a temporary file system.", etc_machine_id); - r = id128_read_fd(fd, ID128_PLAIN, &id); + r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &id); if (r < 0) return log_error_errno(r, "We didn't find a valid machine ID in %s: %m", etc_machine_id); @@ -261,7 +261,7 @@ int machine_id_commit(const char *root) { return r; /* Update a persistent version of etc_machine_id */ - r = id128_write(etc_machine_id, ID128_PLAIN, id, true); + r = id128_write(etc_machine_id, ID128_FORMAT_PLAIN, id, true); if (r < 0) return log_error_errno(r, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id); diff --git a/src/shared/specifier.c b/src/shared/specifier.c index d54ab9f5a..cd651768b 100644 --- a/src/shared/specifier.c +++ b/src/shared/specifier.c @@ -195,7 +195,7 @@ int specifier_machine_id(char specifier, const void *data, const char *root, con /* Translate error for missing os-release file to EUNATCH. */ return fd == -ENOENT ? -EUNATCH : fd; - r = id128_read_fd(fd, ID128_PLAIN, &id); + r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &id); } else r = sd_id128_get_machine(&id); if (r < 0) diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index 3461d3e45..30ba5d297 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -374,7 +374,7 @@ static int freeze_thaw_user_slice(const char **method) { } static int execute_s2h(const SleepConfig *sleep_config) { - _unused_ _cleanup_(freeze_thaw_user_slice) const char *auto_method_thaw = NULL; + _unused_ _cleanup_(freeze_thaw_user_slice) const char *auto_method_thaw = "ThawUnit"; int r, k; assert(sleep_config); @@ -382,8 +382,6 @@ static int execute_s2h(const SleepConfig *sleep_config) { r = freeze_thaw_user_slice(&(const char*) { "FreezeUnit" }); if (r < 0) log_debug_errno(r, "Failed to freeze unit user.slice, ignoring: %m"); - else - auto_method_thaw = "ThawUnit"; /* from now on we want automatic thawing */; r = check_wakeup_type(); if (r < 0) diff --git a/src/test/test-condition.c b/src/test/test-condition.c index 4cd23d8e2..83ee2d5ab 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -250,7 +250,7 @@ TEST(condition_test_host) { int r; r = sd_id128_get_machine(&id); - if (IN_SET(r, -ENOENT, -ENOMEDIUM)) + if (IN_SET(r, -ENOENT, -ENOMEDIUM, -ENOPKG)) return (void) log_tests_skipped("/etc/machine-id missing"); assert_se(r >= 0); diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index 11d79cce1..e6c150054 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -326,7 +326,7 @@ TEST(chase_symlinks) { assert_se(fd >= 0); safe_close(pfd); - assert_se(id128_read_fd(fd, ID128_PLAIN, &a) >= 0); + assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &a) >= 0); assert_se(sd_id128_get_machine(&b) >= 0); assert_se(sd_id128_equal(a, b)); } diff --git a/src/test/test-id128.c b/src/test/test-id128.c index 4b71c5c00..dccf3b7fb 100644 --- a/src/test/test-id128.c +++ b/src/test/test-id128.c @@ -86,17 +86,17 @@ TEST(id128) { /* First, write as UUID */ assert_se(sd_id128_randomize(&id) >= 0); - assert_se(id128_write_fd(fd, ID128_UUID, id, false) >= 0); + assert_se(id128_write_fd(fd, ID128_FORMAT_UUID, id, false) >= 0); assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) == -EINVAL); + assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2) == -EINVAL); assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_UUID, &id2) >= 0); + assert_se(id128_read_fd(fd, ID128_FORMAT_UUID, &id2) >= 0); assert_se(sd_id128_equal(id, id2)); assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_ANY, &id2) >= 0); + assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, &id2) >= 0); assert_se(sd_id128_equal(id, id2)); /* Second, write as plain */ @@ -104,17 +104,17 @@ TEST(id128) { assert_se(ftruncate(fd, 0) >= 0); assert_se(sd_id128_randomize(&id) >= 0); - assert_se(id128_write_fd(fd, ID128_PLAIN, id, false) >= 0); + assert_se(id128_write_fd(fd, ID128_FORMAT_PLAIN, id, false) >= 0); assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_UUID, &id2) == -EINVAL); + assert_se(id128_read_fd(fd, ID128_FORMAT_UUID, &id2) == -EINVAL); assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) >= 0); + assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2) >= 0); assert_se(sd_id128_equal(id, id2)); assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_ANY, &id2) >= 0); + assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, &id2) >= 0); assert_se(sd_id128_equal(id, id2)); /* Third, write plain without trailing newline */ @@ -125,13 +125,13 @@ TEST(id128) { assert_se(write(fd, sd_id128_to_string(id, t), 32) == 32); assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_UUID, &id2) == -EINVAL); + assert_se(id128_read_fd(fd, ID128_FORMAT_UUID, &id2) == -EINVAL); assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) >= 0); + assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2) >= 0); assert_se(sd_id128_equal(id, id2)); - /* Third, write UUID without trailing newline */ + /* Fourth, write UUID without trailing newline */ assert_se(lseek(fd, 0, SEEK_SET) == 0); assert_se(ftruncate(fd, 0) >= 0); @@ -139,12 +139,37 @@ TEST(id128) { assert_se(write(fd, sd_id128_to_uuid_string(id, q), 36) == 36); assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) == -EINVAL); + assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2) == -EINVAL); assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(id128_read_fd(fd, ID128_UUID, &id2) >= 0); + assert_se(id128_read_fd(fd, ID128_FORMAT_UUID, &id2) >= 0); assert_se(sd_id128_equal(id, id2)); + /* Fifth, tests for "uninitialized" */ + assert_se(lseek(fd, 0, SEEK_SET) == 0); + assert_se(ftruncate(fd, 0) >= 0); + assert_se(write(fd, "uninitialized", STRLEN("uninitialized")) == STRLEN("uninitialized")); + assert_se(lseek(fd, 0, SEEK_SET) == 0); + assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -ENOPKG); + + assert_se(lseek(fd, 0, SEEK_SET) == 0); + assert_se(ftruncate(fd, 0) >= 0); + assert_se(write(fd, "uninitialized\n", STRLEN("uninitialized\n")) == STRLEN("uninitialized\n")); + assert_se(lseek(fd, 0, SEEK_SET) == 0); + assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -ENOPKG); + + assert_se(lseek(fd, 0, SEEK_SET) == 0); + assert_se(ftruncate(fd, 0) >= 0); + assert_se(write(fd, "uninitialized\nfoo", STRLEN("uninitialized\nfoo")) == STRLEN("uninitialized\nfoo")); + assert_se(lseek(fd, 0, SEEK_SET) == 0); + assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -EINVAL); + + assert_se(lseek(fd, 0, SEEK_SET) == 0); + assert_se(ftruncate(fd, 0) >= 0); + assert_se(write(fd, "uninit", STRLEN("uninit")) == STRLEN("uninit")); + assert_se(lseek(fd, 0, SEEK_SET) == 0); + assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -EINVAL); + if (sd_booted() > 0 && access("/etc/machine-id", F_OK) >= 0) { assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id) >= 0); assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id2) >= 0); diff --git a/src/test/test-loop-block.c b/src/test/test-loop-block.c index af2a9683a..b06ab0d17 100644 --- a/src/test/test-loop-block.c +++ b/src/test/test-loop-block.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include "alloc-util.h" #include "capability-util.h" @@ -44,6 +46,15 @@ static void verify_dissected_image(DissectedImage *dissected) { assert_se(dissected->partitions[PARTITION_HOME].node); } +static void verify_dissected_image_harder(DissectedImage *dissected) { + verify_dissected_image(dissected); + + assert_se(streq(dissected->partitions[PARTITION_ESP].fstype, "vfat")); + assert_se(streq(dissected->partitions[PARTITION_XBOOTLDR].fstype, "vfat")); + assert_se(streq(dissected->partitions[PARTITION_ROOT].fstype, "ext4")); + assert_se(streq(dissected->partitions[PARTITION_HOME].fstype, "ext4")); +} + static void* thread_func(void *ptr) { int fd = PTR_TO_FD(ptr); int r; @@ -246,8 +257,23 @@ static int run(int argc, char *argv[]) { assert_se(make_filesystem(dissected->partitions[PARTITION_HOME].node, "ext4", "home", NULL, id, true) >= 0); dissected = dissected_image_unref(dissected); + + /* We created the file systems now via the per-partition block devices. But the dissection code might + * probe them via the whole block device. These block devices have separate buffer caches though, + * hence what was written via the partition device might not appear on the whole block device + * yet. Let's hence explicitly flush the whole block device, so that the read-back definitely + * works. */ + assert_se(ioctl(loop->fd, BLKFLSBUF, 0) >= 0); + + /* Try to read once, without pinning or adding partitions, i.e. by only accessing the whole block + * device. */ + assert_se(dissect_loop_device(loop, NULL, NULL, 0, &dissected) >= 0); + verify_dissected_image_harder(dissected); + dissected = dissected_image_unref(dissected); + + /* Now go via the loopback device after all, but this time add/pin, because now we want to mount it. */ assert_se(dissect_loop_device(loop, NULL, NULL, DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES, &dissected) >= 0); - verify_dissected_image(dissected); + verify_dissected_image_harder(dissected); assert_se(mkdtemp_malloc(NULL, &mounted) >= 0); diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c index e9fa00aac..ea1872d0c 100644 --- a/src/timesync/timesyncd-manager.c +++ b/src/timesync/timesyncd-manager.c @@ -119,8 +119,10 @@ static int manager_send_request(Manager *m) { m->event_timeout = sd_event_source_unref(m->event_timeout); r = manager_listen_setup(m); - if (r < 0) - return log_warning_errno(r, "Failed to set up connection socket: %m"); + if (r < 0) { + log_warning_errno(r, "Failed to set up connection socket: %m"); + return manager_connect(m); + } /* * Set transmit timestamp, remember it; the server will send that back @@ -752,7 +754,7 @@ static int manager_resolve_handler(sd_resolve_query *q, int ret, const struct ad assert(ai->ai_addrlen >= offsetof(struct sockaddr, sa_data)); if (!IN_SET(ai->ai_addr->sa_family, AF_INET, AF_INET6)) { - log_warning("Unsuitable address protocol for %s", m->current_server_name->string); + log_debug("Ignoring unsuitable address protocol for %s.", m->current_server_name->string); continue; } @@ -806,11 +808,6 @@ int manager_connect(Manager *m) { if (m->current_server_address && m->current_server_address->addresses_next) manager_set_server_address(m, m->current_server_address->addresses_next); else { - static const struct addrinfo hints = { - .ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG, - .ai_socktype = SOCK_DGRAM, - }; - /* Hmm, we are through all addresses, let's look for the next host instead */ if (m->current_server_name && m->current_server_name->names_next) manager_set_server_name(m, m->current_server_name->names_next); @@ -878,6 +875,12 @@ int manager_connect(Manager *m) { log_debug("Resolving %s...", m->current_server_name->string); + struct addrinfo hints = { + .ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG, + .ai_socktype = SOCK_DGRAM, + .ai_family = socket_ipv6_is_supported() ? AF_UNSPEC : AF_INET, + }; + r = resolve_getaddrinfo(m->resolve, &m->resolve_query, m->current_server_name->string, "123", &hints, manager_resolve_handler, NULL, m); if (r < 0) return log_error_errno(r, "Failed to create resolver: %m"); diff --git a/test/test-functions b/test/test-functions index 194cd682b..561321532 100644 --- a/test/test-functions +++ b/test/test-functions @@ -1328,7 +1328,7 @@ create_empty_image() { fi # Partition sizes are in MiBs - local root_size=500 + local root_size=1000 local data_size=50 if ! get_bool "$NO_BUILD"; then if meson configure "${BUILD_DIR:?}" | grep 'static-lib\|standalone-binaries' | awk '{ print $2 }' | grep -q 'true'; then diff --git a/test/units/testsuite-26.sh b/test/units/testsuite-26.sh index 7c7a12b1a..a8e7a5aba 100755 --- a/test/units/testsuite-26.sh +++ b/test/units/testsuite-26.sh @@ -3,10 +3,18 @@ set -eux set -o pipefail +# shellcheck source=test/units/assert.sh +. "$(dirname "$0")"/assert.sh + +: >/failed + at_exit() { if [[ -v UNIT_NAME && -e "/usr/lib/systemd/system/$UNIT_NAME" ]]; then rm -fv "/usr/lib/systemd/system/$UNIT_NAME" fi + + rm -f /etc/init.d/issue-24990 + return 0 } trap at_exit EXIT @@ -284,6 +292,110 @@ systemctl unset-environment IMPORT_THIS IMPORT_THIS_TOO (! systemctl show-environment | grep "^IMPORT_THIS=") (! systemctl show-environment | grep "^IMPORT_THIS_TOO=") -echo OK >/testok +# test for sysv-generator (issue #24990) +if [[ -x /usr/lib/systemd/system-generators/systemd-sysv-generator ]]; then -exit 0 + # invalid dependency + cat >/etc/init.d/issue-24990 <<\EOF +#!/bin/bash + +### BEGIN INIT INFO +# Provides:test1 test2 +# Required-Start:test1 $remote_fs $network +# Required-Stop:test1 $remote_fs $network +# Description:Test +# Short-Description: Test +### END INIT INFO + +case "$1" in + start) + echo "Starting issue-24990.service" + sleep 1000 & + ;; + stop) + echo "Stopping issue-24990.service" + sleep 10 & + ;; + *) + echo "Usage: service test {start|stop|restart|status}" + ;; +esac +EOF + + chmod +x /etc/init.d/issue-24990 + systemctl daemon-reload + [[ -L /run/systemd/generator.late/test1.service ]] + [[ -L /run/systemd/generator.late/test2.service ]] + assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service" + assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service" + output=$(systemctl cat issue-24990) + assert_in "SourcePath=/etc/init.d/issue-24990" "$output" + assert_in "Description=LSB: Test" "$output" + assert_in "After=test1.service" "$output" + assert_in "After=remote-fs.target" "$output" + assert_in "After=network-online.target" "$output" + assert_in "Wants=network-online.target" "$output" + assert_in "ExecStart=/etc/init.d/issue-24990 start" "$output" + assert_in "ExecStop=/etc/init.d/issue-24990 stop" "$output" + systemctl status issue-24990 || : + systemctl show issue-24990 + assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)" + assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)" + + if ! systemctl is-active network-online.target; then + systemctl start network-online.target + fi + + systemctl restart issue-24990 + systemctl stop issue-24990 + + # valid dependency + cat >/etc/init.d/issue-24990 <<\EOF +#!/bin/bash + +### BEGIN INIT INFO +# Provides:test1 test2 +# Required-Start:$remote_fs +# Required-Stop:$remote_fs +# Description:Test +# Short-Description: Test +### END INIT INFO + +case "$1" in + start) + echo "Starting issue-24990.service" + sleep 1000 & + ;; + stop) + echo "Stopping issue-24990.service" + sleep 10 & + ;; + *) + echo "Usage: service test {start|stop|restart|status}" + ;; +esac +EOF + + chmod +x /etc/init.d/issue-24990 + systemctl daemon-reload + [[ -L /run/systemd/generator.late/test1.service ]] + [[ -L /run/systemd/generator.late/test2.service ]] + assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service" + assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service" + output=$(systemctl cat issue-24990) + assert_in "SourcePath=/etc/init.d/issue-24990" "$output" + assert_in "Description=LSB: Test" "$output" + assert_in "After=remote-fs.target" "$output" + assert_in "ExecStart=/etc/init.d/issue-24990 start" "$output" + assert_in "ExecStop=/etc/init.d/issue-24990 stop" "$output" + systemctl status issue-24990 || : + systemctl show issue-24990 + assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)" + assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)" + + systemctl restart issue-24990 + systemctl stop issue-24990 +fi + +touch /testok +rm /failed