diff options
Diffstat (limited to 'src')
68 files changed, 1661 insertions, 961 deletions
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index f70878fb8e..06bae8decf 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -14,6 +14,7 @@ #include "dirent-util.h" #include "fd-util.h" #include "fs-util.h" +#include "locale-util.h" #include "log.h" #include "macro.h" #include "missing.h" @@ -663,15 +664,42 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) { return r; } -static bool safe_transition(const struct stat *a, const struct stat *b) { +static bool unsafe_transition(const struct stat *a, const struct stat *b) { /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files * making us believe we read something safe even though it isn't safe in the specific context we open it in. */ if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */ - return true; + return false; - return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */ + return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */ +} + +static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) { + _cleanup_free_ char *n1 = NULL, *n2 = NULL; + + if (!FLAGS_SET(flags, CHASE_WARN)) + return -ENOLINK; + + (void) fd_get_path(a, &n1); + (void) fd_get_path(b, &n2); + + return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK), + "Detected unsafe path transition %s %s %s during canonicalization of %s.", + n1, special_glyph(ARROW), n2, path); +} + +static int log_autofs_mount_point(int fd, const char *path, unsigned flags) { + _cleanup_free_ char *n1 = NULL; + + if (!FLAGS_SET(flags, CHASE_WARN)) + return -EREMOTE; + + (void) fd_get_path(fd, &n1); + + return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE), + "Detected autofs mount point %s during canonicalization of %s.", + n1, path); } int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) { @@ -734,6 +762,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, * path is fully normalized, and == 0 for each normalization step. This may be combined with * CHASE_NONEXISTENT, in which case 1 is returned when a component is not found. * + * 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions from + * unprivileged to privileged files or directories. In such cases the return value is -ENOLINK. If + * CHASE_WARN is also set a warning describing the unsafe transition is emitted. + * + * 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, the path normalization is + * aborted and -EREMOTE is returned. If CHASE_WARN is also set a warning showing the path of the mount point + * is emitted. + * * */ /* A root directory of "/" or "" is identical to none */ @@ -848,8 +884,8 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fstat(fd_parent, &st) < 0) return -errno; - if (!safe_transition(&previous_stat, &st)) - return -EPERM; + if (unsafe_transition(&previous_stat, &st)) + return log_unsafe_transition(fd, fd_parent, path, flags); previous_stat = st; } @@ -889,14 +925,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fstat(child, &st) < 0) return -errno; if ((flags & CHASE_SAFE) && - !safe_transition(&previous_stat, &st)) - return -EPERM; + unsafe_transition(&previous_stat, &st)) + return log_unsafe_transition(fd, child, path, flags); previous_stat = st; if ((flags & CHASE_NO_AUTOFS) && fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0) - return -EREMOTE; + return log_autofs_mount_point(child, path, flags); if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) { char *joined; @@ -928,8 +964,8 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fstat(fd, &st) < 0) return -errno; - if (!safe_transition(&previous_stat, &st)) - return -EPERM; + if (unsafe_transition(&previous_stat, &st)) + return log_unsafe_transition(child, fd, path, flags); previous_stat = st; } diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 955b146a6a..7ad030be5d 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -74,6 +74,7 @@ enum { CHASE_TRAIL_SLASH = 1 << 5, /* If set, any trailing slash will be preserved */ CHASE_STEP = 1 << 6, /* If set, just execute a single step of the normalization */ CHASE_NOFOLLOW = 1 << 7, /* Only valid with CHASE_OPEN: when the path's right-most component refers to symlink return O_PATH fd of the symlink, rather than following it. */ + CHASE_WARN = 1 << 8, /* Emit an appropriate warning when an error is encountered */ }; /* How many iterations to execute before returning -ELOOP */ diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c index 3a3479910d..5bfa028b39 100644 --- a/src/basic/hostname-util.c +++ b/src/basic/hostname-util.c @@ -69,12 +69,12 @@ int gethostname_strict(char **ret) { return 0; } -static bool hostname_valid_char(char c) { +bool valid_ldh_char(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || - IN_SET(c, '-', '_', '.'); + c == '-'; } /** @@ -90,7 +90,7 @@ static bool hostname_valid_char(char c) { bool hostname_is_valid(const char *s, bool allow_trailing_dot) { unsigned n_dots = 0; const char *p; - bool dot; + bool dot, hyphen; if (isempty(s)) return false; @@ -100,23 +100,34 @@ bool hostname_is_valid(const char *s, bool allow_trailing_dot) { * sequence. Also ensures that the length stays below * HOST_NAME_MAX. */ - for (p = s, dot = true; *p; p++) { + for (p = s, dot = hyphen = true; *p; p++) if (*p == '.') { - if (dot) + if (dot || hyphen) return false; dot = true; + hyphen = false; n_dots++; + + } else if (*p == '-') { + if (dot) + return false; + + dot = false; + hyphen = true; + } else { - if (!hostname_valid_char(*p)) + if (!valid_ldh_char(*p)) return false; dot = false; + hyphen = false; } - } if (dot && (n_dots < 2 || !allow_trailing_dot)) return false; + if (hyphen) + return false; if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on * Linux, but DNS allows domain names @@ -128,29 +139,38 @@ bool hostname_is_valid(const char *s, bool allow_trailing_dot) { char* hostname_cleanup(char *s) { char *p, *d; - bool dot; + bool dot, hyphen; assert(s); - strshorten(s, HOST_NAME_MAX); - - for (p = s, d = s, dot = true; *p; p++) { + for (p = s, d = s, dot = hyphen = true; *p && d - s < HOST_NAME_MAX; p++) if (*p == '.') { - if (dot) + if (dot || hyphen) continue; *(d++) = '.'; dot = true; - } else if (hostname_valid_char(*p)) { + hyphen = false; + + } else if (*p == '-') { + if (dot) + continue; + + *(d++) = '-'; + dot = false; + hyphen = true; + + } else if (valid_ldh_char(*p)) { *(d++) = *p; dot = false; + hyphen = false; } - } - if (dot && d > s) - d[-1] = 0; - else - *d = 0; + if (d > s && IN_SET(d[-1], '-', '.')) + /* The dot can occur at most once, but we might have multiple + * hyphens, hence the loop */ + d--; + *d = 0; return s; } diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h index 749481723d..7ba386a0fd 100644 --- a/src/basic/hostname-util.h +++ b/src/basic/hostname-util.h @@ -11,6 +11,7 @@ bool hostname_is_set(void); char* gethostname_malloc(void); int gethostname_strict(char **ret); +bool valid_ldh_char(char c) _const_; bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_; char* hostname_cleanup(char *s); diff --git a/src/basic/meson.build b/src/basic/meson.build index fb6fa2fcfa..23b5e75bd8 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -96,6 +96,7 @@ basic_sources = files(''' missing_btrfs.h missing_btrfs_tree.h missing_capability.h + missing_drm.h missing_ethtool.h missing_fcntl.h missing_fib_rules.h diff --git a/src/basic/missing.h b/src/basic/missing.h index 7e14e0a2a4..5067c8fd00 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -6,6 +6,7 @@ #include "missing_audit.h" #include "missing_btrfs_tree.h" #include "missing_capability.h" +#include "missing_drm.h" #include "missing_fcntl.h" #include "missing_fs.h" #include "missing_input.h" diff --git a/src/basic/missing_drm.h b/src/basic/missing_drm.h new file mode 100644 index 0000000000..a64f74efda --- /dev/null +++ b/src/basic/missing_drm.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#ifndef DRM_IOCTL_SET_MASTER +#define DRM_IOCTL_SET_MASTER _IO('d', 0x1e) +#endif + +#ifndef DRM_IOCTL_DROP_MASTER +#define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f) +#endif diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index 0a9f479e42..1e946a0bb6 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -98,7 +98,7 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id if ((flags & AT_EMPTY_PATH) && isempty(filename)) xsprintf(path, "/proc/self/fdinfo/%i", fd); else { - subfd = openat(fd, filename, O_CLOEXEC|O_PATH); + subfd = openat(fd, filename, O_CLOEXEC|O_PATH|(flags & AT_SYMLINK_FOLLOW ? 0 : O_NOFOLLOW)); if (subfd < 0) return -errno; diff --git a/src/core/device.c b/src/core/device.c index f8b6961d3c..960f403718 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -112,6 +112,24 @@ static void device_done(Unit *u) { d->wants_property = strv_free(d->wants_property); } +static int device_load(Unit *u) { + int r; + + r = unit_load_fragment_and_dropin_optional(u); + if (r < 0) + return r; + + if (!u->description) { + /* Generate a description based on the path, to be used until the + device is initialized properly */ + r = unit_name_to_path(u->id, &u->description); + if (r < 0) + log_unit_debug_errno(u, r, "Failed to unescape name: %m"); + } + + return 0; +} + static void device_set_state(Device *d, DeviceState state) { DeviceState old_state; assert(d); @@ -305,32 +323,32 @@ _pure_ static const char *device_sub_state_to_string(Unit *u) { } static int device_update_description(Unit *u, sd_device *dev, const char *path) { - const char *model, *label; + _cleanup_free_ char *j = NULL; + const char *model, *label, *desc; int r; assert(u); - assert(dev); assert(path); - if (sd_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE", &model) >= 0 || - sd_device_get_property_value(dev, "ID_MODEL", &model) >= 0) { + desc = path; + + if (dev && + (sd_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE", &model) >= 0 || + sd_device_get_property_value(dev, "ID_MODEL", &model) >= 0)) { + desc = model; /* Try to concatenate the device model string with a label, if there is one */ if (sd_device_get_property_value(dev, "ID_FS_LABEL", &label) >= 0 || sd_device_get_property_value(dev, "ID_PART_ENTRY_NAME", &label) >= 0 || sd_device_get_property_value(dev, "ID_PART_ENTRY_NUMBER", &label) >= 0) { - _cleanup_free_ char *j; - - j = strjoin(model, " ", label); + desc = j = strjoin(model, " ", label); if (!j) return log_oom(); + } + } - r = unit_set_description(u, j); - } else - r = unit_set_description(u, model); - } else - r = unit_set_description(u, path); + r = unit_set_description(u, desc); if (r < 0) return log_unit_error_errno(u, r, "Failed to set device description: %m"); @@ -530,13 +548,13 @@ static int device_setup_unit(Manager *m, sd_device *dev, const char *path, bool goto fail; } - (void) device_update_description(u, dev, path); - /* The additional systemd udev properties we only interpret for the main object */ if (main) (void) device_add_udev_wants(u, dev); } + (void) device_update_description(u, dev, path); + /* So the user wants the mount units to be bound to the device but a mount unit might has been seen by systemd * before the device appears on its radar. In this case the device unit is partially initialized and includes * the deps on the mount unit but at that time the "bind mounts" flag wasn't not present. Fix this up now. */ @@ -1041,7 +1059,7 @@ const UnitVTable device_vtable = { .init = device_init, .done = device_done, - .load = unit_load_fragment_and_dropin_optional, + .load = device_load, .coldplug = device_coldplug, .catchup = device_catchup, diff --git a/src/core/mount.c b/src/core/mount.c index afdbaa1d9d..ead9bc1f44 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -68,22 +68,18 @@ static bool MOUNT_STATE_WITH_PROCESS(MountState state) { MOUNT_UNMOUNTING_SIGKILL); } -static bool mount_needs_network(const char *options, const char *fstype) { - if (fstab_test_option(options, "_netdev\0")) +static bool mount_is_network(const MountParameters *p) { + assert(p); + + if (fstab_test_option(p->options, "_netdev\0")) return true; - if (fstype && fstype_is_network(fstype)) + if (p->fstype && fstype_is_network(p->fstype)) return true; return false; } -static bool mount_is_network(const MountParameters *p) { - assert(p); - - return mount_needs_network(p->options, p->fstype); -} - static bool mount_is_loop(const MountParameters *p) { assert(p); @@ -129,11 +125,11 @@ static bool mount_is_bound_to_device(const Mount *m) { return fstab_test_option(p->options, "x-systemd.device-bound\0"); } -static bool needs_quota(const MountParameters *p) { +static bool mount_needs_quota(const MountParameters *p) { assert(p); - /* Quotas are not enabled on network filesystems, - * but we want them, for example, on storage connected via iscsi */ + /* Quotas are not enabled on network filesystems, but we want them, for example, on storage connected via + * iscsi. We hence don't use mount_is_network() here, as that would also return true for _netdev devices. */ if (p->fstype && fstype_is_network(p->fstype)) return false; @@ -211,11 +207,9 @@ static void mount_unwatch_control_pid(Mount *m) { static void mount_parameters_done(MountParameters *p) { assert(p); - free(p->what); - free(p->options); - free(p->fstype); - - p->what = p->options = p->fstype = NULL; + p->what = mfree(p->what); + p->options = mfree(p->options); + p->fstype = mfree(p->fstype); } static void mount_done(Unit *u) { @@ -318,7 +312,7 @@ static int mount_add_mount_dependencies(Mount *m) { } static int mount_add_device_dependencies(Mount *m) { - bool device_wants_mount = false; + bool device_wants_mount; UnitDependencyMask mask; MountParameters *p; UnitDependency dep; @@ -348,8 +342,8 @@ static int mount_add_device_dependencies(Mount *m) { if (path_equal(m->where, "/")) return 0; - if (mount_is_auto(p) && !mount_is_automount(p) && MANAGER_IS_SYSTEM(UNIT(m)->manager)) - device_wants_mount = true; + device_wants_mount = + mount_is_auto(p) && !mount_is_automount(p) && MANAGER_IS_SYSTEM(UNIT(m)->manager); /* Mount units from /proc/self/mountinfo are not bound to devices * by default since they're subject to races when devices are @@ -381,7 +375,7 @@ static int mount_add_quota_dependencies(Mount *m) { if (!p) return 0; - if (!needs_quota(p)) + if (!mount_needs_quota(p)) return 0; mask = m->from_fragment ? UNIT_DEPENDENCY_FILE : UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT; @@ -429,10 +423,10 @@ static bool mount_is_extrinsic(Mount *m) { } static int mount_add_default_dependencies(Mount *m) { + const char *after, *before; UnitDependencyMask mask; - int r; MountParameters *p; - const char *after; + int r; assert(m); @@ -474,8 +468,15 @@ static int mount_add_default_dependencies(Mount *m) { return r; after = SPECIAL_REMOTE_FS_PRE_TARGET; - } else + before = SPECIAL_REMOTE_FS_TARGET; + } else { after = SPECIAL_LOCAL_FS_PRE_TARGET; + before = SPECIAL_LOCAL_FS_TARGET; + } + + r = unit_add_dependency_by_name(UNIT(m), UNIT_BEFORE, before, true, mask); + if (r < 0) + return r; r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, true, mask); if (r < 0) @@ -542,6 +543,10 @@ static int mount_add_extras(Mount *m) { assert(m); + /* Note: this call might be called after we already have been loaded once (and even when it has already been + * activated), in case data from /proc/self/mountinfo has changed. This means all code here needs to be ready + * to run with an already set up unit. */ + if (u->fragment_path) m->from_fragment = true; @@ -611,28 +616,33 @@ static int mount_load_root_mount(Unit *u) { static int mount_load(Unit *u) { Mount *m = MOUNT(u); - int r; + int r, q, w; assert(u); assert(u->load_state == UNIT_STUB); r = mount_load_root_mount(u); - if (r < 0) - return r; if (m->from_proc_self_mountinfo || u->perpetual) - r = unit_load_fragment_and_dropin_optional(u); + q = unit_load_fragment_and_dropin_optional(u); else - r = unit_load_fragment_and_dropin(u); + q = unit_load_fragment_and_dropin(u); + + /* Add in some extras. Note we do this in all cases (even if we failed to load the unit) when announced by the + * kernel, because we need some things to be set up no matter what when the kernel establishes a mount and thus + * we need to update the state in our unit to track it. After all, consider that we don't allow changing the + * 'slice' field for a unit once it is active. */ + if (u->load_state == UNIT_LOADED || m->from_proc_self_mountinfo || u->perpetual) + w = mount_add_extras(m); + else + w = 0; + if (r < 0) return r; - - /* This is a new unit? Then let's add in some extras */ - if (u->load_state == UNIT_LOADED) { - r = mount_add_extras(m); - if (r < 0) - return r; - } + if (q < 0) + return q; + if (w < 0) + return w; return mount_verify(m); } @@ -944,7 +954,6 @@ static void mount_enter_mounting(Mount *m) { (void) mkdir_p_label(m->where, m->directory_mode); unit_warn_if_dir_nonempty(UNIT(m), m->where); - unit_warn_leftover_processes(UNIT(m)); m->control_command_id = MOUNT_EXEC_MOUNT; @@ -1048,6 +1057,17 @@ fail: mount_enter_dead_or_mounted(m, MOUNT_SUCCESS); } +static void mount_cycle_clear(Mount *m) { + assert(m); + + /* Clear all state we shall forget for this new cycle */ + + m->result = MOUNT_SUCCESS; + m->reload_result = MOUNT_SUCCESS; + exec_command_reset_status_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX); + UNIT(m)->reset_accounting = true; +} + static int mount_start(Unit *u) { Mount *m = MOUNT(u); int r; @@ -1078,13 +1098,9 @@ static int mount_start(Unit *u) { if (r < 0) return r; - m->result = MOUNT_SUCCESS; - m->reload_result = MOUNT_SUCCESS; - exec_command_reset_status_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX); - - u->reset_accounting = true; - + mount_cycle_clear(m); mount_enter_mounting(m); + return 1; } @@ -1148,6 +1164,7 @@ static int mount_serialize(Unit *u, FILE *f, FDSet *fds) { (void) serialize_item(f, "state", mount_state_to_string(m->state)); (void) serialize_item(f, "result", mount_result_to_string(m->result)); (void) serialize_item(f, "reload-result", mount_result_to_string(m->reload_result)); + (void) serialize_item_format(f, "n-retry-umount", "%u", m->n_retry_umount); if (m->control_pid > 0) (void) serialize_item_format(f, "control-pid", PID_FMT, m->control_pid); @@ -1160,6 +1177,7 @@ static int mount_serialize(Unit *u, FILE *f, FDSet *fds) { static int mount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { Mount *m = MOUNT(u); + int r; assert(u); assert(key); @@ -1173,6 +1191,7 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F log_unit_debug(u, "Failed to parse state value: %s", value); else m->deserialized_state = state; + } else if (streq(key, "result")) { MountResult f; @@ -1191,13 +1210,17 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F else if (f != MOUNT_SUCCESS) m->reload_result = f; + } else if (streq(key, "n-retry-umount")) { + + r = safe_atou(value, &m->n_retry_umount); + if (r < 0) + log_unit_debug(u, "Failed to parse n-retry-umount value: %s", value); + } else if (streq(key, "control-pid")) { - pid_t pid; - if (parse_pid(value, &pid) < 0) + if (parse_pid(value, &m->control_pid) < 0) log_unit_debug(u, "Failed to parse control-pid value: %s", value); - else - m->control_pid = pid; + } else if (streq(key, "control-command")) { MountExecCommand id; @@ -1405,59 +1428,77 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user return 0; } -typedef struct { - bool is_mounted; - bool just_mounted; - bool just_changed; -} MountSetupFlags; +static int update_parameters_proc_self_mount_info( + Mount *m, + const char *what, + const char *options, + const char *fstype) { + + MountParameters *p; + int r, q, w; + + p = &m->parameters_proc_self_mountinfo; + + r = free_and_strdup(&p->what, what); + if (r < 0) + return r; + + q = free_and_strdup(&p->options, options); + if (q < 0) + return q; + + w = free_and_strdup(&p->fstype, fstype); + if (w < 0) + return w; + + return r > 0 || q > 0 || w > 0; +} static int mount_setup_new_unit( - Unit *u, + Manager *m, + const char *name, const char *what, const char *where, const char *options, const char *fstype, - MountSetupFlags *flags) { - - MountParameters *p; + MountProcFlags *ret_flags, + Unit **ret) { - assert(u); - assert(flags); + _cleanup_(unit_freep) Unit *u = NULL; + int r; - u->source_path = strdup("/proc/self/mountinfo"); - MOUNT(u)->where = strdup(where); - if (!u->source_path || !MOUNT(u)->where) - return -ENOMEM; + assert(m); + assert(name); + assert(ret_flags); + assert(ret); - /* Make sure to initialize those fields before mount_is_extrinsic(). */ - MOUNT(u)->from_proc_self_mountinfo = true; - p = &MOUNT(u)->parameters_proc_self_mountinfo; + r = unit_new_for_name(m, sizeof(Mount), name, &u); + if (r < 0) + return r; - p->what = strdup(what); - p->options = strdup(options); - p->fstype = strdup(fstype); - if (!p->what || !p->options || !p->fstype) - return -ENOMEM; + r = free_and_strdup(&u->source_path, "/proc/self/mountinfo"); + if (r < 0) + return r; - if (!mount_is_extrinsic(MOUNT(u))) { - const char *target; - int r; + r = free_and_strdup(&MOUNT(u)->where, where); + if (r < 0) + return r; - target = mount_is_network(p) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET; - r = unit_add_dependency_by_name(u, UNIT_BEFORE, target, true, UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT); - if (r < 0) - return r; + r = update_parameters_proc_self_mount_info(MOUNT(u), what, options, fstype); + if (r < 0) + return r; - r = unit_add_dependency_by_name(u, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, true, UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT); - if (r < 0) - return r; - } + /* This unit was generated because /proc/self/mountinfo reported it. Remember this, so that by the time we load + * the unit file for it (and thus add in extra deps right after) we know what source to attributes the deps + * to.*/ + MOUNT(u)->from_proc_self_mountinfo = true; + /* We have only allocated the stub now, let's enqueue this unit for loading now, so that everything else is + * loaded in now. */ unit_add_to_load_queue(u); - flags->is_mounted = true; - flags->just_mounted = true; - flags->just_changed = true; + *ret_flags = MOUNT_PROC_IS_MOUNTED | MOUNT_PROC_JUST_MOUNTED | MOUNT_PROC_JUST_CHANGED; + *ret = TAKE_PTR(u); return 0; } @@ -1467,11 +1508,10 @@ static int mount_setup_existing_unit( const char *where, const char *options, const char *fstype, - MountSetupFlags *flags) { + MountProcFlags *ret_flags) { - MountParameters *p; - bool load_extras = false; - int r1, r2, r3; + MountProcFlags flags = MOUNT_PROC_IS_MOUNTED; + int r; assert(u); assert(flags); @@ -1482,49 +1522,38 @@ static int mount_setup_existing_unit( return -ENOMEM; } - /* Make sure to initialize those fields before mount_is_extrinsic(). */ - p = &MOUNT(u)->parameters_proc_self_mountinfo; - - r1 = free_and_strdup(&p->what, what); - r2 = free_and_strdup(&p->options, options); - r3 = free_and_strdup(&p->fstype, fstype); - if (r1 < 0 || r2 < 0 || r3 < 0) - return -ENOMEM; - - flags->just_changed = r1 > 0 || r2 > 0 || r3 > 0; - flags->is_mounted = true; - flags->just_mounted = !MOUNT(u)->from_proc_self_mountinfo || MOUNT(u)->just_mounted; - - MOUNT(u)->from_proc_self_mountinfo = true; + r = update_parameters_proc_self_mount_info(MOUNT(u), what, options, fstype); + if (r < 0) + return r; + if (r > 0) + flags |= MOUNT_PROC_JUST_CHANGED; - if (!mount_is_extrinsic(MOUNT(u)) && mount_is_network(p)) { - /* _netdev option may have shown up late, or on a - * remount. Add remote-fs dependencies, even though - * local-fs ones may already be there. - * - * Note: due to a current limitation (we don't track - * in the dependency "Set*" objects who created a - * dependency), we can only add deps, never lose them, - * until the next full daemon-reload. */ - unit_add_dependency_by_name(u, UNIT_BEFORE, SPECIAL_REMOTE_FS_TARGET, true, UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT); - load_extras = true; + if (!MOUNT(u)->from_proc_self_mountinfo) { + flags |= MOUNT_PROC_JUST_MOUNTED; + MOUNT(u)->from_proc_self_mountinfo = true; } - if (u->load_state == UNIT_NOT_FOUND) { + if (IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_BAD_SETTING, UNIT_ERROR)) { + /* The unit was previously not found or otherwise not loaded. Now that the unit shows up in + * /proc/self/mountinfo we should reconsider it this, hence set it to UNIT_LOADED. */ u->load_state = UNIT_LOADED; u->load_error = 0; - /* Load in the extras later on, after we - * finished initialization of the unit */ - - /* FIXME: since we're going to load the unit later on, why setting load_extras=true ? */ - load_extras = true; - flags->just_changed = true; + flags |= MOUNT_PROC_JUST_CHANGED; } - if (load_extras) - return mount_add_extras(MOUNT(u)); + if (FLAGS_SET(flags, MOUNT_PROC_JUST_CHANGED)) { + /* If things changed, then make sure that all deps are regenerated. Let's + * first remove all automatic deps, and then add in the new ones. */ + unit_remove_dependencies(u, UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT); + + r = mount_add_extras(MOUNT(u)); + if (r < 0) + return r; + } + + *ret_flags = flags; return 0; } @@ -1537,7 +1566,7 @@ static int mount_setup_unit( bool set_flags) { _cleanup_free_ char *e = NULL; - MountSetupFlags flags; + MountProcFlags flags; Unit *u; int r; @@ -1561,44 +1590,32 @@ static int mount_setup_unit( r = unit_name_from_path(where, ".mount", &e); if (r < 0) - return r; + return log_error_errno(r, "Failed to generate unit name from path '%s': %m", where); u = manager_get_unit(m, e); - if (!u) { - /* First time we see this mount point meaning that it's - * not been initiated by a mount unit but rather by the - * sysadmin having called mount(8) directly. */ - r = unit_new_for_name(m, sizeof(Mount), e, &u); - if (r < 0) - goto fail; - - r = mount_setup_new_unit(u, what, where, options, fstype, &flags); - if (r < 0) - unit_free(u); - } else + if (u) r = mount_setup_existing_unit(u, what, where, options, fstype, &flags); - + else + /* First time we see this mount point meaning that it's not been initiated by a mount unit but rather + * by the sysadmin having called mount(8) directly. */ + r = mount_setup_new_unit(m, e, what, where, options, fstype, &flags, &u); if (r < 0) - goto fail; - - if (set_flags) { - MOUNT(u)->is_mounted = flags.is_mounted; - MOUNT(u)->just_mounted = flags.just_mounted; - MOUNT(u)->just_changed = flags.just_changed; - } + return log_warning_errno(r, "Failed to set up mount unit: %m"); - if (flags.just_changed) + /* If the mount changed properties or state, let's notify our clients */ + if (flags & (MOUNT_PROC_JUST_CHANGED|MOUNT_PROC_JUST_MOUNTED)) unit_add_to_dbus_queue(u); + if (set_flags) + MOUNT(u)->proc_flags = flags; + return 0; -fail: - return log_warning_errno(r, "Failed to set up mount unit: %m"); } static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL; _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL; - int r = 0; + int r; assert(m); @@ -1611,7 +1628,6 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { if (r < 0) return log_error_errno(r, "Failed to parse /proc/self/mountinfo: %m"); - r = 0; for (;;) { struct libmnt_fs *fs; const char *device, *path, *options, *fstype; @@ -1640,12 +1656,10 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { device_found_node(m, d, DEVICE_FOUND_MOUNT, DEVICE_FOUND_MOUNT); - k = mount_setup_unit(m, d, p, options, fstype, set_flags); - if (r == 0 && k < 0) - r = k; + (void) mount_setup_unit(m, d, p, options, fstype, set_flags); } - return r; + return 0; } static void mount_shutdown(Manager *m) { @@ -1703,7 +1717,7 @@ static void mount_enumerate_perpetual(Manager *m) { static bool mount_is_mounted(Mount *m) { assert(m); - return UNIT(m)->perpetual || m->is_mounted; + return UNIT(m)->perpetual || FLAGS_SET(m->proc_flags, MOUNT_PROC_IS_MOUNTED); } static void mount_enumerate(Manager *m) { @@ -1767,7 +1781,7 @@ fail: } static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - _cleanup_set_free_ Set *around = NULL, *gone = NULL; + _cleanup_set_free_free_ Set *around = NULL, *gone = NULL; Manager *m = userdata; const char *what; Iterator i; @@ -1803,11 +1817,8 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, r = mount_load_proc_self_mountinfo(m, true); if (r < 0) { /* Reset flags, just in case, for later calls */ - LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) { - Mount *mount = MOUNT(u); - - mount->is_mounted = mount->just_mounted = mount->just_changed = false; - } + LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) + MOUNT(u)->proc_flags = 0; return 0; } @@ -1828,7 +1839,7 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, /* Remember that this device might just have disappeared */ if (set_ensure_allocated(&gone, &path_hash_ops) < 0 || - set_put(gone, mount->parameters_proc_self_mountinfo.what) < 0) + set_put_strdup(gone, mount->parameters_proc_self_mountinfo.what) < 0) log_oom(); /* we don't care too much about OOM here... */ } @@ -1837,10 +1848,7 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, switch (mount->state) { case MOUNT_MOUNTED: - /* This has just been unmounted by - * somebody else, follow the state - * change. */ - mount->result = MOUNT_SUCCESS; /* make sure we forget any earlier umount failures */ + /* This has just been unmounted by somebody else, follow the state change. */ mount_enter_dead(mount, MOUNT_SUCCESS); break; @@ -1848,7 +1856,7 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, break; } - } else if (mount->just_mounted || mount->just_changed) { + } else if (mount->proc_flags & (MOUNT_PROC_JUST_MOUNTED|MOUNT_PROC_JUST_CHANGED)) { /* A mount point was added or changed */ @@ -1859,7 +1867,8 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, /* This has just been mounted by somebody else, follow the state change, but let's * generate a new invocation ID for this implicitly and automatically. */ - (void) unit_acquire_invocation_id(UNIT(mount)); + (void) unit_acquire_invocation_id(u); + mount_cycle_clear(mount); mount_enter_mounted(mount, MOUNT_SUCCESS); break; @@ -1881,14 +1890,15 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, if (mount_is_mounted(mount) && mount->from_proc_self_mountinfo && mount->parameters_proc_self_mountinfo.what) { + /* Track devices currently used */ if (set_ensure_allocated(&around, &path_hash_ops) < 0 || - set_put(around, mount->parameters_proc_self_mountinfo.what) < 0) + set_put_strdup(around, mount->parameters_proc_self_mountinfo.what) < 0) log_oom(); } /* Reset the flags for later calls */ - mount->is_mounted = mount->just_mounted = mount->just_changed = false; + mount->proc_flags = 0; } SET_FOREACH(what, gone, i) { diff --git a/src/core/mount.h b/src/core/mount.h index 67ab8ecf93..2e59f1fe04 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -34,6 +34,13 @@ typedef struct MountParameters { char *fstype; } MountParameters; +/* Used while looking for mount points that vanished or got added from/to /proc/self/mountinfo */ +typedef enum MountProcFlags { + MOUNT_PROC_IS_MOUNTED = 1 << 0, + MOUNT_PROC_JUST_MOUNTED = 1 << 1, + MOUNT_PROC_JUST_CHANGED = 1 << 2, +} MountProcFlags; + struct Mount { Unit meta; @@ -45,11 +52,7 @@ struct Mount { bool from_proc_self_mountinfo:1; bool from_fragment:1; - /* Used while looking for mount points that vanished or got - * added from/to /proc/self/mountinfo */ - bool is_mounted:1; - bool just_mounted:1; - bool just_changed:1; + MountProcFlags proc_flags; bool sloppy_options; diff --git a/src/core/service.c b/src/core/service.c index 76f1e16069..d631687205 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -935,8 +935,8 @@ static int service_load_pid_file(Service *s, bool may_warn) { prio = may_warn ? LOG_INFO : LOG_DEBUG; fd = chase_symlinks(s->pid_file, NULL, CHASE_OPEN|CHASE_SAFE, NULL); - if (fd == -EPERM) { - log_unit_full(UNIT(s), LOG_DEBUG, fd, "Permission denied while opening PID file or potentially unsafe symlink chain, will now retry with relaxed checks: %s", s->pid_file); + if (fd == -ENOLINK) { + log_unit_full(UNIT(s), LOG_DEBUG, fd, "Potentially unsafe symlink chain, will now retry with relaxed checks: %s", s->pid_file); questionable_pid_file = true; diff --git a/src/core/swap.c b/src/core/swap.c index 90207a48fa..2d8463b8b1 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -273,77 +273,92 @@ static int swap_load_devnode(Swap *s) { return swap_set_devnode(s, p); } -static int swap_load(Unit *u) { +static int swap_add_extras(Swap *s) { int r; - Swap *s = SWAP(u); assert(s); - assert(u->load_state == UNIT_STUB); - /* Load a .swap file */ - if (SWAP(u)->from_proc_swaps) - r = unit_load_fragment_and_dropin_optional(u); - else - r = unit_load_fragment_and_dropin(u); + if (UNIT(s)->fragment_path) + s->from_fragment = true; + + if (!s->what) { + if (s->parameters_fragment.what) + s->what = strdup(s->parameters_fragment.what); + else if (s->parameters_proc_swaps.what) + s->what = strdup(s->parameters_proc_swaps.what); + else { + r = unit_name_to_path(UNIT(s)->id, &s->what); + if (r < 0) + return r; + } + + if (!s->what) + return -ENOMEM; + } + + path_simplify(s->what, false); + + if (!UNIT(s)->description) { + r = unit_set_description(UNIT(s), s->what); + if (r < 0) + return r; + } + + r = unit_require_mounts_for(UNIT(s), s->what, UNIT_DEPENDENCY_IMPLICIT); if (r < 0) return r; - if (u->load_state == UNIT_LOADED) { - - if (UNIT(s)->fragment_path) - s->from_fragment = true; + r = swap_add_device_dependencies(s); + if (r < 0) + return r; - if (!s->what) { - if (s->parameters_fragment.what) - s->what = strdup(s->parameters_fragment.what); - else if (s->parameters_proc_swaps.what) - s->what = strdup(s->parameters_proc_swaps.what); - else { - r = unit_name_to_path(u->id, &s->what); - if (r < 0) - return r; - } + r = swap_load_devnode(s); + if (r < 0) + return r; - if (!s->what) - return -ENOMEM; - } + r = unit_patch_contexts(UNIT(s)); + if (r < 0) + return r; - path_simplify(s->what, false); + r = unit_add_exec_dependencies(UNIT(s), &s->exec_context); + if (r < 0) + return r; - if (!UNIT(s)->description) { - r = unit_set_description(u, s->what); - if (r < 0) - return r; - } + r = unit_set_default_slice(UNIT(s)); + if (r < 0) + return r; - r = unit_require_mounts_for(UNIT(s), s->what, UNIT_DEPENDENCY_IMPLICIT); - if (r < 0) - return r; + r = swap_add_default_dependencies(s); + if (r < 0) + return r; - r = swap_add_device_dependencies(s); - if (r < 0) - return r; + return 0; +} - r = swap_load_devnode(s); - if (r < 0) - return r; +static int swap_load(Unit *u) { + Swap *s = SWAP(u); + int r, q; - r = unit_patch_contexts(u); - if (r < 0) - return r; + assert(s); + assert(u->load_state == UNIT_STUB); - r = unit_add_exec_dependencies(u, &s->exec_context); - if (r < 0) - return r; + /* Load a .swap file */ + if (SWAP(u)->from_proc_swaps) + r = unit_load_fragment_and_dropin_optional(u); + else + r = unit_load_fragment_and_dropin(u); - r = unit_set_default_slice(u); - if (r < 0) - return r; + /* Add in some extras, and do so either when we successfully loaded something or when /proc/swaps is already + * active. */ + if (u->load_state == UNIT_LOADED || s->from_proc_swaps) + q = swap_add_extras(s); + else + q = 0; - r = swap_add_default_dependencies(s); - if (r < 0) - return r; - } + if (r < 0) + return r; + if (q < 0) + return q; return swap_verify(s); } @@ -370,7 +385,6 @@ static int swap_setup_unit( return log_unit_error_errno(u, r, "Failed to generate unit name from path: %m"); u = manager_get_unit(m, e); - if (u && SWAP(u)->from_proc_swaps && !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps)) @@ -405,6 +419,13 @@ static int swap_setup_unit( } } + /* The unit is definitely around now, mark it as loaded if it was previously referenced but could not be + * loaded. After all we can load it now, from the data in /proc/swaps. */ + if (IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_BAD_SETTING, UNIT_ERROR)) { + u->load_state = UNIT_LOADED; + u->load_error = 0; + } + if (set_flags) { SWAP(u)->is_active = true; SWAP(u)->just_activated = !SWAP(u)->from_proc_swaps; @@ -472,7 +493,7 @@ static int swap_process_new(Manager *m, const char *device, int prio, bool set_f swap_setup_unit(m, devlink, device, prio, set_flags); } - return r; + return 0; } static void swap_set_state(Swap *s, SwapState state) { @@ -813,6 +834,14 @@ fail: swap_enter_dead_or_active(s, SWAP_FAILURE_RESOURCES); } +static void swap_cycle_clear(Swap *s) { + assert(s); + + s->result = SWAP_SUCCESS; + exec_command_reset_status_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX); + UNIT(s)->reset_accounting = true; +} + static int swap_start(Unit *u) { Swap *s = SWAP(u), *other; int r; @@ -852,11 +881,7 @@ static int swap_start(Unit *u) { if (r < 0) return r; - s->result = SWAP_SUCCESS; - exec_command_reset_status_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX); - - u->reset_accounting = true; - + swap_cycle_clear(s); swap_enter_activating(s); return 1; } @@ -1088,7 +1113,6 @@ static int swap_dispatch_timer(sd_event_source *source, usec_t usec, void *userd static int swap_load_proc_swaps(Manager *m, bool set_flags) { unsigned i; - int r = 0; assert(m); @@ -1120,12 +1144,10 @@ static int swap_load_proc_swaps(Manager *m, bool set_flags) { device_found_node(m, d, DEVICE_FOUND_SWAP, DEVICE_FOUND_SWAP); - k = swap_process_new(m, d, prio, set_flags); - if (k < 0) - r = k; + (void) swap_process_new(m, d, prio, set_flags); } - return r; + return 0; } static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { @@ -1156,13 +1178,13 @@ static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v Swap *swap = SWAP(u); if (!swap->is_active) { - /* This has just been deactivated */ swap_unset_proc_swaps(swap); switch (swap->state) { case SWAP_ACTIVE: + /* This has just been deactivated */ swap_enter_dead(swap, SWAP_SUCCESS); break; @@ -1183,7 +1205,8 @@ static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v case SWAP_DEAD: case SWAP_FAILED: - (void) unit_acquire_invocation_id(UNIT(swap)); + (void) unit_acquire_invocation_id(u); + swap_cycle_clear(swap); swap_enter_active(swap, SWAP_SUCCESS); break; diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 69bc447920..4185af63e1 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -18,6 +18,7 @@ #include "hostname-util.h" #include "log.h" #include "logs-show.h" +#include "main-func.h" #include "microhttpd-util.h" #include "os-util.h" #include "parse-util.h" @@ -31,7 +32,11 @@ static char *arg_key_pem = NULL; static char *arg_cert_pem = NULL; static char *arg_trust_pem = NULL; -static char *arg_directory = NULL; +static const char *arg_directory = NULL; + +STATIC_DESTRUCTOR_REGISTER(arg_key_pem, freep); +STATIC_DESTRUCTOR_REGISTER(arg_cert_pem, freep); +STATIC_DESTRUCTOR_REGISTER(arg_trust_pem, freep); typedef struct RequestMeta { sd_journal *journal; @@ -981,95 +986,85 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -int main(int argc, char *argv[]) { - struct MHD_Daemon *d = NULL; +static int run(int argc, char *argv[]) { + _cleanup_(MHD_stop_daemonp) struct MHD_Daemon *d = NULL; + struct MHD_OptionItem opts[] = { + { MHD_OPTION_NOTIFY_COMPLETED, + (intptr_t) request_meta_free, NULL }, + { MHD_OPTION_EXTERNAL_LOGGER, + (intptr_t) microhttpd_logger, NULL }, + { MHD_OPTION_END, 0, NULL }, + { MHD_OPTION_END, 0, NULL }, + { MHD_OPTION_END, 0, NULL }, + { MHD_OPTION_END, 0, NULL }, + { MHD_OPTION_END, 0, NULL }, + }; + int opts_pos = 2; + + /* We force MHD_USE_ITC here, in order to make sure + * libmicrohttpd doesn't use shutdown() on our listening + * socket, which would break socket re-activation. See + * + * https://lists.gnu.org/archive/html/libmicrohttpd/2015-09/msg00014.html + * https://github.com/systemd/systemd/pull/1286 + */ + + int flags = + MHD_USE_DEBUG | + MHD_USE_DUAL_STACK | + MHD_USE_ITC | + MHD_USE_POLL_INTERNAL_THREAD | + MHD_USE_THREAD_PER_CONNECTION; int r, n; log_setup_service(); r = parse_argv(argc, argv); - if (r < 0) - return EXIT_FAILURE; - if (r == 0) - return EXIT_SUCCESS; + if (r <= 0) + return r; sigbus_install(); r = setup_gnutls_logger(NULL); if (r < 0) - return EXIT_FAILURE; + return r; n = sd_listen_fds(1); - if (n < 0) { - log_error_errno(n, "Failed to determine passed sockets: %m"); - goto finish; - } else if (n > 1) { - log_error("Can't listen on more than one socket."); - goto finish; - } else { - struct MHD_OptionItem opts[] = { - { MHD_OPTION_NOTIFY_COMPLETED, - (intptr_t) request_meta_free, NULL }, - { MHD_OPTION_EXTERNAL_LOGGER, - (intptr_t) microhttpd_logger, NULL }, - { MHD_OPTION_END, 0, NULL }, - { MHD_OPTION_END, 0, NULL }, - { MHD_OPTION_END, 0, NULL }, - { MHD_OPTION_END, 0, NULL }, - { MHD_OPTION_END, 0, NULL }}; - int opts_pos = 2; - - /* We force MHD_USE_ITC here, in order to make sure - * libmicrohttpd doesn't use shutdown() on our listening - * socket, which would break socket re-activation. See - * - * https://lists.gnu.org/archive/html/libmicrohttpd/2015-09/msg00014.html - * https://github.com/systemd/systemd/pull/1286 - */ - - int flags = - MHD_USE_DEBUG | - MHD_USE_DUAL_STACK | - MHD_USE_ITC | - MHD_USE_POLL_INTERNAL_THREAD | - MHD_USE_THREAD_PER_CONNECTION; - - if (n > 0) - opts[opts_pos++] = (struct MHD_OptionItem) - {MHD_OPTION_LISTEN_SOCKET, SD_LISTEN_FDS_START}; - if (arg_key_pem) { - assert(arg_cert_pem); - opts[opts_pos++] = (struct MHD_OptionItem) - {MHD_OPTION_HTTPS_MEM_KEY, 0, arg_key_pem}; - opts[opts_pos++] = (struct MHD_OptionItem) - {MHD_OPTION_HTTPS_MEM_CERT, 0, arg_cert_pem}; - flags |= MHD_USE_TLS; - } - if (arg_trust_pem) { - assert(flags & MHD_USE_TLS); - opts[opts_pos++] = (struct MHD_OptionItem) - {MHD_OPTION_HTTPS_MEM_TRUST, 0, arg_trust_pem}; - } - - d = MHD_start_daemon(flags, 19531, - NULL, NULL, - request_handler, NULL, - MHD_OPTION_ARRAY, opts, - MHD_OPTION_END); + if (n < 0) + return log_error_errno(n, "Failed to determine passed sockets: %m"); + if (n > 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Can't listen on more than one socket."); + + if (n == 1) + opts[opts_pos++] = (struct MHD_OptionItem) + { MHD_OPTION_LISTEN_SOCKET, SD_LISTEN_FDS_START }; + + if (arg_key_pem) { + assert(arg_cert_pem); + opts[opts_pos++] = (struct MHD_OptionItem) + { MHD_OPTION_HTTPS_MEM_KEY, 0, arg_key_pem }; + opts[opts_pos++] = (struct MHD_OptionItem) + { MHD_OPTION_HTTPS_MEM_CERT, 0, arg_cert_pem }; + flags |= MHD_USE_TLS; } - if (!d) { - log_error("Failed to start daemon!"); - goto finish; + if (arg_trust_pem) { + assert(flags & MHD_USE_TLS); + opts[opts_pos++] = (struct MHD_OptionItem) + { MHD_OPTION_HTTPS_MEM_TRUST, 0, arg_trust_pem }; } - pause(); - - r = EXIT_SUCCESS; + d = MHD_start_daemon(flags, 19531, + NULL, NULL, + request_handler, NULL, + MHD_OPTION_ARRAY, opts, + MHD_OPTION_END); + if (!d) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to start daemon!"); -finish: - if (d) - MHD_stop_daemon(d); + pause(); - return r; + return 0; } + +DEFINE_MAIN_FUNCTION(run); diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c index b82d4b4a1b..e1748cb46b 100644 --- a/src/journal-remote/journal-remote-main.c +++ b/src/journal-remote/journal-remote-main.c @@ -6,11 +6,13 @@ #include "sd-daemon.h" #include "conf-parser.h" +#include "daemon-util.h" #include "def.h" #include "fd-util.h" #include "fileio.h" #include "journal-remote-write.h" #include "journal-remote.h" +#include "main-func.h" #include "pretty-print.h" #include "process-util.h" #include "rlimit-util.h" @@ -24,25 +26,30 @@ #define CERT_FILE CERTIFICATE_ROOT "/certs/journal-remote.pem" #define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem" -static char* arg_url = NULL; -static char* arg_getter = NULL; -static char* arg_listen_raw = NULL; -static char* arg_listen_http = NULL; -static char* arg_listen_https = NULL; -static char** arg_files = NULL; +static const char* arg_url = NULL; +static const char* arg_getter = NULL; +static const char* arg_listen_raw = NULL; +static const char* arg_listen_http = NULL; +static const char* arg_listen_https = NULL; +static char** arg_files = NULL; /* Do not free this. */ static int arg_compress = true; static int arg_seal = false; static int http_socket = -1, https_socket = -1; static char** arg_gnutls_log = NULL; static JournalWriteSplitMode arg_split_mode = _JOURNAL_WRITE_SPLIT_INVALID; -static char* arg_output = NULL; +static const char* arg_output = NULL; static char *arg_key = NULL; static char *arg_cert = NULL; static char *arg_trust = NULL; static bool arg_trust_all = false; +STATIC_DESTRUCTOR_REGISTER(arg_gnutls_log, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_key, freep); +STATIC_DESTRUCTOR_REGISTER(arg_cert, freep); +STATIC_DESTRUCTOR_REGISTER(arg_trust, freep); + static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = { [JOURNAL_WRITE_SPLIT_NONE] = "none", [JOURNAL_WRITE_SPLIT_HOST] = "host", @@ -615,8 +622,7 @@ static int create_remoteserver( } if (arg_url) { - const char *url; - char *hostname; + const char *url, *hostname; if (!strstr(arg_url, "/entries")) { if (endswith(arg_url, "/")) @@ -637,7 +643,7 @@ static int create_remoteserver( hostname = strndupa(hostname, strcspn(hostname, "/:")); - r = journal_remote_add_source(s, fd, hostname, false); + r = journal_remote_add_source(s, fd, (char *) hostname, false); if (r < 0) return r; } @@ -1061,10 +1067,11 @@ static int load_certificates(char **key, char **cert, char **trust) { return 0; } -int main(int argc, char **argv) { - RemoteServer s = {}; - int r; +static int run(int argc, char **argv) { + _cleanup_(notify_on_cleanup) const char *notify_message = NULL; + _cleanup_(journal_remote_server_destroy) RemoteServer s = {}; _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL; + int r; log_show_color(true); log_parse_environment(); @@ -1074,63 +1081,61 @@ int main(int argc, char **argv) { r = parse_config(); if (r < 0) - return EXIT_FAILURE; + return r; r = parse_argv(argc, argv); if (r <= 0) - return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; + return r; if (arg_listen_http || arg_listen_https) { r = setup_gnutls_logger(arg_gnutls_log); if (r < 0) - return EXIT_FAILURE; + return r; } if (arg_listen_https || https_socket >= 0) { - if (load_certificates(&key, &cert, &trust) < 0) - return EXIT_FAILURE; + r = load_certificates(&key, &cert, &trust); + if (r < 0) + return r; + s.check_trust = !arg_trust_all; } - if (create_remoteserver(&s, key, cert, trust) < 0) - return EXIT_FAILURE; + r = create_remoteserver(&s, key, cert, trust); + if (r < 0) + return r; r = sd_event_set_watchdog(s.events, true); if (r < 0) - log_error_errno(r, "Failed to enable watchdog: %m"); - else - log_debug("Watchdog is %sd.", enable_disable(r > 0)); + return log_error_errno(r, "Failed to enable watchdog: %m"); + + log_debug("Watchdog is %sd.", enable_disable(r > 0)); log_debug("%s running as pid "PID_FMT, program_invocation_short_name, getpid_cached()); - sd_notify(false, - "READY=1\n" - "STATUS=Processing requests..."); + + notify_message = notify_start(NOTIFY_READY, NOTIFY_STOPPING); while (s.active) { r = sd_event_get_state(s.events); if (r < 0) - break; + return r; if (r == SD_EVENT_FINISHED) break; r = sd_event_run(s.events, -1); - if (r < 0) { - log_error_errno(r, "Failed to run event loop: %m"); - break; - } + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); } - sd_notifyf(false, - "STOPPING=1\n" - "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count); - log_info("Finishing after writing %" PRIu64 " entries", s.event_count); - - journal_remote_server_destroy(&s); + notify_message = NULL; + (void) sd_notifyf(false, + "STOPPING=1\n" + "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count); - free(arg_key); - free(arg_cert); - free(arg_trust); + log_info("Finishing after writing %" PRIu64 " entries", s.event_count); - return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; + return 0; } + +DEFINE_MAIN_FUNCTION(run); diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index 1f3cdb932f..3c0916c438 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -346,7 +346,7 @@ static void MHDDaemonWrapper_free(MHDDaemonWrapper *d) { } #endif -RemoteServer* journal_remote_server_destroy(RemoteServer *s) { +void journal_remote_server_destroy(RemoteServer *s) { size_t i; #if HAVE_MICROHTTPD @@ -370,7 +370,6 @@ RemoteServer* journal_remote_server_destroy(RemoteServer *s) { journal_remote_server_global = NULL; /* fds that we're listening on remain open... */ - return NULL; } /********************************************************************** diff --git a/src/journal-remote/journal-remote.h b/src/journal-remote/journal-remote.h index e083ea9c74..4c25d43abf 100644 --- a/src/journal-remote/journal-remote.h +++ b/src/journal-remote/journal-remote.h @@ -62,4 +62,4 @@ int journal_remote_handle_raw_source( uint32_t revents, RemoteServer *s); -RemoteServer* journal_remote_server_destroy(RemoteServer *s); +void journal_remote_server_destroy(RemoteServer *s); diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c index 7f08809c54..1e08fcc554 100644 --- a/src/journal-remote/journal-upload.c +++ b/src/journal-remote/journal-upload.c @@ -10,6 +10,7 @@ #include "alloc-util.h" #include "conf-parser.h" +#include "daemon-util.h" #include "def.h" #include "env-file.h" #include "fd-util.h" @@ -18,6 +19,7 @@ #include "glob-util.h" #include "journal-upload.h" #include "log.h" +#include "main-func.h" #include "mkdir.h" #include "parse-util.h" #include "pretty-print.h" @@ -761,10 +763,11 @@ static int open_journal(sd_journal **j) { return r; } -int main(int argc, char **argv) { - Uploader u; - int r; +static int run(int argc, char **argv) { + _cleanup_(notify_on_cleanup) const char *notify_message = NULL; + _cleanup_(destroy_uploader) Uploader u = {}; bool use_journal; + int r; log_show_color(true); log_parse_environment(); @@ -774,23 +777,23 @@ int main(int argc, char **argv) { r = parse_config(); if (r < 0) - goto finish; + return r; r = parse_argv(argc, argv); if (r <= 0) - goto finish; + return r; sigbus_install(); r = setup_uploader(&u, arg_url, arg_save_state); if (r < 0) - goto cleanup; + return r; sd_event_set_watchdog(u.events, true); r = check_cursor_updating(&u); if (r < 0) - goto cleanup; + return r; log_debug("%s running as pid "PID_FMT, program_invocation_short_name, getpid_cached()); @@ -800,61 +803,51 @@ int main(int argc, char **argv) { sd_journal *j; r = open_journal(&j); if (r < 0) - goto finish; + return r; r = open_journal_for_upload(&u, j, arg_cursor ?: u.last_cursor, arg_cursor ? arg_after_cursor : true, !!arg_follow); if (r < 0) - goto finish; + return r; } - sd_notify(false, - "READY=1\n" - "STATUS=Processing input..."); + notify_message = notify_start("READY=1\n" + "STATUS=Processing input...", + NOTIFY_STOPPING); for (;;) { r = sd_event_get_state(u.events); if (r < 0) - break; + return r; if (r == SD_EVENT_FINISHED) - break; + return 0; if (use_journal) { if (!u.journal) - break; + return 0; r = check_journal_input(&u); } else if (u.input < 0 && !use_journal) { if (optind >= argc) - break; + return 0; log_debug("Using %s as input.", argv[optind]); r = open_file_for_upload(&u, argv[optind++]); } if (r < 0) - goto cleanup; + return r; if (u.uploading) { r = perform_upload(&u); if (r < 0) - break; + return r; } r = sd_event_run(u.events, u.timeout); - if (r < 0) { - log_error_errno(r, "Failed to run event loop: %m"); - break; - } + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); } - -cleanup: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down..."); - - destroy_uploader(&u); - -finish: - return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; } + +DEFINE_MAIN_FUNCTION(run); diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h index a50a2a75c7..364cd0f7cf 100644 --- a/src/journal-remote/microhttpd-util.h +++ b/src/journal-remote/microhttpd-util.h @@ -73,3 +73,5 @@ int check_permissions(struct MHD_Connection *connection, int *code, char **hostn * interesting events without overwhelming detail. */ int setup_gnutls_logger(char **categories); + +DEFINE_TRIVIAL_CLEANUP_FUNC(struct MHD_Daemon*, MHD_stop_daemon); diff --git a/src/libsystemd-network/ndisc-router.c b/src/libsystemd-network/ndisc-router.c index b982dcc07d..6935311b9a 100644 --- a/src/libsystemd-network/ndisc-router.c +++ b/src/libsystemd-network/ndisc-router.c @@ -676,7 +676,7 @@ _public_ int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret) _cleanup_free_ char *normalized = NULL; e[n] = 0; - r = dns_name_normalize(e, &normalized); + r = dns_name_normalize(e, 0, &normalized); if (r < 0) return r; diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index d35e3e2ce1..b3b134d650 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -371,36 +371,6 @@ int config_parse_hwaddrs(const char *unit, return 0; } -int config_parse_iaid(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) { - uint32_t iaid; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = safe_atou32(rvalue, &iaid); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Unable to read IAID, ignoring assignment: %s", rvalue); - return 0; - } - - *((uint32_t *)data) = iaid; - - return 0; -} - int config_parse_bridge_port_priority( const char *unit, const char *filename, diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index 45020c2bcf..0c8da848c1 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -35,7 +35,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr); CONFIG_PARSER_PROTOTYPE(config_parse_hwaddrs); CONFIG_PARSER_PROTOTYPE(config_parse_ifnames); CONFIG_PARSER_PROTOTYPE(config_parse_ifalias); -CONFIG_PARSER_PROTOTYPE(config_parse_iaid); CONFIG_PARSER_PROTOTYPE(config_parse_bridge_port_priority); int net_get_unique_predictable_data(sd_device *device, uint64_t *result); diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 842aeaa6b1..3bdd584bb5 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -345,8 +345,9 @@ int sd_dhcp_client_set_client_id( */ static int dhcp_client_set_iaid_duid_internal( sd_dhcp_client *client, + bool iaid_append, + bool iaid_set, uint32_t iaid, - bool append_iaid, uint16_t duid_type, const void *duid, size_t duid_len, @@ -368,17 +369,17 @@ static int dhcp_client_set_iaid_duid_internal( zero(client->client_id); client->client_id.type = 255; - if (append_iaid) { - /* If IAID is not configured, generate it. */ - if (iaid == 0) { + if (iaid_append) { + if (iaid_set) + client->client_id.ns.iaid = htobe32(iaid); + else { r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, true, &client->client_id.ns.iaid); if (r < 0) return r; - } else - client->client_id.ns.iaid = htobe32(iaid); + } } if (duid != NULL) { @@ -418,10 +419,10 @@ static int dhcp_client_set_iaid_duid_internal( } client->client_id_len = sizeof(client->client_id.type) + len + - (append_iaid ? sizeof(client->client_id.ns.iaid) : 0); + (iaid_append ? sizeof(client->client_id.ns.iaid) : 0); if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { - log_dhcp_client(client, "Configured %sDUID, restarting.", append_iaid ? "IAID+" : ""); + log_dhcp_client(client, "Configured %sDUID, restarting.", iaid_append ? "IAID+" : ""); client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); sd_dhcp_client_start(client); } @@ -431,18 +432,20 @@ static int dhcp_client_set_iaid_duid_internal( int sd_dhcp_client_set_iaid_duid( sd_dhcp_client *client, + bool iaid_set, uint32_t iaid, uint16_t duid_type, const void *duid, size_t duid_len) { - return dhcp_client_set_iaid_duid_internal(client, iaid, true, duid_type, duid, duid_len, 0); + return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, duid_type, duid, duid_len, 0); } int sd_dhcp_client_set_iaid_duid_llt( sd_dhcp_client *client, + bool iaid_set, uint32_t iaid, usec_t llt_time) { - return dhcp_client_set_iaid_duid_internal(client, iaid, true, DUID_TYPE_LLT, NULL, 0, llt_time); + return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, DUID_TYPE_LLT, NULL, 0, llt_time); } int sd_dhcp_client_set_duid( @@ -450,13 +453,13 @@ int sd_dhcp_client_set_duid( uint16_t duid_type, const void *duid, size_t duid_len) { - return dhcp_client_set_iaid_duid_internal(client, 0, false, duid_type, duid, duid_len, 0); + return dhcp_client_set_iaid_duid_internal(client, false, false, 0, duid_type, duid, duid_len, 0); } int sd_dhcp_client_set_duid_llt( sd_dhcp_client *client, usec_t llt_time) { - return dhcp_client_set_iaid_duid_internal(client, 0, false, DUID_TYPE_LLT, NULL, 0, llt_time); + return dhcp_client_set_iaid_duid_internal(client, false, false, 0, DUID_TYPE_LLT, NULL, 0, llt_time); } int sd_dhcp_client_set_hostname( diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 8275d2f31a..13badbf0bf 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -355,7 +355,7 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) { return 0; } - r = dns_name_normalize(name, &normalized); + r = dns_name_normalize(name, 0, &normalized); if (r < 0) return r; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 555ae5f5ec..8ff4585f63 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -56,7 +56,7 @@ struct sd_dhcp6_client { struct sd_dhcp6_lease *lease; int fd; bool information_request; - bool has_iaid; + bool iaid_set; be16_t *req_opts; size_t req_opts_allocated; size_t req_opts_len; @@ -268,7 +268,7 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { client->ia_na.ia_na.id = htobe32(iaid); client->ia_pd.ia_pd.id = htobe32(iaid); - client->has_iaid = true; + client->iaid_set = true; return 0; } @@ -792,7 +792,7 @@ static int client_ensure_iaid(sd_dhcp6_client *client) { assert(client); - if (client->has_iaid) + if (client->iaid_set) return 0; r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, true, &iaid); @@ -801,7 +801,7 @@ static int client_ensure_iaid(sd_dhcp6_client *client) { client->ia_na.ia_na.id = iaid; client->ia_pd.ia_pd.id = iaid; - client->has_iaid = true; + client->iaid_set = true; return 0; } diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build index 071691327b..f441bb6972 100644 --- a/src/libsystemd/meson.build +++ b/src/libsystemd/meson.build @@ -8,7 +8,7 @@ id128_sources = files(''' sd_daemon_c = files('sd-daemon/sd-daemon.c') -sd_event_c = files(''' +sd_event_sources = files(''' sd-event/event-source.h sd-event/event-util.c sd-event/event-util.h @@ -90,7 +90,7 @@ libsystemd_sources = files(''' sd-path/sd-path.c sd-resolve/sd-resolve.c sd-utf8/sd-utf8.c -'''.split()) + id128_sources + sd_daemon_c + sd_event_c + sd_login_c +'''.split()) + id128_sources + sd_daemon_c + sd_event_sources + sd_login_c disable_mempool_c = files('disable-mempool.c') diff --git a/src/login/logind-session-device.c b/src/login/logind-session-device.c index 0fcbe98c2a..f358524ebc 100644 --- a/src/login/logind-session-device.c +++ b/src/login/logind-session-device.c @@ -3,13 +3,9 @@ #include <fcntl.h> #include <linux/input.h> #include <string.h> -#include <stdint.h> #include <sys/ioctl.h> #include <sys/types.h> -/* Old drm.h may needs to be included after stdint.h and sys/types.h */ -#include <drm/drm.h> - #include "sd-device.h" #include "alloc-util.h" diff --git a/src/network/meson.build b/src/network/meson.build index d4fa27a288..8fe4854424 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -64,6 +64,8 @@ sources = files(''' networkd-manager.h networkd-ndisc.c networkd-ndisc.h + networkd-neighbor.c + networkd-neighbor.h networkd-radv.c networkd-radv.h networkd-network-bus.c diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 31dbea9420..980d49e4ff 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -670,10 +670,12 @@ int dhcp4_set_client_identifier(Link *link) { if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0) r = sd_dhcp_client_set_iaid_duid_llt(link->dhcp_client, + link->network->iaid_set, link->network->iaid, duid->llt_time); else r = sd_dhcp_client_set_iaid_duid(link->dhcp_client, + link->network->iaid_set, link->network->iaid, duid->type, duid->raw_data_len > 0 ? duid->raw_data : NULL, diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 4b5c4b8b4b..ed6b9df72b 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -655,9 +655,11 @@ int dhcp6_configure(Link *link) { if (r < 0) return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m"); - r = sd_dhcp6_client_set_iaid(client, link->network->iaid); - if (r < 0) - return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m"); + if (link->network->iaid_set) { + r = sd_dhcp6_client_set_iaid(client, link->network->iaid); + if (r < 0) + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m"); + } duid = link_get_duid(link); if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 86f2e6dca9..d73e85cf25 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -20,6 +20,7 @@ #include "networkd-lldp-tx.h" #include "networkd-manager.h" #include "networkd-ndisc.h" +#include "networkd-neighbor.h" #include "networkd-radv.h" #include "networkd-routing-policy-rule.h" #include "set.h" @@ -719,7 +720,7 @@ static void link_enter_configured(Link *link) { assert(link); assert(link->network); - if (link->state != LINK_STATE_SETTING_ROUTES) + if (link->state != LINK_STATE_CONFIGURING) return; log_link_info(link, "Configured"); @@ -741,6 +742,12 @@ void link_check_ready(Link *link) { if (!link->network) return; + if (!link->addresses_configured) + return; + + if (!link->neighbors_configured) + return; + if (!link->static_routes_configured) return; @@ -821,9 +828,8 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { assert(link); assert(link->route_messages > 0); - assert(IN_SET(link->state, LINK_STATE_SETTING_ADDRESSES, - LINK_STATE_SETTING_ROUTES, LINK_STATE_FAILED, - LINK_STATE_LINGER)); + assert(IN_SET(link->state, LINK_STATE_CONFIGURING, + LINK_STATE_FAILED, LINK_STATE_LINGER)); link->route_messages--; @@ -843,7 +849,7 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { return 1; } -static int link_enter_set_routes(Link *link) { +static int link_request_set_routes(Link *link) { enum { PHASE_NON_GATEWAY, /* First phase: Routes without a gateway */ PHASE_GATEWAY, /* Second phase: Routes with a gateway */ @@ -854,11 +860,13 @@ static int link_enter_set_routes(Link *link) { assert(link); assert(link->network); - assert(link->state == LINK_STATE_SETTING_ADDRESSES); + assert(link->addresses_configured); + assert(link->address_messages == 0); + assert(link->state != _LINK_STATE_INVALID); - (void) link_set_routing_policy_rule(link); + link_set_state(link, LINK_STATE_CONFIGURING); - link_set_state(link, LINK_STATE_SETTING_ROUTES); + (void) link_set_routing_policy_rule(link); /* First add the routes that enable us to talk to gateways, then add in the others that need a gateway. */ for (phase = 0; phase < _PHASE_MAX; phase++) @@ -886,6 +894,34 @@ static int link_enter_set_routes(Link *link) { return 0; } +static int link_request_set_neighbors(Link *link) { + Neighbor *neighbor; + int r; + + assert(link); + assert(link->network); + assert(link->state != _LINK_STATE_INVALID); + + link_set_state(link, LINK_STATE_CONFIGURING); + + LIST_FOREACH(neighbors, neighbor, link->network->neighbors) { + r = neighbor_configure(neighbor, link, NULL); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set neighbor: %m"); + link_enter_failed(link); + return r; + } + } + + if (link->neighbor_messages == 0) { + link->neighbors_configured = true; + link_check_ready(link); + } else + log_link_debug(link, "Setting neighbors"); + + return 0; +} + static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -894,7 +930,7 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) assert(link); assert(link->ifname); assert(link->address_messages > 0); - assert(IN_SET(link->state, LINK_STATE_SETTING_ADDRESSES, + assert(IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_FAILED, LINK_STATE_LINGER)); link->address_messages--; @@ -910,7 +946,8 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) if (link->address_messages == 0) { log_link_debug(link, "Addresses set"); - link_enter_set_routes(link); + link->addresses_configured = true; + link_request_set_routes(link); } return 1; @@ -1027,7 +1064,7 @@ static int link_set_bridge_fdb(Link *link) { return 0; } -static int link_enter_set_addresses(Link *link) { +static int link_request_set_addresses(Link *link) { AddressLabel *label; Address *ad; int r; @@ -1040,7 +1077,9 @@ static int link_enter_set_addresses(Link *link) { if (r < 0) return r; - link_set_state(link, LINK_STATE_SETTING_ADDRESSES); + link_set_state(link, LINK_STATE_CONFIGURING); + + link_request_set_neighbors(link); LIST_FOREACH(addresses, ad, link->network->static_addresses) { r = address_configure(ad, link, address_handler, false); @@ -1181,9 +1220,10 @@ static int link_enter_set_addresses(Link *link) { log_link_debug(link, "Offering DHCPv4 leases"); } - if (link->address_messages == 0) - link_enter_set_routes(link); - else + if (link->address_messages == 0) { + link->addresses_configured = true; + link_request_set_routes(link); + } else log_link_debug(link, "Setting addresses"); return 0; @@ -2269,7 +2309,7 @@ static int link_joined(Link *link) { if (!link_has_carrier(link) && !link->network->configure_without_carrier) return 0; - return link_enter_set_addresses(link); + return link_request_set_addresses(link); } static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { @@ -2306,7 +2346,7 @@ static int link_enter_join_netdev(Link *link) { assert(link->network); assert(link->state == LINK_STATE_PENDING); - link_set_state(link, LINK_STATE_ENSLAVING); + link_set_state(link, LINK_STATE_CONFIGURING); link_dirty(link); @@ -3419,7 +3459,7 @@ static int link_carrier_gained(Link *link) { return r; } - r = link_enter_set_addresses(link); + r = link_request_set_addresses(link); if (r < 0) return r; } @@ -3502,7 +3542,7 @@ int link_update(Link *link, sd_netlink_message *m) { if (link->state == LINK_STATE_LINGER) { log_link_info(link, "Link readded"); - link_set_state(link, LINK_STATE_ENSLAVING); + link_set_state(link, LINK_STATE_CONFIGURING); r = link_new_carrier_maps(link); if (r < 0) @@ -3597,10 +3637,12 @@ int link_update(Link *link, sd_netlink_message *m) { if (r < 0) return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m"); - r = sd_dhcp6_client_set_iaid(link->dhcp6_client, - link->network->iaid); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m"); + if (link->network->iaid_set) { + r = sd_dhcp6_client_set_iaid(link->dhcp6_client, + link->network->iaid); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m"); + } r = sd_dhcp6_client_set_duid(link->dhcp6_client, duid->type, @@ -4030,9 +4072,7 @@ void link_clean(Link *link) { static const char* const link_state_table[_LINK_STATE_MAX] = { [LINK_STATE_PENDING] = "pending", - [LINK_STATE_ENSLAVING] = "configuring", - [LINK_STATE_SETTING_ADDRESSES] = "configuring", - [LINK_STATE_SETTING_ROUTES] = "configuring", + [LINK_STATE_CONFIGURING] = "configuring", [LINK_STATE_CONFIGURED] = "configured", [LINK_STATE_UNMANAGED] = "unmanaged", [LINK_STATE_FAILED] = "failed", diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 3fdfa55b25..00e68fdfaa 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -19,9 +19,7 @@ typedef enum LinkState { LINK_STATE_PENDING, - LINK_STATE_ENSLAVING, - LINK_STATE_SETTING_ADDRESSES, - LINK_STATE_SETTING_ROUTES, + LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED, LINK_STATE_UNMANAGED, LINK_STATE_FAILED, @@ -72,6 +70,7 @@ typedef struct Link { unsigned address_messages; unsigned address_label_messages; + unsigned neighbor_messages; unsigned route_messages; unsigned routing_policy_rule_messages; unsigned routing_policy_rule_remove_messages; @@ -82,6 +81,8 @@ typedef struct Link { Set *routes; Set *routes_foreign; + bool addresses_configured; + sd_dhcp_client *dhcp_client; sd_dhcp_lease *dhcp_lease; char *lease_file; @@ -97,6 +98,8 @@ typedef struct Link { bool ipv4ll_address:1; bool ipv4ll_route:1; + bool neighbors_configured; + bool static_routes_configured; bool routing_policy_rules_configured; bool setting_mtu; diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c new file mode 100644 index 0000000000..254a60bdc3 --- /dev/null +++ b/src/network/networkd-neighbor.c @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "sd-netlink.h" + +#include "alloc-util.h" +#include "conf-parser.h" +#include "ether-addr-util.h" +#include "hashmap.h" +#include "in-addr-util.h" +#include "netlink-util.h" +#include "networkd-link.h" +#include "networkd-manager.h" +#include "networkd-neighbor.h" + +void neighbor_free(Neighbor *neighbor) { + if (!neighbor) + return; + + if (neighbor->network) { + LIST_REMOVE(neighbors, neighbor->network->neighbors, neighbor); + assert(neighbor->network->n_neighbors > 0); + neighbor->network->n_neighbors--; + + if (neighbor->section) { + hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section); + network_config_section_free(neighbor->section); + } + } + + free(neighbor); +} + +static int neighbor_new_static(Network *network, const char *filename, unsigned section_line, Neighbor **ret) { + _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(neighbor_freep) Neighbor *neighbor = NULL; + int r; + + assert(network); + assert(ret); + assert(!!filename == (section_line > 0)); + + if (filename) { + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; + + neighbor = hashmap_get(network->neighbors_by_section, n); + if (neighbor) { + *ret = TAKE_PTR(neighbor); + + return 0; + } + } + + neighbor = new(Neighbor, 1); + if (!neighbor) + return -ENOMEM; + + *neighbor = (Neighbor) { + .network = network, + .family = AF_UNSPEC, + }; + + LIST_APPEND(neighbors, network->neighbors, neighbor); + network->n_neighbors++; + + if (filename) { + neighbor->section = TAKE_PTR(n); + + r = hashmap_ensure_allocated(&network->neighbors_by_section, &network_config_hash_ops); + if (r < 0) + return r; + + r = hashmap_put(network->neighbors_by_section, neighbor->section, neighbor); + if (r < 0) + return r; + } + + *ret = TAKE_PTR(neighbor); + + return 0; +} + +static int neighbor_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(link); + assert(link->neighbor_messages > 0); + + link->neighbor_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) + log_link_warning_errno(link, r, "Could not set neighbor: %m"); + + if (link->neighbor_messages == 0) { + log_link_debug(link, "Neighbors set"); + link->neighbors_configured = true; + link_check_ready(link); + } + + return 1; +} + +int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(neighbor); + assert(link); + assert(link->ifindex > 0); + assert(link->manager); + assert(link->manager->rtnl); + + if (neighbor->family == AF_UNSPEC) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Neighbor without Address= configured"); + if (!neighbor->mac_configured) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Neighbor without MACAddress= configured"); + + r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, + link->ifindex, neighbor->family); + if (r < 0) + return log_error_errno(r, "Could not allocate RTM_NEWNEIGH message: %m"); + + r = sd_rtnl_message_neigh_set_state(req, NUD_PERMANENT); + if (r < 0) + return log_error_errno(r, "Could not set state: %m"); + + r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE); + if (r < 0) + return log_error_errno(r, "Could not set flags: %m"); + + r = sd_netlink_message_append_ether_addr(req, NDA_LLADDR, &neighbor->mac); + if (r < 0) + return log_error_errno(r, "Could not append NDA_LLADDR attribute: %m"); + + switch (neighbor->family) { + case AF_INET6: + r = sd_netlink_message_append_in6_addr(req, NDA_DST, &neighbor->in_addr.in6); + if (r < 0) + return log_error_errno(r, "Could not append NDA_DST attribute: %m"); + break; + case AF_INET: + r = sd_netlink_message_append_in_addr(req, NDA_DST, &neighbor->in_addr.in); + if (r < 0) + return log_error_errno(r, "Could not append NDA_DST attribute: %m"); + break; + default: + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Neighbor with invalid address family"); + } + + r = netlink_call_async(link->manager->rtnl, NULL, req, callback ?: neighbor_handler, + link_netlink_destroy_callback, link); + if (r < 0) + return log_error_errno(r, "Could not send rtnetlink message: %m"); + + link->neighbor_messages++; + link_ref(link); + + return 0; +} + +int config_parse_neighbor_address(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) { + + Network *network = userdata; + _cleanup_(neighbor_freep) Neighbor *n = NULL; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = neighbor_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + r = in_addr_from_string_auto(rvalue, &n->family, &n->in_addr); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor Address is invalid, ignoring assignment: %s", rvalue); + return 0; + } + + TAKE_PTR(n); + + return 0; +} + +int config_parse_neighbor_hwaddr(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) { + + Network *network = userdata; + _cleanup_(neighbor_freep) Neighbor *n = NULL; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = neighbor_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + r = ether_addr_from_string(rvalue, &n->mac); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor MACAddress is invalid, ignoring assignment: %s", rvalue); + return 0; + } + + n->mac_configured = true; + TAKE_PTR(n); + + return 0; +} diff --git a/src/network/networkd-neighbor.h b/src/network/networkd-neighbor.h new file mode 100644 index 0000000000..094bf7977e --- /dev/null +++ b/src/network/networkd-neighbor.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "sd-netlink.h" + +#include "conf-parser.h" +#include "ether-addr-util.h" +#include "in-addr-util.h" +#include "list.h" +#include "macro.h" + +typedef struct Neighbor Neighbor; + +#include "networkd-link.h" +#include "networkd-network.h" + +struct Neighbor { + Network *network; + Link *link; + NetworkConfigSection *section; + + int family; + union in_addr_union in_addr; + bool mac_configured; + struct ether_addr mac; + + LIST_FIELDS(Neighbor, neighbors); +}; + +void neighbor_free(Neighbor *neighbor); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Neighbor*, neighbor_free); + +int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback); + +CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_address); +CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_hwaddr); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 945a3c5705..48d8ae52fa 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -93,6 +93,8 @@ Address.AutoJoin, config_parse_address_flags, Address.Scope, config_parse_address_scope, 0, 0 IPv6AddressLabel.Prefix, config_parse_address_label_prefix, 0, 0 IPv6AddressLabel.Label, config_parse_address_label, 0, 0 +Neighbor.Address, config_parse_neighbor_address, 0, 0 +Neighbor.MACAddress, config_parse_neighbor_hwaddr, 0, 0 RoutingPolicyRule.TypeOfService, config_parse_routing_policy_rule_tos, 0, 0 RoutingPolicyRule.Priority, config_parse_routing_policy_rule_priority, 0, 0 RoutingPolicyRule.Table, config_parse_routing_policy_rule_table, 0, 0 @@ -139,7 +141,7 @@ DHCP.DUIDRawData, config_parse_duid_rawdata, DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric) DHCP.RouteTable, config_parse_dhcp_route_table, 0, 0 DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) -DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid) +DHCP.IAID, config_parse_iaid, 0, 0 DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port) DHCP.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit) DHCP.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 178cdff82b..62dc6a0bf7 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -210,6 +210,7 @@ int network_load_one(Manager *manager, const char *filename) { "Link\0" "Network\0" "Address\0" + "Neighbor\0" "IPv6AddressLabel\0" "RoutingPolicyRule\0" "Route\0" @@ -300,6 +301,7 @@ void network_free(Network *network) { IPv6ProxyNDPAddress *ipv6_proxy_ndp_address; RoutingPolicyRule *rule; FdbEntry *fdb_entry; + Neighbor *neighbor; AddressLabel *label; Prefix *prefix; Address *address; @@ -350,6 +352,9 @@ void network_free(Network *network) { while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses)) ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address); + while ((neighbor = network->neighbors)) + neighbor_free(neighbor); + while ((label = network->address_labels)) address_label_free(label); @@ -362,6 +367,7 @@ void network_free(Network *network) { hashmap_free(network->addresses_by_section); hashmap_free(network->routes_by_section); hashmap_free(network->fdb_entries_by_section); + hashmap_free(network->neighbors_by_section); hashmap_free(network->address_labels_by_section); hashmap_free(network->prefixes_by_section); hashmap_free(network->rules_by_section); @@ -653,7 +659,7 @@ int config_parse_domains( domain = "."; /* make sure we don't allow empty strings, thus write the root domain as "." */ } else { - r = dns_name_normalize(domain, &normalized); + r = dns_name_normalize(domain, 0, &normalized); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "'%s' is not a valid domain name, ignoring.", domain); continue; @@ -1467,3 +1473,35 @@ static const char* const lldp_mode_table[_LLDP_MODE_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES); + +int config_parse_iaid(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) { + Network *network = data; + uint32_t iaid; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(network); + + r = safe_atou32(rvalue, &iaid); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Unable to read IAID, ignoring assignment: %s", rvalue); + return 0; + } + + network->iaid = iaid; + network->iaid_set = true; + + return 0; +} diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 3592b563c0..3a72c5bd9a 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -15,6 +15,7 @@ #include "networkd-fdb.h" #include "networkd-ipv6-proxy-ndp.h" #include "networkd-lldp-tx.h" +#include "networkd-neighbor.h" #include "networkd-radv.h" #include "networkd-route.h" #include "networkd-routing-policy-rule.h" @@ -226,6 +227,8 @@ struct Network { uint32_t iaid; DUID duid; + bool iaid_set; + bool required_for_online; /* Is this network required to be considered online? */ LLDPMode lldp_mode; /* LLDP reception */ @@ -235,6 +238,7 @@ struct Network { LIST_HEAD(Route, static_routes); LIST_HEAD(FdbEntry, static_fdb_entries); LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses); + LIST_HEAD(Neighbor, neighbors); LIST_HEAD(AddressLabel, address_labels); LIST_HEAD(Prefix, static_prefixes); LIST_HEAD(RoutingPolicyRule, rules); @@ -243,6 +247,7 @@ struct Network { unsigned n_static_routes; unsigned n_static_fdb_entries; unsigned n_ipv6_proxy_ndp_addresses; + unsigned n_neighbors; unsigned n_address_labels; unsigned n_static_prefixes; unsigned n_rules; @@ -250,6 +255,7 @@ struct Network { Hashmap *addresses_by_section; Hashmap *routes_by_section; Hashmap *fdb_entries_by_section; + Hashmap *neighbors_by_section; Hashmap *address_labels_by_section; Hashmap *prefixes_by_section; Hashmap *rules_by_section; @@ -302,6 +308,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_lldp_mode); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_route_table); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_class); CONFIG_PARSER_PROTOTYPE(config_parse_ntp); +CONFIG_PARSER_PROTOTYPE(config_parse_iaid); /* Legacy IPv4LL support */ CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll); diff --git a/src/network/networkd.c b/src/network/networkd.c index 4fa5533c60..fcecafe083 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -4,13 +4,16 @@ #include "sd-event.h" #include "capability-util.h" +#include "daemon-util.h" +#include "main-func.h" #include "mkdir.h" #include "networkd-conf.h" #include "networkd-manager.h" #include "signal-util.h" #include "user-util.h" -int main(int argc, char *argv[]) { +static int run(int argc, char *argv[]) { + _cleanup_(notify_on_cleanup) const char *notify_message = NULL; _cleanup_(manager_freep) Manager *m = NULL; const char *user = "systemd-network"; uid_t uid; @@ -21,17 +24,12 @@ int main(int argc, char *argv[]) { umask(0022); - if (argc != 1) { - log_error("This program takes no arguments."); - r = -EINVAL; - goto out; - } + if (argc != 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments."); r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0); - if (r < 0) { - log_error_errno(r, "Cannot resolve user name %s: %m", user); - goto out; - } + if (r < 0) + return log_error_errno(r, "Cannot resolve user name %s: %m", user); /* Create runtime directory. This is not necessary when networkd is * started with "RuntimeDirectory=systemd/netif", or after @@ -49,7 +47,7 @@ int main(int argc, char *argv[]) { (1ULL << CAP_NET_BROADCAST) | (1ULL << CAP_NET_RAW)); if (r < 0) - goto out; + return log_error_errno(r, "Failed to drop privileges: %m"); } /* Always create the directories people can create inotify watches in. @@ -70,72 +68,50 @@ int main(int argc, char *argv[]) { assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); r = manager_new(&m); - if (r < 0) { - log_error_errno(r, "Could not create manager: %m"); - goto out; - } + if (r < 0) + return log_error_errno(r, "Could not create manager: %m"); r = manager_connect_bus(m); - if (r < 0) { - log_error_errno(r, "Could not connect to bus: %m"); - goto out; - } + if (r < 0) + return log_error_errno(r, "Could not connect to bus: %m"); r = manager_parse_config_file(m); if (r < 0) log_warning_errno(r, "Failed to parse configuration file: %m"); r = manager_load_config(m); - if (r < 0) { - log_error_errno(r, "Could not load configuration files: %m"); - goto out; - } + if (r < 0) + return log_error_errno(r, "Could not load configuration files: %m"); r = manager_rtnl_enumerate_links(m); - if (r < 0) { - log_error_errno(r, "Could not enumerate links: %m"); - goto out; - } + if (r < 0) + return log_error_errno(r, "Could not enumerate links: %m"); r = manager_rtnl_enumerate_addresses(m); - if (r < 0) { - log_error_errno(r, "Could not enumerate addresses: %m"); - goto out; - } + if (r < 0) + return log_error_errno(r, "Could not enumerate addresses: %m"); r = manager_rtnl_enumerate_routes(m); - if (r < 0) { - log_error_errno(r, "Could not enumerate routes: %m"); - goto out; - } + if (r < 0) + return log_error_errno(r, "Could not enumerate routes: %m"); r = manager_rtnl_enumerate_rules(m); - if (r < 0) { - log_error_errno(r, "Could not enumerate rules: %m"); - goto out; - } + if (r < 0) + return log_error_errno(r, "Could not enumerate rules: %m"); r = manager_start(m); - if (r < 0) { - log_error_errno(r, "Could not start manager: %m"); - goto out; - } + if (r < 0) + return log_error_errno(r, "Could not start manager: %m"); log_info("Enumeration completed"); - sd_notify(false, - "READY=1\n" - "STATUS=Processing requests..."); + notify_message = notify_start(NOTIFY_READY, NOTIFY_STOPPING); r = sd_event_loop(m->event); - if (r < 0) { - log_error_errno(r, "Event loop failed: %m"); - goto out; - } -out: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down..."); + if (r < 0) + return log_error_errno(r, "Event loop failed: %m"); - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + return 0; } + +DEFINE_MAIN_FUNCTION(run); diff --git a/src/network/wait-online/wait-online.c b/src/network/wait-online/wait-online.c index 7f30862bbf..71b6cf6b87 100644 --- a/src/network/wait-online/wait-online.c +++ b/src/network/wait-online/wait-online.c @@ -4,6 +4,8 @@ #include "sd-daemon.h" +#include "daemon-util.h" +#include "main-func.h" #include "manager.h" #include "pretty-print.h" #include "signal-util.h" @@ -14,6 +16,9 @@ static usec_t arg_timeout = 120 * USEC_PER_SEC; static char **arg_interfaces = NULL; static char **arg_ignore = NULL; +STATIC_DESTRUCTOR_REGISTER(arg_interfaces, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_ignore, strv_freep); + static int help(void) { _cleanup_free_ char *link = NULL; int r; @@ -105,7 +110,8 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -int main(int argc, char *argv[]) { +static int run(int argc, char *argv[]) { + _cleanup_(notify_on_cleanup) const char *notify_message = NULL; _cleanup_(manager_freep) Manager *m = NULL; int r; @@ -123,37 +129,24 @@ int main(int argc, char *argv[]) { assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); r = manager_new(&m, arg_interfaces, arg_ignore, arg_timeout); - if (r < 0) { - log_error_errno(r, "Could not create manager: %m"); - goto finish; - } + if (r < 0) + return log_error_errno(r, "Could not create manager: %m"); - if (manager_all_configured(m)) { - r = 0; - goto finish; - } + if (manager_all_configured(m)) + goto success; - sd_notify(false, - "READY=1\n" - "STATUS=Waiting for network connections..."); + notify_message = notify_start("READY=1\n" + "STATUS=Waiting for network connections...", + "STATUS=Failed to wait for network connectivity..."); r = sd_event_loop(m->event); - if (r < 0) { - log_error_errno(r, "Event loop failed: %m"); - goto finish; - } - -finish: - strv_free(arg_interfaces); - strv_free(arg_ignore); - - if (r >= 0) { - sd_notify(false, "STATUS=All interfaces configured..."); + if (r < 0) + return log_error_errno(r, "Event loop failed: %m"); - return EXIT_SUCCESS; - } else { - sd_notify(false, "STATUS=Failed waiting for network connectivity..."); +success: + notify_message = "STATUS=All interfaces configured..."; - return EXIT_FAILURE; - } + return 0; } + +DEFINE_MAIN_FUNCTION(run); diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index b9fd775526..fbe823da66 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -192,7 +192,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { /* The key names are not necessarily normalized, make sure that they are when we return them to our bus * clients. */ - r = dns_name_normalize(dns_resource_key_name(canonical->key), &normalized); + r = dns_name_normalize(dns_resource_key_name(canonical->key), 0, &normalized); if (r < 0) goto finish; @@ -405,7 +405,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { if (r == 0) continue; - r = dns_name_normalize(rr->ptr.name, &normalized); + r = dns_name_normalize(rr->ptr.name, 0, &normalized); if (r < 0) goto finish; @@ -743,7 +743,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) if (r < 0) return r; - r = dns_name_normalize(rr->srv.name, &normalized); + r = dns_name_normalize(rr->srv.name, 0, &normalized); if (r < 0) return r; @@ -799,7 +799,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) if (canonical) { normalized = mfree(normalized); - r = dns_name_normalize(dns_resource_key_name(canonical->key), &normalized); + r = dns_name_normalize(dns_resource_key_name(canonical->key), 0, &normalized); if (r < 0) return r; } diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 13da4e5991..d9633629e8 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -74,7 +74,7 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) { return -ENOBUFS; for (;;) { - r = dns_label_unescape(&n, buffer, buffer_max); + r = dns_label_unescape(&n, buffer, buffer_max, 0); if (r < 0) return r; if (r == 0) @@ -1705,7 +1705,7 @@ static int dnssec_nsec_wildcard_equal(DnsResourceRecord *rr, const char *name) { return 0; n = dns_resource_key_name(rr->key); - r = dns_label_unescape(&n, label, sizeof(label)); + r = dns_label_unescape(&n, label, sizeof label, 0); if (r <= 0) return r; if (r != 1 || label[0] != '*') @@ -1827,13 +1827,13 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) return r; if (r > 0) /* If the name we are interested in is a child of the NSEC RR, then append the asterisk to the NSEC * RR's name. */ - r = dns_name_concat("*", dns_resource_key_name(rr->key), &wc); + r = dns_name_concat("*", dns_resource_key_name(rr->key), 0, &wc); else { r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix); if (r < 0) return r; - r = dns_name_concat("*", common_suffix, &wc); + r = dns_name_concat("*", common_suffix, 0, &wc); } if (r < 0) return r; diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 0ff444acd0..572271be95 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -535,7 +535,7 @@ int dns_packet_append_name( } } - r = dns_label_unescape(&name, label, sizeof(label)); + r = dns_label_unescape(&name, label, sizeof label, 0); if (r < 0) goto fail; diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index b67c734e04..a1dffb08a3 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -77,7 +77,7 @@ int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key return 0; } - r = dns_name_concat(dns_resource_key_name(key), name, &joined); + r = dns_name_concat(dns_resource_key_name(key), name, 0, &joined); if (r < 0) return r; @@ -222,7 +222,7 @@ int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, if (search_domain) { _cleanup_free_ char *joined = NULL; - r = dns_name_concat(dns_resource_key_name(key), search_domain, &joined); + r = dns_name_concat(dns_resource_key_name(key), search_domain, 0, &joined); if (r < 0) return r; @@ -254,7 +254,7 @@ int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsRe if (search_domain) { _cleanup_free_ char *joined = NULL; - r = dns_name_concat(dns_resource_key_name(key), search_domain, &joined); + r = dns_name_concat(dns_resource_key_name(key), search_domain, 0, &joined); if (r < 0) return r; diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c index 368ec4da19..21c2442c51 100644 --- a/src/resolve/resolved-dns-search-domain.c +++ b/src/resolve/resolved-dns-search-domain.c @@ -19,7 +19,7 @@ int dns_search_domain_new( assert((type == DNS_SEARCH_DOMAIN_LINK) == !!l); assert(name); - r = dns_name_normalize(name, &normalized); + r = dns_name_normalize(name, 0, &normalized); if (r < 0) return r; diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index e05ada29a8..3e69741b88 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -43,16 +43,18 @@ int dns_server_new( return -E2BIG; } - s = new0(DnsServer, 1); + s = new(DnsServer, 1); if (!s) return -ENOMEM; - s->n_ref = 1; - s->manager = m; - s->type = type; - s->family = family; - s->address = *in_addr; - s->ifindex = ifindex; + *s = (DnsServer) { + .n_ref = 1, + .manager = m, + .type = type, + .family = family, + .address = *in_addr, + .ifindex = ifindex, + }; dns_server_reset_features(s); @@ -101,7 +103,7 @@ int dns_server_new( static DnsServer* dns_server_free(DnsServer *s) { assert(s); - dns_stream_unref(s->stream); + dns_server_unref_stream(s); #if ENABLE_DNS_OVER_TLS dnstls_server_free(s); @@ -156,6 +158,9 @@ void dns_server_unlink(DnsServer *s) { if (s->manager->current_dns_server == s) manager_set_dns_server(s->manager, NULL); + /* No need to keep a default stream around anymore */ + dns_server_unref_stream(s); + dns_server_unref(s); } @@ -824,6 +829,9 @@ void dns_server_reset_features(DnsServer *s) { s->warned_downgrade = false; dns_server_reset_counters(s); + + /* Let's close the default stream, so that we reprobe with the new features */ + dns_server_unref_stream(s); } void dns_server_reset_features_all(DnsServer *s) { @@ -884,6 +892,20 @@ void dns_server_dump(DnsServer *s, FILE *f) { yes_no(s->packet_rrsig_missing)); } +void dns_server_unref_stream(DnsServer *s) { + DnsStream *ref; + + assert(s); + + /* Detaches the default stream of this server. Some special care needs to be taken here, as that stream and + * this server reference each other. First, take the stream out of the server. It's destructor will check if it + * is registered with us, hence let's invalidate this separatly, so that it is already unregistered. */ + ref = TAKE_PTR(s->stream); + + /* And then, unref it */ + dns_stream_unref(ref); +} + static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = { [DNS_SERVER_SYSTEM] = "system", [DNS_SERVER_FALLBACK] = "fallback", diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index fda1251049..6e73f32df4 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -54,6 +54,8 @@ struct DnsServer { int ifindex; /* for IPv6 link-local DNS servers */ char *server_string; + + /* The long-lived stream towards this server. */ DnsStream *stream; #if ENABLE_DNS_OVER_TLS @@ -149,3 +151,5 @@ void dns_server_reset_features(DnsServer *s); void dns_server_reset_features_all(DnsServer *s); void dns_server_dump(DnsServer *s, FILE *f); + +void dns_server_unref_stream(DnsServer *s); diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c index 26d4663d74..aee339a4c8 100644 --- a/src/resolve/resolved-dns-stream.c +++ b/src/resolve/resolved-dns-stream.c @@ -17,6 +17,9 @@ static void dns_stream_stop(DnsStream *s) { s->io_event_source = sd_event_source_unref(s->io_event_source); s->timeout_event_source = sd_event_source_unref(s->timeout_event_source); s->fd = safe_close(s->fd); + + /* Disconnect us from the server object if we are now not usable anymore */ + dns_stream_detach(s); } static int dns_stream_update_io(DnsStream *s) { @@ -46,6 +49,8 @@ static int dns_stream_update_io(DnsStream *s) { } static int dns_stream_complete(DnsStream *s, int error) { + _cleanup_(dns_stream_unrefp) _unused_ DnsStream *ref = dns_stream_ref(s); /* Protect stream while we process it */ + assert(s); #if ENABLE_DNS_OVER_TLS @@ -59,6 +64,8 @@ static int dns_stream_complete(DnsStream *s, int error) { #endif dns_stream_stop(s); + dns_stream_detach(s); + if (s->complete) s->complete(s, error); else /* the default action if no completion function is set is to close the stream */ @@ -193,7 +200,7 @@ static int dns_stream_identify(DnsStream *s) { } ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt, int flags) { - ssize_t r; + ssize_t m; assert(s); assert(iov); @@ -203,13 +210,13 @@ ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt, ssize_t ss; size_t i; - r = 0; + m = 0; for (i = 0; i < iovcnt; i++) { ss = dnstls_stream_write(s, iov[i].iov_base, iov[i].iov_len); if (ss < 0) return ss; - r += ss; + m += ss; if (ss != (ssize_t) iov[i].iov_len) continue; } @@ -223,28 +230,28 @@ ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt, .msg_namelen = s->tfo_salen }; - r = sendmsg(s->fd, &hdr, MSG_FASTOPEN); - if (r < 0) { + m = sendmsg(s->fd, &hdr, MSG_FASTOPEN); + if (m < 0) { if (errno == EOPNOTSUPP) { s->tfo_salen = 0; - r = connect(s->fd, &s->tfo_address.sa, s->tfo_salen); - if (r < 0) + if (connect(s->fd, &s->tfo_address.sa, s->tfo_salen) < 0) return -errno; - r = -EAGAIN; - } else if (errno == EINPROGRESS) - r = -EAGAIN; - else - r = -errno; + return -EAGAIN; + } + if (errno == EINPROGRESS) + return -EAGAIN; + + return -errno; } else s->tfo_salen = 0; /* connection is made */ } else { - r = writev(s->fd, iov, iovcnt); - if (r < 0) - r = -errno; + m = writev(s->fd, iov, iovcnt); + if (m < 0) + return -errno; } - return r; + return m; } static ssize_t dns_stream_read(DnsStream *s, void *buf, size_t count) { @@ -258,7 +265,7 @@ static ssize_t dns_stream_read(DnsStream *s, void *buf, size_t count) { { ss = read(s->fd, buf, count); if (ss < 0) - ss = -errno; + return -errno; } return ss; @@ -273,7 +280,7 @@ static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) { } static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) { - DnsStream *s = userdata; + _cleanup_(dns_stream_unrefp) DnsStream *s = dns_stream_ref(userdata); /* Protect stream while we process it */ int r; assert(s); @@ -281,18 +288,16 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use #if ENABLE_DNS_OVER_TLS if (s->encrypted) { r = dnstls_stream_on_io(s, revents); - if (r == DNSTLS_STREAM_CLOSED) return 0; - else if (r == -EAGAIN) + if (r == -EAGAIN) return dns_stream_update_io(s); - else if (r < 0) { + if (r < 0) return dns_stream_complete(s, -r); - } else { - r = dns_stream_update_io(s); - if (r < 0) - return r; - } + + r = dns_stream_update_io(s); + if (r < 0) + return r; } #endif @@ -430,9 +435,6 @@ static DnsStream *dns_stream_free(DnsStream *s) { dns_stream_stop(s); - if (s->server && s->server->stream == s) - s->server->stream = NULL; - if (s->manager) { LIST_REMOVE(streams, s->manager->dns_streams, s); s->manager->n_dns_streams--; @@ -457,28 +459,37 @@ static DnsStream *dns_stream_free(DnsStream *s) { DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsStream, dns_stream, dns_stream_free); -int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd, const union sockaddr_union *tfo_address) { +int dns_stream_new( + Manager *m, + DnsStream **ret, + DnsProtocol protocol, + int fd, + const union sockaddr_union *tfo_address) { + _cleanup_(dns_stream_unrefp) DnsStream *s = NULL; int r; assert(m); + assert(ret); assert(fd >= 0); if (m->n_dns_streams > DNS_STREAMS_MAX) return -EBUSY; - s = new0(DnsStream, 1); + s = new(DnsStream, 1); if (!s) return -ENOMEM; + *s = (DnsStream) { + .n_ref = 1, + .fd = -1, + .protocol = protocol, + }; + r = ordered_set_ensure_allocated(&s->write_queue, &dns_packet_hash_ops); if (r < 0) return r; - s->n_ref = 1; - s->fd = -1; - s->protocol = protocol; - r = sd_event_add_io(m->event, &s->io_event_source, fd, EPOLLIN, on_stream_io, s); if (r < 0) return r; @@ -497,15 +508,16 @@ int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd, co (void) sd_event_source_set_description(s->timeout_event_source, "dns-stream-timeout"); LIST_PREPEND(streams, m->dns_streams, s); + m->n_dns_streams++; s->manager = m; + s->fd = fd; + if (tfo_address) { s->tfo_address = *tfo_address; s->tfo_salen = tfo_address->sa.sa_family == AF_INET6 ? sizeof(tfo_address->in6) : sizeof(tfo_address->in); } - m->n_dns_streams++; - *ret = TAKE_PTR(s); return 0; @@ -515,6 +527,7 @@ int dns_stream_write_packet(DnsStream *s, DnsPacket *p) { int r; assert(s); + assert(p); r = ordered_set_put(s->write_queue, p); if (r < 0) @@ -524,3 +537,31 @@ int dns_stream_write_packet(DnsStream *s, DnsPacket *p) { return dns_stream_update_io(s); } + +DnsPacket *dns_stream_take_read_packet(DnsStream *s) { + assert(s); + + if (!s->read_packet) + return NULL; + + if (s->n_read < sizeof(s->read_size)) + return NULL; + + if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size)) + return NULL; + + s->n_read = 0; + return TAKE_PTR(s->read_packet); +} + +void dns_stream_detach(DnsStream *s) { + assert(s); + + if (!s->server) + return; + + if (s->server->stream != s) + return; + + dns_server_unref_stream(s->server); +} diff --git a/src/resolve/resolved-dns-stream.h b/src/resolve/resolved-dns-stream.h index 46d2704afe..f18fc919e7 100644 --- a/src/resolve/resolved-dns-stream.h +++ b/src/resolve/resolved-dns-stream.h @@ -53,13 +53,12 @@ struct DnsStream { size_t n_written, n_read; OrderedSet *write_queue; - int (*on_connection)(DnsStream *s); int (*on_packet)(DnsStream *s); int (*complete)(DnsStream *s, int error); LIST_HEAD(DnsTransaction, transactions); /* when used by the transaction logic */ DnsServer *server; /* when used by the transaction logic */ - DnsQuery *query; /* when used by the DNS stub logic */ + DnsQuery *query; /* when used by the DNS stub logic */ /* used when DNS-over-TLS is enabled */ bool encrypted:1; @@ -87,3 +86,7 @@ static inline bool DNS_STREAM_QUEUED(DnsStream *s) { return !!s->write_packet; } + +DnsPacket *dns_stream_take_read_packet(DnsStream *s); + +void dns_stream_detach(DnsStream *s); diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index 015aabaf9b..a00716cd85 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -437,13 +437,17 @@ static int manager_dns_stub_udp_fd(Manager *m) { } static int on_dns_stub_stream_packet(DnsStream *s) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + assert(s); - assert(s->read_packet); - if (dns_packet_validate_query(s->read_packet) > 0) { - log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(s->read_packet)); + p = dns_stream_take_read_packet(s); + assert(p); + + if (dns_packet_validate_query(p) > 0) { + log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(p)); - dns_stub_process_query(s->manager, s, s->read_packet); + dns_stub_process_query(s->manager, s, p); } else log_debug("Invalid DNS stub TCP packet, ignoring."); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index ddab2850df..cc748ac95e 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -503,59 +503,54 @@ static int dns_transaction_on_stream_packet(DnsTransaction *t, DnsPacket *p) { } static int on_stream_complete(DnsStream *s, int error) { - _cleanup_(dns_stream_unrefp) DnsStream *p = NULL; - DnsTransaction *t, *n; - int r = 0; - - /* Do not let new transactions use this stream */ - if (s->server && s->server->stream == s) - p = TAKE_PTR(s->server->stream); + assert(s); if (ERRNO_IS_DISCONNECT(error) && s->protocol != DNS_PROTOCOL_LLMNR) { - usec_t usec; - log_debug_errno(error, "Connection failure for DNS TCP stream: %m"); if (s->transactions) { + DnsTransaction *t; + t = s->transactions; - assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &usec) >= 0); dns_server_packet_lost(t->server, IPPROTO_TCP, t->current_feature_level); } } - LIST_FOREACH_SAFE(transactions_by_stream, t, n, s->transactions) - if (error != 0) + if (error != 0) { + DnsTransaction *t, *n; + + LIST_FOREACH_SAFE(transactions_by_stream, t, n, s->transactions) on_transaction_stream_error(t, error); - else if (DNS_PACKET_ID(s->read_packet) == t->id) - /* As each transaction have a unique id the return code is only set once */ - r = dns_transaction_on_stream_packet(t, s->read_packet); + } - return r; + return 0; } -static int dns_stream_on_packet(DnsStream *s) { +static int on_stream_packet(DnsStream *s) { _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - int r = 0; DnsTransaction *t; + assert(s); + /* Take ownership of packet to be able to receive new packets */ - p = TAKE_PTR(s->read_packet); - s->n_read = 0; + p = dns_stream_take_read_packet(s); + assert(p); t = hashmap_get(s->manager->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p))); + if (t) + return dns_transaction_on_stream_packet(t, p); /* Ignore incorrect transaction id as transaction can have been canceled */ - if (t) - r = dns_transaction_on_stream_packet(t, p); - else { - if (dns_packet_validate_reply(p) <= 0) { - log_debug("Invalid TCP reply packet."); - on_stream_complete(s, 0); - } - return 0; + if (dns_packet_validate_reply(p) <= 0) { + log_debug("Invalid TCP reply packet."); + on_stream_complete(s, 0); } - return r; + return 0; +} + +static uint16_t dns_port_for_feature_level(DnsServerFeatureLevel level) { + return DNS_SERVER_FEATURE_LEVEL_IS_TLS(level) ? 853 : 53; } static int dns_transaction_emit_tcp(DnsTransaction *t) { @@ -585,7 +580,7 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) { if (t->server->stream && (DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level) == t->server->stream->encrypted)) s = dns_stream_ref(t->server->stream); else - fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level) ? 853 : 53, &sa); + fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, dns_port_for_feature_level(t->current_feature_level), &sa); break; @@ -629,7 +624,9 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) { fd = -1; #if ENABLE_DNS_OVER_TLS - if (DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level)) { + if (t->scope->protocol == DNS_PROTOCOL_DNS && + DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level)) { + assert(t->server); r = dnstls_stream_connect_tls(s, t->server); if (r < 0) @@ -638,13 +635,13 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) { #endif if (t->server) { - dns_stream_unref(t->server->stream); + dns_server_unref_stream(t->server); t->server->stream = dns_stream_ref(s); s->server = dns_server_ref(t->server); } s->complete = on_stream_complete; - s->on_packet = dns_stream_on_packet; + s->on_packet = on_stream_packet; /* The interface index is difficult to determine if we are * connecting to the local host, hence fill this in right away diff --git a/src/resolve/resolved-dnssd.c b/src/resolve/resolved-dnssd.c index ea96255dc1..2c28ec227a 100644 --- a/src/resolve/resolved-dnssd.c +++ b/src/resolve/resolved-dnssd.c @@ -228,10 +228,10 @@ int dnssd_update_rrs(DnssdService *s) { if (r < 0) return r; - r = dns_name_concat(s->type, "local", &service_name); + r = dns_name_concat(s->type, "local", 0, &service_name); if (r < 0) return r; - r = dns_name_concat(n, service_name, &full_name); + r = dns_name_concat(n, service_name, 0, &full_name); if (r < 0) return r; diff --git a/src/resolve/resolved-etc-hosts.c b/src/resolve/resolved-etc-hosts.c index c5c180943d..01cde4acf7 100644 --- a/src/resolve/resolved-etc-hosts.c +++ b/src/resolve/resolved-etc-hosts.c @@ -46,19 +46,20 @@ static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) { r = extract_first_word(&line, &address_str, NULL, EXTRACT_RELAX); if (r < 0) - return log_error_errno(r, "Couldn't extract address, in line /etc/hosts:%u.", nr); - if (r == 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Premature end of line, in line /etc/hosts:%u.", - nr); + return log_error_errno(r, "/etc/hosts:%u: failed to extract address: %m", nr); + assert(r > 0); /* We already checked that the line is not empty, so it should contain *something* */ r = in_addr_ifindex_from_string_auto(address_str, &address.family, &address.address, NULL); - if (r < 0) - return log_error_errno(r, "Address '%s' is invalid, in line /etc/hosts:%u.", address_str, nr); + if (r < 0) { + log_warning_errno(r, "/etc/hosts:%u: address '%s' is invalid, ignoring: %m", nr, address_str); + return 0; + } r = in_addr_is_null(address.family, &address.address); - if (r < 0) - return r; + if (r < 0) { + log_warning_errno(r, "/etc/hosts:%u: address '%s' is invalid, ignoring: %m", nr, address_str); + return 0; + } if (r > 0) /* This is an 0.0.0.0 or :: item, which we assume means that we shall map the specified hostname to * nothing. */ @@ -92,16 +93,18 @@ static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) { r = extract_first_word(&line, &name, NULL, EXTRACT_RELAX); if (r < 0) - return log_error_errno(r, "Couldn't extract host name, in line /etc/hosts:%u.", nr); + return log_error_errno(r, "/etc/hosts:%u: couldn't extract host name: %m", nr); if (r == 0) break; - r = dns_name_is_valid(name); - if (r <= 0) - return log_error_errno(r, "Hostname %s is not valid, ignoring, in line /etc/hosts:%u.", name, nr); - found = true; + r = dns_name_is_valid_ldh(name); + if (r <= 0) { + log_warning_errno(r, "/etc/hosts:%u: hostname \"%s\" is not valid, ignoring.", nr, name); + continue; + } + if (is_localhost(name)) /* Suppress the "localhost" line that is often seen */ continue; @@ -152,9 +155,7 @@ static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) { } if (!found) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Line is missing any host names, in line /etc/hosts:%u.", - nr); + log_warning("/etc/hosts:%u: line is missing any host names", nr); return 0; } @@ -176,11 +177,13 @@ int etc_hosts_parse(EtcHosts *hosts, FILE *f) { nr++; + l = strchr(line, '#'); + if (l) + *l = '\0'; + l = strstrip(line); if (isempty(l)) continue; - if (l[0] == '#') - continue; r = parse_line(&t, nr, l); if (r < 0) diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c index 65f5ceecd0..dfa55c577c 100644 --- a/src/resolve/resolved-llmnr.c +++ b/src/resolve/resolved-llmnr.c @@ -260,18 +260,21 @@ int manager_llmnr_ipv6_udp_fd(Manager *m) { } static int on_llmnr_stream_packet(DnsStream *s) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; DnsScope *scope; assert(s); - assert(s->read_packet); - scope = manager_find_scope(s->manager, s->read_packet); + p = dns_stream_take_read_packet(s); + assert(p); + + scope = manager_find_scope(s->manager, p); if (!scope) log_debug("Got LLMNR TCP packet on unknown scope. Ignoring."); - else if (dns_packet_validate_query(s->read_packet) > 0) { - log_debug("Got LLMNR TCP query packet for id %u", DNS_PACKET_ID(s->read_packet)); + else if (dns_packet_validate_query(p) > 0) { + log_debug("Got LLMNR TCP query packet for id %u", DNS_PACKET_ID(p)); - dns_scope_process_query(scope, s, s->read_packet); + dns_scope_process_query(scope, s, p); } else log_debug("Invalid LLMNR TCP packet, ignoring."); diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 23a7b87801..1f8c47ccbe 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -334,7 +334,7 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char return log_debug_errno(r, "Can't determine system hostname: %m"); p = h; - r = dns_label_unescape(&p, label, sizeof label); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r < 0) return log_error_errno(r, "Failed to unescape host name: %m"); if (r == 0) @@ -372,7 +372,7 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "System hostname is 'localhost', ignoring."); - r = dns_name_concat(n, "local", mdns_hostname); + r = dns_name_concat(n, "local", 0, mdns_hostname); if (r < 0) return log_error_errno(r, "Failed to determine mDNS hostname: %m"); @@ -404,7 +404,7 @@ static int make_fallback_hostnames(char **full_hostname, char **llmnr_hostname, assert(mdns_hostname); p = fallback_hostname(); - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r < 0) return log_error_errno(r, "Failed to unescape fallback host name: %m"); @@ -414,7 +414,7 @@ static int make_fallback_hostnames(char **full_hostname, char **llmnr_hostname, if (r < 0) return log_error_errno(r, "Failed to escape fallback hostname: %m"); - r = dns_name_concat(n, "local", &m); + r = dns_name_concat(n, "local", 0, &m); if (r < 0) return log_error_errno(r, "Failed to concatenate mDNS hostname: %m"); @@ -1148,7 +1148,7 @@ int manager_next_hostname(Manager *m) { if (r < 0) return r; - r = dns_name_concat(h, "local", &k); + r = dns_name_concat(h, "local", 0, &k); if (r < 0) return r; diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c index 755477096d..f4efddf8e5 100644 --- a/src/resolve/resolved.c +++ b/src/resolve/resolved.c @@ -4,6 +4,8 @@ #include "sd-event.h" #include "capability-util.h" +#include "daemon-util.h" +#include "main-func.h" #include "mkdir.h" #include "resolved-conf.h" #include "resolved-manager.h" @@ -12,7 +14,8 @@ #include "signal-util.h" #include "user-util.h" -int main(int argc, char *argv[]) { +static int run(int argc, char *argv[]) { + _cleanup_(notify_on_cleanup) const char *notify_stop = NULL; _cleanup_(manager_freep) Manager *m = NULL; const char *user = "systemd-resolve"; uid_t uid; @@ -21,32 +24,23 @@ int main(int argc, char *argv[]) { log_setup_service(); - if (argc != 1) { - log_error("This program takes no arguments."); - r = -EINVAL; - goto finish; - } + if (argc != 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments."); umask(0022); r = mac_selinux_init(); - if (r < 0) { - log_error_errno(r, "SELinux setup failed: %m"); - goto finish; - } + if (r < 0) + return log_error_errno(r, "SELinux setup failed: %m"); r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0); - if (r < 0) { - log_error_errno(r, "Cannot resolve user name %s: %m", user); - goto finish; - } + if (r < 0) + return log_error_errno(r, "Cannot resolve user name %s: %m", user); /* Always create the directory where resolv.conf will live */ r = mkdir_safe_label("/run/systemd/resolve", 0755, uid, gid, MKDIR_WARN_MODE); - if (r < 0) { - log_error_errno(r, "Could not create runtime directory: %m"); - goto finish; - } + if (r < 0) + return log_error_errno(r, "Could not create runtime directory: %m"); /* Drop privileges, but only if we have been started as root. If we are not running as root we assume most * privileges are already dropped. */ @@ -58,22 +52,18 @@ int main(int argc, char *argv[]) { (UINT64_C(1) << CAP_NET_BIND_SERVICE)| /* needed to bind on port 53 */ (UINT64_C(1) << CAP_SETPCAP) /* needed in order to drop the caps later */); if (r < 0) - goto finish; + return log_error_errno(r, "Failed to drop privileges: %m"); } assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGUSR1, SIGUSR2, SIGRTMIN+1, -1) >= 0); r = manager_new(&m); - if (r < 0) { - log_error_errno(r, "Could not create manager: %m"); - goto finish; - } + if (r < 0) + return log_error_errno(r, "Could not create manager: %m"); r = manager_start(m); - if (r < 0) { - log_error_errno(r, "Failed to start manager: %m"); - goto finish; - } + if (r < 0) + return log_error_errno(r, "Failed to start manager: %m"); /* Write finish default resolv.conf to avoid a dangling symlink */ (void) manager_write_resolv_conf(m); @@ -82,27 +72,18 @@ int main(int argc, char *argv[]) { /* Let's drop the remaining caps now */ r = capability_bounding_set_drop(0, true); - if (r < 0) { - log_error_errno(r, "Failed to drop remaining caps: %m"); - goto finish; - } + if (r < 0) + return log_error_errno(r, "Failed to drop remaining caps: %m"); - sd_notify(false, - "READY=1\n" - "STATUS=Processing requests..."); + notify_stop = notify_start(NOTIFY_READY, NOTIFY_STOPPING); r = sd_event_loop(m->event); - if (r < 0) { - log_error_errno(r, "Event loop failed: %m"); - goto finish; - } + if (r < 0) + return log_error_errno(r, "Event loop failed: %m"); - sd_event_get_exit_code(m->event, &r); + (void) sd_event_get_exit_code(m->event, &r); -finish: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down..."); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + return r; } + +DEFINE_MAIN_FUNCTION(run); diff --git a/src/resolve/test-resolved-etc-hosts.c b/src/resolve/test-resolved-etc-hosts.c index 6130a036fd..dbff889e3e 100644 --- a/src/resolve/test-resolved-etc-hosts.c +++ b/src/resolve/test-resolved-etc-hosts.c @@ -1,14 +1,22 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/socket.h> + #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "log.h" #include "resolved-etc-hosts.h" +#include "strv.h" #include "tmpfile-util.h" static void test_parse_etc_hosts_system(void) { _cleanup_fclose_ FILE *f = NULL; + log_info("/* %s */", __func__); + f = fopen("/etc/hosts", "re"); if (!f) { assert_se(errno == ENOENT); @@ -19,59 +27,98 @@ static void test_parse_etc_hosts_system(void) { assert_se(etc_hosts_parse(&hosts, f) == 0); } -static void test_parse_etc_hosts(const char *fname) { +#define address_equal_4(_addr, _address) \ + ((_addr)->family == AF_INET && \ + !memcmp(&(_addr)->address.in, &(struct in_addr) { .s_addr = (_address) }, 4)) + +#define address_equal_6(_addr, ...) \ + ((_addr)->family == AF_INET6 && \ + !memcmp(&(_addr)->address.in6, &(struct in6_addr) { .s6_addr = __VA_ARGS__}, 16) ) + +static void test_parse_etc_hosts(void) { _cleanup_(unlink_tempfilep) char t[] = "/tmp/test-resolved-etc-hosts.XXXXXX"; + log_info("/* %s */", __func__); + int fd; _cleanup_fclose_ FILE *f; - - if (fname) { - f = fopen(fname, "re"); - assert_se(f); - } else { - fd = mkostemp_safe(t); - assert_se(fd >= 0); - - f = fdopen(fd, "r+"); - assert_se(f); - fputs("1.2.3.4 some.where\n", f); - fputs("1.2.3.5 some.where\n", f); - fputs("::0 some.where some.other\n", f); - fputs("0.0.0.0 black.listed\n", f); - fputs("::5 some.where some.other foobar.foo.foo\n", f); - fputs(" \n", f); - fflush(f); - rewind(f); - } + const char *s; + + fd = mkostemp_safe(t); + assert_se(fd >= 0); + + f = fdopen(fd, "r+"); + assert_se(f); + fputs("1.2.3.4 some.where\n" + "1.2.3.5 some.where\n" + "1.2.3.6 dash dash-dash.where-dash\n" + "1.2.3.7 bad-dash- -bad-dash -bad-dash.bad-\n" + "1.2.3.8\n" + "1.2.3.9 before.comment # within.comment\n" + "1.2.3.10 before.comment#within.comment2\n" + "1.2.3.11 before.comment# within.comment3\n" + "1.2.3.12 before.comment#\n" + "1.2.3 short.address\n" + "1.2.3.4.5 long.address\n" + "1::2::3 multi.colon\n" + + "::0 some.where some.other\n" + "0.0.0.0 black.listed\n" + "::5\t\t\t \tsome.where\tsome.other foobar.foo.foo\t\t\t\n" + " \n", f); + assert_se(fflush_and_check(f) >= 0); + rewind(f); _cleanup_(etc_hosts_free) EtcHosts hosts = {}; assert_se(etc_hosts_parse(&hosts, f) == 0); - if (fname) - return; - EtcHostsItemByName *bn; assert_se(bn = hashmap_get(hosts.by_name, "some.where")); assert_se(bn->n_addresses == 3); assert_se(bn->n_allocated >= 3); + assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.4"))); + assert_se(address_equal_4(bn->addresses[1], inet_addr("1.2.3.5"))); + assert_se(address_equal_6(bn->addresses[2], {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5})); - assert_se(bn->addresses[0]->family == AF_INET); - assert_se(memcmp(&bn->addresses[0]->address.in, - &(struct in_addr) { .s_addr = htobe32(0x01020304) }, 4) == 0); - assert_se(bn->addresses[1]->family == AF_INET); - assert_se(memcmp(&bn->addresses[1]->address.in, - &(struct in_addr) { .s_addr = htobe32(0x01020305) }, 4) == 0); - assert_se(bn->addresses[2]->family == AF_INET6); - assert_se(memcmp(&bn->addresses[2]->address.in6, - &(struct in6_addr) { .s6_addr = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5} }, 16 ) == 0); + assert_se(bn = hashmap_get(hosts.by_name, "dash")); + assert_se(bn->n_addresses == 1); + assert_se(bn->n_allocated >= 1); + assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.6"))); + + assert_se(bn = hashmap_get(hosts.by_name, "dash-dash.where-dash")); + assert_se(bn->n_addresses == 1); + assert_se(bn->n_allocated >= 1); + assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.6"))); + + /* See https://tools.ietf.org/html/rfc1035#section-2.3.1 */ + FOREACH_STRING(s, "bad-dash-", "-bad-dash", "-bad-dash.bad-") + assert_se(!hashmap_get(hosts.by_name, s)); + + assert_se(bn = hashmap_get(hosts.by_name, "before.comment")); + assert_se(bn->n_addresses == 4); + assert_se(bn->n_allocated >= 4); + assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.9"))); + assert_se(address_equal_4(bn->addresses[1], inet_addr("1.2.3.10"))); + assert_se(address_equal_4(bn->addresses[2], inet_addr("1.2.3.11"))); + assert_se(address_equal_4(bn->addresses[3], inet_addr("1.2.3.12"))); + + assert(!hashmap_get(hosts.by_name, "within.comment")); + assert(!hashmap_get(hosts.by_name, "within.comment2")); + assert(!hashmap_get(hosts.by_name, "within.comment3")); + assert(!hashmap_get(hosts.by_name, "#")); + + assert(!hashmap_get(hosts.by_name, "short.address")); + assert(!hashmap_get(hosts.by_name, "long.address")); + assert(!hashmap_get(hosts.by_name, "multi.colon")); + assert_se(!set_contains(hosts.no_address, "short.address")); + assert_se(!set_contains(hosts.no_address, "long.address")); + assert_se(!set_contains(hosts.no_address, "multi.colon")); assert_se(bn = hashmap_get(hosts.by_name, "some.other")); assert_se(bn->n_addresses == 1); assert_se(bn->n_allocated >= 1); - assert_se(bn->addresses[0]->family == AF_INET6); - assert_se(memcmp(&bn->addresses[0]->address.in6, - &(struct in6_addr) { .s6_addr = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5} }, 16 ) == 0); + assert_se(address_equal_6(bn->addresses[0], {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5})); assert_se( set_contains(hosts.no_address, "some.where")); assert_se( set_contains(hosts.no_address, "some.other")); @@ -79,14 +126,26 @@ static void test_parse_etc_hosts(const char *fname) { assert_se(!set_contains(hosts.no_address, "foobar.foo.foo")); } +static void test_parse_file(const char *fname) { + _cleanup_(etc_hosts_free) EtcHosts hosts = {}; + _cleanup_fclose_ FILE *f; + + log_info("/* %s(\"%s\") */", __func__, fname); + + assert_se(f = fopen(fname, "re")); + assert_se(etc_hosts_parse(&hosts, f) == 0); +} + int main(int argc, char **argv) { log_set_max_level(LOG_DEBUG); log_parse_environment(); log_open(); - if (argc == 1) + if (argc == 1) { test_parse_etc_hosts_system(); - test_parse_etc_hosts(argv[1]); + test_parse_etc_hosts(); + } else + test_parse_file(argv[1]); return 0; } diff --git a/src/shared/daemon-util.h b/src/shared/daemon-util.h new file mode 100644 index 0000000000..5e9eca1d9e --- /dev/null +++ b/src/shared/daemon-util.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include <stdbool.h> + +#include "sd-daemon.h" + +#define NOTIFY_READY "READY=1\n" "STATUS=Processing requests..." +#define NOTIFY_STOPPING "STOPPING=1\n" "STATUS=Shutting down..." + +static inline const char *notify_start(const char *start, const char *stop) { + if (start) + (void) sd_notify(false, start); + + return stop; +} + +/* This is intended to be used with _cleanup_ attribute. */ +static inline void notify_on_cleanup(const char **p) { + if (p) + (void) sd_notify(false, *p); +} diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 5ff60fe191..4b31cb36ed 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -17,6 +17,7 @@ #include "dns-domain.h" #include "hashmap.h" #include "hexdecoct.h" +#include "hostname-util.h" #include "in-addr-util.h" #include "macro.h" #include "parse-util.h" @@ -24,9 +25,9 @@ #include "strv.h" #include "utf8.h" -int dns_label_unescape(const char **name, char *dest, size_t sz) { +int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags) { const char *n; - char *d; + char *d, last_char = 0; int r = 0; assert(name); @@ -36,13 +37,15 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { d = dest; for (;;) { - if (*n == '.') { - n++; - break; - } + if (*n == 0 || *n == '.') { + if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-') + /* Trailing dash */ + return -EINVAL; - if (*n == 0) + if (*n == '.') + n++; break; + } if (r >= DNS_LABEL_MAX) return -EINVAL; @@ -52,6 +55,8 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { if (*n == '\\') { /* Escaped character */ + if (FLAGS_SET(flags, DNS_LABEL_NO_ESCAPES)) + return -EINVAL; n++; @@ -62,6 +67,10 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { else if (IN_SET(*n, '\\', '.')) { /* Escaped backslash or dot */ + if (FLAGS_SET(flags, DNS_LABEL_LDH)) + return -EINVAL; + + last_char = *n; if (d) *(d++) = *n; sz--; @@ -90,6 +99,11 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { if (k > 255) return -EINVAL; + if (FLAGS_SET(flags, DNS_LABEL_LDH) && + !valid_ldh_char((char) k)) + return -EINVAL; + + last_char = (char) k; if (d) *(d++) = (char) k; sz--; @@ -103,6 +117,15 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { /* Normal character */ + if (FLAGS_SET(flags, DNS_LABEL_LDH)) { + if (!valid_ldh_char(*n)) + return -EINVAL; + if (r == 0 && *n == '-') + /* Leading dash */ + return -EINVAL; + } + + last_char = *n; if (d) *(d++) = *n; sz--; @@ -184,7 +207,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha terminal--; } - r = dns_label_unescape(&name, dest, sz); + r = dns_label_unescape(&name, dest, sz, 0); if (r < 0) return r; @@ -378,7 +401,7 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, } #endif -int dns_name_concat(const char *a, const char *b, char **_ret) { +int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) { _cleanup_free_ char *ret = NULL; size_t n = 0, allocated = 0; const char *p; @@ -395,7 +418,7 @@ int dns_name_concat(const char *a, const char *b, char **_ret) { for (;;) { char label[DNS_LABEL_MAX]; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, flags); if (r < 0) return r; if (r == 0) { @@ -468,7 +491,7 @@ void dns_name_hash_func(const char *p, struct siphash *state) { for (;;) { char label[DNS_LABEL_MAX+1]; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r < 0) break; if (r == 0) @@ -521,11 +544,11 @@ int dns_name_equal(const char *x, const char *y) { for (;;) { char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; - r = dns_label_unescape(&x, la, sizeof(la)); + r = dns_label_unescape(&x, la, sizeof la, 0); if (r < 0) return r; - q = dns_label_unescape(&y, lb, sizeof(lb)); + q = dns_label_unescape(&y, lb, sizeof lb, 0); if (q < 0) return q; @@ -552,14 +575,14 @@ int dns_name_endswith(const char *name, const char *suffix) { for (;;) { char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX]; - r = dns_label_unescape(&n, ln, sizeof(ln)); + r = dns_label_unescape(&n, ln, sizeof ln, 0); if (r < 0) return r; if (!saved_n) saved_n = n; - q = dns_label_unescape(&s, ls, sizeof(ls)); + q = dns_label_unescape(&s, ls, sizeof ls, 0); if (q < 0) return q; @@ -590,13 +613,13 @@ int dns_name_startswith(const char *name, const char *prefix) { for (;;) { char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX]; - r = dns_label_unescape(&p, lp, sizeof(lp)); + r = dns_label_unescape(&p, lp, sizeof lp, 0); if (r < 0) return r; if (r == 0) return true; - q = dns_label_unescape(&n, ln, sizeof(ln)); + q = dns_label_unescape(&n, ln, sizeof ln, 0); if (q < 0) return q; @@ -625,14 +648,14 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char if (!saved_before) saved_before = n; - r = dns_label_unescape(&n, ln, sizeof(ln)); + r = dns_label_unescape(&n, ln, sizeof ln, 0); if (r < 0) return r; if (!saved_after) saved_after = n; - q = dns_label_unescape(&s, ls, sizeof(ls)); + q = dns_label_unescape(&s, ls, sizeof ls, 0); if (q < 0) return q; @@ -655,7 +678,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char /* Found it! Now generate the new name */ prefix = strndupa(name, saved_before - name); - r = dns_name_concat(prefix, new_suffix, ret); + r = dns_name_concat(prefix, new_suffix, 0, ret); if (r < 0) return r; @@ -733,7 +756,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { for (i = 0; i < ELEMENTSOF(a); i++) { char label[DNS_LABEL_MAX+1]; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r < 0) return r; if (r == 0) @@ -770,7 +793,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { char label[DNS_LABEL_MAX+1]; int x, y; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r <= 0) return r; if (r != 1) @@ -779,7 +802,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { if (x < 0) return -EINVAL; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r <= 0) return r; if (r != 1) @@ -847,7 +870,7 @@ int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, boo * dns_label_unescape() returns 0 when it hits the end * of the domain name, which we rely on here to encode * the trailing NUL byte. */ - r = dns_label_unescape(&domain, (char *) out, len); + r = dns_label_unescape(&domain, (char *) out, len, 0); if (r < 0) return r; @@ -914,7 +937,7 @@ bool dns_srv_type_is_valid(const char *name) { /* This more or less implements RFC 6335, Section 5.1 */ - r = dns_label_unescape(&name, label, sizeof(label)); + r = dns_label_unescape(&name, label, sizeof label, 0); if (r < 0) return false; if (r == 0) @@ -974,7 +997,7 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha return -EINVAL; if (!name) - return dns_name_concat(type, domain, ret); + return dns_name_concat(type, domain, 0, ret); if (!dns_service_name_is_valid(name)) return -EINVAL; @@ -983,11 +1006,11 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha if (r < 0) return r; - r = dns_name_concat(type, domain, &n); + r = dns_name_concat(type, domain, 0, &n); if (r < 0) return r; - return dns_name_concat(escaped, n, ret); + return dns_name_concat(escaped, n, 0, ret); } static bool dns_service_name_label_is_valid(const char *label, size_t n) { @@ -1012,7 +1035,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do assert(joined); /* Get first label from the full name */ - an = dns_label_unescape(&p, a, sizeof(a)); + an = dns_label_unescape(&p, a, sizeof(a), 0); if (an < 0) return an; @@ -1020,7 +1043,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do x++; /* If there was a first label, try to get the second one */ - bn = dns_label_unescape(&p, b, sizeof(b)); + bn = dns_label_unescape(&p, b, sizeof(b), 0); if (bn < 0) return bn; @@ -1029,7 +1052,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do /* If there was a second label, try to get the third one */ q = p; - cn = dns_label_unescape(&p, c, sizeof(c)); + cn = dns_label_unescape(&p, c, sizeof(c), 0); if (cn < 0) return cn; @@ -1079,7 +1102,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do d = joined; finish: - r = dns_name_normalize(d, &domain); + r = dns_name_normalize(d, 0, &domain); if (r < 0) return r; @@ -1224,12 +1247,12 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) { } x = a_labels[n - 1 - k]; - r = dns_label_unescape(&x, la, sizeof(la)); + r = dns_label_unescape(&x, la, sizeof la, 0); if (r < 0) return r; y = b_labels[m - 1 - k]; - q = dns_label_unescape(&y, lb, sizeof(lb)); + q = dns_label_unescape(&y, lb, sizeof lb, 0); if (q < 0) return q; @@ -1297,13 +1320,13 @@ int dns_name_apply_idna(const char *name, char **ret) { for (;;) { char label[DNS_LABEL_MAX]; - r = dns_label_unescape(&name, label, sizeof(label)); + r = dns_label_unescape(&name, label, sizeof label, 0); if (r < 0) return r; if (r == 0) break; - q = dns_label_apply_idna(label, r, label, sizeof(label)); + q = dns_label_apply_idna(label, r, label, sizeof label); if (q < 0) return q; if (q > 0) diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 42492ad7c0..6ed512c6b1 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -24,13 +24,18 @@ /* Maximum number of labels per valid hostname */ #define DNS_N_LABELS_MAX 127 -int dns_label_unescape(const char **name, char *dest, size_t sz); +typedef enum DNSLabelFlags { + DNS_LABEL_LDH = 1 << 0, /* Follow the "LDH" rule — only letters, digits, and internal hyphens. */ + DNS_LABEL_NO_ESCAPES = 1 << 1, /* Do not treat backslashes specially */ +} DNSLabelFlags; + +int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags); int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz); int dns_label_escape(const char *p, size_t l, char *dest, size_t sz); int dns_label_escape_new(const char *p, size_t l, char **ret); static inline int dns_name_parent(const char **name) { - return dns_label_unescape(name, NULL, DNS_LABEL_MAX); + return dns_label_unescape(name, NULL, DNS_LABEL_MAX, 0); } #if HAVE_LIBIDN @@ -38,18 +43,29 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); #endif -int dns_name_concat(const char *a, const char *b, char **ret); +int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **ret); -static inline int dns_name_normalize(const char *s, char **ret) { +static inline int dns_name_normalize(const char *s, DNSLabelFlags flags, char **ret) { /* dns_name_concat() normalizes as a side-effect */ - return dns_name_concat(s, NULL, ret); + return dns_name_concat(s, NULL, flags, ret); } static inline int dns_name_is_valid(const char *s) { int r; /* dns_name_normalize() verifies as a side effect */ - r = dns_name_normalize(s, NULL); + r = dns_name_normalize(s, 0, NULL); + if (r == -EINVAL) + return 0; + if (r < 0) + return r; + return 1; +} + +static inline int dns_name_is_valid_ldh(const char *s) { + int r; + + r = dns_name_concat(s, NULL, DNS_LABEL_LDH|DNS_LABEL_NO_ESCAPES, NULL); if (r == -EINVAL) return 0; if (r < 0) diff --git a/src/shared/meson.build b/src/shared/meson.build index 5917d43ef9..ca2e05325e 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -41,6 +41,7 @@ shared_sources = files(''' cpu-set-util.h crypt-util.c crypt-util.h + daemon-util.h dev-setup.c dev-setup.h dissect-image.c diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 19f49a5ae6..40fcb2cae3 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3090,6 +3090,7 @@ static int start_unit(int argc, char *argv[], void *userdata) { _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; _cleanup_(wait_context_free) WaitContext wait_context = {}; const char *method, *mode, *one_name, *suffix = NULL; + _cleanup_free_ char **stopped_units = NULL; /* Do not use _cleanup_strv_free_ */ _cleanup_strv_free_ char **names = NULL; int r, ret = EXIT_SUCCESS; sd_bus *bus; @@ -3186,6 +3187,12 @@ static int start_unit(int argc, char *argv[], void *userdata) { r = start_unit_one(bus, method, *name, mode, &error, w, arg_wait ? &wait_context : NULL); if (ret == EXIT_SUCCESS && r < 0) ret = translate_bus_error_to_exit_status(r, &error); + + if (r >= 0 && streq(method, "StopUnit")) { + r = strv_push(&stopped_units, *name); + if (r < 0) + return log_oom(); + } } if (!arg_no_block) { @@ -3210,8 +3217,8 @@ static int start_unit(int argc, char *argv[], void *userdata) { /* When stopping units, warn if they can still be triggered by * another active unit (socket, path, timer) */ - if (!arg_quiet && streq(method, "StopUnit")) - STRV_FOREACH(name, names) + if (!arg_quiet) + STRV_FOREACH(name, stopped_units) (void) check_triggering_units(bus, *name); } diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index e388552064..bd0d429df6 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -23,6 +23,7 @@ #include <net/ethernet.h> #include <netinet/in.h> #include <sys/types.h> +#include <stdbool.h> #include "sd-dhcp-lease.h" #include "sd-event.h" @@ -127,12 +128,14 @@ int sd_dhcp_client_set_client_id( size_t data_len); int sd_dhcp_client_set_iaid_duid( sd_dhcp_client *client, + bool iaid_set, uint32_t iaid, uint16_t duid_type, const void *duid, size_t duid_len); int sd_dhcp_client_set_iaid_duid_llt( sd_dhcp_client *client, + bool iaid_set, uint32_t iaid, uint64_t llt_time); int sd_dhcp_client_set_duid( diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index cbfe5ef390..ead5311705 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -6,39 +6,65 @@ #include "string-util.h" #include "tests.h" -static void test_dns_label_unescape_one(const char *what, const char *expect, size_t buffer_sz, int ret) { +static void test_dns_label_unescape_one(const char *what, const char *expect, size_t buffer_sz, int ret, int ret_ldh) { char buffer[buffer_sz]; int r; + const char *w = what; - r = dns_label_unescape(&what, buffer, buffer_sz); + log_info("%s, %s, %zu, →%d/%d", what, expect, buffer_sz, ret, ret_ldh); + + r = dns_label_unescape(&w, buffer, buffer_sz, 0); assert_se(r == ret); + if (r >= 0) + assert_se(streq(buffer, expect)); - if (r < 0) - return; + w = what; + r = dns_label_unescape(&w, buffer, buffer_sz, DNS_LABEL_LDH); + assert_se(r == ret_ldh); + if (r >= 0) + assert_se(streq(buffer, expect)); - assert_se(streq(buffer, expect)); + w = what; + r = dns_label_unescape(&w, buffer, buffer_sz, DNS_LABEL_NO_ESCAPES); + const int ret_noe = strchr(what, '\\') ? -EINVAL : ret; + assert_se(r == ret_noe); + if (r >= 0) + assert_se(streq(buffer, expect)); } static void test_dns_label_unescape(void) { - test_dns_label_unescape_one("hallo", "hallo", 6, 5); - test_dns_label_unescape_one("hallo", "hallo", 4, -ENOBUFS); - test_dns_label_unescape_one("", "", 10, 0); - test_dns_label_unescape_one("hallo\\.foobar", "hallo.foobar", 20, 12); - test_dns_label_unescape_one("hallo.foobar", "hallo", 10, 5); - test_dns_label_unescape_one("hallo\n.foobar", "hallo", 20, -EINVAL); - test_dns_label_unescape_one("hallo\\", "hallo", 20, -EINVAL); - test_dns_label_unescape_one("hallo\\032 ", "hallo ", 20, 7); - test_dns_label_unescape_one(".", "", 20, 0); - test_dns_label_unescape_one("..", "", 20, -EINVAL); - test_dns_label_unescape_one(".foobar", "", 20, -EINVAL); - test_dns_label_unescape_one("foobar.", "foobar", 20, 6); - test_dns_label_unescape_one("foobar..", "foobar", 20, -EINVAL); + log_info("/* %s */", __func__); + + test_dns_label_unescape_one("hallo", "hallo", 6, 5, 5); + test_dns_label_unescape_one("hallo", "hallo", 4, -ENOBUFS, -ENOBUFS); + test_dns_label_unescape_one("", "", 10, 0, 0); + test_dns_label_unescape_one("hallo\\.foobar", "hallo.foobar", 20, 12, -EINVAL); + test_dns_label_unescape_one("hallo.foobar", "hallo", 10, 5, 5); + test_dns_label_unescape_one("hallo\n.foobar", "hallo", 20, -EINVAL, -EINVAL); + test_dns_label_unescape_one("hallo\\", "hallo", 20, -EINVAL, -EINVAL); + test_dns_label_unescape_one("hallo\\032 ", "hallo ", 20, 7, -EINVAL); + test_dns_label_unescape_one(".", "", 20, 0, 0); + test_dns_label_unescape_one("..", "", 20, -EINVAL, -EINVAL); + test_dns_label_unescape_one(".foobar", "", 20, -EINVAL, -EINVAL); + test_dns_label_unescape_one("foobar.", "foobar", 20, 6, 6); + test_dns_label_unescape_one("foobar..", "foobar", 20, -EINVAL, -EINVAL); + test_dns_label_unescape_one("foo-bar", "foo-bar", 20, 7, 7); + test_dns_label_unescape_one("foo-", "foo-", 20, 4, -EINVAL); + test_dns_label_unescape_one("-foo", "-foo", 20, 4, -EINVAL); + test_dns_label_unescape_one("-foo-", "-foo-", 20, 5, -EINVAL); + test_dns_label_unescape_one("foo-.", "foo-", 20, 4, -EINVAL); + test_dns_label_unescape_one("foo.-", "foo", 20, 3, 3); + test_dns_label_unescape_one("foo\\032", "foo ", 20, 4, -EINVAL); + test_dns_label_unescape_one("foo\\045", "foo-", 20, 4, -EINVAL); + test_dns_label_unescape_one("głąb", "głąb", 20, 6, -EINVAL); } static void test_dns_name_to_wire_format_one(const char *what, const char *expect, size_t buffer_sz, int ret) { uint8_t buffer[buffer_sz]; int r; + log_info("%s, %s, %zu, →%d", what, expect, buffer_sz, ret); + r = dns_name_to_wire_format(what, buffer, buffer_sz, false); assert_se(r == ret); @@ -80,6 +106,8 @@ static void test_dns_name_to_wire_format(void) { 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', 3, 'a', '1', '2', 0 }; + log_info("/* %s */", __func__); + test_dns_name_to_wire_format_one("", out0, sizeof(out0), sizeof(out0)); test_dns_name_to_wire_format_one("foo", out1, sizeof(out1), sizeof(out1)); @@ -100,6 +128,8 @@ static void test_dns_label_unescape_suffix_one(const char *what, const char *exp const char *label; int r; + log_info("%s, %s, %s, %zu, %d, %d", what, expect1, expect2, buffer_sz, ret1, ret2); + label = what + strlen(what); r = dns_label_unescape_suffix(what, &label, buffer, buffer_sz); @@ -114,6 +144,8 @@ static void test_dns_label_unescape_suffix_one(const char *what, const char *exp } static void test_dns_label_unescape_suffix(void) { + log_info("/* %s */", __func__); + test_dns_label_unescape_suffix_one("hallo", "hallo", "", 6, 5, 0); test_dns_label_unescape_suffix_one("hallo", "hallo", "", 4, -ENOBUFS, -ENOBUFS); test_dns_label_unescape_suffix_one("", "", "", 10, 0, 0); @@ -139,6 +171,8 @@ static void test_dns_label_escape_one(const char *what, size_t l, const char *ex _cleanup_free_ char *t = NULL; int r; + log_info("%s, %zu, %s, →%d", what, l, expect, ret); + r = dns_label_escape_new(what, l, &t); assert_se(r == ret); @@ -149,6 +183,8 @@ static void test_dns_label_escape_one(const char *what, size_t l, const char *ex } static void test_dns_label_escape(void) { + log_info("/* %s */", __func__); + test_dns_label_escape_one("", 0, NULL, -EINVAL); test_dns_label_escape_one("hallo", 5, "hallo", 5); test_dns_label_escape_one("hallo", 6, "hallo\\000", 9); @@ -159,7 +195,7 @@ static void test_dns_name_normalize_one(const char *what, const char *expect, in _cleanup_free_ char *t = NULL; int r; - r = dns_name_normalize(what, &t); + r = dns_name_normalize(what, 0, &t); assert_se(r == ret); if (r < 0) @@ -320,7 +356,7 @@ static void test_dns_name_reverse(void) { static void test_dns_name_concat_one(const char *a, const char *b, int r, const char *result) { _cleanup_free_ char *p = NULL; - assert_se(dns_name_concat(a, b, &p) == r); + assert_se(dns_name_concat(a, b, 0, &p) == r); assert_se(streq_ptr(p, result)); } @@ -339,46 +375,68 @@ static void test_dns_name_concat(void) { test_dns_name_concat_one(NULL, "foo", 0, "foo"); } -static void test_dns_name_is_valid_one(const char *s, int ret) { +static void test_dns_name_is_valid_one(const char *s, int ret, int ret_ldh) { + log_info("%s, →%d", s, ret); + assert_se(dns_name_is_valid(s) == ret); + assert_se(dns_name_is_valid_ldh(s) == ret_ldh); } static void test_dns_name_is_valid(void) { - test_dns_name_is_valid_one("foo", 1); - test_dns_name_is_valid_one("foo.", 1); - test_dns_name_is_valid_one("foo..", 0); - test_dns_name_is_valid_one("Foo", 1); - test_dns_name_is_valid_one("foo.bar", 1); - test_dns_name_is_valid_one("foo.bar.baz", 1); - test_dns_name_is_valid_one("", 1); - test_dns_name_is_valid_one("foo..bar", 0); - test_dns_name_is_valid_one(".foo.bar", 0); - test_dns_name_is_valid_one("foo.bar.", 1); - test_dns_name_is_valid_one("foo.bar..", 0); - test_dns_name_is_valid_one("\\zbar", 0); - test_dns_name_is_valid_one("ä", 1); - test_dns_name_is_valid_one("\n", 0); + log_info("/* %s */", __func__); + + test_dns_name_is_valid_one("foo", 1, 1); + test_dns_name_is_valid_one("foo.", 1, 1); + test_dns_name_is_valid_one("foo..", 0, 0); + test_dns_name_is_valid_one("Foo", 1, 1); + test_dns_name_is_valid_one("foo.bar", 1, 1); + test_dns_name_is_valid_one("foo.bar.baz", 1, 1); + test_dns_name_is_valid_one("", 1, 1); + test_dns_name_is_valid_one("foo..bar", 0, 0); + test_dns_name_is_valid_one(".foo.bar", 0, 0); + test_dns_name_is_valid_one("foo.bar.", 1, 1); + test_dns_name_is_valid_one("foo.bar..", 0, 0); + test_dns_name_is_valid_one("\\zbar", 0, 0); + test_dns_name_is_valid_one("ä", 1, 0); + test_dns_name_is_valid_one("\n", 0, 0); + + test_dns_name_is_valid_one("dash-", 1, 0); + test_dns_name_is_valid_one("-dash", 1, 0); + test_dns_name_is_valid_one("dash-dash", 1, 1); + test_dns_name_is_valid_one("foo.dash-", 1, 0); + test_dns_name_is_valid_one("foo.-dash", 1, 0); + test_dns_name_is_valid_one("foo.dash-dash", 1, 1); + test_dns_name_is_valid_one("foo.dash-.bar", 1, 0); + test_dns_name_is_valid_one("foo.-dash.bar", 1, 0); + test_dns_name_is_valid_one("foo.dash-dash.bar", 1, 1); + test_dns_name_is_valid_one("dash-.bar", 1, 0); + test_dns_name_is_valid_one("-dash.bar", 1, 0); + test_dns_name_is_valid_one("dash-dash.bar", 1, 1); + test_dns_name_is_valid_one("-.bar", 1, 0); + test_dns_name_is_valid_one("foo.-", 1, 0); /* 256 characters */ - test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345", 0); + test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345", 0, 0); /* 255 characters */ - test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a1234", 0); + test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a1234", 0, 0); /* 254 characters */ - test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a123", 0); + test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a123", 0, 0); /* 253 characters */ - test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12", 1); + test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12", 1, 1); /* label of 64 chars length */ - test_dns_name_is_valid_one("a123456789a123456789a123456789a123456789a123456789a123456789a123", 0); + test_dns_name_is_valid_one("a123456789a123456789a123456789a123456789a123456789a123456789a123", 0, 0); /* label of 63 chars length */ - test_dns_name_is_valid_one("a123456789a123456789a123456789a123456789a123456789a123456789a12", 1); + test_dns_name_is_valid_one("a123456789a123456789a123456789a123456789a123456789a123456789a12", 1, 1); } static void test_dns_service_name_is_valid(void) { + log_info("/* %s */", __func__); + assert_se(dns_service_name_is_valid("Lennart's Compüter")); assert_se(dns_service_name_is_valid("piff.paff")); @@ -390,6 +448,7 @@ static void test_dns_service_name_is_valid(void) { } static void test_dns_srv_type_is_valid(void) { + log_info("/* %s */", __func__); assert_se(dns_srv_type_is_valid("_http._tcp")); assert_se(dns_srv_type_is_valid("_foo-bar._tcp")); @@ -413,6 +472,7 @@ static void test_dns_srv_type_is_valid(void) { } static void test_dnssd_srv_type_is_valid(void) { + log_info("/* %s */", __func__); assert_se(dnssd_srv_type_is_valid("_http._tcp")); assert_se(dnssd_srv_type_is_valid("_foo-bar._tcp")); @@ -439,6 +499,8 @@ static void test_dnssd_srv_type_is_valid(void) { static void test_dns_service_join_one(const char *a, const char *b, const char *c, int r, const char *d) { _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *t = NULL; + log_info("%s, %s, %s, →%d, %s", a, b, c, r, d); + assert_se(dns_service_join(a, b, c, &t) == r); assert_se(streq_ptr(t, d)); @@ -452,6 +514,8 @@ static void test_dns_service_join_one(const char *a, const char *b, const char * } static void test_dns_service_join(void) { + log_info("/* %s */", __func__); + test_dns_service_join_one("", "", "", -EINVAL, NULL); test_dns_service_join_one("", "_http._tcp", "", -EINVAL, NULL); test_dns_service_join_one("", "_http._tcp", "foo", -EINVAL, NULL); @@ -469,6 +533,8 @@ static void test_dns_service_join(void) { static void test_dns_service_split_one(const char *joined, const char *a, const char *b, const char *c, int r) { _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *t = NULL; + log_info("%s, %s, %s, %s, →%d", joined, a, b, c, r); + assert_se(dns_service_split(joined, &x, &y, &z) == r); assert_se(streq_ptr(x, a)); assert_se(streq_ptr(y, b)); @@ -485,6 +551,8 @@ static void test_dns_service_split_one(const char *joined, const char *a, const } static void test_dns_service_split(void) { + log_info("/* %s */", __func__); + test_dns_service_split_one("", NULL, NULL, ".", 0); test_dns_service_split_one("foo", NULL, NULL, "foo", 0); test_dns_service_split_one("foo.bar", NULL, NULL, "foo.bar", 0); @@ -497,11 +565,15 @@ static void test_dns_service_split(void) { static void test_dns_name_change_suffix_one(const char *name, const char *old_suffix, const char *new_suffix, int r, const char *result) { _cleanup_free_ char *s = NULL; + log_info("%s, %s, %s, →%s", name, old_suffix, new_suffix, result); + assert_se(dns_name_change_suffix(name, old_suffix, new_suffix, &s) == r); assert_se(streq_ptr(s, result)); } static void test_dns_name_change_suffix(void) { + log_info("/* %s */", __func__); + test_dns_name_change_suffix_one("foo.bar", "bar", "waldo", 1, "foo.waldo"); test_dns_name_change_suffix_one("foo.bar.waldi.quux", "foo.bar.waldi.quux", "piff.paff", 1, "piff.paff"); test_dns_name_change_suffix_one("foo.bar.waldi.quux", "bar.waldi.quux", "piff.paff", 1, "foo.piff.paff"); @@ -516,11 +588,15 @@ static void test_dns_name_change_suffix(void) { static void test_dns_name_suffix_one(const char *name, unsigned n_labels, const char *result, int ret) { const char *p = NULL; + log_info("%s, %d, →%s, %d", name, n_labels, result, ret); + assert_se(ret == dns_name_suffix(name, n_labels, &p)); assert_se(streq_ptr(p, result)); } static void test_dns_name_suffix(void) { + log_info("/* %s */", __func__); + test_dns_name_suffix_one("foo.bar", 2, "foo.bar", 0); test_dns_name_suffix_one("foo.bar", 1, "bar", 1); test_dns_name_suffix_one("foo.bar", 0, "", 2); @@ -538,10 +614,14 @@ static void test_dns_name_suffix(void) { } static void test_dns_name_count_labels_one(const char *name, int n) { + log_info("%s, →%d", name, n); + assert_se(dns_name_count_labels(name) == n); } static void test_dns_name_count_labels(void) { + log_info("/* %s */", __func__); + test_dns_name_count_labels_one("foo.bar.quux.", 3); test_dns_name_count_labels_one("foo.bar.quux", 3); test_dns_name_count_labels_one("foo.bar.", 2); @@ -554,10 +634,14 @@ static void test_dns_name_count_labels(void) { } static void test_dns_name_equal_skip_one(const char *a, unsigned n_labels, const char *b, int ret) { + log_info("%s, %u, %s, →%d", a, n_labels, b, ret); + assert_se(dns_name_equal_skip(a, n_labels, b) == ret); } static void test_dns_name_equal_skip(void) { + log_info("/* %s */", __func__); + test_dns_name_equal_skip_one("foo", 0, "bar", 0); test_dns_name_equal_skip_one("foo", 0, "foo", 1); test_dns_name_equal_skip_one("foo", 1, "foo", 0); @@ -585,6 +669,8 @@ static void test_dns_name_equal_skip(void) { } static void test_dns_name_compare_func(void) { + log_info("/* %s */", __func__); + assert_se(dns_name_compare_func("", "") == 0); assert_se(dns_name_compare_func("", ".") == 0); assert_se(dns_name_compare_func(".", "") == 0); @@ -600,11 +686,15 @@ static void test_dns_name_compare_func(void) { static void test_dns_name_common_suffix_one(const char *a, const char *b, const char *result) { const char *c; + log_info("%s, %s, →%s", a, b, result); + assert_se(dns_name_common_suffix(a, b, &c) >= 0); assert_se(streq(c, result)); } static void test_dns_name_common_suffix(void) { + log_info("/* %s */", __func__); + test_dns_name_common_suffix_one("", "", ""); test_dns_name_common_suffix_one("foo", "", ""); test_dns_name_common_suffix_one("", "foo", ""); @@ -639,6 +729,7 @@ static void test_dns_name_apply_idna(void) { #else const int ret = 0; #endif + log_info("/* %s */", __func__); /* IDNA2008 forbids names with hyphens in third and fourth positions * (https://tools.ietf.org/html/rfc5891#section-4.2.3.1). @@ -676,6 +767,8 @@ static void test_dns_name_apply_idna(void) { } static void test_dns_name_is_valid_or_address(void) { + log_info("/* %s */", __func__); + assert_se(dns_name_is_valid_or_address(NULL) == 0); assert_se(dns_name_is_valid_or_address("") == 0); assert_se(dns_name_is_valid_or_address("foobar") > 0); diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index 75466a1f1c..b3a4b1749c 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -248,11 +248,11 @@ static void test_chase_symlinks(void) { assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0); assert_se(chown(q, 0, 0) >= 0); - assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -EPERM); + assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -ENOLINK); assert_se(rmdir(q) >= 0); assert_se(symlink("/etc/passwd", q) >= 0); - assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -EPERM); + assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -ENOLINK); assert_se(chown(p, 0, 0) >= 0); assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0); diff --git a/src/test/test-hostname-util.c b/src/test/test-hostname-util.c index 8ba47d316b..4126a24ceb 100644 --- a/src/test/test-hostname-util.c +++ b/src/test/test-hostname-util.c @@ -53,6 +53,12 @@ static void test_hostname_cleanup(void) { assert_se(streq(hostname_cleanup(s), "foobar.com")); s = strdupa("foobar.com."); assert_se(streq(hostname_cleanup(s), "foobar.com")); + s = strdupa("foo-bar.-com-."); + assert_se(streq(hostname_cleanup(s), "foo-bar.com")); + s = strdupa("foo-bar-.-com-."); + assert_se(streq(hostname_cleanup(s), "foo-bar--com")); + s = strdupa("--foo-bar.-com"); + assert_se(streq(hostname_cleanup(s), "foo-bar.com")); s = strdupa("fooBAR"); assert_se(streq(hostname_cleanup(s), "fooBAR")); s = strdupa("fooBAR.com"); @@ -79,6 +85,8 @@ static void test_hostname_cleanup(void) { assert_se(streq(hostname_cleanup(s), "foo.bar")); s = strdupa("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); assert_se(streq(hostname_cleanup(s), "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")); + s = strdupa("xxxx........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + assert_se(streq(hostname_cleanup(s), "xxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")); } static void test_read_etc_hostname(void) { diff --git a/src/test/test-systemd-tmpfiles.py b/src/test/test-systemd-tmpfiles.py index 28dadd0c5b..83a66e8772 100755 --- a/src/test/test-systemd-tmpfiles.py +++ b/src/test/test-systemd-tmpfiles.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 -# SPDX-License-Identifier: LGPL-2.1+ +# SPDX-License-Identifier: LGPL-2.1+ # -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. import os import sys diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c index 4055295555..70774d757b 100644 --- a/src/timesync/timesyncd.c +++ b/src/timesync/timesyncd.c @@ -5,8 +5,10 @@ #include "capability-util.h" #include "clock-util.h" +#include "daemon-util.h" #include "fd-util.h" #include "fs-util.h" +#include "main-func.h" #include "mkdir.h" #include "network-util.h" #include "process-util.h" @@ -83,7 +85,8 @@ settime: return 0; } -int main(int argc, char *argv[]) { +static int run(int argc, char *argv[]) { + _cleanup_(notify_on_cleanup) const char *notify_message = NULL; _cleanup_(manager_freep) Manager *m = NULL; const char *user = "systemd-timesync"; uid_t uid, uid_current; @@ -95,48 +98,39 @@ int main(int argc, char *argv[]) { umask(0022); - if (argc != 1) { - log_error("This program does not take arguments."); - r = -EINVAL; - goto finish; - } + if (argc != 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program does not take arguments."); uid = uid_current = geteuid(); gid = getegid(); if (uid_current == 0) { r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0); - if (r < 0) { - log_error_errno(r, "Cannot resolve user name %s: %m", user); - goto finish; - } + if (r < 0) + return log_error_errno(r, "Cannot resolve user name %s: %m", user); } r = load_clock_timestamp(uid, gid); if (r < 0) - goto finish; + return r; /* Drop privileges, but only if we have been started as root. If we are not running as root we assume all * privileges are already dropped. */ if (uid_current == 0) { r = drop_privileges(uid, gid, (1ULL << CAP_SYS_TIME)); if (r < 0) - goto finish; + return log_error_errno(r, "Failed to drop privileges: %m"); } assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); r = manager_new(&m); - if (r < 0) { - log_error_errno(r, "Failed to allocate manager: %m"); - goto finish; - } + if (r < 0) + return log_error_errno(r, "Failed to allocate manager: %m"); r = manager_connect_bus(m); - if (r < 0) { - log_error_errno(r, "Could not connect to bus: %m"); - goto finish; - } + if (r < 0) + return log_error_errno(r, "Could not connect to bus: %m"); if (clock_is_localtime(NULL) > 0) { log_info("The system is configured to read the RTC time in the local time zone. " @@ -149,27 +143,24 @@ int main(int argc, char *argv[]) { log_warning_errno(r, "Failed to parse configuration file: %m"); r = manager_parse_fallback_string(m, NTP_SERVERS); - if (r < 0) { - log_error_errno(r, "Failed to parse fallback server strings: %m"); - goto finish; - } + if (r < 0) + return log_error_errno(r, "Failed to parse fallback server strings: %m"); log_debug("systemd-timesyncd running as pid " PID_FMT, getpid_cached()); - sd_notify(false, - "READY=1\n" - "STATUS=Daemon is running"); + + notify_message = notify_start("READY=1\n" + "STATUS=Daemon is running", + NOTIFY_STOPPING); if (network_is_online()) { r = manager_connect(m); if (r < 0) - goto finish; + return r; } r = sd_event_loop(m->event); - if (r < 0) { - log_error_errno(r, "Failed to run event loop: %m"); - goto finish; - } + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); /* if we got an authoritative time, store it in the file system */ if (m->sync) { @@ -178,12 +169,9 @@ int main(int argc, char *argv[]) { log_debug_errno(r, "Failed to touch %s, ignoring: %m", CLOCK_FILE); } - sd_event_get_exit_code(m->event, &r); - -finish: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down..."); + (void) sd_event_get_exit_code(m->event, &r); - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + return r; } + +DEFINE_MAIN_FUNCTION(run); diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 9cd317e97b..08c57a3fb1 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -855,10 +855,8 @@ static int path_open_parent_safe(const char *path) { if (!dn) return log_oom(); - fd = chase_symlinks(dn, NULL, CHASE_OPEN|CHASE_SAFE, NULL); - if (fd == -EPERM) - return log_error_errno(fd, "Unsafe symlinks encountered in %s, refusing.", path); - if (fd < 0) + fd = chase_symlinks(dn, NULL, CHASE_OPEN|CHASE_SAFE|CHASE_WARN, NULL); + if (fd < 0 && fd != -ENOLINK) return log_error_errno(fd, "Failed to validate path %s: %m", path); return fd; @@ -878,10 +876,8 @@ static int path_open_safe(const char *path) { "Failed to open invalid path '%s'.", path); - fd = chase_symlinks(path, NULL, CHASE_OPEN|CHASE_SAFE|CHASE_NOFOLLOW, NULL); - if (fd == -EPERM) - return log_error_errno(fd, "Unsafe symlinks encountered in %s, refusing.", path); - if (fd < 0) + fd = chase_symlinks(path, NULL, CHASE_OPEN|CHASE_SAFE|CHASE_WARN|CHASE_NOFOLLOW, NULL); + if (fd < 0 && fd != -ENOLINK) return log_error_errno(fd, "Failed to validate path %s: %m", path); return fd; @@ -2259,11 +2255,12 @@ static int process_item(Item *i, OperationMask operation) { i->done |= operation; - r = chase_symlinks(i->path, NULL, CHASE_NO_AUTOFS, NULL); + r = chase_symlinks(i->path, NULL, CHASE_NO_AUTOFS|CHASE_WARN, NULL); if (r == -EREMOTE) { - log_debug_errno(r, "Item '%s' is below autofs, skipping.", i->path); + log_notice_errno(r, "Skipping %s", i->path); return 0; - } else if (r < 0) + } + if (r < 0) log_debug_errno(r, "Failed to determine whether '%s' is below autofs, ignoring: %m", i->path); r = FLAGS_SET(operation, OPERATION_CREATE) ? create_item(i) : 0; |