From afb14803b34a9f4abf5265365aebe9a3132344c2 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Wed, 9 Dec 2015 15:46:46 +0100 Subject: automount: implement transient automounts --- src/core/automount.c | 3 +++ src/core/dbus-automount.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++ src/core/dbus-automount.h | 2 ++ 3 files changed, 59 insertions(+) diff --git a/src/core/automount.c b/src/core/automount.c index 85803a9c4a..10ea4ee17d 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -1105,6 +1105,9 @@ const UnitVTable automount_vtable = { .reset_failed = automount_reset_failed, .bus_vtable = bus_automount_vtable, + .bus_set_property = bus_automount_set_property, + + .can_transient = true, .shutdown = automount_shutdown, .supported = automount_supported, diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c index b2806ad86f..26212b3a95 100644 --- a/src/core/dbus-automount.c +++ b/src/core/dbus-automount.c @@ -32,3 +32,57 @@ const sd_bus_vtable bus_automount_vtable[] = { SD_BUS_PROPERTY("TimeoutIdleUSec", "t", bus_property_get_usec, offsetof(Automount, timeout_idle_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_VTABLE_END }; + +static int bus_automount_set_transient_property( + Automount *a, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + int r; + + assert(a); + assert(name); + assert(message); + + if (streq(name, "TimeoutIdleUSec")) { + usec_t timeout_idle_usec; + r = sd_bus_message_read(message, "t", &timeout_idle_usec); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + char time[FORMAT_TIMESPAN_MAX]; + + a->timeout_idle_usec = timeout_idle_usec; + unit_write_drop_in_format(UNIT(a), mode, name, "[Automount]\nTimeoutIdleSec=%s\n", + format_timespan(time, sizeof(time), timeout_idle_usec, USEC_PER_MSEC)); + } + } else + return 0; + + return 1; +} + +int bus_automount_set_property( + Unit *u, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + Automount *a = AUTOMOUNT(u); + int r = 0; + + assert(a); + assert(name); + assert(message); + + if (u->transient && u->load_state == UNIT_STUB) + /* This is a transient unit, let's load a little more */ + + r = bus_automount_set_transient_property(a, name, message, mode, error); + + return r; +} diff --git a/src/core/dbus-automount.h b/src/core/dbus-automount.h index 7b51eb973a..f41adda2a6 100644 --- a/src/core/dbus-automount.h +++ b/src/core/dbus-automount.h @@ -21,3 +21,5 @@ extern const sd_bus_vtable bus_automount_vtable[]; + +int bus_automount_set_property(Unit *u, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); -- cgit v1.2.3 From b294b79fb0f9749afa53624c8f06b145ea2c1525 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Wed, 9 Dec 2015 16:02:10 +0100 Subject: mount: use get_mount_parameters_fragment() consistently There are multiple different checks, that all mean the same thing: Is it a explicitly configured mount unit where actions need to be taken to mount it, or is is just mirroring 'mountinfo': 'from_fragment' to set if fragment_path is not NULL, and get_mount_parameters_fragment() just wraps that and returns fragment_path. Use get_mount_parameters_fragment() everywhere to be consistent. This is just a cleanup without functional change. --- src/core/mount.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/core/mount.c b/src/core/mount.c index 665a60bb55..7b20892b0d 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -482,6 +482,7 @@ static int mount_add_default_dependencies(Mount *m) { static int mount_verify(Mount *m) { _cleanup_free_ char *e = NULL; + MountParameters *p; int r; assert(m); @@ -506,7 +507,8 @@ static int mount_verify(Mount *m) { return -EINVAL; } - if (UNIT(m)->fragment_path && !m->parameters_fragment.what) { + p = get_mount_parameters_fragment(m); + if (p && !p->what) { log_unit_error(UNIT(m), "What= setting is missing. Refusing."); return -EBADMSG; } @@ -850,11 +852,6 @@ fail: mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES); } -static int mount_get_opts(Mount *m, char **ret) { - return fstab_filter_options(m->parameters_fragment.options, - "nofail\0" "noauto\0" "auto\0", NULL, NULL, ret); -} - static void mount_enter_mounting(Mount *m) { int r; MountParameters *p; @@ -877,19 +874,18 @@ static void mount_enter_mounting(Mount *m) { if (p && mount_is_bind(p)) (void) mkdir_p_label(p->what, m->directory_mode); - if (m->from_fragment) { + if (p) { _cleanup_free_ char *opts = NULL; - r = mount_get_opts(m, &opts); + r = fstab_filter_options(p->options, "nofail\0" "noauto\0" "auto\0", NULL, NULL, &opts); if (r < 0) goto fail; - r = exec_command_set(m->control_command, MOUNT_PATH, - m->parameters_fragment.what, m->where, NULL); + r = exec_command_set(m->control_command, MOUNT_PATH, p->what, m->where, NULL); if (r >= 0 && m->sloppy_options) r = exec_command_append(m->control_command, "-s", NULL); - if (r >= 0 && m->parameters_fragment.fstype) - r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL); + if (r >= 0 && p->fstype) + r = exec_command_append(m->control_command, "-t", p->fstype, NULL); if (r >= 0 && !isempty(opts)) r = exec_command_append(m->control_command, "-o", opts, NULL); } else @@ -915,27 +911,29 @@ fail: static void mount_enter_remounting(Mount *m) { int r; + MountParameters *p; assert(m); m->control_command_id = MOUNT_EXEC_REMOUNT; m->control_command = m->exec_command + MOUNT_EXEC_REMOUNT; - if (m->from_fragment) { + p = get_mount_parameters_fragment(m); + if (p) { const char *o; - if (m->parameters_fragment.options) - o = strjoina("remount,", m->parameters_fragment.options); + if (p->options) + o = strjoina("remount,", p->options); else o = "remount"; r = exec_command_set(m->control_command, MOUNT_PATH, - m->parameters_fragment.what, m->where, + p->what, m->where, "-o", o, NULL); if (r >= 0 && m->sloppy_options) r = exec_command_append(m->control_command, "-s", NULL); - if (r >= 0 && m->parameters_fragment.fstype) - r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL); + if (r >= 0 && p->fstype) + r = exec_command_append(m->control_command, "-t", p->fstype, NULL); } else r = -ENOENT; -- cgit v1.2.3 From 96f5957f1f0d98584bb463d73616a6755e02624b Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Wed, 9 Dec 2015 16:02:51 +0100 Subject: mount: write drop-in file when setting transient properties This fixes transient mount units in general. With this change, 'from_fragment' is for transient mount units. As a result, the normal logic for 'fragment' mount units vs. 'mountinfo' mount units works for transient mount units as well. --- src/core/dbus-mount.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c index 935db7c48b..b4bbee0648 100644 --- a/src/core/dbus-mount.c +++ b/src/core/dbus-mount.c @@ -157,6 +157,9 @@ static int bus_mount_set_transient_property( if (!p) return -ENOMEM; + unit_write_drop_in_format(UNIT(m), mode, name, "[Mount]\n%s=%s\n", + name, new_property); + free(*property); *property = p; } -- cgit v1.2.3 From 3228995c534c4cae609e97502f7c7ca1d4a14840 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 23 Jun 2016 11:52:45 +0200 Subject: cgroup: detect cgroup namespaces - define CLONE_NEWCGROUP - add fun to detect whether cgroup namespaces are supported --- src/basic/cgroup-util.c | 14 ++++++++++++++ src/basic/cgroup-util.h | 2 ++ src/basic/missing.h | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 7cdc97ee3c..0561a07ed9 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -134,6 +134,20 @@ int cg_read_event(const char *controller, const char *path, const char *event, return -ENOENT; } +bool cg_ns_supported(void) { + static thread_local int enabled = -1; + + if (enabled >= 0) + return enabled; + + if (access("/proc/self/ns/cgroup", F_OK) == 0) + enabled = 1; + else + enabled = 0; + + return enabled; +} + int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) { _cleanup_free_ char *fs = NULL; int r; diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index 4bb5291296..5c1c474112 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -214,6 +214,8 @@ int cg_mask_supported(CGroupMask *ret); int cg_kernel_controllers(Set *controllers); +bool cg_ns_supported(void); + int cg_unified(void); void cg_unified_flush(void); diff --git a/src/basic/missing.h b/src/basic/missing.h index b1272f8799..f8e096605e 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -445,6 +445,10 @@ struct btrfs_ioctl_quota_ctl_args { #define CGROUP2_SUPER_MAGIC 0x63677270 #endif +#ifndef CLONE_NEWCGROUP +#define CLONE_NEWCGROUP 0x02000000 +#endif + #ifndef TMPFS_MAGIC #define TMPFS_MAGIC 0x01021994 #endif -- cgit v1.2.3 From 0996ef00fb5c0770d49670f81a230fcc2552af89 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 23 Jun 2016 13:41:56 +0200 Subject: nspawn: handle cgroup namespaces (NOTE: Cgroup namespaces work with legacy and unified hierarchies: "This is completely backward compatible and will be completely invisible to any existing cgroup users (except for those running inside a cgroup namespace and looking at /proc/pid/cgroup of tasks outside their namespace.)" (https://lists.linuxfoundation.org/pipermail/containers/2016-January/036582.html) So there is no need to special case unified.) If cgroup namespaces are supported we skip mount_cgroups() in the outer_child(). Instead, we unshare(CLONE_NEWCGROUP) in the inner_child() and only then do we call mount_cgroups(). The clean way to handle cgroup namespaces would be to delegate mounting of cgroups completely to the init system in the container. However, this would likely break backward compatibility with the UNIFIED_CGROUP_HIERARCHY flag of systemd-nspawn. Also no cgroupfs would be mounted whenever the user simply requests a shell and no init is available to mount cgroups. Hence, we introduce mount_legacy_cgns_supported(). After calling unshare(CLONE_NEWCGROUP) it parses /proc/self/cgroup to find the mounted controllers and mounts them inside the new cgroup namespace. This should preserve backward compatibility with the UNIFIED_CGROUP_HIERARCHY flag and mount a cgroupfs when no init in the container is running. --- src/nspawn/nspawn-mount.c | 188 ++++++++++++++++++++++++++++++++++++++++++---- src/nspawn/nspawn.c | 41 +++++++--- 2 files changed, 201 insertions(+), 28 deletions(-) diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 8e2d2d543c..b76b09b9cb 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -23,6 +23,8 @@ #include "alloc-util.h" #include "cgroup-util.h" #include "escape.h" +#include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "label.h" #include "mkdir.h" @@ -181,13 +183,15 @@ int tmpfs_mount_parse(CustomMount **l, unsigned *n, const char *s) { static int tmpfs_patch_options( const char *options, - bool userns, uid_t uid_shift, uid_t uid_range, + bool userns, + uid_t uid_shift, uid_t uid_range, + bool patch_ids, const char *selinux_apifs_context, char **ret) { char *buf = NULL; - if (userns && uid_shift != 0) { + if ((userns && uid_shift != 0) || patch_ids) { assert(uid_shift != UID_INVALID); if (options) @@ -218,7 +222,13 @@ static int tmpfs_patch_options( } #endif + if (!buf && options) { + buf = strdup(options); + if (!buf) + return -ENOMEM; + } *ret = buf; + return !!buf; } @@ -271,7 +281,15 @@ int mount_sysfs(const char *dest) { return log_error_errno(errno, "Failed to remove %s: %m", full); x = prefix_roota(top, "/fs/kdbus"); - (void) mkdir(x, 0755); + (void) mkdir_p(x, 0755); + + /* Create mountpoint for cgroups. Otherwise we are not allowed since we + * remount /sys read-only. + */ + if (cg_ns_supported()) { + x = prefix_roota(top, "/fs/cgroup"); + (void) mkdir_p(x, 0755); + } if (mount(NULL, top, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL) < 0) return log_error_errno(errno, "Failed to make %s read-only: %m", top); @@ -348,7 +366,7 @@ int mount_all(const char *dest, o = mount_table[k].options; if (streq_ptr(mount_table[k].type, "tmpfs")) { - r = tmpfs_patch_options(o, use_userns, uid_shift, uid_range, selinux_apifs_context, &options); + r = tmpfs_patch_options(o, use_userns, uid_shift, uid_range, false, selinux_apifs_context, &options); if (r < 0) return log_oom(); if (r > 0) @@ -485,7 +503,7 @@ static int mount_tmpfs( if (r < 0 && r != -EEXIST) return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where); - r = tmpfs_patch_options(m->options, userns, uid_shift, uid_range, selinux_apifs_context, &buf); + r = tmpfs_patch_options(m->options, userns, uid_shift, uid_range, false, selinux_apifs_context, &buf); if (r < 0) return log_oom(); options = r > 0 ? buf : m->options; @@ -600,6 +618,48 @@ int mount_custom( return 0; } +/* Retrieve existing subsystems. This function is called in a new cgroup + * namespace. + */ +static int get_controllers(Set *subsystems) { + _cleanup_fclose_ FILE *f = NULL; + char line[LINE_MAX]; + + assert(subsystems); + + f = fopen("/proc/self/cgroup", "re"); + if (!f) + return errno == ENOENT ? -ESRCH : -errno; + + FOREACH_LINE(line, f, return -errno) { + int r; + char *e, *l, *p; + + truncate_nl(line); + + l = strchr(line, ':'); + if (!l) + continue; + + l++; + e = strchr(l, ':'); + if (!e) + continue; + + *e = 0; + + if (streq(l, "") || streq(l, "name=systemd")) + continue; + + p = strdup(l); + r = set_consume(subsystems, p); + if (r < 0) + return r; + } + + return 0; +} + static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controller, const char *hierarchy, bool read_only) { char *to; int r; @@ -628,11 +688,107 @@ static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controlle return 1; } -static int mount_legacy_cgroups( - const char *dest, +/* Mount a legacy cgroup hierarchy when cgroup namespaces are supported. */ +static int mount_legacy_cgns_supported( bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context) { + _cleanup_set_free_free_ Set *controllers = NULL; + const char *cgroup_root = "/sys/fs/cgroup", *c; + int r; + (void) mkdir_p(cgroup_root, 0755); + + /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */ + r = path_is_mount_point(cgroup_root, AT_SYMLINK_FOLLOW); + if (r < 0) + return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m"); + if (r == 0) { + _cleanup_free_ char *options = NULL; + + /* When cgroup namespaces are enabled and user namespaces are + * used then the mount of the cgroupfs is done *inside* the new + * user namespace. We're root in the new user namespace and the + * kernel will happily translate our uid/gid to the correct + * uid/gid as seen from e.g. /proc/1/mountinfo. So we simply + * pass uid 0 and not uid_shift to tmpfs_patch_options(). + */ + r = tmpfs_patch_options("mode=755", userns, 0, uid_range, true, selinux_apifs_context, &options); + if (r < 0) + return log_oom(); + + if (mount("tmpfs", cgroup_root, "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options) < 0) + return log_error_errno(errno, "Failed to mount /sys/fs/cgroup: %m"); + } + + if (cg_unified() > 0) + goto skip_controllers; + + controllers = set_new(&string_hash_ops); + if (!controllers) + return log_oom(); + + r = get_controllers(controllers); + if (r < 0) + return log_error_errno(r, "Failed to determine cgroup controllers: %m"); + + for (;;) { + _cleanup_free_ const char *controller = NULL; + + controller = set_steal_first(controllers); + if (!controller) + break; + + r = mount_legacy_cgroup_hierarchy("", controller, controller, !userns); + if (r < 0) + return r; + + /* When multiple hierarchies are co-mounted, make their + * constituting individual hierarchies a symlink to the + * co-mount. + */ + c = controller; + for (;;) { + _cleanup_free_ char *target = NULL, *tok = NULL; + + r = extract_first_word(&c, &tok, ",", 0); + if (r < 0) + return log_error_errno(r, "Failed to extract co-mounted cgroup controller: %m"); + if (r == 0) + break; + + target = prefix_root("/sys/fs/cgroup", tok); + if (!target) + return log_oom(); + + if (streq(controller, tok)) + break; + + r = symlink_idempotent(controller, target); + if (r == -EINVAL) + return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m"); + if (r < 0) + return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m"); + } + } + +skip_controllers: + r = mount_legacy_cgroup_hierarchy("", "none,name=systemd,xattr", "systemd", false); + if (r < 0) + return r; + + if (!userns) { + if (mount(NULL, cgroup_root, NULL, MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755") < 0) + return log_error_errno(errno, "Failed to remount %s read-only: %m", cgroup_root); + } + + return 0; +} + +/* Mount legacy cgroup hierarchy when cgroup namespaces are unsupported. */ +static int mount_legacy_cgns_unsupported( + const char *dest, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { _cleanup_set_free_free_ Set *controllers = NULL; const char *cgroup_root; int r; @@ -648,7 +804,7 @@ static int mount_legacy_cgroups( if (r == 0) { _cleanup_free_ char *options = NULL; - r = tmpfs_patch_options("mode=755", userns, uid_shift, uid_range, selinux_apifs_context, &options); + r = tmpfs_patch_options("mode=755", userns, uid_shift, uid_range, false, selinux_apifs_context, &options); if (r < 0) return log_oom(); @@ -707,10 +863,8 @@ static int mount_legacy_cgroups( return r; r = symlink_idempotent(combined, target); - if (r == -EINVAL) { - log_error("Invalid existing symlink for combined hierarchy"); - return r; - } + if (r == -EINVAL) + return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m"); if (r < 0) return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m"); } @@ -765,8 +919,10 @@ int mount_cgroups( if (unified_requested) return mount_unified_cgroups(dest); - else - return mount_legacy_cgroups(dest, userns, uid_shift, uid_range, selinux_apifs_context); + else if (cg_ns_supported()) + return mount_legacy_cgns_supported(userns, uid_shift, uid_range, selinux_apifs_context); + + return mount_legacy_cgns_unsupported(dest, userns, uid_shift, uid_range, selinux_apifs_context); } int mount_systemd_cgroup_writable( @@ -834,7 +990,7 @@ int setup_volatile_state( return log_error_errno(errno, "Failed to create %s: %m", directory); options = "mode=755"; - r = tmpfs_patch_options(options, userns, uid_shift, uid_range, selinux_apifs_context, &buf); + r = tmpfs_patch_options(options, userns, uid_shift, uid_range, false, selinux_apifs_context, &buf); if (r < 0) return log_oom(); if (r > 0) @@ -870,7 +1026,7 @@ int setup_volatile( return log_error_errno(errno, "Failed to create temporary directory: %m"); options = "mode=755"; - r = tmpfs_patch_options(options, userns, uid_shift, uid_range, selinux_apifs_context, &buf); + r = tmpfs_patch_options(options, userns, uid_shift, uid_range, false, selinux_apifs_context, &buf); if (r < 0) return log_oom(); if (r > 0) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 73c56d7310..9e74a77758 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -2594,9 +2594,24 @@ static int inner_child( return -ESRCH; } - r = mount_systemd_cgroup_writable("", arg_unified_cgroup_hierarchy); - if (r < 0) - return r; + if (cg_ns_supported()) { + r = unshare(CLONE_NEWCGROUP); + if (r < 0) + return log_error_errno(errno, "Failed to unshare cgroup namespace"); + r = mount_cgroups( + "", + arg_unified_cgroup_hierarchy, + arg_userns_mode != USER_NAMESPACE_NO, + arg_uid_shift, + arg_uid_range, + arg_selinux_apifs_context); + if (r < 0) + return r; + } else { + r = mount_systemd_cgroup_writable("", arg_unified_cgroup_hierarchy); + if (r < 0) + return r; + } r = reset_uid_gid(); if (r < 0) @@ -2978,15 +2993,17 @@ static int outer_child( if (r < 0) return r; - r = mount_cgroups( - directory, - arg_unified_cgroup_hierarchy, - arg_userns_mode != USER_NAMESPACE_NO, - arg_uid_shift, - arg_uid_range, - arg_selinux_apifs_context); - if (r < 0) - return r; + if (!cg_ns_supported()) { + r = mount_cgroups( + directory, + arg_unified_cgroup_hierarchy, + arg_userns_mode != USER_NAMESPACE_NO, + arg_uid_shift, + arg_uid_range, + arg_selinux_apifs_context); + if (r < 0) + return r; + } r = mount_move_root(directory); if (r < 0) -- cgit v1.2.3 From b52a109ad38cd37b660ccd5394ff5c171a5e5355 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 Jul 2016 18:19:29 +0200 Subject: gpt-generator: use /efi as mount point for the ESP if it exists Let's make the EFI generator a bit smarter: if /efi exists it is used as mount point for the ESP, otherwise /boot is used. This should increase compatibility with distros which use legacy boot loaders that insist on having /boot as something that isn't the ESP. --- man/systemd-gpt-auto-generator.xml | 23 +++++++++------- src/gpt-auto-generator/gpt-auto-generator.c | 41 ++++++++++++++++------------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/man/systemd-gpt-auto-generator.xml b/man/systemd-gpt-auto-generator.xml index e890c4dce2..d26206710f 100644 --- a/man/systemd-gpt-auto-generator.xml +++ b/man/systemd-gpt-auto-generator.xml @@ -137,6 +137,11 @@ Swap All swap partitions located on the disk the root partition is located on are enabled. + + c12a7328-f81f-11d2-ba4b-00a0c93ec93b + EFI System Partition (ESP) + The first ESP located on the disk the root partition is located on is mounted to /boot or /efi, see below. + @@ -150,16 +155,14 @@ /etc/crypttab with a different device mapper device name. - Mount and automount units for the EFI System Partition (ESP), - mounting it to /boot, are generated on EFI - systems where the boot loader communicates the used ESP to the operating - system. Since this generator creates an automount unit, the mount will - only be activated on-demand, when accessed. On systems where - /boot is an explicitly configured mount - (for example, listed in - fstab5) - or where the /boot mount point is non-empty, no - mount units are generated. + Mount and automount units for the EFI System Partition (ESP) are generated on EFI systems. The ESP is mounted + to /boot, unless a mount point directory /efi exists, in which case it is + mounted there. Since this generator creates an automount unit, the mount will only be activated on-demand, when + accessed. On systems where /boot (or /efi if it exists) is an explicitly + configured mount (for example, listed in fstab5) or where the + /boot (or /efi) mount point is non-empty, no mount units are + generated. When using this generator in conjunction with btrfs file systems, make sure to set the correct default subvolumes on them, diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index a4938a7c3a..dede86eabf 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -453,33 +453,37 @@ static int add_boot(const char *what) { _cleanup_blkid_free_probe_ blkid_probe b = NULL; const char *fstype = NULL, *uuid = NULL; sd_id128_t id, type_id; + const char *esp; int r; assert(what); if (!is_efi_boot()) { - log_debug("Not an EFI boot, ignoring /boot."); + log_debug("Not an EFI boot, ignoring the ESP."); return 0; } if (in_initrd()) { - log_debug("In initrd, ignoring /boot."); + log_debug("In initrd, ignoring the ESP."); return 0; } if (detect_container() > 0) { - log_debug("In a container, ignoring /boot."); + log_debug("In a container, ignoring the ESP."); return 0; } + /* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice */ + esp = access("/efi/", F_OK) >= 0 ? "/efi" : "/boot"; + /* We create an .automount which is not overridden by the .mount from the fstab generator. */ - if (fstab_is_mount_point("/boot")) { - log_debug("/boot specified in fstab, ignoring."); + if (fstab_is_mount_point(esp)) { + log_debug("%s specified in fstab, ignoring.", esp); return 0; } - if (path_is_busy("/boot")) { - log_debug("/boot already populated, ignoring."); + if (path_is_busy(esp)) { + log_debug("%s already populated, ignoring.", esp); return 0; } @@ -488,7 +492,6 @@ static int add_boot(const char *what) { log_debug("EFI loader partition unknown."); return 0; } - if (r < 0) { log_error_errno(r, "Failed to read ESP partition UUID: %m"); return r; @@ -514,35 +517,35 @@ static int add_boot(const char *what) { (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); if (!streq_ptr(fstype, "vfat")) { - log_debug("Partition for /boot is not a FAT filesystem, ignoring."); + log_debug("Partition for %s is not a FAT filesystem, ignoring.", esp); return 0; } errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid, NULL); if (r != 0) { - log_debug_errno(errno, "Partition for /boot does not have a UUID, ignoring."); + log_debug_errno(errno, "Partition for %s does not have a UUID, ignoring.", esp); return 0; } if (sd_id128_from_string(uuid, &type_id) < 0) { - log_debug("Partition for /boot does not have a valid UUID, ignoring."); + log_debug("Partition for %s does not have a valid UUID, ignoring.", esp); return 0; } if (!sd_id128_equal(type_id, id)) { - log_debug("Partition for /boot does not appear to be the partition we are booted from."); + log_debug("Partition for %s does not appear to be the partition we are booted from.", esp); return 0; } r = add_automount("boot", - what, - "/boot", - "vfat", - true, - "umask=0077", - "EFI System Partition Automount", - 120 * USEC_PER_SEC); + what, + esp, + "vfat", + true, + "umask=0077", + "EFI System Partition Automount", + 120 * USEC_PER_SEC); return r; } -- cgit v1.2.3 From a6bc7db980532d294c29f1cd5654f03453519c92 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 Jul 2016 18:36:04 +0200 Subject: nspawn: if an ESP is part of the disk image to operate on, mount it to /efi or /boot Matching the behaviour of gpt-auto-generator, if we find an ESP while dissecting a container image, mount it to /efi or /boot if those dirs exist and are empty. This should enable us to run "bootctl" inside a container and do the right thing. --- src/nspawn/nspawn.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index e4be0a2251..befe342b02 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1803,17 +1803,18 @@ static int dissect_image( char **root_device, bool *root_device_rw, char **home_device, bool *home_device_rw, char **srv_device, bool *srv_device_rw, + char **esp_device, bool *secondary) { #ifdef HAVE_BLKID - int home_nr = -1, srv_nr = -1; + int home_nr = -1, srv_nr = -1, esp_nr = -1; #ifdef GPT_ROOT_NATIVE int root_nr = -1; #endif #ifdef GPT_ROOT_SECONDARY int secondary_root_nr = -1; #endif - _cleanup_free_ char *home = NULL, *root = NULL, *secondary_root = NULL, *srv = NULL, *generic = NULL; + _cleanup_free_ char *home = NULL, *root = NULL, *secondary_root = NULL, *srv = NULL, *esp = NULL, *generic = NULL; _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; _cleanup_udev_device_unref_ struct udev_device *d = NULL; _cleanup_blkid_free_probe_ blkid_probe b = NULL; @@ -1831,6 +1832,7 @@ static int dissect_image( assert(root_device); assert(home_device); assert(srv_device); + assert(esp_device); assert(secondary); assert(arg_image); @@ -2044,6 +2046,16 @@ static int dissect_image( r = free_and_strdup(&srv, node); if (r < 0) return log_oom(); + } else if (sd_id128_equal(type_id, GPT_ESP)) { + + if (esp && nr >= esp_nr) + continue; + + esp_nr = nr; + + r = free_and_strdup(&esp, node); + if (r < 0) + return log_oom(); } #ifdef GPT_ROOT_NATIVE else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) { @@ -2161,6 +2173,11 @@ static int dissect_image( *srv_device_rw = srv_rw; } + if (esp) { + *esp_device = esp; + esp = NULL; + } + return 0; #else log_error("--image= is not supported, compiled without blkid support."); @@ -2289,7 +2306,8 @@ static int mount_devices( const char *where, const char *root_device, bool root_device_rw, const char *home_device, bool home_device_rw, - const char *srv_device, bool srv_device_rw) { + const char *srv_device, bool srv_device_rw, + const char *esp_device) { int r; assert(where); @@ -2312,6 +2330,27 @@ static int mount_devices( return log_error_errno(r, "Failed to mount server data directory: %m"); } + if (esp_device) { + const char *mp, *x; + + /* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */ + + mp = "/efi"; + x = strjoina(arg_directory, mp); + r = dir_is_empty(x); + if (r == -ENOENT) { + mp = "/boot"; + x = strjoina(arg_directory, mp); + r = dir_is_empty(x); + } + + if (r > 0) { + r = mount_device(esp_device, arg_directory, mp, true); + if (r < 0) + return log_error_errno(r, "Failed to mount ESP: %m"); + } + } + return 0; } @@ -2785,6 +2824,7 @@ static int outer_child( const char *root_device, bool root_device_rw, const char *home_device, bool home_device_rw, const char *srv_device, bool srv_device_rw, + const char *esp_device, bool interactive, bool secondary, int pid_socket, @@ -2846,7 +2886,8 @@ static int outer_child( r = mount_devices(directory, root_device, root_device_rw, home_device, home_device_rw, - srv_device, srv_device_rw); + srv_device, srv_device_rw, + esp_device); if (r < 0) return r; @@ -3449,7 +3490,7 @@ static int load_settings(void) { int main(int argc, char *argv[]) { - _cleanup_free_ char *device_path = NULL, *root_device = NULL, *home_device = NULL, *srv_device = NULL, *console = NULL; + _cleanup_free_ char *device_path = NULL, *root_device = NULL, *home_device = NULL, *srv_device = NULL, *esp_device = NULL, *console = NULL; bool root_device_rw = true, home_device_rw = true, srv_device_rw = true; _cleanup_close_ int master = -1, image_fd = -1; _cleanup_fdset_free_ FDSet *fds = NULL; @@ -3631,6 +3672,7 @@ int main(int argc, char *argv[]) { &root_device, &root_device_rw, &home_device, &home_device_rw, &srv_device, &srv_device_rw, + &esp_device, &secondary); if (r < 0) goto finish; @@ -3805,6 +3847,7 @@ int main(int argc, char *argv[]) { root_device, root_device_rw, home_device, home_device_rw, srv_device, srv_device_rw, + esp_device, interactive, secondary, pid_socket_pair[1], -- cgit v1.2.3 From 25579a43efcf5fdced4041427164e0852c035ab0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 Jul 2016 18:39:54 +0200 Subject: bootctl: minor coding style improvements --- src/boot/bootctl.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 7cb2259717..6e0c961527 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -46,6 +46,9 @@ #include "string-util.h" #include "util.h" +static const char *arg_path = "/boot"; +static bool arg_touch_variables = true; + static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t *psize, sd_id128_t *uuid) { struct statfs sfs; struct stat st, st2; @@ -930,9 +933,6 @@ static int help(void) { return 0; } -static const char *arg_path = "/boot"; -static bool arg_touch_variables = true; - static int parse_argv(int argc, char *argv[]) { enum { ARG_PATH = 0x100, @@ -1036,12 +1036,10 @@ static int bootctl_main(int argc, char*argv[]) { return r; switch (arg_action) { + case ACTION_STATUS: { - _cleanup_free_ char *fw_type = NULL; - _cleanup_free_ char *fw_info = NULL; - _cleanup_free_ char *loader = NULL; - _cleanup_free_ char *loader_path = NULL; - sd_id128_t loader_part_uuid = {}; + _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL; + sd_id128_t loader_part_uuid = SD_ID128_NULL; if (is_efi_boot()) { read_loader_efi_var("LoaderFirmwareType", &fw_type); -- cgit v1.2.3 From c18532e031d2923e095a189999c289be13aea7b8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 Jul 2016 18:40:03 +0200 Subject: bootctl: fix error message check --- src/boot/bootctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 6e0c961527..f337eb6675 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -1049,7 +1049,7 @@ static int bootctl_main(int argc, char*argv[]) { if (loader_path) efi_tilt_backslashes(loader_path); r = efi_loader_get_device_part_uuid(&loader_part_uuid); - if (r < 0 && r == -ENOENT) + if (r < 0 && r != -ENOENT) log_warning_errno(r, "Failed to read EFI variable LoaderDevicePartUUID: %m"); printf("System:\n"); -- cgit v1.2.3 From 2f2c539cd4af7ca526296adecff2a6f90d7d7b0c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 Jul 2016 20:19:20 +0200 Subject: bootctl: rework to use common verbs parsing, and add searching of ESP path This rearranges bootctl a bit, so that it uses the usual verbs parsing routines, and automatically searches the ESP in /boot, /efi or /boot/efi, thus increasing compatibility with mainstream distros that insist on /boot/efi. This also adds minimal support for running bootctl in a container environment: when run inside a container verification of the ESP via raw block device access, trusting the container manager to mount the ESP correctly. Moreover, EFI variables are not accessed when running in the container. --- man/bootctl.xml | 6 +- src/boot/bootctl.c | 342 ++++++++++++++++++++++++++++++++++------------------- 2 files changed, 224 insertions(+), 124 deletions(-) diff --git a/man/bootctl.xml b/man/bootctl.xml index 6e835c037f..82c162ee70 100644 --- a/man/bootctl.xml +++ b/man/bootctl.xml @@ -101,8 +101,10 @@ - - Path to the EFI system partition. The default is /boot. + + Path to the EFI System Partition (ESP). If not specified, /efi, + /boot, and /boot/efi are checked in turn. It is recommended to mount + the ESP to /boot, if possible. diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index f337eb6675..8b2407f145 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -42,25 +42,52 @@ #include "fd-util.h" #include "fileio.h" #include "locale-util.h" +#include "parse-util.h" #include "rm-rf.h" #include "string-util.h" +#include "strv.h" +#include "umask-util.h" #include "util.h" +#include "verbs.h" +#include "virt.h" -static const char *arg_path = "/boot"; +static char *arg_path = NULL; static bool arg_touch_variables = true; -static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t *psize, sd_id128_t *uuid) { - struct statfs sfs; - struct stat st, st2; - _cleanup_free_ char *t = NULL; +static int verify_esp( + bool searching, + const char *p, + uint32_t *ret_part, + uint64_t *ret_pstart, + uint64_t *ret_psize, + sd_id128_t *ret_uuid) { + _cleanup_blkid_free_probe_ blkid_probe b = NULL; - int r; + _cleanup_free_ char *t = NULL; + uint64_t pstart = 0, psize = 0; + struct stat st, st2; const char *v, *t2; + struct statfs sfs; + sd_id128_t uuid = SD_ID128_NULL; + uint32_t part = 0; + int r; + + assert(p); + + if (statfs(p, &sfs) < 0) { + + /* If we are searching for the mount point, don't generate a log message if we can't find the path */ + if (errno == ENOENT && searching) + return -ENOENT; - if (statfs(p, &sfs) < 0) return log_error_errno(errno, "Failed to check file system type of \"%s\": %m", p); + } if (sfs.f_type != 0x4d44) { + + if (searching) + return -EADDRNOTAVAIL; + log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p); return -ENODEV; } @@ -83,6 +110,11 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t return -ENODEV; } + /* In a container we don't have access to block devices, skip this part of the verification, we trust the + * container manager set everything up correctly on its own. */ + if (detect_container() > 0) + goto finish; + r = asprintf(&t, "/dev/block/%u:%u", major(st.st_dev), minor(st.st_dev)); if (r < 0) return log_oom(); @@ -120,7 +152,6 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t r = errno ? -errno : -EIO; return log_error_errno(r, "Failed to probe file system type \"%s\": %m", p); } - if (!streq(v, "vfat")) { log_error("File system \"%s\" is not FAT.", p); return -ENODEV; @@ -132,7 +163,6 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t r = errno ? -errno : -EIO; return log_error_errno(r, "Failed to probe partition scheme \"%s\": %m", p); } - if (!streq(v, "gpt")) { log_error("File system \"%s\" is not on a GPT partition table.", p); return -ENODEV; @@ -144,7 +174,6 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t r = errno ? -errno : -EIO; return log_error_errno(r, "Failed to probe partition type UUID \"%s\": %m", p); } - if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) { log_error("File system \"%s\" has wrong type for an EFI System Partition (ESP).", p); return -ENODEV; @@ -156,8 +185,7 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t r = errno ? -errno : -EIO; return log_error_errno(r, "Failed to probe partition entry UUID \"%s\": %m", p); } - - r = sd_id128_from_string(v, uuid); + r = sd_id128_from_string(v, &uuid); if (r < 0) { log_error("Partition \"%s\" has invalid UUID \"%s\".", p, v); return -EIO; @@ -169,7 +197,9 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t r = errno ? -errno : -EIO; return log_error_errno(r, "Failed to probe partition number \"%s\": m", p); } - *part = strtoul(v, NULL, 10); + r = safe_atou32(v, &part); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field."); errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL); @@ -177,7 +207,9 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t r = errno ? -errno : -EIO; return log_error_errno(r, "Failed to probe partition offset \"%s\": %m", p); } - *pstart = strtoul(v, NULL, 10); + r = safe_atou64(v, &pstart); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field."); errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL); @@ -185,11 +217,50 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t r = errno ? -errno : -EIO; return log_error_errno(r, "Failed to probe partition size \"%s\": %m", p); } - *psize = strtoul(v, NULL, 10); + r = safe_atou64(v, &psize); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field."); + +finish: + if (ret_part) + *ret_part = part; + if (ret_pstart) + *ret_pstart = pstart; + if (ret_psize) + *ret_psize = psize; + if (ret_uuid) + *ret_uuid = uuid; return 0; } +static int find_esp(uint32_t *part, uint64_t *pstart, uint64_t *psize, sd_id128_t *uuid) { + const char *path; + int r; + + if (arg_path) + return verify_esp(false, arg_path, part, pstart, psize, uuid); + + FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") { + + r = verify_esp(true, path, part, pstart, psize, uuid); + if (IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */ + continue; + if (r < 0) + return r; + + arg_path = strdup(path); + if (!arg_path) + return log_oom(); + + log_info("Using EFI System Parition at %s.", path); + return 0; + } + + log_error("Couldn't find EFI system partition. It is recommended to mount it to /boot. Alternatively, use --path= to specify path to mount point."); + return -ENOENT; +} + /* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */ static int get_file_version(int fd, char **v) { struct stat st; @@ -914,7 +985,8 @@ static int install_loader_config(const char *esp_path) { return 0; } -static int help(void) { +static int help(int argc, char *argv[], void *userdata) { + printf("%s [COMMAND] [OPTIONS...]\n" "\n" "Install, update or remove the systemd-boot EFI boot manager.\n\n" @@ -948,7 +1020,7 @@ static int parse_argv(int argc, char *argv[]) { { NULL, 0, NULL, 0 } }; - int c; + int c, r; assert(argc >= 0); assert(argv); @@ -957,14 +1029,16 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { case 'h': - help(); + help(0, NULL, NULL); return 0; case ARG_VERSION: return version(); case ARG_PATH: - arg_path = optarg; + r = free_and_strdup(&arg_path, optarg); + if (r < 0) + return log_oom(); break; case ARG_NO_VARIABLES: @@ -989,147 +1063,170 @@ static void read_loader_efi_var(const char *name, char **var) { log_warning_errno(r, "Failed to read EFI variable %s: %m", name); } -static int bootctl_main(int argc, char*argv[]) { - enum action { - ACTION_STATUS, - ACTION_INSTALL, - ACTION_UPDATE, - ACTION_REMOVE - } arg_action = ACTION_STATUS; - static const struct { - const char* verb; - enum action action; - } verbs[] = { - { "status", ACTION_STATUS }, - { "install", ACTION_INSTALL }, - { "update", ACTION_UPDATE }, - { "remove", ACTION_REMOVE }, - }; +static int must_be_root(void) { - sd_id128_t uuid = {}; - uint32_t part = 0; - uint64_t pstart = 0, psize = 0; - int r, q; + if (geteuid() == 0) + return 0; - if (argv[optind]) { - unsigned i; + log_error("Need to be root."); + return -EPERM; +} - for (i = 0; i < ELEMENTSOF(verbs); i++) { - if (!streq(argv[optind], verbs[i].verb)) - continue; - arg_action = verbs[i].action; - break; - } - if (i >= ELEMENTSOF(verbs)) { - log_error("Unknown operation \"%s\"", argv[optind]); - return -EINVAL; - } - } +static int verb_status(int argc, char *argv[], void *userdata) { - if (geteuid() != 0) - return log_error_errno(EPERM, "Need to be root."); + sd_id128_t uuid = SD_ID128_NULL; + int r; - r = verify_esp(arg_path, &part, &pstart, &psize, &uuid); - if (r == -ENODEV && !arg_path) - log_notice("You might want to use --path= to indicate the path to your ESP, in case it is not mounted on /boot."); + r = must_be_root(); if (r < 0) return r; - switch (arg_action) { + r = find_esp(NULL, NULL, NULL, &uuid); + if (r < 0) + return r; - case ACTION_STATUS: { + if (is_efi_boot()) { _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL; sd_id128_t loader_part_uuid = SD_ID128_NULL; - if (is_efi_boot()) { - read_loader_efi_var("LoaderFirmwareType", &fw_type); - read_loader_efi_var("LoaderFirmwareInfo", &fw_info); - read_loader_efi_var("LoaderInfo", &loader); - read_loader_efi_var("LoaderImageIdentifier", &loader_path); - if (loader_path) - efi_tilt_backslashes(loader_path); - r = efi_loader_get_device_part_uuid(&loader_part_uuid); - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read EFI variable LoaderDevicePartUUID: %m"); - - printf("System:\n"); - printf(" Firmware: %s (%s)\n", strna(fw_type), strna(fw_info)); - - r = is_efi_secure_boot(); - if (r < 0) - log_warning_errno(r, "Failed to query secure boot status: %m"); - else - printf(" Secure Boot: %s\n", r ? "enabled" : "disabled"); + read_loader_efi_var("LoaderFirmwareType", &fw_type); + read_loader_efi_var("LoaderFirmwareInfo", &fw_info); + read_loader_efi_var("LoaderInfo", &loader); + read_loader_efi_var("LoaderImageIdentifier", &loader_path); - r = is_efi_secure_boot_setup_mode(); - if (r < 0) - log_warning_errno(r, "Failed to query secure boot mode: %m"); - else - printf(" Setup Mode: %s\n", r ? "setup" : "user"); - printf("\n"); - - printf("Loader:\n"); - printf(" Product: %s\n", strna(loader)); - if (!sd_id128_equal(loader_part_uuid, SD_ID128_NULL)) - printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", - SD_ID128_FORMAT_VAL(loader_part_uuid)); - else - printf(" Partition: n/a\n"); - printf(" File: %s%s\n", special_glyph(TREE_RIGHT), strna(loader_path)); - printf("\n"); - } else - printf("System:\n Not booted with EFI\n"); - - r = status_binaries(arg_path, uuid); + if (loader_path) + efi_tilt_backslashes(loader_path); + + r = efi_loader_get_device_part_uuid(&loader_part_uuid); + if (r < 0 && r != -ENOENT) + log_warning_errno(r, "Failed to read EFI variable LoaderDevicePartUUID: %m"); + + printf("System:\n"); + printf(" Firmware: %s (%s)\n", strna(fw_type), strna(fw_info)); + + r = is_efi_secure_boot(); if (r < 0) - return r; + log_warning_errno(r, "Failed to query secure boot status: %m"); + else + printf(" Secure Boot: %s\n", r ? "enabled" : "disabled"); - if (arg_touch_variables) - r = status_variables(); - break; - } + r = is_efi_secure_boot_setup_mode(); + if (r < 0) + log_warning_errno(r, "Failed to query secure boot mode: %m"); + else + printf(" Setup Mode: %s\n", r ? "setup" : "user"); + printf("\n"); + + printf("Loader:\n"); + printf(" Product: %s\n", strna(loader)); + if (!sd_id128_equal(loader_part_uuid, SD_ID128_NULL)) + printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", + SD_ID128_FORMAT_VAL(loader_part_uuid)); + else + printf(" Partition: n/a\n"); + printf(" File: %s%s\n", special_glyph(TREE_RIGHT), strna(loader_path)); + printf("\n"); + } else + printf("System:\n Not booted with EFI\n"); + + r = status_binaries(arg_path, uuid); + if (r < 0) + return r; + + if (arg_touch_variables) + r = status_variables(); + + return r; +} - case ACTION_INSTALL: - case ACTION_UPDATE: - umask(0002); +static int verb_install(int argc, char *argv[], void *userdata) { - r = install_binaries(arg_path, arg_action == ACTION_INSTALL); + sd_id128_t uuid = SD_ID128_NULL; + uint64_t pstart = 0, psize = 0; + uint32_t part = 0; + bool install; + int r; + + r = must_be_root(); + if (r < 0) + return r; + + r = find_esp(&part, &pstart, &psize, &uuid); + if (r < 0) + return r; + + install = streq(argv[0], "install"); + + RUN_WITH_UMASK(0002) { + r = install_binaries(arg_path, install); if (r < 0) return r; - if (arg_action == ACTION_INSTALL) { + if (install) { r = install_loader_config(arg_path); if (r < 0) return r; } + } + + if (arg_touch_variables) + r = install_variables(arg_path, + part, pstart, psize, uuid, + "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", + install); - if (arg_touch_variables) - r = install_variables(arg_path, - part, pstart, psize, uuid, - "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", - arg_action == ACTION_INSTALL); - break; + return r; +} - case ACTION_REMOVE: - r = remove_binaries(arg_path); +static int verb_remove(int argc, char *argv[], void *userdata) { + sd_id128_t uuid = SD_ID128_NULL; + int r; - if (arg_touch_variables) { - q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true); - if (q < 0 && r == 0) - r = q; - } - break; + r = must_be_root(); + if (r < 0) + return r; + + r = find_esp(NULL, NULL, NULL, &uuid); + if (r < 0) + return r; + + r = remove_binaries(arg_path); + + if (arg_touch_variables) { + int q; + + q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true); + if (q < 0 && r == 0) + r = q; } return r; } +static int bootctl_main(int argc, char *argv[]) { + + static const Verb verbs[] = { + { "help", VERB_ANY, VERB_ANY, 0, help }, + { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status }, + { "install", VERB_ANY, 1, 0, verb_install }, + { "update", VERB_ANY, 1, 0, verb_install }, + { "remove", VERB_ANY, 1, 0, verb_remove }, + {} + }; + + return dispatch_verb(argc, argv, verbs, NULL); +} + int main(int argc, char *argv[]) { int r; log_parse_environment(); log_open(); + /* If we run in a container, automatically turn of EFI file system access */ + if (detect_container() > 0) + arg_touch_variables = false; + r = parse_argv(argc, argv); if (r <= 0) goto finish; @@ -1137,5 +1234,6 @@ int main(int argc, char *argv[]) { r = bootctl_main(argc, argv); finish: + free(arg_path); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } -- cgit v1.2.3 From 181ccb43ead439059d4302252870a77ff4441688 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 Jul 2016 20:26:24 +0200 Subject: bootctl: make use of STRV_FOREACH() where we can --- src/boot/bootctl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 8b2407f145..d9c3897392 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -628,11 +628,11 @@ static const char *efi_subdirs[] = { }; static int create_dirs(const char *esp_path) { + const char **i; int r; - unsigned i; - for (i = 0; i < ELEMENTSOF(efi_subdirs); i++) { - r = mkdir_one(esp_path, efi_subdirs[i]); + STRV_FOREACH(i, efi_subdirs) { + r = mkdir_one(esp_path, *i); if (r < 0) return r; } @@ -836,7 +836,7 @@ static int install_variables(const char *esp_path, "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" : "Failed to determine current boot order: %m"); - if (first || r == false) { + if (first || r == 0) { r = efi_add_boot_option(slot, "Linux Boot Manager", part, pstart, psize, uuid, path); -- cgit v1.2.3 From 617eec3452b6c906615babf61adf51275e04d41d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 Jul 2016 20:29:21 +0200 Subject: man: use in bootctl man page where appropriate --- man/bootctl.xml | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/man/bootctl.xml b/man/bootctl.xml index 82c162ee70..3548ad12fb 100644 --- a/man/bootctl.xml +++ b/man/bootctl.xml @@ -71,19 +71,14 @@ currently installed versions of the boot loader binaries and all current EFI boot variables. - bootctl update updates all installed - versions of systemd-boot, if the current version is newer than the - version installed in the EFI system partition. This also includes - the EFI default/fallback loader at /EFI/BOOT/BOOT*.EFI. A - systemd-boot entry in the EFI boot variables is created if there - is no current entry. The created entry will be added to the end of - the boot order list. - - bootctl install installs systemd-boot into - the EFI system partition. A copy of systemd-boot will be stored as - the EFI default/fallback loader at /EFI/BOOT/BOOT*.EFI. A systemd-boot - entry in the EFI boot variables is created and added to the top - of the boot order list. + bootctl update updates all installed versions of systemd-boot, if the current version is + newer than the version installed in the EFI system partition. This also includes the EFI default/fallback loader at + /EFI/BOOT/BOOT*.EFI. A systemd-boot entry in the EFI boot variables is created if there is no + current entry. The created entry will be added to the end of the boot order list. + + bootctl install installs systemd-boot into the EFI system partition. A copy of + systemd-boot will be stored as the EFI default/fallback loader at /EFI/BOOT/BOOT*.EFI. A + systemd-boot entry in the EFI boot variables is created and added to the top of the boot order list. bootctl remove removes all installed versions of systemd-boot from the EFI system partition, and removes -- cgit v1.2.3 From fdc1af0a8ac1801a32192d2ad5d7e1245cecc34b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 Jul 2016 20:30:16 +0200 Subject: man: add missing whitespace to bootctl command lines --- man/bootctl.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/man/bootctl.xml b/man/bootctl.xml index 3548ad12fb..e2575a4751 100644 --- a/man/bootctl.xml +++ b/man/bootctl.xml @@ -47,16 +47,16 @@ - bootctl OPTIONSstatus + bootctl OPTIONS status - bootctl OPTIONSupdate + bootctl OPTIONS update - bootctl OPTIONSinstall + bootctl OPTIONS install - bootctl OPTIONSremove + bootctl OPTIONS remove -- cgit v1.2.3 From 846b8fc30d52a63dd6a010e497aa80164fea31d8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 Jul 2016 20:43:54 +0200 Subject: bootctl: move toupper() implementation to string-util.h We already have tolower() calls there, hence let's unify this at one place. Also, update the code to only use ASCII operations, so that we don't end up being locale dependant. --- src/basic/string-util.c | 19 +++++++++++++++++++ src/basic/string-util.h | 3 +++ src/boot/bootctl.c | 11 +---------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 293a15f9c0..e9856b90d3 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -323,6 +323,14 @@ char ascii_tolower(char x) { return x; } +char ascii_toupper(char x) { + + if (x >= 'a' && x <= 'z') + return x - 'a' + 'A'; + + return x; +} + char *ascii_strlower(char *t) { char *p; @@ -334,6 +342,17 @@ char *ascii_strlower(char *t) { return t; } +char *ascii_strupper(char *t) { + char *p; + + assert(t); + + for (p = t; *p; p++) + *p = ascii_toupper(*p); + + return t; +} + char *ascii_strlower_n(char *t, size_t n) { size_t i; diff --git a/src/basic/string-util.h b/src/basic/string-util.h index 1209e1e2e1..b75aba63c2 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -137,6 +137,9 @@ char ascii_tolower(char x); char *ascii_strlower(char *s); char *ascii_strlower_n(char *s, size_t n); +char ascii_toupper(char x); +char *ascii_strupper(char *s); + int ascii_strcasecmp_n(const char *a, const char *b, size_t n); int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m); diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index d9c3897392..5c3e7a04a0 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -597,15 +597,6 @@ error: return r; } -static char* strupper(char *s) { - char *p; - - for (p = s; *p; p++) - *p = toupper(*p); - - return s; -} - static int mkdir_one(const char *prefix, const char *suffix) { char *p; @@ -654,7 +645,7 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) { /* Create the EFI default boot loader name (specified for removable devices) */ v = strjoina(esp_path, "/EFI/BOOT/BOOT", name + strlen("systemd-boot")); - strupper(strrchr(v, '/') + 1); + ascii_strupper(strrchr(v, '/') + 1); k = copy_file(p, v, force); if (k < 0 && r == 0) -- cgit v1.2.3 From 5fa6c13c7bd6df488c0585608688a5feb8a03d5d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 Jul 2016 20:45:10 +0200 Subject: bootctl: use F_TYPE_EQUAL() to compare statfs' .f_type field After all, the field is kinda borked. --- src/boot/bootctl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 5c3e7a04a0..9a24ecd6b2 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,7 @@ #include "util.h" #include "verbs.h" #include "virt.h" +#include "stat-util.h" static char *arg_path = NULL; static bool arg_touch_variables = true; @@ -83,7 +85,7 @@ static int verify_esp( return log_error_errno(errno, "Failed to check file system type of \"%s\": %m", p); } - if (sfs.f_type != 0x4d44) { + if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC)) { if (searching) return -EADDRNOTAVAIL; -- cgit v1.2.3 From db6d9faeb0179b15cf29e87bd20d29d6210142ef Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 Jul 2016 20:46:17 +0200 Subject: bootctl: clean up get_file_version() Make sure that we always initialize the return parameter on success, and that all errors result in an error message, not just some. --- src/boot/bootctl.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 9a24ecd6b2..ff14cff166 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -275,14 +275,16 @@ static int get_file_version(int fd, char **v) { assert(v); if (fstat(fd, &st) < 0) - return -errno; + return log_error_errno(errno, "Failed to stat EFI binary: %m"); - if (st.st_size < 27) + if (st.st_size < 27) { + *v = NULL; return 0; + } buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (buf == MAP_FAILED) - return -errno; + return log_error_errno(errno, "Failed to memory map EFI binary: %m"); s = memmem(buf, st.st_size - 8, "#### LoaderInfo: ", 17); if (!s) @@ -304,7 +306,7 @@ static int get_file_version(int fd, char **v) { r = 1; finish: - munmap(buf, st.st_size); + (void) munmap(buf, st.st_size); *v = x; return r; } -- cgit v1.2.3 From f939cff715775553bb1ec903454acf0d95bf5023 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 Jul 2016 20:47:09 +0200 Subject: bootctl: various coding style updates --- src/boot/bootctl.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index ff14cff166..9cbea5041e 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -416,9 +416,10 @@ static int status_variables(void) { n_options = efi_get_boot_options(&options); if (n_options == -ENOENT) - return log_error_errno(ENOENT, "Failed to access EFI variables, efivarfs" + return log_error_errno(n_options, + "Failed to access EFI variables, efivarfs" " needs to be available at /sys/firmware/efi/efivars/."); - else if (n_options < 0) + if (n_options < 0) return log_error_errno(n_options, "Failed to read EFI boot entries: %m"); n_order = efi_get_boot_order(&order); @@ -438,11 +439,9 @@ static int status_variables(void) { for (j = 0; j < n_order; j++) if (options[i] == order[j]) - goto next; + continue; print_efi_option(options[i], false); - next: - continue; } return 0; @@ -820,8 +819,8 @@ static int install_variables(const char *esp_path, if (access(p, F_OK) < 0) { if (errno == ENOENT) return 0; - else - return log_error_errno(errno, "Cannot access \"%s\": %m", p); + + return log_error_errno(errno, "Cannot access \"%s\": %m", p); } r = find_slot(uuid, path, &slot); @@ -941,8 +940,8 @@ static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) { if (in_order) return remove_from_order(slot); - else - return 0; + + return 0; } static int install_loader_config(const char *esp_path) { -- cgit v1.2.3 From d5ff6d6d341d56de725633a38e23778e58140b9e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 Jul 2016 20:48:22 +0200 Subject: bootctl: modernize install_loader_config() let's the proper APIs to read the machine ID, and properly check for all errors. --- src/boot/bootctl.c | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 9cbea5041e..f0a88ab3ac 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -945,36 +945,28 @@ static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) { } static int install_loader_config(const char *esp_path) { - char *p; - char line[64]; - char *machine = NULL; - _cleanup_fclose_ FILE *f = NULL, *g = NULL; - f = fopen("/etc/machine-id", "re"); - if (!f) - return errno == ENOENT ? 0 : -errno; + _cleanup_fclose_ FILE *f = NULL; + char machine_string[SD_ID128_STRING_MAX]; + sd_id128_t machine_id; + const char *p; + int r; - if (fgets(line, sizeof(line), f) != NULL) { - char *s; + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return log_error_errno(r, "Failed to get machine did: %m"); - s = strchr(line, '\n'); - if (s) - s[0] = '\0'; - if (strlen(line) == 32) - machine = line; - } + p = strjoina(esp_path, "/loader/loader.conf"); + f = fopen(p, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to open loader.conf for writing: %m"); - if (!machine) - return -ESRCH; + fprintf(f, "#timeout 3\n"); + fprintf(f, "default %s-*\n", sd_id128_to_string(machine_id, machine_string)); - p = strjoina(esp_path, "/loader/loader.conf"); - g = fopen(p, "wxe"); - if (g) { - fprintf(g, "#timeout 3\n"); - fprintf(g, "default %s-*\n", machine); - if (ferror(g)) - return log_error_errno(EIO, "Failed to write \"%s\": %m", p); - } + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write \"%s\": %m", p); return 0; } -- cgit v1.2.3 From 5b8411a2aa2d986a0c7e995c57d01ed6e56e74da Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 Jul 2016 21:08:57 +0200 Subject: kernel-install: when searching for location to place kernel consider /efi With this change kernel-install will now first look for an existing kernel installation in /efi, /boot and /boot/efi. If none is found, /efi is used if it is a mount point, otherwise /boot/efi if it is one. If nothing of that worked /boot is used without further checking. This means /boot should be the default unless something was installed before or something else was explicitly mounted. --- src/kernel-install/kernel-install | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/kernel-install/kernel-install b/src/kernel-install/kernel-install index 1159dc384d..c66bcfc092 100644 --- a/src/kernel-install/kernel-install +++ b/src/kernel-install/kernel-install @@ -86,10 +86,15 @@ if [[ ! $COMMAND ]] || [[ ! $KERNEL_VERSION ]]; then exit 1 fi -if [[ -d /boot/loader/entries ]] || [[ -d /boot/$MACHINE_ID ]]; then +if [[ -d /efi/loader/entries ]] || [[ -d /efi/$MACHINE_ID ]]; then + BOOT_DIR_ABS="/efi/$MACHINE_ID/$KERNEL_VERSION" +elif [[ -d /boot/loader/entries ]] || [[ -d /boot/$MACHINE_ID ]]; then BOOT_DIR_ABS="/boot/$MACHINE_ID/$KERNEL_VERSION" -elif [[ -d /boot/efi/loader/entries ]] || [[ -d /boot/efi/$MACHINE_ID ]] \ - || mountpoint -q /boot/efi; then +elif [[ -d /boot/efi/loader/entries ]] || [[ -d /boot/efi/$MACHINE_ID ]]; then + BOOT_DIR_ABS="/boot/efi/$MACHINE_ID/$KERNEL_VERSION" +elif mountpoint -q /efi; then + BOOT_DIR_ABS="/efi/$MACHINE_ID/$KERNEL_VERSION" +elif mountpoint -q /boot/efi; then BOOT_DIR_ABS="/boot/efi/$MACHINE_ID/$KERNEL_VERSION" else BOOT_DIR_ABS="/boot/$MACHINE_ID/$KERNEL_VERSION" -- cgit v1.2.3 From 7ba25ab5616339a8340117b85cbecd061c52d8cc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 Jul 2016 11:30:02 +0200 Subject: gpt-generator: relax EFI check a bit Previously, we'd not mount the ESP except on EFI boots, and only when the ESP used for booting matches the ESP we found. With this change on non-EFI boots we'll mount a discovered ESP anyway, and on EFI boots we'll only mount it if it matches the ESP we booted from. --- src/gpt-auto-generator/gpt-auto-generator.c | 107 ++++++++++++++-------------- 1 file changed, 53 insertions(+), 54 deletions(-) diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index dede86eabf..e4c913f2c4 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -450,19 +450,11 @@ static int add_automount( } static int add_boot(const char *what) { - _cleanup_blkid_free_probe_ blkid_probe b = NULL; - const char *fstype = NULL, *uuid = NULL; - sd_id128_t id, type_id; const char *esp; int r; assert(what); - if (!is_efi_boot()) { - log_debug("Not an EFI boot, ignoring the ESP."); - return 0; - } - if (in_initrd()) { log_debug("In initrd, ignoring the ESP."); return 0; @@ -487,58 +479,67 @@ static int add_boot(const char *what) { return 0; } - r = efi_loader_get_device_part_uuid(&id); - if (r == -ENOENT) { - log_debug("EFI loader partition unknown."); - return 0; - } - if (r < 0) { - log_error_errno(r, "Failed to read ESP partition UUID: %m"); - return r; - } + if (is_efi_boot()) { + _cleanup_blkid_free_probe_ blkid_probe b = NULL; + const char *fstype = NULL, *uuid_string = NULL; + sd_id128_t loader_uuid, part_uuid; - errno = 0; - b = blkid_new_probe_from_filename(what); - if (!b) { - if (errno == 0) - return log_oom(); - return log_error_errno(errno, "Failed to allocate prober: %m"); - } + /* If this is an EFI boot, be extra careful, and only mount the ESP if it was the ESP used for booting. */ - blkid_probe_enable_partitions(b, 1); - blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); + r = efi_loader_get_device_part_uuid(&loader_uuid); + if (r == -ENOENT) { + log_debug("EFI loader partition unknown."); + return 0; + } + if (r < 0) { + log_error_errno(r, "Failed to read ESP partition UUID: %m"); + return r; + } - errno = 0; - r = blkid_do_safeprobe(b); - if (r == -2 || r == 1) /* no result or uncertain */ - return 0; - else if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what); + errno = 0; + b = blkid_new_probe_from_filename(what); + if (!b) { + if (errno == 0) + return log_oom(); + return log_error_errno(errno, "Failed to allocate prober: %m"); + } - (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); - if (!streq_ptr(fstype, "vfat")) { - log_debug("Partition for %s is not a FAT filesystem, ignoring.", esp); - return 0; - } + blkid_probe_enable_partitions(b, 1); + blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid, NULL); - if (r != 0) { - log_debug_errno(errno, "Partition for %s does not have a UUID, ignoring.", esp); - return 0; - } + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -2 || r == 1) /* no result or uncertain */ + return 0; + else if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what); - if (sd_id128_from_string(uuid, &type_id) < 0) { - log_debug("Partition for %s does not have a valid UUID, ignoring.", esp); - return 0; - } + (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); + if (!streq_ptr(fstype, "vfat")) { + log_debug("Partition for %s is not a FAT filesystem, ignoring.", esp); + return 0; + } - if (!sd_id128_equal(type_id, id)) { - log_debug("Partition for %s does not appear to be the partition we are booted from.", esp); - return 0; - } + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid_string, NULL); + if (r != 0) { + log_debug_errno(errno, "Partition for %s does not have a UUID, ignoring.", esp); + return 0; + } - r = add_automount("boot", + if (sd_id128_from_string(uuid_string, &part_uuid) < 0) { + log_debug("Partition for %s does not have a valid UUID, ignoring.", esp); + return 0; + } + + if (!sd_id128_equal(part_uuid, loader_uuid)) { + log_debug("Partition for %s does not appear to be the partition we are booted from.", esp); + return 0; + } + } else + log_debug("Not an EFI boot, skipping ESP check."); + + return add_automount("boot", what, esp, "vfat", @@ -546,8 +547,6 @@ static int add_boot(const char *what) { "umask=0077", "EFI System Partition Automount", 120 * USEC_PER_SEC); - - return r; } #else static int add_boot(const char *what) { -- cgit v1.2.3 From e4631b48e17e63a3859456df639482063a0276fd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 14 Jul 2016 12:23:39 +0200 Subject: sysusers: move various user credential validity checks to src/basic/ This way we can reuse them for validating User=/Group= settings in unit files (to be added in a later commit). Also, add some tests for them. --- src/basic/user-util.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++ src/basic/user-util.h | 5 +++ src/sysusers/sysusers.c | 75 -------------------------------------- src/test/test-user-util.c | 87 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 185 insertions(+), 75 deletions(-) diff --git a/src/basic/user-util.c b/src/basic/user-util.c index e9d668ddfc..122d9a0c7c 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "missing.h" #include "alloc-util.h" @@ -39,6 +40,7 @@ #include "path-util.h" #include "string-util.h" #include "user-util.h" +#include "utf8.h" bool uid_is_valid(uid_t uid) { @@ -479,3 +481,94 @@ int take_etc_passwd_lock(const char *root) { return fd; } + +bool valid_user_group_name(const char *u) { + const char *i; + long sz; + + /* Checks if the specified name is a valid user/group name. */ + + if (isempty(u)) + return false; + + if (!(u[0] >= 'a' && u[0] <= 'z') && + !(u[0] >= 'A' && u[0] <= 'Z') && + u[0] != '_') + return false; + + for (i = u+1; *i; i++) { + if (!(*i >= 'a' && *i <= 'z') && + !(*i >= 'A' && *i <= 'Z') && + !(*i >= '0' && *i <= '9') && + *i != '_' && + *i != '-') + return false; + } + + sz = sysconf(_SC_LOGIN_NAME_MAX); + assert_se(sz > 0); + + if ((size_t) (i-u) > (size_t) sz) + return false; + + if ((size_t) (i-u) > UT_NAMESIZE - 1) + return false; + + return true; +} + +bool valid_user_group_name_or_id(const char *u) { + + /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right + * range, and not the invalid user ids. */ + + if (isempty(u)) + return false; + + if (valid_user_group_name(u)) + return true; + + return parse_uid(u, NULL) >= 0; +} + +bool valid_gecos(const char *d) { + + if (!d) + return false; + + if (!utf8_is_valid(d)) + return false; + + if (string_has_cc(d, NULL)) + return false; + + /* Colons are used as field separators, and hence not OK */ + if (strchr(d, ':')) + return false; + + return true; +} + +bool valid_home(const char *p) { + + if (isempty(p)) + return false; + + if (!utf8_is_valid(p)) + return false; + + if (string_has_cc(p, NULL)) + return false; + + if (!path_is_absolute(p)) + return false; + + if (!path_is_safe(p)) + return false; + + /* Colons are used as field separators, and hence not OK */ + if (strchr(p, ':')) + return false; + + return true; +} diff --git a/src/basic/user-util.h b/src/basic/user-util.h index 8026eca3f4..36f71fb004 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -68,3 +68,8 @@ int take_etc_passwd_lock(const char *root); static inline bool userns_supported(void) { return access("/proc/self/uid_map", F_OK) >= 0; } + +bool valid_user_group_name(const char *u); +bool valid_user_group_name_or_id(const char *u); +bool valid_gecos(const char *d); +bool valid_home(const char *p); diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 787d68a009..5d72493725 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -1299,81 +1299,6 @@ static bool item_equal(Item *a, Item *b) { return true; } -static bool valid_user_group_name(const char *u) { - const char *i; - long sz; - - if (isempty(u)) - return false; - - if (!(u[0] >= 'a' && u[0] <= 'z') && - !(u[0] >= 'A' && u[0] <= 'Z') && - u[0] != '_') - return false; - - for (i = u+1; *i; i++) { - if (!(*i >= 'a' && *i <= 'z') && - !(*i >= 'A' && *i <= 'Z') && - !(*i >= '0' && *i <= '9') && - *i != '_' && - *i != '-') - return false; - } - - sz = sysconf(_SC_LOGIN_NAME_MAX); - assert_se(sz > 0); - - if ((size_t) (i-u) > (size_t) sz) - return false; - - if ((size_t) (i-u) > UT_NAMESIZE - 1) - return false; - - return true; -} - -static bool valid_gecos(const char *d) { - - if (!d) - return false; - - if (!utf8_is_valid(d)) - return false; - - if (string_has_cc(d, NULL)) - return false; - - /* Colons are used as field separators, and hence not OK */ - if (strchr(d, ':')) - return false; - - return true; -} - -static bool valid_home(const char *p) { - - if (isempty(p)) - return false; - - if (!utf8_is_valid(p)) - return false; - - if (string_has_cc(p, NULL)) - return false; - - if (!path_is_absolute(p)) - return false; - - if (!path_is_safe(p)) - return false; - - /* Colons are used as field separators, and hence not OK */ - if (strchr(p, ':')) - return false; - - return true; -} - static int parse_line(const char *fname, unsigned line, const char *buffer) { static const Specifier specifier_table[] = { diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c index 8d1ec19f17..2a344a9f93 100644 --- a/src/test/test-user-util.c +++ b/src/test/test-user-util.c @@ -61,6 +61,88 @@ static void test_uid_ptr(void) { assert_se(PTR_TO_UID(UID_TO_PTR(1000)) == 1000); } +static void test_valid_user_group_name(void) { + assert_se(!valid_user_group_name(NULL)); + assert_se(!valid_user_group_name("")); + assert_se(!valid_user_group_name("1")); + assert_se(!valid_user_group_name("65535")); + assert_se(!valid_user_group_name("-1")); + assert_se(!valid_user_group_name("-kkk")); + assert_se(!valid_user_group_name("rööt")); + assert_se(!valid_user_group_name(".")); + assert_se(!valid_user_group_name("eff.eff")); + assert_se(!valid_user_group_name("foo\nbar")); + assert_se(!valid_user_group_name("0123456789012345678901234567890123456789")); + assert_se(!valid_user_group_name_or_id("aaa:bbb")); + + assert_se(valid_user_group_name("root")); + assert_se(valid_user_group_name("lennart")); + assert_se(valid_user_group_name("LENNART")); + assert_se(valid_user_group_name("_kkk")); + assert_se(valid_user_group_name("kkk-")); + assert_se(valid_user_group_name("kk-k")); + + assert_se(valid_user_group_name("some5")); + assert_se(!valid_user_group_name("5some")); + assert_se(valid_user_group_name("INNER5NUMBER")); +} + +static void test_valid_user_group_name_or_id(void) { + assert_se(!valid_user_group_name_or_id(NULL)); + assert_se(!valid_user_group_name_or_id("")); + assert_se(valid_user_group_name_or_id("0")); + assert_se(valid_user_group_name_or_id("1")); + assert_se(valid_user_group_name_or_id("65534")); + assert_se(!valid_user_group_name_or_id("65535")); + assert_se(valid_user_group_name_or_id("65536")); + assert_se(!valid_user_group_name_or_id("-1")); + assert_se(!valid_user_group_name_or_id("-kkk")); + assert_se(!valid_user_group_name_or_id("rööt")); + assert_se(!valid_user_group_name_or_id(".")); + assert_se(!valid_user_group_name_or_id("eff.eff")); + assert_se(!valid_user_group_name_or_id("foo\nbar")); + assert_se(!valid_user_group_name_or_id("0123456789012345678901234567890123456789")); + assert_se(!valid_user_group_name_or_id("aaa:bbb")); + + assert_se(valid_user_group_name_or_id("root")); + assert_se(valid_user_group_name_or_id("lennart")); + assert_se(valid_user_group_name_or_id("LENNART")); + assert_se(valid_user_group_name_or_id("_kkk")); + assert_se(valid_user_group_name_or_id("kkk-")); + assert_se(valid_user_group_name_or_id("kk-k")); + + assert_se(valid_user_group_name_or_id("some5")); + assert_se(!valid_user_group_name_or_id("5some")); + assert_se(valid_user_group_name_or_id("INNER5NUMBER")); +} + +static void test_valid_gecos(void) { + + assert_se(!valid_gecos(NULL)); + assert_se(valid_gecos("")); + assert_se(valid_gecos("test")); + assert_se(valid_gecos("Ümläüt")); + assert_se(!valid_gecos("In\nvalid")); + assert_se(!valid_gecos("In:valid")); +} + +static void test_valid_home(void) { + + assert_se(!valid_home(NULL)); + assert_se(!valid_home("")); + assert_se(!valid_home(".")); + assert_se(!valid_home("/home/..")); + assert_se(!valid_home("/home/../")); + assert_se(!valid_home("/home\n/foo")); + assert_se(!valid_home("./piep")); + assert_se(!valid_home("piep")); + assert_se(!valid_home("/home/user:lennart")); + + assert_se(valid_home("/")); + assert_se(valid_home("/home")); + assert_se(valid_home("/home/foo")); +} + int main(int argc, char*argv[]) { test_uid_to_name_one(0, "root"); @@ -75,5 +157,10 @@ int main(int argc, char*argv[]) { test_parse_uid(); test_uid_ptr(); + test_valid_user_group_name(); + test_valid_user_group_name_or_id(); + test_valid_gecos(); + test_valid_home(); + return 0; } -- cgit v1.2.3 From 66dccd8d85aac9f029c626aac8d2b7e58d239b47 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 14 Jul 2016 12:28:06 +0200 Subject: core: be stricter when parsing User=/Group= fields Let's verify the validity of the syntax of the user/group names set. --- src/core/load-fragment-gperf.gperf.m4 | 10 +-- src/core/load-fragment.c | 118 ++++++++++++++++++++++++++++++++++ src/core/load-fragment.h | 2 + 3 files changed, 125 insertions(+), 5 deletions(-) diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 6a5c16a000..7f7c2fe185 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -19,9 +19,9 @@ m4_dnl Define the context options only once m4_define(`EXEC_CONTEXT_CONFIG_ITEMS', `$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context) $1.RootDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.root_directory) -$1.User, config_parse_unit_string_printf, 0, offsetof($1, exec_context.user) -$1.Group, config_parse_unit_string_printf, 0, offsetof($1, exec_context.group) -$1.SupplementaryGroups, config_parse_strv, 0, offsetof($1, exec_context.supplementary_groups) +$1.User, config_parse_user_group, 0, offsetof($1, exec_context.user) +$1.Group, config_parse_user_group, 0, offsetof($1, exec_context.group) +$1.SupplementaryGroups, config_parse_user_group_strv, 0, offsetof($1, exec_context.supplementary_groups) $1.Nice, config_parse_exec_nice, 0, offsetof($1, exec_context) $1.OOMScoreAdjust, config_parse_exec_oom_score_adjust, 0, offsetof($1, exec_context) $1.IOSchedulingClass, config_parse_exec_io_class, 0, offsetof($1, exec_context) @@ -285,8 +285,8 @@ Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command) Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command) Socket.TimeoutSec, config_parse_sec, 0, offsetof(Socket, timeout_usec) -Socket.SocketUser, config_parse_unit_string_printf, 0, offsetof(Socket, user) -Socket.SocketGroup, config_parse_unit_string_printf, 0, offsetof(Socket, group) +Socket.SocketUser, config_parse_user_group, 0, offsetof(Socket, user) +Socket.SocketGroup, config_parse_user_group, 0, offsetof(Socket, group) Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode) Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode) Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 782e420e4c..d8aaf4ef40 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -64,6 +64,7 @@ #include "unit-name.h" #include "unit-printf.h" #include "unit.h" +#include "user-util.h" #include "utf8.h" #include "web-util.h" @@ -1763,6 +1764,123 @@ int config_parse_sec_fix_0( return 0; } +int config_parse_user_group( + 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) { + + char **user = data, *n; + Unit *u = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(u); + + if (isempty(rvalue)) + n = NULL; + else { + _cleanup_free_ char *k = NULL; + + r = unit_full_printf(u, rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue); + return 0; + } + + if (!valid_user_group_name_or_id(k)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID, ignoring: %s", k); + return 0; + } + + n = k; + k = NULL; + } + + free(*user); + *user = n; + + return 0; +} + +int config_parse_user_group_strv( + 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) { + + char ***users = data; + Unit *u = userdata; + const char *p; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(u); + + if (isempty(rvalue)) { + char **empty; + + empty = new0(char*, 1); + if (!empty) + return log_oom(); + + strv_free(*users); + *users = empty; + + return 0; + } + + p = rvalue; + for (;;) { + _cleanup_free_ char *word = NULL, *k = NULL; + + r = extract_first_word(&p, &word, WHITESPACE, 0); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); + break; + } + + r = unit_full_printf(u, word, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", word); + continue; + } + + if (!valid_user_group_name_or_id(k)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID, ignoring: %s", k); + continue; + } + + r = strv_push(users, k); + if (r < 0) + return log_oom(); + + k = NULL; + } + + return 0; +} + int config_parse_busname_service( const char *unit, const char *filename, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index b36a2e3a02..213bce55a7 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -111,6 +111,8 @@ int config_parse_exec_utmp_mode(const char *unit, const char *filename, unsigned int config_parse_working_directory(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); int config_parse_fdname(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); int config_parse_sec_fix_0(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); +int config_parse_user_group(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); +int config_parse_user_group_strv(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); /* gperf prototypes */ const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length); -- cgit v1.2.3 From 29206d4619843252c2e04f20dc03c246547600a2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 14 Jul 2016 12:37:28 +0200 Subject: core: add a concept of "dynamic" user ids, that are allocated as long as a service is running This adds a new boolean setting DynamicUser= to service files. If set, a new user will be allocated dynamically when the unit is started, and released when it is stopped. The user ID is allocated from the range 61184..65519. The user will not be added to /etc/passwd (but an NSS module to be added later should make it show up in getent passwd). For now, care should be taken that the service writes no files to disk, since this might result in files owned by UIDs that might get assigned dynamically to a different service later on. Later patches will tighten sandboxing in order to ensure that this cannot happen, except for a few selected directories. A simple way to test this is: systemd-run -p DynamicUser=1 /bin/sleep 99999 --- Makefile.am | 2 + man/systemd.exec.xml | 36 +- src/basic/socket-util.c | 14 + src/basic/socket-util.h | 2 + src/core/dbus-execute.c | 6 +- src/core/dbus-manager.c | 60 ++- src/core/dynamic-user.c | 763 ++++++++++++++++++++++++++++++ src/core/dynamic-user.h | 66 +++ src/core/execute.c | 75 ++- src/core/execute.h | 6 + src/core/load-fragment-gperf.gperf.m4 | 1 + src/core/manager.c | 18 +- src/core/manager.h | 3 + src/core/mount.c | 15 +- src/core/mount.h | 3 +- src/core/service.c | 16 +- src/core/service.h | 1 + src/core/socket.c | 15 +- src/core/socket.h | 2 + src/core/swap.c | 15 +- src/core/swap.h | 1 + src/core/unit.c | 63 +++ src/core/unit.h | 5 + src/libsystemd/sd-bus/bus-common-errors.c | 1 + src/libsystemd/sd-bus/bus-common-errors.h | 1 + src/shared/bus-unit-util.c | 11 +- 26 files changed, 1166 insertions(+), 35 deletions(-) create mode 100644 src/core/dynamic-user.c create mode 100644 src/core/dynamic-user.h diff --git a/Makefile.am b/Makefile.am index c9fb4917ad..3d5ce1e2c3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1197,6 +1197,8 @@ libcore_la_SOURCES = \ src/core/load-dropin.h \ src/core/execute.c \ src/core/execute.h \ + src/core/dynamic-user.c \ + src/core/dynamic-user.h \ src/core/kill.c \ src/core/kill.h \ src/core/dbus.c \ diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 49fea98a95..bfb4101d99 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -143,10 +143,38 @@ User= Group= - Sets the Unix user or group that the processes - are executed as, respectively. Takes a single user or group - name or ID as argument. If no group is set, the default group - of the user is chosen. These do not affect commands prefixed with !. + Set the UNIX user or group that the processes are executed as, respectively. Takes a single + user or group name, or numeric ID as argument. If no group is set, the default group of the user is used. This + setting does not affect commands whose command line is prefixed with !. + + + + DynamicUser= + + Takes a boolean parameter. If set, a UNIX user and group pair is allocated dynamically when the + unit is started, and released as soon as it is stopped. The user and group will not be added to + /etc/passwd or /etc/group, but are managed transiently during + runtime. The nss-systemd8 + glibc NSS module provides integration of these dynamic users/groups into the system's user and group + databases. The user and group name to use may be configured via User= and + Group= (see above). If these options are not used and dynamic user/group allocation is + enabled for a unit, the name of the dynamic user/group is implicitly derived from the unit name. If the unit + name without the type suffix qualifies as valid user name it is used directly, otherwise a name incorporating a + hash of it is used. If a statically allocated user or group of the configured name already exists, it is used + and no dynamic user/group is allocated. Dynamic users/groups are allocated from the UID/GID range + 61184…65519. It is recommended to avoid this range for regular system or login users. At any point in time + each UID/GID from this range is only assigned to zero or one dynamically allocated users/groups in + use. However, UID/GIDs are recycled after a unit is terminated. Care should be taken that any processes running + as part of a unit for which dynamic users/groups are enabled do not leave files or directories owned by these + users/groups around, as a different unit might get the same UID/GID assigned later on, and thus gain access to + these files or directories. If DynamicUser= is enabled, PrivateTmp= is + implied. This ensures that the lifetime of temporary files created by the executed processes is bound to the + runtime of the service, and hence the lifetime of the dynamic user/group. Since /tmp and + /var/tmp are usually the only world-writable directories on a system this ensures that a + unit making use of dynamic user/group allocation cannot leave files around after unit termination. Use + RuntimeDirectory= (see below) in order to assign a writable runtime directory to a service, + owned by the dynamic user/group and removed automatically when the unit is terminated. Defaults to + off. diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 385c3e4df3..6093e47172 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -1046,3 +1046,17 @@ int flush_accept(int fd) { close(cfd); } } + +struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length) { + struct cmsghdr *cmsg; + + assert(mh); + + CMSG_FOREACH(cmsg, mh) + if (cmsg->cmsg_level == level && + cmsg->cmsg_type == type && + (length == (socklen_t) -1 || length == cmsg->cmsg_len)) + return cmsg; + + return NULL; +} diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index e9230e4a9f..2536b085f9 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -142,6 +142,8 @@ int flush_accept(int fd); #define CMSG_FOREACH(cmsg, mh) \ for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg))) +struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length); + /* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */ #define SOCKADDR_UN_LEN(sa) \ ({ \ diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 307c3d8e7a..a6896c6e6c 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -693,6 +693,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("AmbientCapabilities", "t", property_get_ambient_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DynamicUser", "b", bus_property_get_bool, offsetof(ExecContext, dynamic_user), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PAMName", "s", NULL, offsetof(ExecContext, pam_name), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ReadWriteDirectories", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), @@ -1061,7 +1062,8 @@ int bus_exec_context_set_transient_property( } else if (STR_IN_SET(name, "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "PrivateTmp", "PrivateDevices", "PrivateNetwork", - "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute", "RestrictRealtime")) { + "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute", + "RestrictRealtime", "DynamicUser")) { int b; r = sd_bus_message_read(message, "b", &b); @@ -1089,6 +1091,8 @@ int bus_exec_context_set_transient_property( c->memory_deny_write_execute = b; else if (streq(name, "RestrictRealtime")) c->restrict_realtime = b; + else if (streq(name, "DynamicUser")) + c->dynamic_user = b; unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, yes_no(b)); } diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index d05968bd65..ef05a75a8b 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -43,6 +43,7 @@ #include "string-util.h" #include "strv.h" #include "syslog-util.h" +#include "user-util.h" #include "virt.h" #include "watchdog.h" @@ -1511,8 +1512,8 @@ static int method_unset_and_set_environment(sd_bus_message *message, void *userd } static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_error *error) { - uint8_t code; Manager *m = userdata; + uint8_t code; int r; assert(message); @@ -1534,6 +1535,61 @@ static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_ return sd_bus_reply_method_return(message, NULL); } +static int method_lookup_dynamic_user_by_name(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *name; + uid_t uid; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read_basic(message, 's', &name); + if (r < 0) + return r; + + if (!MANAGER_IS_SYSTEM(m)) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Dynamic users are only supported in the system instance."); + if (!valid_user_group_name(name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name invalid: %s", name); + + r = dynamic_user_lookup_name(m, name, &uid); + if (r == -ESRCH) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_DYNAMIC_USER, "Dynamic user %s does not exist.", name); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, "u", (uint32_t) uid); +} + +static int method_lookup_dynamic_user_by_uid(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *name = NULL; + Manager *m = userdata; + uid_t uid; + int r; + + assert(message); + assert(m); + + assert_cc(sizeof(uid) == sizeof(uint32_t)); + r = sd_bus_message_read_basic(message, 'u', &uid); + if (r < 0) + return r; + + if (!MANAGER_IS_SYSTEM(m)) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Dynamic users are only supported in the system instance."); + if (!uid_is_valid(uid)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User ID invalid: " UID_FMT, uid); + + r = dynamic_user_lookup_uid(m, uid, &name); + if (r == -ESRCH) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_DYNAMIC_USER, "Dynamic user ID " UID_FMT " does not exist.", uid); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, "s", name); +} + static int list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; Manager *m = userdata; @@ -2199,6 +2255,8 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("PresetAllUnitFiles", "sbb", "a(sss)", method_preset_all_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("AddDependencyUnitFiles", "asssbb", "a(sss)", method_add_dependency_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetExitCode", "y", NULL, method_set_exit_code, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("LookupDynamicUserByName", "s", "u", method_lookup_dynamic_user_by_name, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("LookupDynamicUserByUID", "u", "s", method_lookup_dynamic_user_by_uid, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_SIGNAL("UnitNew", "so", 0), SD_BUS_SIGNAL("UnitRemoved", "so", 0), diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c new file mode 100644 index 0000000000..8035bee231 --- /dev/null +++ b/src/core/dynamic-user.c @@ -0,0 +1,763 @@ +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + 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 distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "dynamic-user.h" +#include "fd-util.h" +#include "fs-util.h" +#include "parse-util.h" +#include "random-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "user-util.h" +#include "fileio.h" + +/* Let's pick a UIDs within the 16bit range, so that we are compatible with containers using 16bit user namespacing. At + * least on Fedora normal users are allocated until UID 60000, hence do not allocate from below this. Also stay away + * from the upper end of the range as that is often used for overflow/nobody users. */ +#define UID_PICK_MIN ((uid_t) UINT32_C(0x0000EF00)) +#define UID_PICK_MAX ((uid_t) UINT32_C(0x0000FFEF)) + +/* Takes a value generated randomly or by hashing and turns it into a UID in the right range */ +#define UID_CLAMP_INTO_RANGE(rnd) (((uid_t) (rnd) % (UID_PICK_MAX - UID_PICK_MIN + 1)) + UID_PICK_MIN) + +static DynamicUser* dynamic_user_free(DynamicUser *d) { + if (!d) + return NULL; + + if (d->manager) + (void) hashmap_remove(d->manager->dynamic_users, d->name); + + safe_close_pair(d->storage_socket); + free(d); + + return NULL; +} + +static int dynamic_user_add(Manager *m, const char *name, int storage_socket[2], DynamicUser **ret) { + DynamicUser *d = NULL; + int r; + + assert(m); + assert(name); + assert(storage_socket); + + r = hashmap_ensure_allocated(&m->dynamic_users, &string_hash_ops); + if (r < 0) + return r; + + d = malloc0(offsetof(DynamicUser, name) + strlen(name) + 1); + if (!d) + return -ENOMEM; + + strcpy(d->name, name); + + d->storage_socket[0] = storage_socket[0]; + d->storage_socket[1] = storage_socket[1]; + + r = hashmap_put(m->dynamic_users, d->name, d); + if (r < 0) { + free(d); + return r; + } + + d->manager = m; + + if (ret) + *ret = d; + + return 0; +} + +int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) { + _cleanup_close_pair_ int storage_socket[2] = { -1, -1 }; + DynamicUser *d; + int r; + + assert(m); + assert(name); + + /* Return the DynamicUser structure for a specific user name. Note that this won't actually allocate a UID for + * it, but just prepare the data structure for it. The UID is allocated only on demand, when it's really + * needed, and in the child process we fork off, since allocation involves NSS checks which are not OK to do + * from PID 1. To allow the children and PID 1 share information about allocated UIDs we use an anonymous + * AF_UNIX/SOCK_DGRAM socket (called the "storage socket") that contains at most one datagram with the + * allocated UID number, plus an fd referencing the lock file for the UID + * (i.e. /run/systemd/dynamic-uid/$UID). Why involve the socket pair? So that PID 1 and all its children can + * share the same storage for the UID and lock fd, simply by inheriting the storage socket fds. The socket pair + * may exist in three different states: + * + * a) no datagram stored. This is the initial state. In this case the dynamic user was never realized. + * + * b) a datagram containing a UID stored, but no lock fd attached to it. In this case there was already a + * statically assigned UID by the same name, which we are reusing. + * + * c) a datagram containing a UID stored, and a lock fd is attached to it. In this case we allocated a dynamic + * UID and locked it in the file system, using the lock fd. + * + * As PID 1 and various children might access the socket pair simultaneously, and pop the datagram or push it + * back in any time, we also maintain a lock on the socket pair. Note one peculiarity regarding locking here: + * the UID lock on disk is protected via a BSD file lock (i.e. an fd-bound lock), so that the lock is kept in + * place as long as there's a reference to the fd open. The lock on the storage socket pair however is a POSIX + * file lock (i.e. a process-bound lock), as all users share the same fd of this (after all it is anonymous, + * nobody else could get any access to it except via our own fd) and we want to synchronize access between all + * processes that have access to it. */ + + d = hashmap_get(m->dynamic_users, name); + if (d) { + /* We already have a structure for the dynamic user, let's increase the ref count and reuse it */ + d->n_ref++; + *ret = d; + return 0; + } + + if (!valid_user_group_name_or_id(name)) + return -EINVAL; + + if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, storage_socket) < 0) + return -errno; + + r = dynamic_user_add(m, name, storage_socket, &d); + if (r < 0) + return r; + + storage_socket[0] = storage_socket[1] = -1; + + if (ret) { + d->n_ref++; + *ret = d; + } + + return 1; +} + +static int pick_uid(const char *name, uid_t *ret_uid) { + + static const uint8_t hash_key[] = { + 0x37, 0x53, 0x7e, 0x31, 0xcf, 0xce, 0x48, 0xf5, + 0x8a, 0xbb, 0x39, 0x57, 0x8d, 0xd9, 0xec, 0x59 + }; + + unsigned n_tries = 100; + uid_t candidate; + int r; + + /* A static user by this name does not exist yet. Let's find a free ID then, and use that. We start with a UID + * generated as hash from the user name. */ + candidate = UID_CLAMP_INTO_RANGE(siphash24(name, strlen(name), hash_key)); + + (void) mkdir("/run/systemd/dynamic-uid", 0755); + + for (;;) { + char lock_path[strlen("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1]; + _cleanup_close_ int lock_fd = -1; + ssize_t l; + + if (--n_tries <= 0) /* Give up retrying eventually */ + return -EBUSY; + + if (candidate < UID_PICK_MIN || candidate > UID_PICK_MAX) + goto next; + + xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, candidate); + + for (;;) { + struct stat st; + + lock_fd = open(lock_path, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600); + if (lock_fd < 0) + return -errno; + + r = flock(lock_fd, LOCK_EX|LOCK_NB); /* Try to get a BSD file lock on the UID lock file */ + if (r < 0) { + if (errno == EBUSY || errno == EAGAIN) + goto next; /* already in use */ + + return -errno; + } + + if (fstat(lock_fd, &st) < 0) + return -errno; + if (st.st_nlink > 0) + break; + + /* Oh, bummer, we got got the lock, but the file was unlinked between the time we opened it and + * got the lock. Close it, and try again. */ + lock_fd = safe_close(lock_fd); + } + + /* Some superficial check whether this UID/GID might already be taken by some static user */ + if (getpwuid(candidate) || getgrgid((gid_t) candidate)) { + (void) unlink(lock_path); + goto next; + } + + /* Let's store the user name in the lock file, so that we can use it for looking up the username for a UID */ + l = pwritev(lock_fd, + (struct iovec[2]) { + { .iov_base = (char*) name, .iov_len = strlen(name) }, + { .iov_base = (char[1]) { '\n' }, .iov_len = 1 } + }, 2, 0); + if (l < 0) { + (void) unlink(lock_path); + return -errno; + } + + (void) ftruncate(lock_fd, l); + + *ret_uid = candidate; + r = lock_fd; + lock_fd = -1; + + return r; + + next: + /* Pick another random UID, and see if that works for us. */ + random_bytes(&candidate, sizeof(candidate)); + candidate = UID_CLAMP_INTO_RANGE(candidate); + } +} + +static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) { + uid_t uid = UID_INVALID; + struct iovec iov = { + .iov_base = &uid, + .iov_len = sizeof(uid), + }; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int))]; + } control = {}; + struct msghdr mh = { + .msg_control = &control, + .msg_controllen = sizeof(control), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + struct cmsghdr *cmsg; + + ssize_t k; + int lock_fd = -1; + + assert(d); + assert(ret_uid); + assert(ret_lock_fd); + + /* Read the UID and lock fd that is stored in the storage AF_UNIX socket. This should be called with the lock + * on the socket taken. */ + + k = recvmsg(d->storage_socket[0], &mh, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_CMSG_CLOEXEC); + if (k < 0) + return -errno; + + cmsg = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int))); + if (cmsg) + lock_fd = *(int*) CMSG_DATA(cmsg); + else + cmsg_close_all(&mh); /* just in case... */ + + *ret_uid = uid; + *ret_lock_fd = lock_fd; + + return 0; +} + +static int dynamic_user_push(DynamicUser *d, uid_t uid, int lock_fd) { + struct iovec iov = { + .iov_base = &uid, + .iov_len = sizeof(uid), + }; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int))]; + } control = {}; + struct msghdr mh = { + .msg_control = &control, + .msg_controllen = sizeof(control), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + ssize_t k; + + assert(d); + + /* Store the UID and lock_fd in the storage socket. This should be called with the socket pair lock taken. */ + + if (lock_fd >= 0) { + struct cmsghdr *cmsg; + + cmsg = CMSG_FIRSTHDR(&mh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &lock_fd, sizeof(int)); + + mh.msg_controllen = CMSG_SPACE(sizeof(int)); + } else { + mh.msg_control = NULL; + mh.msg_controllen = 0; + } + + k = sendmsg(d->storage_socket[1], &mh, MSG_DONTWAIT|MSG_NOSIGNAL); + if (k < 0) + return -errno; + + return 0; +} + +static void unlink_uid_lock(int lock_fd, uid_t uid) { + char lock_path[strlen("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1]; + + if (lock_fd < 0) + return; + + xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid); + (void) unlink_noerrno(lock_path); +} + +int dynamic_user_realize(DynamicUser *d, uid_t *ret) { + + _cleanup_close_ int etc_passwd_lock_fd = -1, uid_lock_fd = -1; + uid_t uid = UID_INVALID; + int r; + + assert(d); + + /* Acquire a UID for the user name. This will allocate a UID for the user name if the user doesn't exist + * yet. If it already exists its existing UID/GID will be reused. */ + + if (lockf(d->storage_socket[0], F_LOCK, 0) < 0) + return -errno; + + r = dynamic_user_pop(d, &uid, &uid_lock_fd); + if (r < 0) { + int new_uid_lock_fd; + uid_t new_uid; + + if (r != -EAGAIN) + goto finish; + + /* OK, nothing stored yet, let's try to find something useful. While we are working on this release the + * lock however, so that nobody else blocks on our NSS lookups. */ + (void) lockf(d->storage_socket[0], F_ULOCK, 0); + + /* Let's see if a proper, static user or group by this name exists. Try to take the lock on + * /etc/passwd, if that fails with EROFS then /etc is read-only. In that case it's fine if we don't + * take the lock, given that users can't be added there anyway in this case. */ + etc_passwd_lock_fd = take_etc_passwd_lock(NULL); + if (etc_passwd_lock_fd < 0 && etc_passwd_lock_fd != -EROFS) + return etc_passwd_lock_fd; + + /* First, let's parse this as numeric UID */ + r = parse_uid(d->name, &uid); + if (r < 0) { + struct passwd *p; + struct group *g; + + /* OK, this is not a numeric UID. Let's see if there's a user by this name */ + p = getpwnam(d->name); + if (p) + uid = p->pw_uid; + + /* Let's see if there's a group by this name */ + g = getgrnam(d->name); + if (g) { + /* If the UID/GID of the user/group of the same don't match, refuse operation */ + if (uid != UID_INVALID && uid != (uid_t) g->gr_gid) + return -EILSEQ; + + uid = (uid_t) g->gr_gid; + } + } + + if (uid == UID_INVALID) { + /* No static UID assigned yet, excellent. Let's pick a new dynamic one, and lock it. */ + + uid_lock_fd = pick_uid(d->name, &uid); + if (uid_lock_fd < 0) + return uid_lock_fd; + } + + /* So, we found a working UID/lock combination. Let's see if we actually still need it. */ + if (lockf(d->storage_socket[0], F_LOCK, 0) < 0) { + unlink_uid_lock(uid_lock_fd, uid); + return -errno; + } + + r = dynamic_user_pop(d, &new_uid, &new_uid_lock_fd); + if (r < 0) { + if (r != -EAGAIN) { + /* OK, something bad happened, let's get rid of the bits we acquired. */ + unlink_uid_lock(uid_lock_fd, uid); + goto finish; + } + + /* Great! Nothing is stored here, still. Store our newly acquired data. */ + } else { + /* Hmm, so as it appears there's now something stored in the storage socket. Throw away what we + * acquired, and use what's stored now. */ + + unlink_uid_lock(uid_lock_fd, uid); + safe_close(uid_lock_fd); + + uid = new_uid; + uid_lock_fd = new_uid_lock_fd; + } + } + + /* If the UID/GID was already allocated dynamically, push the data we popped out back in. If it was already + * allocated statically, push the UID back too, but do not push the lock fd in. If we allocated the UID + * dynamically right here, push that in along with the lock fd for it. */ + r = dynamic_user_push(d, uid, uid_lock_fd); + if (r < 0) + goto finish; + + *ret = uid; + r = 0; + +finish: + (void) lockf(d->storage_socket[0], F_ULOCK, 0); + return r; +} + +int dynamic_user_current(DynamicUser *d, uid_t *ret) { + _cleanup_close_ int lock_fd = -1; + uid_t uid; + int r; + + assert(d); + assert(ret); + + /* Get the currently assigned UID for the user, if there's any. This simply pops the data from the storage socket, and pushes it back in right-away. */ + + if (lockf(d->storage_socket[0], F_LOCK, 0) < 0) + return -errno; + + r = dynamic_user_pop(d, &uid, &lock_fd); + if (r < 0) + goto finish; + + r = dynamic_user_push(d, uid, lock_fd); + if (r < 0) + goto finish; + + *ret = uid; + r = 0; + +finish: + (void) lockf(d->storage_socket[0], F_ULOCK, 0); + return r; +} + +DynamicUser* dynamic_user_ref(DynamicUser *d) { + if (!d) + return NULL; + + assert(d->n_ref > 0); + d->n_ref++; + + return d; +} + +DynamicUser* dynamic_user_unref(DynamicUser *d) { + if (!d) + return NULL; + + /* Note that this doesn't actually release any resources itself. If a dynamic user should be fully destroyed + * and its UID released, use dynamic_user_destroy() instead. NB: the dynamic user table may contain entries + * with no references, which is commonly the case right before a daemon reload. */ + + assert(d->n_ref > 0); + d->n_ref--; + + return NULL; +} + +static int dynamic_user_close(DynamicUser *d) { + _cleanup_close_ int lock_fd = -1; + uid_t uid; + int r; + + /* Release the user ID, by releasing the lock on it, and emptying the storage socket. After this the user is + * unrealized again, much like it was after it the DynamicUser object was first allocated. */ + + if (lockf(d->storage_socket[0], F_LOCK, 0) < 0) + return -errno; + + r = dynamic_user_pop(d, &uid, &lock_fd); + if (r == -EAGAIN) { + /* User wasn't realized yet, nothing to do. */ + r = 0; + goto finish; + } + if (r < 0) + goto finish; + + /* This dynamic user was realized and dynamically allocated. In this case, let's remove the lock file. */ + unlink_uid_lock(lock_fd, uid); + r = 1; + +finish: + (void) lockf(d->storage_socket[0], F_ULOCK, 0); + return r; +} + +DynamicUser* dynamic_user_destroy(DynamicUser *d) { + if (!d) + return NULL; + + /* Drop a reference to a DynamicUser object, and destroy the user completely if this was the last + * reference. This is called whenever a service is shut down and wants its dynamic UID gone. Note that + * dynamic_user_unref() is what is called whenever a service is simply freed, for example during a reload + * cycle, where the dynamic users should not be destroyed, but our datastructures should. */ + + dynamic_user_unref(d); + + if (d->n_ref > 0) + return NULL; + + (void) dynamic_user_close(d); + return dynamic_user_free(d); +} + +int dynamic_user_serialize(Manager *m, FILE *f, FDSet *fds) { + DynamicUser *d; + Iterator i; + + assert(m); + assert(f); + assert(fds); + + /* Dump the dynamic user database into the manager serialization, to deal with daemon reloads. */ + + HASHMAP_FOREACH(d, m->dynamic_users, i) { + int copy0, copy1; + + copy0 = fdset_put_dup(fds, d->storage_socket[0]); + if (copy0 < 0) + return copy0; + + copy1 = fdset_put_dup(fds, d->storage_socket[1]); + if (copy1 < 0) + return copy1; + + fprintf(f, "dynamic-user=%s %i %i\n", d->name, copy0, copy1); + } + + return 0; +} + +void dynamic_user_deserialize_one(Manager *m, const char *value, FDSet *fds) { + _cleanup_free_ char *name = NULL, *s0 = NULL, *s1 = NULL; + int r, fd0, fd1; + + assert(m); + assert(value); + assert(fds); + + /* Parse the serialization again, after a daemon reload */ + + r = extract_many_words(&value, NULL, 0, &name, &s0, &s1, NULL); + if (r != 3 || !isempty(value)) { + log_debug("Unable to parse dynamic user line."); + return; + } + + if (safe_atoi(s0, &fd0) < 0 || !fdset_contains(fds, fd0)) { + log_debug("Unable to process dynamic user fd specification."); + return; + } + + if (safe_atoi(s1, &fd1) < 0 || !fdset_contains(fds, fd1)) { + log_debug("Unable to process dynamic user fd specification."); + return; + } + + r = dynamic_user_add(m, name, (int[]) { fd0, fd1 }, NULL); + if (r < 0) { + log_debug_errno(r, "Failed to add dynamic user: %m"); + return; + } + + (void) fdset_remove(fds, fd0); + (void) fdset_remove(fds, fd1); +} + +void dynamic_user_vacuum(Manager *m, bool close_user) { + DynamicUser *d; + Iterator i; + + assert(m); + + /* Empty the dynamic user database, optionally cleaning up orphaned dynamic users, i.e. destroy and free users + * to which no reference exist. This is called after a daemon reload finished, in order to destroy users which + * might not be referenced anymore. */ + + HASHMAP_FOREACH(d, m->dynamic_users, i) { + if (d->n_ref > 0) + continue; + + if (close_user) { + log_debug("Removing orphaned dynamic user %s", d->name); + (void) dynamic_user_close(d); + } + + dynamic_user_free(d); + } +} + +int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret) { + char lock_path[strlen("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1]; + _cleanup_free_ char *user = NULL; + uid_t check_uid; + int r; + + assert(m); + assert(ret); + + /* A friendly way to translate a dynamic user's UID into a his name. */ + + if (uid < UID_PICK_MIN) + return -ESRCH; + if (uid > UID_PICK_MAX) + return -ESRCH; + + xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid); + r = read_one_line_file(lock_path, &user); + if (r == -ENOENT) + return -ESRCH; + if (r < 0) + return r; + + /* The lock file might be stale, hence let's verify the data before we return it */ + r = dynamic_user_lookup_name(m, user, &check_uid); + if (r < 0) + return r; + if (check_uid != uid) /* lock file doesn't match our own idea */ + return -ESRCH; + + *ret = user; + user = NULL; + + return 0; +} + +int dynamic_user_lookup_name(Manager *m, const char *name, uid_t *ret) { + DynamicUser *d; + int r; + + assert(m); + assert(name); + assert(ret); + + /* A friendly call for translating a dynamic user's name into its UID */ + + d = hashmap_get(m->dynamic_users, name); + if (!d) + return -ESRCH; + + r = dynamic_user_current(d, ret); + if (r == -EAGAIN) /* not realized yet? */ + return -ESRCH; + + return r; +} + +int dynamic_creds_acquire(DynamicCreds *creds, Manager *m, const char *user, const char *group) { + bool acquired = false; + int r; + + assert(creds); + assert(m); + + /* A DynamicUser object encapsulates an allocation of both a UID and a GID for a specific name. However, some + * services use different user and groups. For cases like that there's DynamicCreds containing a pair of user + * and group. This call allocates a pair. */ + + if (!creds->user && user) { + r = dynamic_user_acquire(m, user, &creds->user); + if (r < 0) + return r; + + acquired = true; + } + + if (!creds->group) { + + if (creds->user && (!group || streq_ptr(user, group))) + creds->group = dynamic_user_ref(creds->user); + else { + r = dynamic_user_acquire(m, group, &creds->group); + if (r < 0) { + if (acquired) + creds->user = dynamic_user_unref(creds->user); + return r; + } + } + } + + return 0; +} + +int dynamic_creds_realize(DynamicCreds *creds, uid_t *uid, gid_t *gid) { + uid_t u = UID_INVALID; + gid_t g = GID_INVALID; + int r; + + assert(creds); + assert(uid); + assert(gid); + + /* Realize both the referenced user and group */ + + if (creds->user) { + r = dynamic_user_realize(creds->user, &u); + if (r < 0) + return r; + } + + if (creds->group && creds->group != creds->user) { + r = dynamic_user_realize(creds->group, &g); + if (r < 0) + return r; + } else + g = u; + + *uid = u; + *gid = g; + + return 0; +} + +void dynamic_creds_unref(DynamicCreds *creds) { + assert(creds); + + creds->user = dynamic_user_unref(creds->user); + creds->group = dynamic_user_unref(creds->group); +} + +void dynamic_creds_destroy(DynamicCreds *creds) { + assert(creds); + + creds->user = dynamic_user_destroy(creds->user); + creds->group = dynamic_user_destroy(creds->group); +} diff --git a/src/core/dynamic-user.h b/src/core/dynamic-user.h new file mode 100644 index 0000000000..0b8bce1a72 --- /dev/null +++ b/src/core/dynamic-user.h @@ -0,0 +1,66 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + 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 distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef struct DynamicUser DynamicUser; + +typedef struct DynamicCreds { + /* A combination of a dynamic user and group */ + DynamicUser *user; + DynamicUser *group; +} DynamicCreds; + +#include "manager.h" + +/* Note that this object always allocates a pair of user and group under the same name, even if one of them isn't + * used. This means, if you want to allocate a group and user pair, and they might have two different names, then you + * need to allocated two of these objects. DynamicCreds below makes that easy. */ +struct DynamicUser { + int n_ref; + Manager *manager; + + /* An AF_UNIX socket pair that contains a datagram containing both the numeric ID assigned, as well as a lock + * file fd locking the user ID we picked. */ + int storage_socket[2]; + + char name[]; +}; + +int dynamic_user_acquire(Manager *m, const char *name, DynamicUser **ret); + +int dynamic_user_realize(DynamicUser *d, uid_t *ret); +int dynamic_user_current(DynamicUser *d, uid_t *ret); + +DynamicUser* dynamic_user_ref(DynamicUser *d); +DynamicUser* dynamic_user_unref(DynamicUser *d); +DynamicUser* dynamic_user_destroy(DynamicUser *d); + +int dynamic_user_serialize(Manager *m, FILE *f, FDSet *fds); +void dynamic_user_deserialize_one(Manager *m, const char *value, FDSet *fds); +void dynamic_user_vacuum(Manager *m, bool close_user); + +int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret); +int dynamic_user_lookup_name(Manager *m, const char *name, uid_t *ret); + +int dynamic_creds_acquire(DynamicCreds *creds, Manager *m, const char *user, const char *group); +int dynamic_creds_realize(DynamicCreds *creds, uid_t *uid, gid_t *gid); + +void dynamic_creds_unref(DynamicCreds *creds); +void dynamic_creds_destroy(DynamicCreds *creds); diff --git a/src/core/execute.c b/src/core/execute.c index 7c178b97c3..c186f2a705 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1526,14 +1526,28 @@ static bool exec_needs_mount_namespace( return false; } +static void append_socket_pair(int *array, unsigned *n, int pair[2]) { + assert(array); + assert(n); + + if (!pair) + return; + + if (pair[0] >= 0) + array[(*n)++] = pair[0]; + if (pair[1] >= 0) + array[(*n)++] = pair[1]; +} + static int close_remaining_fds( const ExecParameters *params, ExecRuntime *runtime, + DynamicCreds *dcreds, int socket_fd, int *fds, unsigned n_fds) { unsigned n_dont_close = 0; - int dont_close[n_fds + 7]; + int dont_close[n_fds + 11]; assert(params); @@ -1551,11 +1565,14 @@ static int close_remaining_fds( n_dont_close += n_fds; } - if (runtime) { - if (runtime->netns_storage_socket[0] >= 0) - dont_close[n_dont_close++] = runtime->netns_storage_socket[0]; - if (runtime->netns_storage_socket[1] >= 0) - dont_close[n_dont_close++] = runtime->netns_storage_socket[1]; + if (runtime) + append_socket_pair(dont_close, &n_dont_close, runtime->netns_storage_socket); + + if (dcreds) { + if (dcreds->user) + append_socket_pair(dont_close, &n_dont_close, dcreds->user->storage_socket); + if (dcreds->group) + append_socket_pair(dont_close, &n_dont_close, dcreds->group->storage_socket); } return close_all_fds(dont_close, n_dont_close); @@ -1567,6 +1584,7 @@ static int exec_child( const ExecContext *context, const ExecParameters *params, ExecRuntime *runtime, + DynamicCreds *dcreds, char **argv, int socket_fd, int *fds, unsigned n_fds, @@ -1617,7 +1635,7 @@ static int exec_child( log_forget_fds(); - r = close_remaining_fds(params, runtime, socket_fd, fds, n_fds); + r = close_remaining_fds(params, runtime, dcreds, socket_fd, fds, n_fds); if (r < 0) { *exit_status = EXIT_FDS; return r; @@ -1650,25 +1668,42 @@ static int exec_child( } } - if (context->user) { - username = context->user; - r = get_user_creds(&username, &uid, &gid, &home, &shell); + if (context->dynamic_user && dcreds) { + + r = dynamic_creds_realize(dcreds, &uid, &gid); if (r < 0) { *exit_status = EXIT_USER; return r; } - } - if (context->group) { - const char *g = context->group; + if (uid == UID_INVALID || gid == GID_INVALID) { + *exit_status = EXIT_USER; + return -ESRCH; + } - r = get_group_creds(&g, &gid); - if (r < 0) { - *exit_status = EXIT_GROUP; - return r; + if (dcreds->user) + username = dcreds->user->name; + + } else { + if (context->user) { + username = context->user; + r = get_user_creds(&username, &uid, &gid, &home, &shell); + if (r < 0) { + *exit_status = EXIT_USER; + return r; + } } - } + if (context->group) { + const char *g = context->group; + + r = get_group_creds(&g, &gid); + if (r < 0) { + *exit_status = EXIT_GROUP; + return r; + } + } + } /* If a socket is connected to STDIN/STDOUT/STDERR, we * must sure to drop O_NONBLOCK */ @@ -2192,6 +2227,7 @@ int exec_spawn(Unit *unit, const ExecContext *context, const ExecParameters *params, ExecRuntime *runtime, + DynamicCreds *dcreds, pid_t *ret) { _cleanup_strv_free_ char **files_env = NULL; @@ -2250,6 +2286,7 @@ int exec_spawn(Unit *unit, context, params, runtime, + dcreds, argv, socket_fd, fds, n_fds, @@ -2723,6 +2760,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { if (c->group) fprintf(f, "%sGroup: %s\n", prefix, c->group); + fprintf(f, "%sDynamicUser: %s\n", prefix, yes_no(c->dynamic_user)); + if (strv_length(c->supplementary_groups) > 0) { fprintf(f, "%sSupplementaryGroups:", prefix); strv_fprintf(f, c->supplementary_groups); diff --git a/src/core/execute.h b/src/core/execute.h index 189c4d0999..48cc18fbb3 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -92,6 +92,8 @@ struct ExecRuntime { char *tmp_dir; char *var_tmp_dir; + /* An AF_UNIX socket pair, that contains a datagram containing a file descriptor referring to the network + * namespace. */ int netns_storage_socket[2]; }; @@ -174,6 +176,8 @@ struct ExecContext { bool no_new_privileges; + bool dynamic_user; + /* This is not exposed to the user but available * internally. We need it to make sure that whenever we spawn * /usr/bin/mount it is run in the same process group as us so @@ -235,12 +239,14 @@ struct ExecParameters { }; #include "unit.h" +#include "dynamic-user.h" int exec_spawn(Unit *unit, ExecCommand *command, const ExecContext *context, const ExecParameters *exec_params, ExecRuntime *runtime, + DynamicCreds *dynamic_creds, pid_t *ret); void exec_command_done(ExecCommand *c); diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 7f7c2fe185..c9cdbe8ba7 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -34,6 +34,7 @@ $1.UMask, config_parse_mode, 0, $1.Environment, config_parse_environ, 0, offsetof($1, exec_context.environment) $1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files) $1.PassEnvironment, config_parse_pass_environ, 0, offsetof($1, exec_context.pass_environment) +$1.DynamicUser, config_parse_bool, 0, offsetof($1, exec_context.dynamic_user) $1.StandardInput, config_parse_input, 0, offsetof($1, exec_context.std_input) $1.StandardOutput, config_parse_output, 0, offsetof($1, exec_context.std_output) $1.StandardError, config_parse_output, 0, offsetof($1, exec_context.std_error) diff --git a/src/core/manager.c b/src/core/manager.c index a0181e2138..a4d027f0fc 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -1004,6 +1004,9 @@ Manager* manager_free(Manager *m) { bus_done(m); + dynamic_user_vacuum(m, false); + hashmap_free(m->dynamic_users); + hashmap_free(m->units); hashmap_free(m->jobs); hashmap_free(m->watch_pids1); @@ -1227,6 +1230,9 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { /* Third, fire things up! */ manager_coldplug(m); + /* Release any dynamic users no longer referenced */ + dynamic_user_vacuum(m, true); + if (serialization) { assert(m->n_reloading > 0); m->n_reloading--; @@ -2403,6 +2409,10 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { bus_track_serialize(m->subscribed, f); + r = dynamic_user_serialize(m, f, fds); + if (r < 0) + return r; + fputc('\n', f); HASHMAP_FOREACH_KEY(u, t, m->units, i) { @@ -2579,7 +2589,9 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { m->kdbus_fd = fdset_remove(fds, fd); } - } else { + } else if (startswith(l, "dynamic-user=")) + dynamic_user_deserialize_one(m, l + 13, fds); + else { int k; k = bus_track_deserialize_item(&m->deserialized_subscribed, l); @@ -2660,6 +2672,7 @@ int manager_reload(Manager *m) { manager_clear_jobs_and_units(m); lookup_paths_flush_generator(&m->lookup_paths); lookup_paths_free(&m->lookup_paths); + dynamic_user_vacuum(m, false); q = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL); if (q < 0 && r >= 0) @@ -2696,6 +2709,9 @@ int manager_reload(Manager *m) { /* Third, fire things up! */ manager_coldplug(m); + /* Release any dynamic users no longer referenced */ + dynamic_user_vacuum(m, true); + /* Sync current state of bus names with our set of listening units */ if (m->api_bus) manager_sync_bus_names(m, m->api_bus); diff --git a/src/core/manager.h b/src/core/manager.h index 6ed15c1a41..c681d5dc46 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -298,6 +298,9 @@ struct Manager { /* Used for processing polkit authorization responses */ Hashmap *polkit_registry; + /* Dynamic users/groups, indexed by their name */ + Hashmap *dynamic_users; + /* When the user hits C-A-D more than 7 times per 2s, reboot immediately... */ RateLimit ctrl_alt_del_ratelimit; diff --git a/src/core/mount.c b/src/core/mount.c index fda4d65d6f..db5cafcb11 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -245,6 +245,8 @@ static void mount_done(Unit *u) { exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX); m->control_command = NULL; + dynamic_creds_unref(&m->dynamic_creds); + mount_unwatch_control_pid(m); m->timer_event_source = sd_event_source_unref(m->timer_event_source); @@ -648,6 +650,9 @@ static int mount_coldplug(Unit *u) { return r; } + if (!IN_SET(new_state, MOUNT_DEAD, MOUNT_FAILED)) + (void) unit_setup_dynamic_creds(u); + mount_set_state(m, new_state); return 0; } @@ -716,6 +721,10 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { if (r < 0) return r; + r = unit_setup_dynamic_creds(UNIT(m)); + if (r < 0) + return r; + r = mount_arm_timer(m, usec_add(now(CLOCK_MONOTONIC), m->timeout_usec)); if (r < 0) return r; @@ -732,6 +741,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { &m->exec_context, &exec_params, m->exec_runtime, + &m->dynamic_creds, &pid); if (r < 0) return r; @@ -752,12 +762,14 @@ static void mount_enter_dead(Mount *m, MountResult f) { if (f != MOUNT_SUCCESS) m->result = f; + mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD); + exec_runtime_destroy(m->exec_runtime); m->exec_runtime = exec_runtime_unref(m->exec_runtime); exec_context_destroy_runtime_directory(&m->exec_context, manager_get_runtime_prefix(UNIT(m)->manager)); - mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD); + dynamic_creds_destroy(&m->dynamic_creds); } static void mount_enter_mounted(Mount *m, MountResult f) { @@ -1817,6 +1829,7 @@ const UnitVTable mount_vtable = { .cgroup_context_offset = offsetof(Mount, cgroup_context), .kill_context_offset = offsetof(Mount, kill_context), .exec_runtime_offset = offsetof(Mount, exec_runtime), + .dynamic_creds_offset = offsetof(Mount, dynamic_creds), .sections = "Unit\0" diff --git a/src/core/mount.h b/src/core/mount.h index da529c44f4..ac27b518cc 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -21,8 +21,8 @@ typedef struct Mount Mount; -#include "execute.h" #include "kill.h" +#include "dynamic-user.h" typedef enum MountExecCommand { MOUNT_EXEC_MOUNT, @@ -85,6 +85,7 @@ struct Mount { CGroupContext cgroup_context; ExecRuntime *exec_runtime; + DynamicCreds dynamic_creds; MountState state, deserialized_state; diff --git a/src/core/service.c b/src/core/service.c index afb198507b..4d59d78ecb 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -322,6 +322,8 @@ static void service_done(Unit *u) { s->control_command = NULL; s->main_command = NULL; + dynamic_creds_unref(&s->dynamic_creds); + exit_status_set_free(&s->restart_prevent_status); exit_status_set_free(&s->restart_force_status); exit_status_set_free(&s->success_status); @@ -1030,6 +1032,9 @@ static int service_coldplug(Unit *u) { if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) service_start_watchdog(s); + if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) + (void) unit_setup_dynamic_creds(u); + service_set_state(s, s->deserialized_state); return 0; } @@ -1184,6 +1189,10 @@ static int service_spawn( if (r < 0) return r; + r = unit_setup_dynamic_creds(UNIT(s)); + if (r < 0) + return r; + if (pass_fds || s->exec_context.std_input == EXEC_INPUT_SOCKET || s->exec_context.std_output == EXEC_OUTPUT_SOCKET || @@ -1285,6 +1294,7 @@ static int service_spawn( &s->exec_context, &exec_params, s->exec_runtime, + &s->dynamic_creds, &pid); if (r < 0) return r; @@ -1418,9 +1428,12 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) exec_runtime_destroy(s->exec_runtime); s->exec_runtime = exec_runtime_unref(s->exec_runtime); - /* Also, remove the runtime directory in */ + /* Also, remove the runtime directory */ exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager)); + /* Release the user, and destroy it if we are the only remaining owner */ + dynamic_creds_destroy(&s->dynamic_creds); + /* Try to delete the pid file. At this point it will be * out-of-date, and some software might be confused by it, so * let's remove it. */ @@ -3323,6 +3336,7 @@ const UnitVTable service_vtable = { .cgroup_context_offset = offsetof(Service, cgroup_context), .kill_context_offset = offsetof(Service, kill_context), .exec_runtime_offset = offsetof(Service, exec_runtime), + .dynamic_creds_offset = offsetof(Service, dynamic_creds), .sections = "Unit\0" diff --git a/src/core/service.h b/src/core/service.h index cfef375b03..8e56e1acb9 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -148,6 +148,7 @@ struct Service { /* Runtime data of the execution context */ ExecRuntime *exec_runtime; + DynamicCreds dynamic_creds; pid_t main_pid, control_pid; int socket_fd; diff --git a/src/core/socket.c b/src/core/socket.c index e098055885..1ce41a1f07 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -150,6 +150,8 @@ static void socket_done(Unit *u) { exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX); s->control_command = NULL; + dynamic_creds_unref(&s->dynamic_creds); + socket_unwatch_control_pid(s); unit_ref_unset(&s->service); @@ -1602,6 +1604,9 @@ static int socket_coldplug(Unit *u) { return r; } + if (!IN_SET(s->deserialized_state, SOCKET_DEAD, SOCKET_FAILED)) + (void) unit_setup_dynamic_creds(u); + socket_set_state(s, s->deserialized_state); return 0; } @@ -1633,6 +1638,10 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { if (r < 0) return r; + r = unit_setup_dynamic_creds(UNIT(s)); + if (r < 0) + return r; + r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec)); if (r < 0) return r; @@ -1654,6 +1663,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { &s->exec_context, &exec_params, s->exec_runtime, + &s->dynamic_creds, &pid); if (r < 0) return r; @@ -1757,12 +1767,14 @@ static void socket_enter_dead(Socket *s, SocketResult f) { if (f != SOCKET_SUCCESS) s->result = f; + socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD); + exec_runtime_destroy(s->exec_runtime); s->exec_runtime = exec_runtime_unref(s->exec_runtime); exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager)); - socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD); + dynamic_creds_destroy(&s->dynamic_creds); } static void socket_enter_signal(Socket *s, SocketState state, SocketResult f); @@ -2930,6 +2942,7 @@ const UnitVTable socket_vtable = { .cgroup_context_offset = offsetof(Socket, cgroup_context), .kill_context_offset = offsetof(Socket, kill_context), .exec_runtime_offset = offsetof(Socket, exec_runtime), + .dynamic_creds_offset = offsetof(Socket, dynamic_creds), .sections = "Unit\0" diff --git a/src/core/socket.h b/src/core/socket.h index 0f1ac69c6f..6c32d67bef 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -94,7 +94,9 @@ struct Socket { ExecContext exec_context; KillContext kill_context; CGroupContext cgroup_context; + ExecRuntime *exec_runtime; + DynamicCreds dynamic_creds; /* For Accept=no sockets refers to the one service we'll activate. For Accept=yes sockets is either NULL, or filled diff --git a/src/core/swap.c b/src/core/swap.c index a532b15be8..66a318d01f 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -153,6 +153,8 @@ static void swap_done(Unit *u) { exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX); s->control_command = NULL; + dynamic_creds_unref(&s->dynamic_creds); + swap_unwatch_control_pid(s); s->timer_event_source = sd_event_source_unref(s->timer_event_source); @@ -553,6 +555,9 @@ static int swap_coldplug(Unit *u) { return r; } + if (!IN_SET(new_state, SWAP_DEAD, SWAP_FAILED)) + (void) unit_setup_dynamic_creds(u); + swap_set_state(s, new_state); return 0; } @@ -628,6 +633,10 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { if (r < 0) goto fail; + r = unit_setup_dynamic_creds(UNIT(s)); + if (r < 0) + return r; + r = swap_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec)); if (r < 0) goto fail; @@ -644,6 +653,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { &s->exec_context, &exec_params, s->exec_runtime, + &s->dynamic_creds, &pid); if (r < 0) goto fail; @@ -668,12 +678,14 @@ static void swap_enter_dead(Swap *s, SwapResult f) { if (f != SWAP_SUCCESS) s->result = f; + swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD); + exec_runtime_destroy(s->exec_runtime); s->exec_runtime = exec_runtime_unref(s->exec_runtime); exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager)); - swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD); + dynamic_creds_destroy(&s->dynamic_creds); } static void swap_enter_active(Swap *s, SwapResult f) { @@ -1466,6 +1478,7 @@ const UnitVTable swap_vtable = { .cgroup_context_offset = offsetof(Swap, cgroup_context), .kill_context_offset = offsetof(Swap, kill_context), .exec_runtime_offset = offsetof(Swap, exec_runtime), + .dynamic_creds_offset = offsetof(Swap, dynamic_creds), .sections = "Unit\0" diff --git a/src/core/swap.h b/src/core/swap.h index fbf66debdc..b0ef50f1e8 100644 --- a/src/core/swap.h +++ b/src/core/swap.h @@ -82,6 +82,7 @@ struct Swap { CGroupContext cgroup_context; ExecRuntime *exec_runtime; + DynamicCreds dynamic_creds; SwapState state, deserialized_state; diff --git a/src/core/unit.c b/src/core/unit.c index 4934a0e56f..ff7c562fba 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -3224,6 +3224,33 @@ void unit_ref_unset(UnitRef *ref) { ref->unit = NULL; } +static int user_from_unit_name(Unit *u, char **ret) { + + static const uint8_t hash_key[] = { + 0x58, 0x1a, 0xaf, 0xe6, 0x28, 0x58, 0x4e, 0x96, + 0xb4, 0x4e, 0xf5, 0x3b, 0x8c, 0x92, 0x07, 0xec + }; + + _cleanup_free_ char *n = NULL; + int r; + + r = unit_name_to_prefix(u->id, &n); + if (r < 0) + return r; + + if (valid_user_group_name(n)) { + *ret = n; + n = NULL; + return 0; + } + + /* If we can't use the unit name as a user name, then let's hash it and use that */ + if (asprintf(ret, "_du%016" PRIx64, siphash24(n, strlen(n), hash_key)) < 0) + return -ENOMEM; + + return 0; +} + int unit_patch_contexts(Unit *u) { CGroupContext *cc; ExecContext *ec; @@ -3268,6 +3295,22 @@ int unit_patch_contexts(Unit *u) { if (ec->private_devices) ec->capability_bounding_set &= ~(UINT64_C(1) << CAP_MKNOD); + + if (ec->dynamic_user) { + if (!ec->user) { + r = user_from_unit_name(u, &ec->user); + if (r < 0) + return r; + } + + if (!ec->group) { + ec->group = strdup(ec->user); + if (!ec->group) + return -ENOMEM; + } + + ec->private_tmp = true; + } } cc = unit_get_cgroup_context(u); @@ -3776,6 +3819,26 @@ int unit_setup_exec_runtime(Unit *u) { return exec_runtime_make(rt, unit_get_exec_context(u), u->id); } +int unit_setup_dynamic_creds(Unit *u) { + ExecContext *ec; + DynamicCreds *dcreds; + size_t offset; + + assert(u); + + offset = UNIT_VTABLE(u)->dynamic_creds_offset; + assert(offset > 0); + dcreds = (DynamicCreds*) ((uint8_t*) u + offset); + + ec = unit_get_exec_context(u); + assert(ec); + + if (!ec->dynamic_user) + return 0; + + return dynamic_creds_acquire(dcreds, u->manager, ec->user, ec->group); +} + bool unit_type_supported(UnitType t) { if (_unlikely_(t < 0)) return false; diff --git a/src/core/unit.h b/src/core/unit.h index 1eabfa51e2..47eb8d50a6 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -291,6 +291,10 @@ struct UnitVTable { * that */ size_t exec_runtime_offset; + /* If greater than 0, the offset into the object where the pointer to DynamicCreds is found, if the unit type + * has that. */ + size_t dynamic_creds_offset; + /* The name of the configuration file section with the private settings of this unit */ const char *private_section; @@ -589,6 +593,7 @@ CGroupContext *unit_get_cgroup_context(Unit *u) _pure_; ExecRuntime *unit_get_exec_runtime(Unit *u) _pure_; int unit_setup_exec_runtime(Unit *u); +int unit_setup_dynamic_creds(Unit *u); int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data); int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) _printf_(4,5); diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index 02e3bf904c..32be3cdc38 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -44,6 +44,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_NO_ISOLATION, EPERM), SD_BUS_ERROR_MAP(BUS_ERROR_SHUTTING_DOWN, ECANCELED), SD_BUS_ERROR_MAP(BUS_ERROR_SCOPE_NOT_RUNNING, EHOSTDOWN), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DYNAMIC_USER, ESRCH), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_MACHINE, ENXIO), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_IMAGE, ENOENT), diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index c8f369cb78..befb6fbfe0 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -40,6 +40,7 @@ #define BUS_ERROR_NO_ISOLATION "org.freedesktop.systemd1.NoIsolation" #define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown" #define BUS_ERROR_SCOPE_NOT_RUNNING "org.freedesktop.systemd1.ScopeNotRunning" +#define BUS_ERROR_NO_SUCH_DYNAMIC_USER "org.freedesktop.systemd1.NoSuchDynamicUser" #define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine" #define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage" diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 94ffa8af87..5c50c0cd2e 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -179,11 +179,12 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur); } else if (STR_IN_SET(field, - "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting", "TasksAccounting", - "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", - "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", - "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges", - "SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute")) { + "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting", "TasksAccounting", + "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", + "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", + "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges", + "SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute", + "RestrictRealtime", "DynamicUser")) { r = parse_boolean(eq); if (r < 0) -- cgit v1.2.3 From 6f3e79859d91aecb3a75097b69fb9cba086b2cb1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 14 Jul 2016 19:17:18 +0200 Subject: core: enforce user/group name validity also when creating transient units --- src/core/dbus-execute.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index a6896c6e6c..9c50cd93e5 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -44,6 +44,7 @@ #endif #include "strv.h" #include "syslog-util.h" +#include "user-util.h" #include "utf8.h" BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutput); @@ -841,6 +842,9 @@ int bus_exec_context_set_transient_property( if (r < 0) return r; + if (!isempty(uu) && !valid_user_group_name_or_id(uu)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user name: %s", uu); + if (mode != UNIT_CHECK) { if (isempty(uu)) @@ -860,6 +864,9 @@ int bus_exec_context_set_transient_property( if (r < 0) return r; + if (!isempty(gg) && !valid_user_group_name_or_id(gg)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group name: %s", gg); + if (mode != UNIT_CHECK) { if (isempty(gg)) -- cgit v1.2.3 From 409093fe10685ed55915ef256f09cdf144b6528b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 14 Jul 2016 19:19:49 +0200 Subject: nss: add new "nss-systemd" NSS module for mapping dynamic users With this NSS module all dynamic service users will be resolvable via NSS like any real user. --- Makefile-man.am | 7 + Makefile.am | 21 +++ README | 21 ++- man/nss-myhostname.xml | 5 +- man/nss-mymachines.xml | 5 +- man/nss-resolve.xml | 7 +- man/nss-systemd.xml | 107 +++++++++++++ src/core/execute.c | 6 + src/nss-systemd/Makefile | 1 + src/nss-systemd/nss-systemd.c | 332 ++++++++++++++++++++++++++++++++++++++++ src/nss-systemd/nss-systemd.sym | 17 ++ 11 files changed, 515 insertions(+), 14 deletions(-) create mode 100644 man/nss-systemd.xml create mode 120000 src/nss-systemd/Makefile create mode 100644 src/nss-systemd/nss-systemd.c create mode 100644 src/nss-systemd/nss-systemd.sym diff --git a/Makefile-man.am b/Makefile-man.am index 8ab733360d..3ac1906a4a 100644 --- a/Makefile-man.am +++ b/Makefile-man.am @@ -23,6 +23,7 @@ MANPAGES += \ man/localtime.5 \ man/machine-id.5 \ man/machine-info.5 \ + man/nss-systemd.8 \ man/os-release.5 \ man/sd-bus-errors.3 \ man/sd-bus.3 \ @@ -255,6 +256,7 @@ MANPAGES_ALIAS += \ man/SD_WARNING.3 \ man/init.1 \ man/journald.conf.d.5 \ + man/libnss_systemd.so.2.8 \ man/poweroff.8 \ man/reboot.8 \ man/sd_bus_creds_get_audit_login_uid.3 \ @@ -587,6 +589,7 @@ man/SD_NOTICE.3: man/sd-daemon.3 man/SD_WARNING.3: man/sd-daemon.3 man/init.1: man/systemd.1 man/journald.conf.d.5: man/journald.conf.5 +man/libnss_systemd.so.2.8: man/nss-systemd.8 man/poweroff.8: man/halt.8 man/reboot.8: man/halt.8 man/sd_bus_creds_get_audit_login_uid.3: man/sd_bus_creds_get_pid.3 @@ -1071,6 +1074,9 @@ man/init.html: man/systemd.html man/journald.conf.d.html: man/journald.conf.html $(html-alias) +man/libnss_systemd.so.2.html: man/nss-systemd.html + $(html-alias) + man/poweroff.html: man/halt.html $(html-alias) @@ -2519,6 +2525,7 @@ EXTRA_DIST += \ man/nss-myhostname.xml \ man/nss-mymachines.xml \ man/nss-resolve.xml \ + man/nss-systemd.xml \ man/os-release.xml \ man/pam_systemd.xml \ man/resolved.conf.xml \ diff --git a/Makefile.am b/Makefile.am index 3d5ce1e2c3..a4241122d5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5001,6 +5001,27 @@ test_nss_LDADD = \ manual_tests += \ test-nss +# ------------------------------------------------------------------------------ +libnss_systemd_la_SOURCES = \ + src/nss-systemd/nss-systemd.sym \ + src/nss-systemd/nss-systemd.c + +libnss_systemd_la_LDFLAGS = \ + $(AM_LDFLAGS) \ + -module \ + -export-dynamic \ + -avoid-version \ + -shared \ + -shrext .so.2 \ + -Wl,--version-script=$(top_srcdir)/src/nss-systemd/nss-systemd.sym + +libnss_systemd_la_LIBADD = \ + libsystemd-internal.la \ + libbasic.la + +lib_LTLIBRARIES += \ + libnss_systemd.la + # ------------------------------------------------------------------------------ if HAVE_MYHOSTNAME libnss_myhostname_la_SOURCES = \ diff --git a/README b/README index ca8993cb12..19c15a70b0 100644 --- a/README +++ b/README @@ -201,7 +201,7 @@ USERS AND GROUPS: "systemd-coredump" system user and group to exist. NSS: - systemd ships with three NSS modules: + systemd ships with four glibc NSS modules: nss-myhostname resolves the local hostname to locally configured IP addresses, as well as "localhost" to @@ -210,15 +210,22 @@ NSS: nss-resolve enables DNS resolution via the systemd-resolved DNS/LLMNR caching stub resolver "systemd-resolved". - nss-mymachines enables resolution of all local containers - registered with machined to their respective IP addresses. + nss-mymachines enables resolution of all local containers registered + with machined to their respective IP addresses. It also maps UID/GIDs + ranges used by containers to useful names. - To make use of these NSS modules, please add them to the - "hosts: " line in /etc/nsswitch.conf. The "resolve" module - should replace the glibc "dns" module in this file. + nss-systemd enables resolution of all dynamically allocated service + users. (See the DynamicUser= setting in unit files.) - The three modules should be used in the following order: + To make use of these NSS modules, please add them to the "hosts:", + "passwd:" and "group:" lines in /etc/nsswitch.conf. The "resolve" + module should replace the glibc "dns" module in this file (and don't + worry, it chain-loads the "dns" module if it can't talk to resolved). + The four modules should be used in the following order: + + passwd: compat mymachines systemd + group: compat mymachines systemd hosts: files mymachines resolve myhostname SYSV INIT.D SCRIPTS: diff --git a/man/nss-myhostname.xml b/man/nss-myhostname.xml index a920ec334f..b1daaba02b 100644 --- a/man/nss-myhostname.xml +++ b/man/nss-myhostname.xml @@ -106,8 +106,8 @@ Here is an example /etc/nsswitch.conf file that enables nss-myhostname correctly: -passwd: compat mymachines -group: compat mymachines +passwd: compat mymachines systemd +group: compat mymachines systemd shadow: compat hosts: files mymachines resolve myhostname @@ -138,6 +138,7 @@ netgroup: nis See Also systemd1, + nss-systemd8, nss-resolve8, nss-mymachines8, nsswitch.conf5, diff --git a/man/nss-mymachines.xml b/man/nss-mymachines.xml index ec047449bf..a70119e256 100644 --- a/man/nss-mymachines.xml +++ b/man/nss-mymachines.xml @@ -82,8 +82,8 @@ Here is an example /etc/nsswitch.conf file that enables nss-mymachines correctly: - passwd: compat mymachines -group: compat mymachines + passwd: compat mymachines systemd +group: compat mymachines systemd shadow: compat hosts: files mymachines resolve myhostname @@ -103,6 +103,7 @@ netgroup: nis systemd1, systemd-machined.service8, + nss-systemd8, nss-resolve8, nss-myhostname8, nsswitch.conf5, diff --git a/man/nss-resolve.xml b/man/nss-resolve.xml index d9e56453e8..e6cc1d982a 100644 --- a/man/nss-resolve.xml +++ b/man/nss-resolve.xml @@ -81,8 +81,8 @@ Here is an example /etc/nsswitch.conf file that enables nss-resolve correctly: -passwd: compat mymachines -group: compat mymachines +passwd: compat mymachines systemd +group: compat mymachines systemd shadow: compat hosts: files mymachines resolve myhostname @@ -102,8 +102,9 @@ netgroup: nis systemd1, systemd-resolved8, - nss-mymachines8, + nss-systemd8, nss-myhostname8, + nss-mymachines8, nsswitch.conf5 diff --git a/man/nss-systemd.xml b/man/nss-systemd.xml new file mode 100644 index 0000000000..4228372e51 --- /dev/null +++ b/man/nss-systemd.xml @@ -0,0 +1,107 @@ + + + + + + + + + nss-systemd + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + nss-systemd + 8 + + + + nss-systemd + libnss_systemd.so.2 + Provide UNIX user and group name resolution for dynamic users and groups. + + + + libnss_systemd.so.2 + + + + Description + + nss-systemd is a plug-in module for the GNU Name Service Switch (NSS) functionality of the + GNU C Library (glibc), providing UNIX user and group name resolution for dynamic users and + groups allocated through the DynamicUser= option in systemd unit files. See + systemd.exec5 for details on + this option. + + To activate the NSS module, add systemd to the lines starting with + passwd: and group: in /etc/nsswitch.conf. + + It is recommended to place systemd after the files or + compat entry of the /etc/nsswitch.conf lines so that + /etc/passwd and /etc/group based mappings take precedence. + + + + Example + + Here is an example /etc/nsswitch.conf file that enables + nss-systemd correctly: + + passwd: compat mymachines systemd +group: compat mymachines systemd +shadow: compat + +hosts: files mymachines resolve myhostname +networks: files + +protocols: db files +services: db files +ethers: db files +rpc: db files + +netgroup: nis + + + + + See Also + + systemd1, + systemd.exec5, + nss-resolve8, + nss-myhostname8, + nss-mymachines8, + nsswitch.conf5, + getent1 + + + + diff --git a/src/core/execute.c b/src/core/execute.c index c186f2a705..26e9cd5339 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1670,6 +1670,12 @@ static int exec_child( if (context->dynamic_user && dcreds) { + /* Make sure we bypass our own NSS module for any NSS checks */ + if (putenv((char*) "SYSTEMD_NSS_DYNAMIC_BYPASS=1") != 0) { + *exit_status = EXIT_USER; + return -errno; + } + r = dynamic_creds_realize(dcreds, &uid, &gid); if (r < 0) { *exit_status = EXIT_USER; diff --git a/src/nss-systemd/Makefile b/src/nss-systemd/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/nss-systemd/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c new file mode 100644 index 0000000000..e7a4393bb0 --- /dev/null +++ b/src/nss-systemd/nss-systemd.c @@ -0,0 +1,332 @@ +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + 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 distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "sd-bus.h" + +#include "bus-common-errors.h" +#include "env-util.h" +#include "macro.h" +#include "nss-util.h" +#include "signal-util.h" +#include "user-util.h" +#include "util.h" + +NSS_GETPW_PROTOTYPES(systemd); +NSS_GETGR_PROTOTYPES(systemd); + +enum nss_status _nss_systemd_getpwnam_r( + const char *name, + struct passwd *pwd, + char *buffer, size_t buflen, + int *errnop) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + uint32_t translated; + size_t l; + int r; + + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); + assert(pwd); + + /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */ + if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) + goto not_found; + + r = sd_bus_open_system(&bus); + if (r < 0) + goto fail; + + r = sd_bus_call_method(bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "LookupDynamicUserByName", + &error, + &reply, + "s", + name); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) + goto not_found; + + goto fail; + } + + r = sd_bus_message_read(reply, "u", &translated); + if (r < 0) + goto fail; + + l = strlen(name); + if (buflen < l+1) { + *errnop = ENOMEM; + return NSS_STATUS_TRYAGAIN; + } + + memcpy(buffer, name, l+1); + + pwd->pw_name = buffer; + pwd->pw_uid = (uid_t) translated; + pwd->pw_gid = (uid_t) translated; + pwd->pw_gecos = (char*) "Dynamic User"; + pwd->pw_passwd = (char*) "*"; /* locked */ + pwd->pw_dir = (char*) "/"; + pwd->pw_shell = (char*) "/sbin/nologin"; + + *errnop = 0; + return NSS_STATUS_SUCCESS; + +not_found: + *errnop = 0; + return NSS_STATUS_NOTFOUND; + +fail: + *errnop = -r; + return NSS_STATUS_UNAVAIL; +} + +enum nss_status _nss_systemd_getpwuid_r( + uid_t uid, + struct passwd *pwd, + char *buffer, size_t buflen, + int *errnop) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + const char *translated; + size_t l; + int r; + + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + if (!uid_is_valid(uid)) { + r = -EINVAL; + goto fail; + } + + if (uid <= SYSTEM_UID_MAX) + goto not_found; + + if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) + goto not_found; + + r = sd_bus_open_system(&bus); + if (r < 0) + goto fail; + + r = sd_bus_call_method(bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "LookupDynamicUserByUID", + &error, + &reply, + "u", + (uint32_t) uid); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) + goto not_found; + + goto fail; + } + + r = sd_bus_message_read(reply, "s", &translated); + if (r < 0) + goto fail; + + l = strlen(translated) + 1; + if (buflen < l) { + *errnop = ENOMEM; + return NSS_STATUS_TRYAGAIN; + } + + memcpy(buffer, translated, l); + + pwd->pw_name = buffer; + pwd->pw_uid = uid; + pwd->pw_gid = uid; + pwd->pw_gecos = (char*) "Dynamic User"; + pwd->pw_passwd = (char*) "*"; /* locked */ + pwd->pw_dir = (char*) "/"; + pwd->pw_shell = (char*) "/sbin/nologin"; + + *errnop = 0; + return NSS_STATUS_SUCCESS; + +not_found: + *errnop = 0; + return NSS_STATUS_NOTFOUND; + +fail: + *errnop = -r; + return NSS_STATUS_UNAVAIL; +} + +enum nss_status _nss_systemd_getgrnam_r( + const char *name, + struct group *gr, + char *buffer, size_t buflen, + int *errnop) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + uint32_t translated; + size_t l; + int r; + + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); + assert(gr); + + if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) + goto not_found; + + r = sd_bus_open_system(&bus); + if (r < 0) + goto fail; + + r = sd_bus_call_method(bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "LookupDynamicUserByName", + &error, + &reply, + "s", + name); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) + goto not_found; + + goto fail; + } + + r = sd_bus_message_read(reply, "u", &translated); + if (r < 0) + goto fail; + + l = sizeof(char*) + strlen(name) + 1; + if (buflen < l) { + *errnop = ENOMEM; + return NSS_STATUS_TRYAGAIN; + } + + memzero(buffer, sizeof(char*)); + strcpy(buffer + sizeof(char*), name); + + gr->gr_name = buffer + sizeof(char*); + gr->gr_gid = (gid_t) translated; + gr->gr_passwd = (char*) "*"; /* locked */ + gr->gr_mem = (char**) buffer; + + *errnop = 0; + return NSS_STATUS_SUCCESS; + +not_found: + *errnop = 0; + return NSS_STATUS_NOTFOUND; + +fail: + *errnop = -r; + return NSS_STATUS_UNAVAIL; +} + +enum nss_status _nss_systemd_getgrgid_r( + gid_t gid, + struct group *gr, + char *buffer, size_t buflen, + int *errnop) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + const char *translated; + size_t l; + int r; + + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + if (!gid_is_valid(gid)) { + r = -EINVAL; + goto fail; + } + + if (gid <= SYSTEM_GID_MAX) + goto not_found; + + if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) + goto not_found; + + r = sd_bus_open_system(&bus); + if (r < 0) + goto fail; + + r = sd_bus_call_method(bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "LookupDynamicUserByUID", + &error, + &reply, + "u", + (uint32_t) gid); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) + goto not_found; + + goto fail; + } + + r = sd_bus_message_read(reply, "s", &translated); + if (r < 0) + goto fail; + + l = sizeof(char*) + strlen(translated) + 1; + if (buflen < l) { + *errnop = ENOMEM; + return NSS_STATUS_TRYAGAIN; + } + + memzero(buffer, sizeof(char*)); + strcpy(buffer + sizeof(char*), translated); + + gr->gr_name = buffer + sizeof(char*); + gr->gr_gid = gid; + gr->gr_passwd = (char*) "*"; /* locked */ + gr->gr_mem = (char**) buffer; + + *errnop = 0; + return NSS_STATUS_SUCCESS; + +not_found: + *errnop = 0; + return NSS_STATUS_NOTFOUND; + +fail: + *errnop = -r; + return NSS_STATUS_UNAVAIL; +} diff --git a/src/nss-systemd/nss-systemd.sym b/src/nss-systemd/nss-systemd.sym new file mode 100644 index 0000000000..955078788a --- /dev/null +++ b/src/nss-systemd/nss-systemd.sym @@ -0,0 +1,17 @@ +/*** + This file is part of systemd. + + 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. +***/ + +{ +global: + _nss_systemd_getpwnam_r; + _nss_systemd_getpwuid_r; + _nss_systemd_getgrnam_r; + _nss_systemd_getgrgid_r; +local: *; +}; -- cgit v1.2.3 From d82047bef5b8a35fb2d1d4685f241383df1a1d76 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 14 Jul 2016 12:26:07 +0200 Subject: update TODO --- TODO | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/TODO b/TODO index ef25ef578e..bb36522bf9 100644 --- a/TODO +++ b/TODO @@ -33,6 +33,29 @@ Janitorial Clean-ups: Features: +* RemoveIPC= in unit files for removing POSIX/SysV IPC objects + +* Set SERVICE_RESULT= as env var while running ExecStop= + +* Introduce ProtectSystem=strict for making the entire OS hierarchy read-only + except for a select few + +* nspawn: start UID allocation loop from hash of container name + +* in the DynamicUser=1 nss module, also map "nobody" and "root" statically + +* pid1: log about all processes we kill with with SIGKILL or in abandoned scopes, as this should normally not happen + +* nspawn: support that /proc, /sys/, /dev are pre-mounted + +* nspawn: mount esp, so that bootctl can work + +* define gpt header bits to select volatility mode + +* nspawn: mount loopback filesystems with "discard" + +* Make TasksMax= take percentages, taken relative to the pids_max sysctl and pids.max cgroup limit + * ProtectKernelLogs= (drops CAP_SYSLOG, add seccomp for syslog() syscall, and DeviceAllow to /dev/kmsg) in service files * ProtectClock= (drops CAP_SYS_TIMES, adds seecomp filters for settimeofday, adjtimex), sets DeviceAllow o /dev/rtc @@ -46,7 +69,7 @@ Features: * PrivateUsers= which maps the all user ids except root and the one specified in User= to nobody -* Add AllocateUser= for allowing dynamic user ids per-service +* ProtectControlGroups= which mounts all of /sys/fs/cgroup read-only * Add DataDirectory=, CacheDirectory= and LogDirectory= to match RuntimeDirectory=, and create it as necessary when starting a service, owned by the right user. @@ -60,8 +83,6 @@ Features: * RestrictNamespaces= or so in services (taking away the ability to create namespaces, with setns, unshare, clone) -* nspawn: make /proc/sys/net writable? - * make sure the ratelimit object can deal with USEC_INFINITY as way to turn off things * journalctl: make sure -f ends when the container indicated by -M terminates -- cgit v1.2.3 From c92fcc4f4375b0aebc5919311bbf703138b21918 Mon Sep 17 00:00:00 2001 From: Martin Pitt Date: Mon, 25 Jul 2016 22:01:35 +0200 Subject: units: add graphical-session.target user unit (#3678) This unit acts as a dynamic "alias" target for any concrete graphical user session like gnome-session.target; these should declare "BindsTo=graphical-session.target" so that both targets stop and start at the same time. This allows services that run in a particular graphical user session (e. g. gnome-settings-daemon.service) to declare "PartOf=graphical-session.target" without having to know or get updated for all/new session types. This will ensure that stopping the graphical session will stop all services which are associated to it. --- Makefile.am | 3 ++- man/systemd.special.xml | 51 +++++++++++++++++++++++++++++++++++++ units/user/graphical-session.target | 13 ++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 units/user/graphical-session.target diff --git a/Makefile.am b/Makefile.am index 0c27f81986..c1e1c63634 100644 --- a/Makefile.am +++ b/Makefile.am @@ -573,7 +573,8 @@ endif dist_userunit_DATA = \ units/user/basic.target \ units/user/default.target \ - units/user/exit.target + units/user/exit.target \ + units/user/graphical-session.target nodist_userunit_DATA = \ units/user/systemd-exit.service diff --git a/man/systemd.special.xml b/man/systemd.special.xml index 18ad8f92e5..18142598cb 100644 --- a/man/systemd.special.xml +++ b/man/systemd.special.xml @@ -878,6 +878,57 @@ sound.target. + + Special Passive User Units + + + graphical-session.target + + This target is active whenever any graphical session is running. It + is used to stop user services which only apply to a graphical (X, + Wayland, etc.) session when the session is terminated. Such services + should have PartOf=graphical-session.target in their + [Unit] section. A target for a particular session + (e. g. gnome-session.target) starts and stops + graphical-session.target with + BindsTo=graphical-session.target. + + Which services are started by a session target is determined by the + Wants= and Requires= dependencies. + For services that can be enabled independently, symlinks in + .wants/ and .requires/ should be + used, see + systemd.unit5. + Those symlinks should either be shipped in packages, or should be added + dynamically after installation, for example using systemctl add-wants, see + systemctl1. + + + + Nautilus as part of a GNOME session + + gnome-session.target pulls in Nautilus as + top-level service: + + [Unit] +Description=User systemd services for GNOME graphical session +Wants=nautilus.service +BindsTo=graphical-session.target + + + nautilus.service gets stopped when the session stops: + + [Unit] +Description=Render the desktop icons with Nautilus +PartOf=graphical-session.target + +[Service] +... + + + + + Special Slice Units diff --git a/units/user/graphical-session.target b/units/user/graphical-session.target new file mode 100644 index 0000000000..00d16230b7 --- /dev/null +++ b/units/user/graphical-session.target @@ -0,0 +1,13 @@ +# This file is part of systemd. +# +# 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. + +[Unit] +Description=Current graphical user session +Documentation=man:systemd.special(7) +Requires=basic.target +RefuseManualStart=yes +StopWhenUnneeded=yes -- cgit v1.2.3 From 76153ad45f09b6ae45464f2e03d3afefbb4b2afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 26 Jul 2016 02:19:33 -0400 Subject: journald: deprecate SplitMode=login (#3805) In this mode, messages from processes which are not part of the session land in the main journal file, and only output of processes which are properly part of the session land in the user's journal. This is confusing, in particular because systemd-coredump runs outside of the login session. "Deprecate" SplitMode=login by removing it from documentation, to discourage people from using it. --- NEWS | 7 +++++++ man/journald.conf.xml | 26 +++++++++----------------- src/journal/journald-server.h | 2 +- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/NEWS b/NEWS index ca54685878..7ff4a44835 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,12 @@ systemd System and Service Manager +CHANGES WITH 232 in spe + + * Journald's SplitMode=login setting has been deprecated. It has been + removed from documentation, and it's use is discouraged. In a future + release it will be completely removed, and made equivalent to current + default of SplitMode=uid. + CHANGES WITH 231: * In service units the various ExecXYZ= settings have been extended diff --git a/man/journald.conf.xml b/man/journald.conf.xml index fef4fde898..a9562c121a 100644 --- a/man/journald.conf.xml +++ b/man/journald.conf.xml @@ -129,23 +129,15 @@ SplitMode= - Controls whether to split up journal files per user. Split-up journal files are primarily - useful for access control: on UNIX/Linux access control is managed per file, and the journal daemon will assign - users read access to their journal files. This setting takes one of uid, - login or none. If uid, all regular users will get each - their own journal files regardless of whether their processes possess login sessions or not, however system - users will log into the system journal. If login, actually logged-in users will get each - their own journal files, but users without login session and system users will log into the system - journal. Note that in this mode, user code running outside of any login session will log into the system log - instead of the split-out user logs. Most importantly, this means that information about core dumps of user - processes collected via the - systemd-coredump8 subsystem - will end up in the system logs instead of the user logs, and thus not be accessible to the owning users. If - none, journal files are not split up by user and all messages are instead stored in the - single system journal. In this mode unprivileged users generally do not have access to their own log data. Note - that splitting up journal files by user is only available for journals stored persistently. If journals are - stored on volatile storage (see above), only a single journal file for all user IDs is kept. Defaults to - uid. + Controls whether to split up journal files per user, either uid or + none. Split journal files are primarily useful for access control: on UNIX/Linux access + control is managed per file, and the journal daemon will assign users read access to their journal files. If + uid, all regular users will each get their own journal files, and system users will log to + the system journal. If none, journal files are not split up by user and all messages are + instead stored in the single system journal. In this mode unprivileged users generally do not have access to + their own log data. Note that splitting up journal files by user is only available for journals stored + persistently. If journals are stored on volatile storage (see Storage= above), only a single + journal file is used. Defaults to uid. diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index e025a4cf90..d2a32ab422 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -43,7 +43,7 @@ typedef enum Storage { typedef enum SplitMode { SPLIT_UID, - SPLIT_LOGIN, + SPLIT_LOGIN, /* deprecated */ SPLIT_NONE, _SPLIT_MAX, _SPLIT_INVALID = -1 -- cgit v1.2.3 From 1d3c86c06fca8311923fcf81af0ab0bbb66e1edd Mon Sep 17 00:00:00 2001 From: Michal Sekletar Date: Tue, 26 Jul 2016 14:25:52 +0200 Subject: systemctl: allow disable on the unit file path, but warn about it (#3806) systemd now returns an error when it is asked to perform disable on the unit file path. In the past this was allowed, but systemd never really considered an actual content of the [Install] section of the unit file. Instead it performed disable on the unit name, i.e. purged all symlinks pointing to the given unit file (undo of implicit link action done by systemd when enable is called on the unit file path) and all symlinks that have the same basename as the given unit file. However, to notice that [Install] info of the file is not consulted one must create additional symlinks manually. I argue that in most cases users do not create such links. Let's be nice to our users and don't break existing scripts that expect disable to work with the unit file path. Fixes #3706. --- src/systemctl/systemctl.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 6a0ed79a53..91caae009e 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -5670,6 +5670,29 @@ static int mangle_names(char **original_names, char ***mangled_names) { return 0; } +static int normalize_names(char **names, bool warn_if_path) { + char **u; + bool was_path = false; + + STRV_FOREACH(u, names) { + int r; + + if (!is_path(*u)) + continue; + + r = free_and_strdup(u, basename(*u)); + if (r < 0) + return log_error_errno(r, "Failed to normalize unit file path: %m"); + + was_path = true; + } + + if (warn_if_path && was_path) + log_warning("Warning: Can't execute disable on the unit file path. Proceeding with the unit name."); + + return 0; +} + static int unit_exists(const char *unit) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -5737,6 +5760,12 @@ static int enable_unit(int argc, char *argv[], void *userdata) { return daemon_reload(argc, argv, userdata); } + if (streq(verb, "disable")) { + r = normalize_names(names, true); + if (r < 0) + return r; + } + if (install_client_side()) { if (streq(verb, "enable")) { r = unit_file_enable(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes); -- cgit v1.2.3 From 5a8ff0e61dd8094b2b5d0b35df2ca13b489e0dfa Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 26 Jul 2016 16:49:15 +0200 Subject: nspawn: add SYSTEMD_NSPAWN_USE_CGNS env variable (#3809) SYSTEMD_NSPAWN_USE_CGNS allows to disable the use of cgroup namespaces. --- src/nspawn/nspawn-mount.c | 5 +++-- src/nspawn/nspawn-mount.h | 2 +- src/nspawn/nspawn.c | 17 +++++++++++++---- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index ac93357ef4..803caef3dd 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -916,11 +916,12 @@ int mount_cgroups( const char *dest, bool unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, - const char *selinux_apifs_context) { + const char *selinux_apifs_context, + bool use_cgns) { if (unified_requested) return mount_unified_cgroups(dest); - else if (cg_ns_supported()) + else if (use_cgns && cg_ns_supported()) return mount_legacy_cgns_supported(userns, uid_shift, uid_range, selinux_apifs_context); return mount_legacy_cgns_unsupported(dest, userns, uid_shift, uid_range, selinux_apifs_context); diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h index 0daf145412..0eff8e1006 100644 --- a/src/nspawn/nspawn-mount.h +++ b/src/nspawn/nspawn-mount.h @@ -58,7 +58,7 @@ int custom_mount_compare(const void *a, const void *b); int mount_all(const char *dest, bool use_userns, bool in_userns, bool use_netns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); int mount_sysfs(const char *dest); -int mount_cgroups(const char *dest, bool unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); +int mount_cgroups(const char *dest, bool unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context, bool use_cgns); int mount_systemd_cgroup_writable(const char *dest, bool unified_requested); int mount_custom(const char *dest, CustomMount *mounts, unsigned n, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index f8a43d89a2..6cc1b9177d 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -194,6 +194,7 @@ static int arg_settings_trusted = -1; static char **arg_parameters = NULL; static const char *arg_container_service_name = "systemd-nspawn"; static bool arg_notify_ready = false; +static bool arg_use_cgns = true; static void help(void) { printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n" @@ -1104,6 +1105,12 @@ static int parse_argv(int argc, char *argv[]) { if (e) arg_container_service_name = e; + r = getenv_bool("SYSTEMD_NSPAWN_USE_CGNS"); + if (r < 0) + arg_use_cgns = cg_ns_supported(); + else + arg_use_cgns = r; + return 1; } @@ -2628,7 +2635,7 @@ static int inner_child( return -ESRCH; } - if (cg_ns_supported()) { + if (arg_use_cgns && cg_ns_supported()) { r = unshare(CLONE_NEWCGROUP); if (r < 0) return log_error_errno(errno, "Failed to unshare cgroup namespace"); @@ -2638,7 +2645,8 @@ static int inner_child( arg_userns_mode != USER_NAMESPACE_NO, arg_uid_shift, arg_uid_range, - arg_selinux_apifs_context); + arg_selinux_apifs_context, + arg_use_cgns); if (r < 0) return r; } else { @@ -3029,14 +3037,15 @@ static int outer_child( if (r < 0) return r; - if (!cg_ns_supported()) { + if (!arg_use_cgns || !cg_ns_supported()) { r = mount_cgroups( directory, arg_unified_cgroup_hierarchy, arg_userns_mode != USER_NAMESPACE_NO, arg_uid_shift, arg_uid_range, - arg_selinux_apifs_context); + arg_selinux_apifs_context, + arg_use_cgns); if (r < 0) return r; } -- cgit v1.2.3 From c2f2c51c53e91232e6f2aa686d734728ad703f9f Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: don't do GIO_SCRNMAP / GIO_UNISCRNMAP GIO_SCRNMAP / GIO_UNISCRNMAP are related to what setfont does with -m option - namely setting intermediate map from 8bit values into unicode values. This map is global, so single setfont invocation sets it for all applicable consoles. Furthermore calling GIO_SCRNMAP before GIO_UNISCRNMAP causes issues as the former corrupts values > 255 (UNI alone would be sufficient). The bug can be easily tested with the following conf: KEYMAP=pl FONT=LatArCyrHeb-16 FONT_MAP=8859-2 --- src/vconsole/vconsole-setup.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 1118118450..abf3871fd8 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -192,8 +192,6 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map, */ static void font_copy_to_all_vcs(int fd) { struct vt_stat vcs = {}; - unsigned char map8[E_TABSZ]; - unsigned short map16[E_TABSZ]; struct unimapdesc unimapd; _cleanup_free_ struct unipair* unipairs = NULL; int i, r; @@ -234,14 +232,6 @@ static void font_copy_to_all_vcs(int fd) { cfo.height = vcs.v_active-1; /* tty1 == index 0 */ (void) ioctl(vcfd, KDFONTOP, &cfo); - /* copy map of 8bit chars */ - if (ioctl(fd, GIO_SCRNMAP, map8) >= 0) - (void) ioctl(vcfd, PIO_SCRNMAP, map8); - - /* copy map of 8bit chars -> 16bit Unicode values */ - if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0) - (void) ioctl(vcfd, PIO_UNISCRNMAP, map16); - /* copy unicode translation table */ /* unimapd is a ushort count and a pointer to an array of struct unipair { ushort, ushort } */ -- cgit v1.2.3 From 9fa71843bbb31c89f81976819eec009335b09f2d Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: copy font to 63 consoles instead of 15 We copy only to allocated consoles, so the cost of looping over all possible ones is minuscule. --- src/vconsole/vconsole-setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index abf3871fd8..df18c231e0 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -209,7 +209,7 @@ static void font_copy_to_all_vcs(int fd) { return; } - for (i = 1; i <= 15; i++) { + for (i = 1; i <= 63; i++) { char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)]; _cleanup_close_ int vcfd = -1; struct console_font_op cfo = {}; -- cgit v1.2.3 From 042d7f5065c9c19b3f96804f9403b14d910e46d1 Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: add two new toggle functions, remove old enable/disable ones Add toggle_utf8() and toggle_utf8_sysfs() and use them in place of old enable/disable functions. toggle_utf8() also adds iutf8 setting and is set up to be called per-console (in subsequent patches). Note, that old disable_utf8() didn't bother checking if it was ok to change the kbdmode. --- src/vconsole/vconsole-setup.c | 73 ++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 46 deletions(-) diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index df18c231e0..016cf004a0 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "alloc-util.h" @@ -50,56 +51,38 @@ static bool is_vconsole(int fd) { return ioctl(fd, TIOCLINUX, data) >= 0; } -static int disable_utf8(int fd) { - int r = 0, k; - - if (ioctl(fd, KDSKBMODE, K_XLATE) < 0) - r = -errno; - - k = loop_write(fd, "\033%@", 3, false); - if (k < 0) - r = k; - - k = write_string_file("/sys/module/vt/parameters/default_utf8", "0", 0); - if (k < 0) - r = k; +static int toggle_utf8(int fd, bool utf8) { + int r; + struct termios tc = {}; + r = ioctl(fd, KDSKBMODE, utf8 ? K_UNICODE : K_XLATE); if (r < 0) - log_warning_errno(r, "Failed to disable UTF-8: %m"); - - return r; -} + return log_warning_errno(errno, "Failed to %s UTF-8 kbdmode: %m", utf8 ? "enable" : "disable"); -static int enable_utf8(int fd) { - int r = 0, k; - long current = 0; - - if (ioctl(fd, KDGKBMODE, ¤t) < 0 || current == K_XLATE) { - /* - * Change the current keyboard to unicode, unless it - * is currently in raw or off mode anyway. We - * shouldn't interfere with X11's processing of the - * key events. - * - * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html - * - */ - - if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0) - r = -errno; + r = loop_write(fd, utf8 ? "\033%G" : "\033%@", 3, false); + if (r < 0) + return log_warning_errno(r, "Failed to %s UTF-8 term processing: %m", utf8 ? "enable" : "disable"); + + r = tcgetattr(fd, &tc); + if (r >= 0) { + if (utf8) + tc.c_iflag |= IUTF8; + else + tc.c_iflag &= ~IUTF8; + r = tcsetattr(fd, TCSANOW, &tc); } + if (r < 0) + return log_warning_errno(errno, "Failed to %s iutf8 flag: %m", utf8 ? "enable" : "disable"); - k = loop_write(fd, "\033%G", 3, false); - if (k < 0) - r = k; + return 0; +} - k = write_string_file("/sys/module/vt/parameters/default_utf8", "1", 0); - if (k < 0) - r = k; +static int toggle_utf8_sysfs(bool utf8) { + int r; + r = write_string_file("/sys/module/vt/parameters/default_utf8", one_zero(utf8), 0); if (r < 0) - log_warning_errno(r, "Failed to enable UTF-8: %m"); - + log_warning_errno(r, "Failed to %s sysfs UTF-8 flag: %m", utf8 ? "enable" : "disable"); return r; } @@ -306,10 +289,8 @@ int main(int argc, char **argv) { log_warning_errno(r, "Failed to read /proc/cmdline: %m"); } - if (utf8) - (void) enable_utf8(fd); - else - (void) disable_utf8(fd); + toggle_utf8_sysfs(utf8); + toggle_utf8(fd, utf8); font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) > 0; keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0; -- cgit v1.2.3 From 03044059bfff8b79d47efc53e2468e122f502a72 Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: Add generic is_*() functions is_allocated() and is_allocated_byfd(): Checks if the console is allocated by its index (first function) or its open descriptor (second function). is_settable(): Checks if the console is in xlate or unicode mode, so we can adjust is safely without interfering with X. --- src/vconsole/vconsole-setup.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 016cf004a0..15e7ed39b4 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -51,6 +51,37 @@ static bool is_vconsole(int fd) { return ioctl(fd, TIOCLINUX, data) >= 0; } +static bool is_allocated(unsigned int idx) { + char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)]; + + xsprintf(vcname, "/dev/vcs%i", idx); + return access(vcname, F_OK) == 0; +} + +static bool is_allocated_byfd(int fd) { + struct vt_stat vcs = {}; + + if (ioctl(fd, VT_GETSTATE, &vcs) < 0) { + log_warning_errno(errno, "VT_GETSTATE failed: %m"); + return false; + } + return is_allocated(vcs.v_active); +} + +static bool is_settable(int fd) { + int r, curr_mode; + + r = ioctl(fd, KDGKBMODE, &curr_mode); + /* + * Make sure we only adjust consoles in K_XLATE or K_UNICODE mode. + * Oterwise we would (likely) interfere with X11's processing of the + * key events. + * + * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html + */ + return r == 0 && IN_SET(curr_mode, K_XLATE, K_UNICODE); +} + static int toggle_utf8(int fd, bool utf8) { int r; struct termios tc = {}; @@ -262,6 +293,16 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } + if (!is_allocated_byfd(fd)) { + log_error("Virtual console %s is not allocated.", vc); + return EXIT_FAILURE; + } + + if (!is_settable(fd)) { + log_error("Virtual console %s is not in K_XLATE or K_UNICODE.", vc); + return EXIT_FAILURE; + } + utf8 = is_locale_utf8(); r = parse_env_file("/etc/vconsole.conf", NEWLINE, -- cgit v1.2.3 From c9d2b3d0f00a00049aeb8a6dba78200e6df48244 Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: updates of keyboard/font loading functions Change return convention to -errno/==0 and use isempty() instead of just pointer tests. --- src/vconsole/vconsole-setup.c | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 15e7ed39b4..c98c699922 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -119,12 +119,12 @@ static int toggle_utf8_sysfs(bool utf8) { static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) { const char *args[8]; - int i = 0, r; + int i = 0; pid_t pid; /* An empty map means kernel map */ if (isempty(map)) - return 1; + return 0; args[i++] = KBD_LOADKEYS; args[i++] = "-q"; @@ -149,34 +149,31 @@ static int keyboard_load_and_wait(const char *vc, const char *map, const char *m _exit(EXIT_FAILURE); } - r = wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true); - if (r < 0) - return r; - - return r == 0; + return wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true); } static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) { const char *args[9]; - int i = 0, r; + int i = 0; pid_t pid; - /* An empty font means kernel font */ - if (isempty(font)) - return 1; + /* Any part can be set independently */ + if (isempty(font) && isempty(map) && isempty(unimap)) + return 0; args[i++] = KBD_SETFONT; args[i++] = "-C"; args[i++] = vc; - args[i++] = font; - if (map) { + if (!isempty(map)) { args[i++] = "-m"; args[i++] = map; } - if (unimap) { + if (!isempty(unimap)) { args[i++] = "-u"; args[i++] = unimap; } + if (!isempty(font)) + args[i++] = font; args[i++] = NULL; pid = fork(); @@ -191,11 +188,7 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map, _exit(EXIT_FAILURE); } - r = wait_for_terminate_and_warn(KBD_SETFONT, pid, true); - if (r < 0) - return r; - - return r == 0; + return wait_for_terminate_and_warn(KBD_SETFONT, pid, true); } /* @@ -333,8 +326,8 @@ int main(int argc, char **argv) { toggle_utf8_sysfs(utf8); toggle_utf8(fd, utf8); - font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) > 0; - keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0; + font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) == 0; + keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) == 0; /* Only copy the font when we executed setfont successfully */ if (font_copy && font_ok) -- cgit v1.2.3 From eb22d84b47f16bedb60824b441900715fd79841d Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: use KD_FONT_OP_GET/SET to handle copying We now use KD_FONT_OP_GET & KD_FONT_OP_SET instead of problematic KD_FONT_OP_COPY. --- src/vconsole/vconsole-setup.c | 93 ++++++++++++++++++++++++++++++------------- 1 file changed, 66 insertions(+), 27 deletions(-) diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index c98c699922..5da807bf17 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -196,11 +196,21 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map, * we update all possibly already allocated VTs with the configured * font. It also allows to restart systemd-vconsole-setup.service, * to apply a new font to all VTs. + * + * We also setup per-console utf8 related stuff: kbdmode, term + * processing, stty iutf8. */ -static void font_copy_to_all_vcs(int fd) { +static void setup_remaining_vcs(int fd, bool utf8) { + struct console_font_op cfo = { + .op = KD_FONT_OP_GET, .flags = 0, + .width = 32, .height = 32, + .charcount = 512, + }; struct vt_stat vcs = {}; + struct unimapinit adv = {}; struct unimapdesc unimapd; _cleanup_free_ struct unipair* unipairs = NULL; + _cleanup_free_ void *fontbuf = NULL; int i, r; unipairs = new(struct unipair, USHRT_MAX); @@ -209,46 +219,73 @@ static void font_copy_to_all_vcs(int fd) { return; } + fontbuf = malloc(cfo.width * cfo.height * cfo.charcount / 8); + if (!fontbuf) { + log_oom(); + return; + } + /* get active, and 16 bit mask of used VT numbers */ r = ioctl(fd, VT_GETSTATE, &vcs); if (r < 0) { - log_debug_errno(errno, "VT_GETSTATE failed, ignoring: %m"); + log_warning_errno(errno, "VT_GETSTATE failed, ignoring remaining consoles: %m"); return; } + /* get fonts from source console */ + cfo.data = fontbuf; + r = ioctl(fd, KDFONTOP, &cfo); + if (r < 0) + log_warning_errno(errno, "KD_FONT_OP_GET failed, fonts will not be copied: %m"); + else { + unimapd.entries = unipairs; + unimapd.entry_ct = USHRT_MAX; + r = ioctl(fd, GIO_UNIMAP, &unimapd); + if (r < 0) + log_warning_errno(errno, "GIO_UNIMAP failed, fonts will not be copied: %m"); + else + cfo.op = KD_FONT_OP_SET; + } + for (i = 1; i <= 63; i++) { - char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)]; - _cleanup_close_ int vcfd = -1; - struct console_font_op cfo = {}; + char ttyname[strlen("/dev/tty") + DECIMAL_STR_MAX(int)]; + _cleanup_close_ int fd_d = -1; - if (i == vcs.v_active) + if (i == vcs.v_active || !is_allocated(i)) continue; - /* skip non-allocated ttys */ - xsprintf(vcname, "/dev/vcs%i", i); - if (access(vcname, F_OK) < 0) + /* try to open terminal */ + xsprintf(ttyname, "/dev/tty%i", i); + fd_d = open_terminal(ttyname, O_RDWR|O_CLOEXEC); + if (fd_d < 0) { + log_warning_errno(fd_d, "Unable to open tty%i, fonts will not be copied: %m", i); continue; + } - xsprintf(vcname, "/dev/tty%i", i); - vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC); - if (vcfd < 0) + if (!is_settable(fd_d)) continue; - /* copy font from active VT, where the font was uploaded to */ - cfo.op = KD_FONT_OP_COPY; - cfo.height = vcs.v_active-1; /* tty1 == index 0 */ - (void) ioctl(vcfd, KDFONTOP, &cfo); + toggle_utf8(fd_d, utf8); + + if (cfo.op != KD_FONT_OP_SET) + continue; + + r = ioctl(fd_d, KDFONTOP, &cfo); + if (r < 0) { + log_warning_errno(errno, "KD_FONT_OP_SET failed, fonts will not be copied to tty%i: %m", i); + continue; + } /* copy unicode translation table */ /* unimapd is a ushort count and a pointer to an array of struct unipair { ushort, ushort } */ - unimapd.entries = unipairs; - unimapd.entry_ct = USHRT_MAX; - if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) { - struct unimapinit adv = { 0, 0, 0 }; - - (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv); - (void) ioctl(vcfd, PIO_UNIMAP, &unimapd); + r = ioctl(fd_d, PIO_UNIMAPCLR, &adv); + if (r < 0) + log_warning_errno(errno, "PIO_UNIMAPCLR failed, unimaps might be incorrect for tty%i: %m", i); + else { + r = ioctl(fd_d, PIO_UNIMAP, &unimapd); + if (r < 0) + log_warning_errno(errno, "PIO_UNIMAP failed, unimaps might be incorrect for tty%i: %m", i); } } } @@ -325,13 +362,15 @@ int main(int argc, char **argv) { toggle_utf8_sysfs(utf8); toggle_utf8(fd, utf8); - font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) == 0; keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) == 0; - /* Only copy the font when we executed setfont successfully */ - if (font_copy && font_ok) - (void) font_copy_to_all_vcs(fd); + if (font_copy) { + if (font_ok) + setup_remaining_vcs(fd, utf8); + else + log_warning("Setting source virtual console failed, ignoring remaining ones."); + } return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE; } -- cgit v1.2.3 From 8125e8d38e3aa099c7dce8b0161997b8842aebdc Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: Don't do static installation under sysinit.target Udev rules cover all the necessary initializations. As the service now is neither installed, nor installable - we can remove explicit dependencies and RemainAfterExit=yes option. --- Makefile.am | 3 --- units/systemd-vconsole-setup.service.in | 3 --- 2 files changed, 6 deletions(-) diff --git a/Makefile.am b/Makefile.am index c9fb4917ad..7535b505fb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4628,9 +4628,6 @@ nodist_udevrules_DATA += \ nodist_systemunit_DATA += \ units/systemd-vconsole-setup.service - -SYSINIT_TARGET_WANTS += \ - systemd-vconsole-setup.service endif EXTRA_DIST += \ diff --git a/units/systemd-vconsole-setup.service.in b/units/systemd-vconsole-setup.service.in index 6160361871..2bd1fd1a5d 100644 --- a/units/systemd-vconsole-setup.service.in +++ b/units/systemd-vconsole-setup.service.in @@ -9,11 +9,8 @@ Description=Setup Virtual Console Documentation=man:systemd-vconsole-setup.service(8) man:vconsole.conf(5) DefaultDependencies=no -Conflicts=shutdown.target -Before=sysinit.target shutdown.target ConditionPathExists=/dev/tty0 [Service] Type=oneshot -RemainAfterExit=yes ExecStart=@rootlibexecdir@/systemd-vconsole-setup -- cgit v1.2.3 From af7a5213f13051b5c399787d8a88ab640382192c Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: add copyright line --- src/vconsole/vconsole-setup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 5da807bf17..a8f4f05771 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -2,6 +2,7 @@ This file is part of systemd. Copyright 2010 Kay Sievers + Copyright 2016 Michal Soltys 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 -- cgit v1.2.3 From 9e303250acafe284a3406b71dbe9a7620ee71fcc Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: correct kernel command line namespace --- src/vconsole/vconsole-setup.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index a8f4f05771..59e6c90c9a 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -351,8 +351,12 @@ int main(int argc, char **argv) { if (detect_container() <= 0) { r = parse_env_file("/proc/cmdline", WHITESPACE, "vconsole.keymap", &vc_keymap, - "vconsole.keymap.toggle", &vc_keymap_toggle, + "vconsole.keymap_toggle", &vc_keymap_toggle, "vconsole.font", &vc_font, + "vconsole.font_map", &vc_font_map, + "vconsole.font_unimap", &vc_font_unimap, + /* compatibility with obsolete multiple-dot scheme */ + "vconsole.keymap.toggle", &vc_keymap_toggle, "vconsole.font.map", &vc_font_map, "vconsole.font.unimap", &vc_font_unimap, NULL); -- cgit v1.2.3 From 61eca97fceaa95f76732a47ae2a7eb98cc6995c5 Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: update man page - about namespace - about udev rules --- man/vconsole.conf.xml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/man/vconsole.conf.xml b/man/vconsole.conf.xml index 27196d44e9..7f6ae3452f 100644 --- a/man/vconsole.conf.xml +++ b/man/vconsole.conf.xml @@ -55,8 +55,11 @@ The /etc/vconsole.conf file configures the virtual console, i.e. keyboard mapping and console font. It is - applied at boot by - systemd-vconsole-setup.service8. + applied at boot by udev using 90-vconsole.rules file. + You can safely mask this file if you want to avoid this kind of initialization. + There is also systemd-vconsole-setup.service8 + provided that you can conveniently use at any time to [re]initialize consoles. + The basic file format of the vconsole.conf is a newline-separated list of @@ -68,10 +71,10 @@ Note that the kernel command line options vconsole.keymap=, - vconsole.keymap.toggle=, + vconsole.keymap_toggle=, vconsole.font=, - vconsole.font.map=, - vconsole.font.unimap= may be used + vconsole.font_map=, + vconsole.font_unimap= may be used to override the console settings at boot. Depending on the operating system other configuration files -- cgit v1.2.3 From b6b609dbc202e5645fc58e87b8a7d46426ee4bb7 Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Wed, 27 Jul 2016 05:32:37 +0200 Subject: string-util: rework memory_erase() to not use GCC optimize attribute (#3812) "#pragma GCC optimize" is merely a convenience to decorate multiple functions with attribute optimize. And the manual has this to say about this attribute: This attribute should be used for debugging purposes only. It is not suitable in production code. Some versions of GCC also seem to have a problem with this pragma in combination with LTO, resulting in ICEs. So use a different approach (indirect the memset call via a volatile function pointer) as implemented in openssl's crypto/mem_clr.c. Closes: #3811 --- src/basic/string-util.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/basic/string-util.c b/src/basic/string-util.c index e9856b90d3..5d4510e1b3 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "alloc-util.h" #include "gunicode.h" @@ -822,25 +823,20 @@ int free_and_strdup(char **p, const char *s) { return 1; } -#pragma GCC push_options -#pragma GCC optimize("O0") +/* + * Pointer to memset is volatile so that compiler must de-reference + * the pointer and can't assume that it points to any function in + * particular (such as memset, which it then might further "optimize") + * This approach is inspired by openssl's crypto/mem_clr.c. + */ +typedef void *(*memset_t)(void *,int,size_t); -void* memory_erase(void *p, size_t l) { - volatile uint8_t* x = (volatile uint8_t*) p; - - /* This basically does what memset() does, but hopefully isn't - * optimized away by the compiler. One of those days, when - * glibc learns memset_s() we should replace this call by - * memset_s(), but until then this has to do. */ - - for (; l > 0; l--) - *(x++) = 'x'; +static volatile memset_t memset_func = memset; - return p; +void* memory_erase(void *p, size_t l) { + return memset_func(p, 'x', l); } -#pragma GCC pop_options - char* string_erase(char *x) { if (!x) -- cgit v1.2.3 From d5317f3544654d9a95bd589c75b94ea39facc3b5 Mon Sep 17 00:00:00 2001 From: Dennis Wassenberg Date: Tue, 26 Jul 2016 16:41:47 +0200 Subject: hwdb: add Lenovo T440p pointing stick speed fix Like many other recent thinkpads the factory default pointingstick sensitivity on these devices is quite low, making the pointingstick very slow in moving the cursor. This extends the existing hwdb rules for tweaking the sensitivity to also apply to the T440p models. --- hwdb/70-pointingstick.hwdb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb index ec166ead40..d69e234773 100644 --- a/hwdb/70-pointingstick.hwdb +++ b/hwdb/70-pointingstick.hwdb @@ -97,6 +97,8 @@ evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX230 evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX230Tablet:* # Lenovo Thinkpad X240 evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX240:* +# Lenovo Thinkpad T440p +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT440p:* # Lenovo Thinkpad T440s evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT440s:* # Lenovo Thinkpad T540p -- cgit v1.2.3 From f8a0b229d2c5193a9a8538807338f8f7199debc2 Mon Sep 17 00:00:00 2001 From: Dennis Wassenberg Date: Tue, 26 Jul 2016 16:47:54 +0200 Subject: hwdb: add Lenovo L450 pointing stick speed fix Like many other recent thinkpads the factory default pointingstick sensitivity on these devices is quite low, making the pointingstick very slow in moving the cursor. This extends the existing hwdb rules for tweaking the sensitivity to also apply to the L450 models. --- hwdb/70-pointingstick.hwdb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb index d69e234773..f38ff3fae2 100644 --- a/hwdb/70-pointingstick.hwdb +++ b/hwdb/70-pointingstick.hwdb @@ -103,6 +103,8 @@ evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT440 evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT440s:* # Lenovo Thinkpad T540p evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT540p:* +# Lenovo Thinkpad L450 +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadL450:* # Lenovo Thinkpad T550 / W550s evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT550:* # Lenovo Thinkpad X1 Carbon 3rd gen -- cgit v1.2.3 From 9e67418c407bd6d75a7612b66ef7d945c15689a6 Mon Sep 17 00:00:00 2001 From: Dennis Wassenberg Date: Tue, 26 Jul 2016 16:50:07 +0200 Subject: hwdb: add Lenovo T450s pointing stick speed fix Like many other recent thinkpads the factory default pointingstick sensitivity on these devices is quite low, making the pointingstick very slow in moving the cursor. This extends the existing hwdb rules for tweaking the sensitivity to also apply to the T450s models. --- hwdb/70-pointingstick.hwdb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb index f38ff3fae2..92966bafb8 100644 --- a/hwdb/70-pointingstick.hwdb +++ b/hwdb/70-pointingstick.hwdb @@ -105,6 +105,8 @@ evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT440 evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT540p:* # Lenovo Thinkpad L450 evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadL450:* +# Lenovo Thinkpad T450s +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT450s:* # Lenovo Thinkpad T550 / W550s evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT550:* # Lenovo Thinkpad X1 Carbon 3rd gen -- cgit v1.2.3 From 612ad804827545a5da0ec14558c6ddb5f816b228 Mon Sep 17 00:00:00 2001 From: Dennis Wassenberg Date: Wed, 27 Jul 2016 10:56:50 +0200 Subject: hwdb: add Lenovo X250 pointing stick speed fix Like many other recent thinkpads the factory default pointingstick sensitivity on these devices is quite low, making the pointingstick very slow in moving the cursor. This extends the existing hwdb rules for tweaking the sensitivity to also apply to the X250 models. --- hwdb/70-pointingstick.hwdb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb index 92966bafb8..ba1939c8e7 100644 --- a/hwdb/70-pointingstick.hwdb +++ b/hwdb/70-pointingstick.hwdb @@ -109,6 +109,8 @@ evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadL450 evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT450s:* # Lenovo Thinkpad T550 / W550s evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT550:* +# Lenovo Thinkpad X250 +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX250:* # Lenovo Thinkpad X1 Carbon 3rd gen evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Carbon3rd:* # Lenovo Thinkpad X1 Carbon 4th gen -- cgit v1.2.3 From 95f6fc608d63aa0fbeb5d170dbe1066e5a70a1cb Mon Sep 17 00:00:00 2001 From: Dennis Wassenberg Date: Wed, 27 Jul 2016 10:59:38 +0200 Subject: hwdb: add Lenovo L460 pointing stick speed fix Like many other recent thinkpads the factory default pointingstick sensitivity on these devices is quite low, making the pointingstick very slow in moving the cursor. This extends the existing hwdb rules for tweaking the sensitivity to also apply to the L460 models. --- hwdb/70-pointingstick.hwdb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb index ba1939c8e7..cdda2e8837 100644 --- a/hwdb/70-pointingstick.hwdb +++ b/hwdb/70-pointingstick.hwdb @@ -111,6 +111,8 @@ evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT450 evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT550:* # Lenovo Thinkpad X250 evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX250:* +# Lenovo Thinkpad L460 +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadL460:* # Lenovo Thinkpad X1 Carbon 3rd gen evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Carbon3rd:* # Lenovo Thinkpad X1 Carbon 4th gen -- cgit v1.2.3 From ded495d94774dce142540ed2463142b2aed0592f Mon Sep 17 00:00:00 2001 From: Dennis Wassenberg Date: Wed, 27 Jul 2016 11:00:27 +0200 Subject: hwdb: add Lenovo T460s pointing stick speed fix Like many other recent thinkpads the factory default pointingstick sensitivity on these devices is quite low, making the pointingstick very slow in moving the cursor. This extends the existing hwdb rules for tweaking the sensitivity to also apply to the T460s models. --- hwdb/70-pointingstick.hwdb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb index cdda2e8837..31306ce77c 100644 --- a/hwdb/70-pointingstick.hwdb +++ b/hwdb/70-pointingstick.hwdb @@ -113,6 +113,8 @@ evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT550 evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX250:* # Lenovo Thinkpad L460 evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadL460:* +# Lenovo Thinkpad T460s +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT460s:* # Lenovo Thinkpad X1 Carbon 3rd gen evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Carbon3rd:* # Lenovo Thinkpad X1 Carbon 4th gen -- cgit v1.2.3 From 4499df99c1e3e376438287a20a64a5bc8b30a2d7 Mon Sep 17 00:00:00 2001 From: Dennis Wassenberg Date: Wed, 27 Jul 2016 11:00:56 +0200 Subject: hwdb: add Lenovo T560 pointing stick speed fix Like many other recent thinkpads the factory default pointingstick sensitivity on these devices is quite low, making the pointingstick very slow in moving the cursor. This extends the existing hwdb rules for tweaking the sensitivity to also apply to the T560 models. --- hwdb/70-pointingstick.hwdb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb index 31306ce77c..16695f2b2c 100644 --- a/hwdb/70-pointingstick.hwdb +++ b/hwdb/70-pointingstick.hwdb @@ -115,6 +115,8 @@ evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX250 evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadL460:* # Lenovo Thinkpad T460s evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT460s:* +# Lenovo Thinkpad T560 +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT560:* # Lenovo Thinkpad X1 Carbon 3rd gen evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Carbon3rd:* # Lenovo Thinkpad X1 Carbon 4th gen -- cgit v1.2.3 From 57b8b16237187e64bcc99564af4d0133b7bed698 Mon Sep 17 00:00:00 2001 From: Dennis Wassenberg Date: Wed, 27 Jul 2016 11:01:27 +0200 Subject: hwdb: add Lenovo X260 pointing stick speed fix Like many other recent thinkpads the factory default pointingstick sensitivity on these devices is quite low, making the pointingstick very slow in moving the cursor. This extends the existing hwdb rules for tweaking the sensitivity to also apply to the X260 models. --- hwdb/70-pointingstick.hwdb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb index 16695f2b2c..8a74573904 100644 --- a/hwdb/70-pointingstick.hwdb +++ b/hwdb/70-pointingstick.hwdb @@ -117,6 +117,8 @@ evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadL460 evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT460s:* # Lenovo Thinkpad T560 evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT560:* +# Lenovo Thinkpad X260 +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX260:* # Lenovo Thinkpad X1 Carbon 3rd gen evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Carbon3rd:* # Lenovo Thinkpad X1 Carbon 4th gen -- cgit v1.2.3 From 689e4e6a94222b4d58a8b9cb3c51cc2f82268aa9 Mon Sep 17 00:00:00 2001 From: Christian Rebischke Date: Thu, 28 Jul 2016 04:40:20 +0200 Subject: systemctl: be sure to be quiet with 'systemctl is-enabled --quiet' (#3819) Fixes #3813. --- src/systemctl/systemctl.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 91caae009e..782824ff38 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -5566,10 +5566,12 @@ static int enable_sysv_units(const char *verb, char **args) { if (!found_sysv) continue; - if (found_native) - log_info("Synchronizing state of %s with SysV service script with %s.", name, argv[0]); - else - log_info("%s is not a native service, redirecting to systemd-sysv-install.", name); + if (!arg_quiet) { + if (found_native) + log_info("Synchronizing state of %s with SysV service script with %s.", name, argv[0]); + else + log_info("%s is not a native service, redirecting to systemd-sysv-install.", name); + } if (!isempty(arg_root)) argv[c++] = q = strappend("--root=", arg_root); -- cgit v1.2.3 From 4e3382bdd70a54628ffe030c5d37e06713c329ab Mon Sep 17 00:00:00 2001 From: Davide Cavalca Date: Thu, 28 Jul 2016 20:42:45 -0700 Subject: build-sys: conditionally disable LTO if requested (#3823) This adds a --disable-lto option to ./configure, but does not change the default behavior. --- configure.ac | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index cf595e68c0..a86deca471 100644 --- a/configure.ac +++ b/configure.ac @@ -211,9 +211,12 @@ AS_CASE([$CC], [*clang*], -Wno-gnu-variable-sized-type-not-at-end \ ])]) +AC_ARG_ENABLE([lto], [AS_HELP_STRING([--disable-lto], [disable -flto])], + [], [enable_lto=yes]) AS_CASE([$CFLAGS], [*-O[[12345sz\ ]]*], - [CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [\ - -flto])], + [AS_IF([test "x$enable_lto" = "xyes"], + [CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [-flto])], + [AC_MSG_RESULT([disabling -flto as requested])])], [AC_MSG_RESULT([skipping -flto, optimization not enabled])]) AC_SUBST([OUR_CFLAGS], "$with_cflags $sanitizer_cflags") -- cgit v1.2.3 From e4d214efc20245c37d9b1e3a7e35c6a4443da131 Mon Sep 17 00:00:00 2001 From: Davide Cavalca Date: Wed, 27 Jul 2016 14:17:52 -0700 Subject: tests: skip udev-test if running inside a chroot --- test/udev-test.pl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/udev-test.pl b/test/udev-test.pl index da0a4e1f6b..35a2668fb3 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -1535,6 +1535,13 @@ if (!($<==0)) { exit($EXIT_TEST_SKIP); } +# skip the test when running in a chroot +system("systemd-detect-virt", "-r", "-q"); +if ($? >> 8 == 0) { + print "Running in a chroot, skipping the test.\n"; + exit($EXIT_TEST_SKIP); +} + # skip the test when running in a container system("systemd-detect-virt", "-c", "-q"); if ($? >> 8 == 0) { -- cgit v1.2.3 From 40a23924efc64598d580b5fd92ef3937583d1fc9 Mon Sep 17 00:00:00 2001 From: Steve Muir Date: Wed, 27 Jul 2016 14:19:37 -0700 Subject: tests: don't test hostname if it looks like an id128 The condition tests for hostname will fail if hostname looks like an id128. The test function attempts to convert hostname to an id128, and if that succeeds compare it to the machine ID (presumably because the 'hostname' condition test is overloaded to also test machine ID). That will typically fail, and unfortunately the 'mock' utility generates a random hostname that happens to have the same format as an id128, thus causing a test failure. --- src/test/test-condition.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/test/test-condition.c b/src/test/test-condition.c index 987862f1c6..4ef61ebfa5 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -25,6 +25,7 @@ #include "audit-util.h" #include "condition.h" #include "hostname-util.h" +#include "id128-util.h" #include "ima-util.h" #include "log.h" #include "macro.h" @@ -142,9 +143,14 @@ static void test_condition_test_host(void) { hostname = gethostname_malloc(); assert_se(hostname); - condition = condition_new(CONDITION_HOST, hostname, false, false); - assert_se(condition_test(condition)); - condition_free(condition); + /* if hostname looks like an id128 then skip testing it */ + if (id128_is_valid(hostname)) { + log_notice("hostname is an id128, skipping test"); + } else { + condition = condition_new(CONDITION_HOST, hostname, false, false); + assert_se(condition_test(condition)); + condition_free(condition); + } } static void test_condition_test_architecture(void) { -- cgit v1.2.3 From 76c19e9f6c8e8e5922e43716137d532c542e34c8 Mon Sep 17 00:00:00 2001 From: Davide Cavalca Date: Wed, 27 Jul 2016 14:22:26 -0700 Subject: tests: skip process 1 tests if systemd not is running No point running tests against process 1 if systemd is not running as that process. This is a rework of an unpublished patch by @9muir. --- src/test/test-process-util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c index 562ad4acb8..9ada46b1e9 100644 --- a/src/test/test-process-util.c +++ b/src/test/test-process-util.c @@ -40,6 +40,7 @@ #include "stdio-util.h" #include "string-util.h" #include "terminal-util.h" +#include "test-helper.h" #include "util.h" #include "virt.h" @@ -357,7 +358,7 @@ int main(int argc, char *argv[]) { (void) parse_pid(argv[1], &pid); test_get_process_comm(pid); } else { - test_get_process_comm(1); + TEST_REQ_RUNNING_SYSTEMD(test_get_process_comm(1)); test_get_process_comm(getpid()); } -- cgit v1.2.3 From 4dd4cb8fe40baea007336a346ec2aa645890eec3 Mon Sep 17 00:00:00 2001 From: Steve Muir Date: Wed, 27 Jul 2016 14:23:44 -0700 Subject: tests: don't run private device tests if running in a container Private devices don't exist when running in a container, so skip the related tests. --- src/test/test-execute.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 77ef4e8b2a..baf5b96487 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -33,6 +33,7 @@ #include "test-helper.h" #include "unit.h" #include "util.h" +#include "virt.h" typedef void (*test_function_t)(Manager *m); @@ -111,6 +112,10 @@ static void test_exec_privatetmp(Manager *m) { } static void test_exec_privatedevices(Manager *m) { + if (detect_container() > 0) { + log_notice("testing in container, skipping private device tests"); + return; + } test(m, "exec-privatedevices-yes.service", 0, CLD_EXITED); test(m, "exec-privatedevices-no.service", 0, CLD_EXITED); } -- cgit v1.2.3 From 65c1cdb282d33f72d2c247dfe13dd7c8baa8f302 Mon Sep 17 00:00:00 2001 From: Maxime de Roucy Date: Sun, 31 Jul 2016 14:38:56 +0200 Subject: documentation: add cgroup-v2.txt link add cgroup-v2.txt link in section "Unified and Legacy Control Group Hierarchies" of systemd.resource-control man. --- man/systemd.resource-control.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml index bf44a68345..76f3370b53 100644 --- a/man/systemd.resource-control.xml +++ b/man/systemd.resource-control.xml @@ -99,9 +99,10 @@ Unified and Legacy Control Group Hierarchies - The unified control group hierarchy is the new version of kernel control group interface. Depending on the - resource type, there are differences in resource control capabilities. Also, because of interface changes, some - resource types have a separate set of options on the unified hierarchy. + The unified control group hierarchy is the new version of kernel control group interface, see cgroup-v2.txt. Depending on the resource type, + there are differences in resource control capabilities. Also, because of interface changes, some resource types + have separate set of options on the unified hierarchy. -- cgit v1.2.3 From c23b2c70bf65d3fdf869c60eee4072b1d4e5905b Mon Sep 17 00:00:00 2001 From: Maxime de Roucy Date: Sun, 31 Jul 2016 14:43:23 +0200 Subject: documentation: cgroup-v1 and systemd user instance Explain in the systemd.resource-control man that systemd user instance can't use resource control on cgroup-v1. --- man/systemd.resource-control.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml index 76f3370b53..cb8c896e5c 100644 --- a/man/systemd.resource-control.xml +++ b/man/systemd.resource-control.xml @@ -127,6 +127,13 @@ settings of a unit for a given resource type are for the other hierarchy type, the settings are translated and applied. If there are any valid settings for the hierarchy in use, all translations are disabled for the resource type. Mixing the two types of settings on a unit can lead to confusing results. + + Legacy control group hierarchy (see cgroups.txt), also called cgroup-v1, + doesn't allow safe delegation of controllers to unprivileged processes. If the system use legacy control group + hierarchy, resource control is disabled for systemd user instance, see + systemd1. + -- cgit v1.2.3 From cb3e4417590196bd30e1b8097348dca6ba34bd15 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 31 Jul 2016 21:38:47 -0400 Subject: logind: 0% and 100% should be valid for UserTasksMax (#3836) config_parse_user_tasks_max() was incorrectly accepting percentage value between 1 and 99. Update it to accept 0% and 100%. This brings it in line with TasksMax handling in systemd. --- src/login/logind-user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 348e396292..63363035e7 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -893,7 +893,7 @@ int config_parse_user_tasks_max( /* First, try to parse as percentage */ r = parse_percent(rvalue); - if (r > 0 && r < 100) + if (r >= 0) k = system_tasks_max_scale(r, 100U); else { -- cgit v1.2.3 From 6d1e2ddd59f929ce98e400cb14fba7a873e2b409 Mon Sep 17 00:00:00 2001 From: Mike Gilbert Date: Sun, 31 Jul 2016 21:50:50 -0400 Subject: test-path-util: check for /lt-test-path-util or /test-path-util (#3841) Depending on how binutils was configured and the --enable-fast-install configure option, the test binary might be called either name. Fixes: https://github.com/systemd/systemd/issues/3838 --- src/test/test-path-util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index 6094d4c3e5..164a10d8a8 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -114,7 +114,8 @@ static void test_find_binary(const char *self) { assert_se(find_binary(self, &p) == 0); puts(p); - assert_se(endswith(p, "/lt-test-path-util")); + /* libtool might prefix the binary name with "lt-" */ + assert_se(endswith(p, "/lt-test-path-util") || endswith(p, "/test-path-util")); assert_se(path_is_absolute(p)); free(p); -- cgit v1.2.3 From 2d37cd5356f666b399c5ae93ce77053f501d0e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 24 Jul 2016 14:12:58 -0400 Subject: Add enable_disable() helper In this patch "enabled" and "disabled" is used exclusively, but "enable" and "disable" forms are need for the following patch. --- src/basic/util.h | 4 ++++ src/boot/bootctl.c | 2 +- src/journal-remote/journal-remote.c | 2 +- src/network/networkd-link.c | 8 ++------ src/timedate/timedated.c | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/basic/util.h b/src/basic/util.h index 44497dcd78..bb2fc318ef 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -61,6 +61,10 @@ static inline const char* one_zero(bool b) { return b ? "1" : "0"; } +static inline const char* enable_disable(bool b) { + return b ? "enable" : "disable"; +} + void execute_directories(const char* const* directories, usec_t timeout, char *argv[]); bool plymouth_running(void); diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index ff8c7a38dd..a7cdf92ed2 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -1094,7 +1094,7 @@ static int verb_status(int argc, char *argv[], void *userdata) { if (r < 0) log_warning_errno(r, "Failed to query secure boot status: %m"); else - printf(" Secure Boot: %s\n", r ? "enabled" : "disabled"); + printf(" Secure Boot: %sd\n", enable_disable(r)); r = is_efi_secure_boot_setup_mode(); if (r < 0) diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index 35a1e55f9e..f1ef90ed7a 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -1564,7 +1564,7 @@ int main(int argc, char **argv) { if (r < 0) log_error_errno(r, "Failed to enable watchdog: %m"); else - log_debug("Watchdog is %s.", r > 0 ? "enabled" : "disabled"); + log_debug("Watchdog is %sd.", enable_disable(r > 0)); log_debug("%s running as pid "PID_FMT, program_invocation_short_name, getpid()); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 82f56158be..a0da697707 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -256,12 +256,8 @@ static int link_enable_ipv6(Link *link) { r = write_string_file(p, one_zero(disabled), WRITE_STRING_FILE_VERIFY_ON_FAILURE); if (r < 0) log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m", disabled ? "disable" : "enable", link->ifname); - else { - if (disabled) - log_link_info(link, "IPv6 disabled for interface: %m"); - else - log_link_info(link, "IPv6 enabled for interface: %m"); - } + else + log_link_info(link, "IPv6 %sd for interface: %m", enable_disable(!disabled)); return 0; } diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c index ffec609c69..490929e93b 100644 --- a/src/timedate/timedated.c +++ b/src/timedate/timedated.c @@ -637,7 +637,7 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error return r; c->use_ntp = enabled; - log_info("Set NTP to %s", enabled ? "enabled" : "disabled"); + log_info("Set NTP to %sd", enable_disable(enabled)); (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL); -- cgit v1.2.3 From aaa709bbaa6b909278f3ec0679b49a484423d913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 24 Jul 2016 14:14:20 -0400 Subject: vconsole-setup: add lots of debug messages For error messages, make them more meaningful by printing the tty name. Follow-up for #3742. --- src/vconsole/vconsole-setup.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 59e6c90c9a..c0d76f9685 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -83,17 +83,19 @@ static bool is_settable(int fd) { return r == 0 && IN_SET(curr_mode, K_XLATE, K_UNICODE); } -static int toggle_utf8(int fd, bool utf8) { +static int toggle_utf8(const char *name, int fd, bool utf8) { int r; struct termios tc = {}; + assert(name); + r = ioctl(fd, KDSKBMODE, utf8 ? K_UNICODE : K_XLATE); if (r < 0) - return log_warning_errno(errno, "Failed to %s UTF-8 kbdmode: %m", utf8 ? "enable" : "disable"); + return log_warning_errno(errno, "Failed to %s UTF-8 kbdmode on %s: %m", enable_disable(utf8), name); r = loop_write(fd, utf8 ? "\033%G" : "\033%@", 3, false); if (r < 0) - return log_warning_errno(r, "Failed to %s UTF-8 term processing: %m", utf8 ? "enable" : "disable"); + return log_warning_errno(r, "Failed to %s UTF-8 term processing on %s: %m", enable_disable(utf8), name); r = tcgetattr(fd, &tc); if (r >= 0) { @@ -104,8 +106,9 @@ static int toggle_utf8(int fd, bool utf8) { r = tcsetattr(fd, TCSANOW, &tc); } if (r < 0) - return log_warning_errno(errno, "Failed to %s iutf8 flag: %m", utf8 ? "enable" : "disable"); + return log_warning_errno(errno, "Failed to %s iutf8 flag on %s: %m", enable_disable(utf8), name); + log_debug("UTF-8 kbdmode %sd on %s", enable_disable(utf8), name); return 0; } @@ -114,8 +117,10 @@ static int toggle_utf8_sysfs(bool utf8) { r = write_string_file("/sys/module/vt/parameters/default_utf8", one_zero(utf8), 0); if (r < 0) - log_warning_errno(r, "Failed to %s sysfs UTF-8 flag: %m", utf8 ? "enable" : "disable"); - return r; + return log_warning_errno(r, "Failed to %s sysfs UTF-8 flag: %m", enable_disable(utf8)); + + log_debug("Sysfs UTF-8 flag %sd", enable_disable(utf8)); + return 0; } static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) { @@ -266,7 +271,7 @@ static void setup_remaining_vcs(int fd, bool utf8) { if (!is_settable(fd_d)) continue; - toggle_utf8(fd_d, utf8); + toggle_utf8(ttyname, fd_d, utf8); if (cfo.op != KD_FONT_OP_SET) continue; @@ -281,13 +286,18 @@ static void setup_remaining_vcs(int fd, bool utf8) { /* unimapd is a ushort count and a pointer to an array of struct unipair { ushort, ushort } */ r = ioctl(fd_d, PIO_UNIMAPCLR, &adv); - if (r < 0) + if (r < 0) { log_warning_errno(errno, "PIO_UNIMAPCLR failed, unimaps might be incorrect for tty%i: %m", i); - else { - r = ioctl(fd_d, PIO_UNIMAP, &unimapd); - if (r < 0) - log_warning_errno(errno, "PIO_UNIMAP failed, unimaps might be incorrect for tty%i: %m", i); + continue; } + + r = ioctl(fd_d, PIO_UNIMAP, &unimapd); + if (r < 0) { + log_warning_errno(errno, "PIO_UNIMAP failed, unimaps might be incorrect for tty%i: %m", i); + continue; + } + + log_debug("Font and unimap successfully copied to %s", ttyname); } } @@ -366,7 +376,7 @@ int main(int argc, char **argv) { } toggle_utf8_sysfs(utf8); - toggle_utf8(fd, utf8); + toggle_utf8(vc, fd, utf8); font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) == 0; keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) == 0; @@ -374,7 +384,7 @@ int main(int argc, char **argv) { if (font_ok) setup_remaining_vcs(fd, utf8); else - log_warning("Setting source virtual console failed, ignoring remaining ones."); + log_warning("Setting source virtual console failed, ignoring remaining ones"); } return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE; -- cgit v1.2.3 From 72ccee50d0d08e2339ec283d669eb9ed282aac29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 29 Jul 2016 01:01:04 -0400 Subject: man: move description of kernel vconsole.conf overrides to vconsole.conf(5) They were outdated, and this way it's less likely that they'll get out of sync again. Anyway, it's easier for the reader to have the kernel and config file options next to one another. --- man/kernel-command-line.xml | 11 +++++----- man/systemd-vconsole-setup.service.xml | 36 +------------------------------- man/vconsole.conf.xml | 38 +++++++++++++++++++++++++++------- 3 files changed, 36 insertions(+), 49 deletions(-) diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml index 3ecc969c10..1fa31a14b7 100644 --- a/man/kernel-command-line.xml +++ b/man/kernel-command-line.xml @@ -224,15 +224,14 @@ vconsole.keymap= - vconsole.keymap.toggle= + vconsole.keymap_toggle= vconsole.font= - vconsole.font.map= - vconsole.font.unimap= + vconsole.font_map= + vconsole.font_unimap= - Parameters understood by the virtual console setup - logic. For details, see - systemd-vconsole-setup.service8. + Parameters understood by the virtual console setup logic. For details, see + vconsole.conf5. diff --git a/man/systemd-vconsole-setup.service.xml b/man/systemd-vconsole-setup.service.xml index ff079761c1..e048258621 100644 --- a/man/systemd-vconsole-setup.service.xml +++ b/man/systemd-vconsole-setup.service.xml @@ -63,41 +63,7 @@ See vconsole.conf5 - for information about the configuration files understood by this - service. - - - - - - Kernel Command Line - - A few configuration parameters from - vconsole.conf may be overridden on the kernel - command line: - - - - vconsole.keymap= - vconsole.keymap.toggle= - - Overrides the key mapping table for the - keyboard and the second toggle keymap. - - - - vconsole.font= - vconsole.font.map= - vconsole.font.unimap= - - Configures the console font, the console map, - and the unicode font map. - - - - See - vconsole.conf5 - for information about these settings. + for information about the configuration files and kernel command line options understood by this program. diff --git a/man/vconsole.conf.xml b/man/vconsole.conf.xml index 7f6ae3452f..fa30ca6569 100644 --- a/man/vconsole.conf.xml +++ b/man/vconsole.conf.xml @@ -57,8 +57,6 @@ the virtual console, i.e. keyboard mapping and console font. It is applied at boot by udev using 90-vconsole.rules file. You can safely mask this file if you want to avoid this kind of initialization. - There is also systemd-vconsole-setup.service8 - provided that you can conveniently use at any time to [re]initialize consoles. The basic file format of the @@ -93,12 +91,10 @@ KEYMAP= KEYMAP_TOGGLE= - Configures the key mapping table for the - keyboard. KEYMAP= defaults to - us if not set. The - KEYMAP_TOGGLE= can be used to configure a - second toggle keymap and is by default - unset. + Configures the key mapping table for the keyboard. + KEYMAP= defaults to us if not set. The + KEYMAP_TOGGLE= can be used to configure a second toggle keymap and is by + default unset. @@ -113,6 +109,32 @@ + + Kernel Command Line + + A few configuration parameters from vconsole.conf may be overridden + on the kernel command line: + + + + vconsole.keymap= + vconsole.keymap_toggle= + + Overrides KEYMAP= and KEYMAP_TOGGLE=. + + + + + vconsole.font= + vconsole.font_map= + vconsole.font_unimap= + + Overrides FONT=, FONT_MAP=, and + FONT_UNIMAP=. + + + + Example -- cgit v1.2.3 From 494294d6f88612c20339cc34e4ef893a14565a0f Mon Sep 17 00:00:00 2001 From: 0xAX <0xAX@users.noreply.github.com> Date: Mon, 1 Aug 2016 13:38:25 +0300 Subject: main: get rid of ACTION_DONE (#3849) the ACTION_DONE was introduced in the 4288f61921 (dbus: automatically generate and install introspection files ) commit and was used in systemd --introspect command. Later 'introspect' command was removed in the ca2871d9b (bus: remove static introspection file export) commit and have no users anymore. So we can remove it. --- src/core/main.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/core/main.c b/src/core/main.c index f2adca7d2b..74b8ea139f 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -92,8 +92,7 @@ static enum { ACTION_HELP, ACTION_VERSION, ACTION_TEST, - ACTION_DUMP_CONFIGURATION_ITEMS, - ACTION_DONE + ACTION_DUMP_CONFIGURATION_ITEMS } arg_action = ACTION_RUN; static char *arg_default_unit = NULL; static bool arg_system = false; @@ -1618,9 +1617,6 @@ int main(int argc, char *argv[]) { unit_dump_config_items(stdout); retval = EXIT_SUCCESS; goto finish; - } else if (arg_action == ACTION_DONE) { - retval = EXIT_SUCCESS; - goto finish; } if (!arg_system && -- cgit v1.2.3 From aa0c34279ee40bce2f9681b496922dedbadfca19 Mon Sep 17 00:00:00 2001 From: Leonardo Brondani Schenkel Date: Mon, 1 Aug 2016 15:04:49 +0200 Subject: virt: detect bhyve (FreeBSD hypervisor) (#3840) The CPUID and DMI vendor strings do not seem to be documented. Values were found experimentally and by inspecting the source code. --- man/systemd-detect-virt.xml | 7 ++++++- src/basic/virt.c | 5 +++++ src/basic/virt.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/man/systemd-detect-virt.xml b/man/systemd-detect-virt.xml index 2b7f4e69ab..61a5f8937f 100644 --- a/man/systemd-detect-virt.xml +++ b/man/systemd-detect-virt.xml @@ -88,7 +88,7 @@ - VM + VM qemu QEMU software virtualization @@ -138,6 +138,11 @@ Parallels Desktop, Parallels Server + + bhyve + bhyve, FreeBSD hypervisor + + Container openvz diff --git a/src/basic/virt.c b/src/basic/virt.c index dace1f4328..10a2043746 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -49,6 +49,8 @@ static int detect_vm_cpuid(void) { { "VMwareVMware", VIRTUALIZATION_VMWARE }, /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */ { "Microsoft Hv", VIRTUALIZATION_MICROSOFT }, + /* https://wiki.freebsd.org/bhyve */ + { "bhyve bhyve ", VIRTUALIZATION_BHYVE }, }; uint32_t eax, ecx; @@ -178,6 +180,8 @@ static int detect_vm_dmi(void) { { "Xen", VIRTUALIZATION_XEN }, { "Bochs", VIRTUALIZATION_BOCHS }, { "Parallels", VIRTUALIZATION_PARALLELS }, + /* https://wiki.freebsd.org/bhyve */ + { "BHYVE", VIRTUALIZATION_BHYVE }, }; unsigned i; int r; @@ -502,6 +506,7 @@ static const char *const virtualization_table[_VIRTUALIZATION_MAX] = { [VIRTUALIZATION_MICROSOFT] = "microsoft", [VIRTUALIZATION_ZVM] = "zvm", [VIRTUALIZATION_PARALLELS] = "parallels", + [VIRTUALIZATION_BHYVE] = "bhyve", [VIRTUALIZATION_VM_OTHER] = "vm-other", [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn", diff --git a/src/basic/virt.h b/src/basic/virt.h index a538f07f6b..bc5b3ae94d 100644 --- a/src/basic/virt.h +++ b/src/basic/virt.h @@ -37,6 +37,7 @@ enum { VIRTUALIZATION_MICROSOFT, VIRTUALIZATION_ZVM, VIRTUALIZATION_PARALLELS, + VIRTUALIZATION_BHYVE, VIRTUALIZATION_VM_OTHER, VIRTUALIZATION_VM_LAST = VIRTUALIZATION_VM_OTHER, -- cgit v1.2.3 From a912ab0474e2fcdc346d0315a8493f0f3ac867aa Mon Sep 17 00:00:00 2001 From: 0xAX <0xAX@users.noreply.github.com> Date: Mon, 1 Aug 2016 18:39:27 +0300 Subject: machinectl: hide legend in a case when no data (#3839) For this moment machinectl prints legend and count of machines/images/etc. But in a case when we have no images,machines,etc., there is no sense to show legend: ~$ machinectl MACHINE CLASS SERVICE 0 machines listed. Let's print only 'No machines', 'No images', 'No transfers' in this case. --- src/machine/machinectl.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index ddec6cb4d6..c78ca7ad76 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -185,7 +185,7 @@ static int list_machines(int argc, char *argv[], void *userdata) { qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info); - if (arg_legend) + if (arg_legend && n_machines > 0) printf("%-*s %-*s %-*s\n", (int) max_name, "MACHINE", (int) max_class, "CLASS", @@ -197,8 +197,10 @@ static int list_machines(int argc, char *argv[], void *userdata) { (int) max_class, machines[j].class, (int) max_service, machines[j].service); - if (arg_legend) + if (arg_legend && n_machines > 0) printf("\n%zu machines listed.\n", n_machines); + else + printf("No machines.\n"); return 0; } @@ -305,7 +307,7 @@ static int list_images(int argc, char *argv[], void *userdata) { qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info); - if (arg_legend) + if (arg_legend && n_images > 0) printf("%-*s %-*s %-3s %-*s %-*s %-*s\n", (int) max_name, "NAME", (int) max_type, "TYPE", @@ -326,8 +328,10 @@ static int list_images(int argc, char *argv[], void *userdata) { (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime))); } - if (arg_legend) + if (arg_legend && n_images > 0) printf("\n%zu images listed.\n", n_images); + else + printf("No images.\n"); return 0; } @@ -2314,7 +2318,7 @@ static int list_transfers(int argc, char *argv[], void *userdata) { qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info); - if (arg_legend) + if (arg_legend && n_transfers > 0) printf("%-*s %-*s %-*s %-*s %-*s\n", (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID", (int) 7, "PERCENT", @@ -2330,8 +2334,10 @@ static int list_transfers(int argc, char *argv[], void *userdata) { (int) max_local, transfers[j].local, (int) max_remote, transfers[j].remote); - if (arg_legend) + if (arg_legend && n_transfers > 0) printf("\n%zu transfers listed.\n", n_transfers); + else + printf("No transfers.\n"); return 0; } -- cgit v1.2.3 From d7032b1fcd6e51b736698a8b264273c625084643 Mon Sep 17 00:00:00 2001 From: Jakub Filak Date: Tue, 26 Apr 2016 09:54:39 +0200 Subject: coredump: save /proc/[pid]/mountinfo The file contains information one can use to debug processes running within a container. --- src/coredump/coredump.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index dcc09fcc6d..953f04e205 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -933,11 +933,12 @@ static int process_kernel(int argc, char* argv[]) { /* The larger ones we allocate on the heap */ _cleanup_free_ char *core_owner_uid = NULL, *core_open_fds = NULL, *core_proc_status = NULL, - *core_proc_maps = NULL, *core_proc_limits = NULL, *core_proc_cgroup = NULL, *core_environ = NULL; + *core_proc_maps = NULL, *core_proc_limits = NULL, *core_proc_cgroup = NULL, *core_environ = NULL, + *core_proc_mountinfo = NULL; _cleanup_free_ char *exe = NULL, *comm = NULL; const char *context[_CONTEXT_MAX]; - struct iovec iovec[25]; + struct iovec iovec[26]; size_t n_iovec = 0; uid_t owner_uid; const char *p; @@ -1110,6 +1111,15 @@ static int process_kernel(int argc, char* argv[]) { IOVEC_SET_STRING(iovec[n_iovec++], core_proc_cgroup); } + p = procfs_file_alloca(pid, "mountinfo"); + if (read_full_file(p, &t, NULL) >=0) { + core_proc_mountinfo = strappend("COREDUMP_PROC_MOUNTINFO=", t); + free(t); + + if (core_proc_mountinfo) + IOVEC_SET_STRING(iovec[n_iovec++], core_proc_mountinfo); + } + if (get_process_cwd(pid, &t) >= 0) { core_cwd = strjoina("COREDUMP_CWD=", t); free(t); -- cgit v1.2.3 From 7633f8ef371a0992374956437fb7bb8189156b10 Mon Sep 17 00:00:00 2001 From: tblume Date: Tue, 2 Aug 2016 14:55:25 +0200 Subject: systemd-ask-password: make sure directory watch is started before cryptsetup (#3850) The password directory watch should get ordered before cryptsetup to make sure that the password for unlocking the crypt device gets prompted. --- units/systemd-ask-password-console.path | 2 +- units/systemd-ask-password-wall.path | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/units/systemd-ask-password-console.path b/units/systemd-ask-password-console.path index 2949635fea..7899ae788f 100644 --- a/units/systemd-ask-password-console.path +++ b/units/systemd-ask-password-console.path @@ -11,7 +11,7 @@ Documentation=man:systemd-ask-password-console.service(8) DefaultDependencies=no Conflicts=shutdown.target After=plymouth-start.service -Before=paths.target shutdown.target +Before=paths.target shutdown.target cryptsetup.target ConditionPathExists=!/run/plymouth/pid [Path] diff --git a/units/systemd-ask-password-wall.path b/units/systemd-ask-password-wall.path index 95ec9bc8a0..a3ca617256 100644 --- a/units/systemd-ask-password-wall.path +++ b/units/systemd-ask-password-wall.path @@ -10,7 +10,7 @@ Description=Forward Password Requests to Wall Directory Watch Documentation=man:systemd-ask-password-console.service(8) DefaultDependencies=no Conflicts=shutdown.target -Before=paths.target shutdown.target +Before=paths.target shutdown.target cryptsetup.target [Path] DirectoryNotEmpty=/run/systemd/ask-password -- cgit v1.2.3 From 98d2d46876c08d6f2ae63284ec5a28f90cbbb8ac Mon Sep 17 00:00:00 2001 From: Martin Pitt Date: Tue, 2 Aug 2016 14:56:45 +0200 Subject: units: add graphical-session-pre.target user unit (#3848) This complements graphical-session.target for services which set up the environment (e. g. dbus-update-activation-environment) and need to run before the actual graphical session. --- Makefile.am | 3 ++- man/systemd.special.xml | 13 +++++++++++++ units/user/graphical-session-pre.target | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 units/user/graphical-session-pre.target diff --git a/Makefile.am b/Makefile.am index 7dd021b56a..763b16289d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -574,7 +574,8 @@ dist_userunit_DATA = \ units/user/basic.target \ units/user/default.target \ units/user/exit.target \ - units/user/graphical-session.target + units/user/graphical-session.target \ + units/user/graphical-session-pre.target nodist_userunit_DATA = \ units/user/systemd-exit.service diff --git a/man/systemd.special.xml b/man/systemd.special.xml index 18142598cb..d977298cd8 100644 --- a/man/systemd.special.xml +++ b/man/systemd.special.xml @@ -927,6 +927,19 @@ PartOf=graphical-session.target + + + graphical-session-pre.target + + This target contains services which set up the environment or + global configuration of a graphical session, such as SSH/GPG agents + (which need to export an environment variable into all desktop processes) + or migration of obsolete d-conf keys after an OS upgrade (which needs to + happen before starting any process that might use them). This target must + be started before starting a graphical session + like gnome-session.target. + + diff --git a/units/user/graphical-session-pre.target b/units/user/graphical-session-pre.target new file mode 100644 index 0000000000..86d15aff33 --- /dev/null +++ b/units/user/graphical-session-pre.target @@ -0,0 +1,14 @@ +# This file is part of systemd. +# +# 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. + +[Unit] +Description=Session services which should run early before the graphical session is brought up +Documentation=man:systemd.special(7) +Requires=basic.target +Before=graphical-session.target +RefuseManualStart=yes +StopWhenUnneeded=yes -- cgit v1.2.3 From 96694e998be1f631861c8c72de67c581a5306afa Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Tue, 2 Aug 2016 15:58:30 +0300 Subject: main: load Smack policy before IMA policy (#3859) IMA wiki says: "If the IMA policy contains LSM labels, then the LSM policy must be loaded prior to the IMA policy." Right now, in case of Smack, the IMA policy is loaded before the Smack policy. Move the order around to allow Smack labels to be used in IMA policy. --- src/core/main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/main.c b/src/core/main.c index 74b8ea139f..c46d886653 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1414,12 +1414,12 @@ int main(int argc, char *argv[]) { if (mac_selinux_setup(&loaded_policy) < 0) { error_message = "Failed to load SELinux policy"; goto finish; - } else if (ima_setup() < 0) { - error_message = "Failed to load IMA policy"; - goto finish; } else if (mac_smack_setup(&loaded_policy) < 0) { error_message = "Failed to load SMACK policy"; goto finish; + } else if (ima_setup() < 0) { + error_message = "Failed to load IMA policy"; + goto finish; } dual_timestamp_get(&security_finish_timestamp); } -- cgit v1.2.3 From 7ed03ce69ede34b03107702efd6efb100e9a23bc Mon Sep 17 00:00:00 2001 From: Jakub Filak Date: Wed, 27 Apr 2016 15:23:49 +0200 Subject: coredump: save process container parent cmdline Process container parent is the process used to start processes with a new user namespace - e.g systemd-nspawn, runc, lxc, etc. There is not standard way how to find such a process - or I do not know about it - hence I have decided to find the first process in the parent process hierarchy with a different mount namespace and different /proc/self/root's inode. I have decided for this criteria because in ABRT we take special care only if the crashed process runs different code than installed on the host. Other processes with namespaces different than PID 1's namespaces are just processes running code shipped by the OS vendor and bug reporting tools can get information about the provider of the code without the need to deal with changed root and so on. --- src/coredump/coredump.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 3 deletions(-) diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 953f04e205..e3d17c864d 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -558,6 +558,89 @@ static int compose_open_fds(pid_t pid, char **open_fds) { return 0; } +static int get_process_ns(pid_t pid, const char *namespace, ino_t *ns) { + const char *p; + struct stat stbuf; + _cleanup_close_ int proc_ns_dir_fd; + + p = procfs_file_alloca(pid, "ns"); + + proc_ns_dir_fd = open(p, O_DIRECTORY | O_CLOEXEC | O_RDONLY); + if (proc_ns_dir_fd < 0) + return -errno; + + if (fstatat(proc_ns_dir_fd, namespace, &stbuf, /* flags */0) < 0) + return -errno; + + *ns = stbuf.st_ino; + return 0; +} + +static int get_mount_namespace_leader(pid_t pid, pid_t *container_pid) { + pid_t cpid = pid, ppid = 0; + ino_t proc_mntns; + int r = 0; + + r = get_process_ns(pid, "mnt", &proc_mntns); + if (r < 0) + return r; + + while (1) { + ino_t parent_mntns; + + r = get_process_ppid(cpid, &ppid); + if (r < 0) + return r; + + r = get_process_ns(ppid, "mnt", &parent_mntns); + if (r < 0) + return r; + + if (proc_mntns != parent_mntns) + break; + + if (ppid == 1) + return -ENOENT; + + cpid = ppid; + } + + *container_pid = ppid; + return 0; +} + +/* Returns 1 if the parent was found. + * Returns 0 if there is not a process we can call the pid's + * container parent (the pid's process isn't 'containerized'). + * Returns a negative number on errors. + */ +static int get_process_container_parent_cmdline(pid_t pid, char** cmdline) { + int r = 0; + pid_t container_pid; + const char *proc_root_path; + struct stat root_stat, proc_root_stat; + + /* To compare inodes of / and /proc/[pid]/root */ + if (stat("/", &root_stat) < 0) + return -errno; + + proc_root_path = procfs_file_alloca(pid, "root"); + if (stat(proc_root_path, &proc_root_stat) < 0) + return -errno; + + /* The process uses system root. */ + if (proc_root_stat.st_ino == root_stat.st_ino) { + *cmdline = NULL; + return 0; + } + + r = get_mount_namespace_leader(pid, &container_pid); + if (r < 0) + return r; + + return get_process_cmdline(container_pid, 0, false, cmdline); +} + static int change_uid_gid(const char *context[]) { uid_t uid; gid_t gid; @@ -934,11 +1017,12 @@ static int process_kernel(int argc, char* argv[]) { _cleanup_free_ char *core_owner_uid = NULL, *core_open_fds = NULL, *core_proc_status = NULL, *core_proc_maps = NULL, *core_proc_limits = NULL, *core_proc_cgroup = NULL, *core_environ = NULL, - *core_proc_mountinfo = NULL; + *core_proc_mountinfo = NULL, *core_container_cmdline = NULL; _cleanup_free_ char *exe = NULL, *comm = NULL; const char *context[_CONTEXT_MAX]; - struct iovec iovec[26]; + bool proc_self_root_is_slash; + struct iovec iovec[27]; size_t n_iovec = 0; uid_t owner_uid; const char *p; @@ -1129,9 +1213,20 @@ static int process_kernel(int argc, char* argv[]) { if (get_process_root(pid, &t) >= 0) { core_root = strjoina("COREDUMP_ROOT=", t); - free(t); IOVEC_SET_STRING(iovec[n_iovec++], core_root); + + /* If the process' root is "/", then there is a chance it has + * mounted own root and hence being containerized. */ + proc_self_root_is_slash = strcmp(t, "/") == 0; + free(t); + if (proc_self_root_is_slash && get_process_container_parent_cmdline(pid, &t) > 0) { + core_container_cmdline = strappend("COREDUMP_CONTAINER_CMDLINE=", t); + free(t); + + if (core_container_cmdline) + IOVEC_SET_STRING(iovec[n_iovec++], core_container_cmdline); + } } if (get_process_environ(pid, &t) >= 0) { -- cgit v1.2.3 From cce9c80af32ac7f84607123c81da8c2ee028e55e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 2 Aug 2016 10:04:39 -0400 Subject: gitignore: libsystemd-journal.pc is no more (#3863) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …since 4de282cf9324ab. --- src/journal/.gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/src/journal/.gitignore b/src/journal/.gitignore index 04d5852547..b93a9462fa 100644 --- a/src/journal/.gitignore +++ b/src/journal/.gitignore @@ -1,4 +1,3 @@ /journald-gperf.c -/libsystemd-journal.pc /audit_type-list.txt /audit_type-*-name.* -- cgit v1.2.3 From 125918635516491dc8ec1d803c38b9225b3599b9 Mon Sep 17 00:00:00 2001 From: Jan Synacek Date: Tue, 2 Aug 2016 16:22:56 +0200 Subject: test: fix test-execute personality tests on ppc64 and aarch64 (#3825) --- Makefile.am | 3 +++ src/test/test-execute.c | 10 ++++++++++ test/test-execute/exec-personality-aarch64.service | 7 +++++++ test/test-execute/exec-personality-ppc64.service | 7 +++++++ test/test-execute/exec-personality-ppc64le.service | 7 +++++++ 5 files changed, 34 insertions(+) create mode 100644 test/test-execute/exec-personality-aarch64.service create mode 100644 test/test-execute/exec-personality-ppc64.service create mode 100644 test/test-execute/exec-personality-ppc64le.service diff --git a/Makefile.am b/Makefile.am index 763b16289d..bf853c8f90 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1629,6 +1629,9 @@ EXTRA_DIST += \ test/test-execute/exec-personality-x86-64.service \ test/test-execute/exec-personality-x86.service \ test/test-execute/exec-personality-s390.service \ + test/test-execute/exec-personality-ppc64.service \ + test/test-execute/exec-personality-ppc64le.service \ + test/test-execute/exec-personality-aarch64.service \ test/test-execute/exec-privatedevices-no.service \ test/test-execute/exec-privatedevices-yes.service \ test/test-execute/exec-privatetmp-no.service \ diff --git a/src/test/test-execute.c b/src/test/test-execute.c index baf5b96487..1d24115b5c 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -92,6 +92,16 @@ static void test_exec_personality(Manager *m) { #elif defined(__s390__) test(m, "exec-personality-s390.service", 0, CLD_EXITED); +#elif defined(__powerpc64__) +# if __BYTE_ORDER == __BIG_ENDIAN + test(m, "exec-personality-ppc64.service", 0, CLD_EXITED); +# else + test(m, "exec-personality-ppc64le.service", 0, CLD_EXITED); +# endif + +#elif defined(__aarch64__) + test(m, "exec-personality-aarch64.service", 0, CLD_EXITED); + #elif defined(__i386__) test(m, "exec-personality-x86.service", 0, CLD_EXITED); #endif diff --git a/test/test-execute/exec-personality-aarch64.service b/test/test-execute/exec-personality-aarch64.service new file mode 100644 index 0000000000..40b6d95e3a --- /dev/null +++ b/test/test-execute/exec-personality-aarch64.service @@ -0,0 +1,7 @@ +Unit] +Description=Test for Personality=aarch64 + +[Service] +ExecStart=/bin/sh -c 'echo $(uname -m); exit $(test $(uname -m) = "aarch64")' +Type=oneshot +Personality=aarch64 diff --git a/test/test-execute/exec-personality-ppc64.service b/test/test-execute/exec-personality-ppc64.service new file mode 100644 index 0000000000..ccc2c8d83d --- /dev/null +++ b/test/test-execute/exec-personality-ppc64.service @@ -0,0 +1,7 @@ +[Unit] +Description=Test for Personality=ppc64 + +[Service] +ExecStart=/bin/sh -c 'echo $(uname -m); exit $(test $(uname -m) = "ppc64")' +Type=oneshot +Personality=ppc64 diff --git a/test/test-execute/exec-personality-ppc64le.service b/test/test-execute/exec-personality-ppc64le.service new file mode 100644 index 0000000000..2a7625087d --- /dev/null +++ b/test/test-execute/exec-personality-ppc64le.service @@ -0,0 +1,7 @@ +[Unit] +Description=Test for Personality=ppc64le + +[Service] +ExecStart=/bin/sh -c 'echo $(uname -m); exit $(test $(uname -m) = "ppc64le")' +Type=oneshot +Personality=ppc64le -- cgit v1.2.3 From 9d565427647a874b7eda64ef0cb41ea74a96659b Mon Sep 17 00:00:00 2001 From: Susant Sahani Date: Tue, 2 Aug 2016 23:18:23 +0530 Subject: socket: add support to control no. of connections from one source (#3607) Introduce MaxConnectionsPerSource= that is number of concurrent connections allowed per IP. RFE: 1939 --- man/systemd.socket.xml | 8 ++ src/core/dbus-socket.c | 1 + src/core/load-fragment-gperf.gperf.m4 | 1 + src/core/service.c | 1 + src/core/service.h | 1 + src/core/socket.c | 185 ++++++++++++++++++++++++++++++++++ src/core/socket.h | 16 +++ 7 files changed, 213 insertions(+) diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 5bf54d8ef3..26e5d3ce7b 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -443,6 +443,14 @@ + MaxConnectionsPerSource= + The maximum number of connections for a service per source IP address. + This is is very similar to the MaxConnections= directive + above. Disabled by default. + + + + KeepAlive= Takes a boolean argument. If true, the TCP/IP stack will send a keep alive message after 2h (depending on diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index 961340608d..9a071a1355 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -137,6 +137,7 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("Symlinks", "as", NULL, offsetof(Socket, symlinks), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Mark", "i", bus_property_get_int, offsetof(Socket, mark), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MaxConnections", "u", bus_property_get_unsigned, offsetof(Socket, max_connections), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MaxConnectionsPerSource", "u", bus_property_get_unsigned, offsetof(Socket, max_connections_per_source), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MessageQueueMaxMessages", "x", bus_property_get_long, offsetof(Socket, mq_maxmsg), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MessageQueueMessageSize", "x", bus_property_get_long, offsetof(Socket, mq_msgsize), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ReusePort", "b", bus_property_get_bool, offsetof(Socket, reuse_port), SD_BUS_VTABLE_PROPERTY_CONST), diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index c9cdbe8ba7..396f847213 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -293,6 +293,7 @@ Socket.DirectoryMode, config_parse_mode, 0, Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept) Socket.Writable, config_parse_bool, 0, offsetof(Socket, writable) Socket.MaxConnections, config_parse_unsigned, 0, offsetof(Socket, max_connections) +Socket.MaxConnectionsPerSource, config_parse_unsigned, 0, offsetof(Socket, max_connections_per_source) Socket.KeepAlive, config_parse_bool, 0, offsetof(Socket, keep_alive) Socket.KeepAliveTimeSec, config_parse_sec, 0, offsetof(Socket, keep_alive_time) Socket.KeepAliveIntervalSec, config_parse_sec, 0, offsetof(Socket, keep_alive_interval) diff --git a/src/core/service.c b/src/core/service.c index 4d59d78ecb..eb125cb999 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -342,6 +342,7 @@ static void service_done(Unit *u) { s->bus_name_owner = mfree(s->bus_name_owner); service_close_socket_fd(s); + s->peer = socket_peer_unref(s->peer); unit_ref_unset(&s->accept_socket); diff --git a/src/core/service.h b/src/core/service.h index 8e56e1acb9..888007cc0b 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -152,6 +152,7 @@ struct Service { pid_t main_pid, control_pid; int socket_fd; + SocketPeer *peer; bool socket_fd_selinux_context_net; bool permissions_start_only; diff --git a/src/core/socket.c b/src/core/socket.c index 1ce41a1f07..ff55885fb3 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -57,6 +57,7 @@ #include "unit-printf.h" #include "unit.h" #include "user-util.h" +#include "in-addr-util.h" static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { [SOCKET_DEAD] = UNIT_INACTIVE, @@ -77,6 +78,9 @@ static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); +SocketPeer *socket_peer_new(void); +int socket_find_peer(Socket *s, int fd, SocketPeer **p); + static void socket_init(Unit *u) { Socket *s = SOCKET(u); @@ -141,11 +145,17 @@ void socket_free_ports(Socket *s) { static void socket_done(Unit *u) { Socket *s = SOCKET(u); + SocketPeer *p; assert(s); socket_free_ports(s); + while ((p = hashmap_steal_first(s->peers_by_address))) + p->socket = NULL; + + s->peers_by_address = hashmap_free(s->peers_by_address); + s->exec_runtime = exec_runtime_unref(s->exec_runtime); exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX); s->control_command = NULL; @@ -468,6 +478,40 @@ static int socket_verify(Socket *s) { return 0; } +static void peer_address_hash_func(const void *p, struct siphash *state) { + const SocketPeer *s = p; + + assert(s); + + if (s->peer.sa.sa_family == AF_INET) + siphash24_compress(&s->peer.in.sin_addr, sizeof(s->peer.in.sin_addr), state); + else if (s->peer.sa.sa_family == AF_INET6) + siphash24_compress(&s->peer.in6.sin6_addr, sizeof(s->peer.in6.sin6_addr), state); +} + +static int peer_address_compare_func(const void *a, const void *b) { + const SocketPeer *x = a, *y = b; + + if (x->peer.sa.sa_family < y->peer.sa.sa_family) + return -1; + if (x->peer.sa.sa_family > y->peer.sa.sa_family) + return 1; + + switch(x->peer.sa.sa_family) { + case AF_INET: + return memcmp(&x->peer.in.sin_addr, &y->peer.in.sin_addr, sizeof(x->peer.in.sin_addr)); + case AF_INET6: + return memcmp(&x->peer.in6.sin6_addr, &y->peer.in6.sin6_addr, sizeof(x->peer.in6.sin6_addr)); + } + + return -1; +} + +const struct hash_ops peer_address_hash_ops = { + .hash = peer_address_hash_func, + .compare = peer_address_compare_func +}; + static int socket_load(Unit *u) { Socket *s = SOCKET(u); int r; @@ -475,6 +519,10 @@ static int socket_load(Unit *u) { assert(u); assert(u->load_state == UNIT_STUB); + r = hashmap_ensure_allocated(&s->peers_by_address, &peer_address_hash_ops); + if (r < 0) + return r; + r = unit_load_fragment_and_dropin(u); if (r < 0) return r; @@ -2050,6 +2098,7 @@ static void socket_enter_running(Socket *s, int cfd) { socket_set_state(s, SOCKET_RUNNING); } else { _cleanup_free_ char *prefix = NULL, *instance = NULL, *name = NULL; + _cleanup_(socket_peer_unrefp) SocketPeer *p = NULL; Service *service; if (s->n_connections >= s->max_connections) { @@ -2058,6 +2107,21 @@ static void socket_enter_running(Socket *s, int cfd) { return; } + if (s->max_connections_per_source > 0) { + r = socket_find_peer(s, cfd, &p); + if (r < 0) { + safe_close(cfd); + return; + } + + if (p->n_ref > s->max_connections_per_source) { + log_unit_warning(UNIT(s), "Too many incoming connections (%u) from source, refusing connection attempt.", p->n_ref); + safe_close(cfd); + p = NULL; + return; + } + } + r = socket_instantiate_service(s); if (r < 0) goto fail; @@ -2099,6 +2163,11 @@ static void socket_enter_running(Socket *s, int cfd) { cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */ s->n_connections++; + if (s->max_connections_per_source > 0) { + service->peer = socket_peer_ref(p); + p = NULL; + } + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL); if (r < 0) { /* We failed to activate the new service, but it still exists. Let's make sure the service @@ -2244,7 +2313,9 @@ static int socket_stop(Unit *u) { static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { Socket *s = SOCKET(u); + SocketPeer *k; SocketPort *p; + Iterator i; int r; assert(u); @@ -2295,6 +2366,16 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { } } + HASHMAP_FOREACH(k, s->peers_by_address, i) { + _cleanup_free_ char *t = NULL; + + r = sockaddr_pretty(&k->peer.sa, FAMILY_ADDRESS_SIZE(k->peer.sa.sa_family), true, true, &t); + if (r < 0) + return r; + + unit_serialize_item_format(u, f, "peer", "%u %s", k->n_ref, t); + } + return 0; } @@ -2458,6 +2539,33 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, } } + } else if (streq(key, "peer")) { + _cleanup_(socket_peer_unrefp) SocketPeer *p; + int n_ref, skip = 0; + SocketAddress a; + int r; + + if (sscanf(value, "%u %n", &n_ref, &skip) < 1 || n_ref < 1) + log_unit_debug(u, "Failed to parse socket peer value: %s", value); + else { + r = socket_address_parse(&a, value+skip); + if (r < 0) + return r; + + p = socket_peer_new(); + if (!p) + return log_oom(); + + p->n_ref = n_ref; + memcpy(&p->peer, &a.sockaddr, sizeof(a.sockaddr)); + p->socket = s; + + r = hashmap_put(s->peers_by_address, p, p); + if (r < 0) + return r; + + p = NULL; + } } else log_unit_debug(UNIT(s), "Unknown serialization key: %s", key); @@ -2554,6 +2662,83 @@ _pure_ static bool socket_check_gc(Unit *u) { return s->n_connections > 0; } +SocketPeer *socket_peer_new(void) { + SocketPeer *p; + + p = new0(SocketPeer, 1); + if (!p) + return NULL; + + p->n_ref = 1; + + return p; +} + +SocketPeer *socket_peer_ref(SocketPeer *p) { + if (!p) + return NULL; + + assert(p->n_ref > 0); + p->n_ref++; + + return p; +} + +SocketPeer *socket_peer_unref(SocketPeer *p) { + if (!p) + return NULL; + + assert(p->n_ref > 0); + + p->n_ref--; + + if (p->n_ref > 0) + return NULL; + + if (p->socket) + (void) hashmap_remove(p->socket->peers_by_address, p); + + free(p); + + return NULL; +} + +int socket_find_peer(Socket *s, int fd, SocketPeer **p) { + _cleanup_free_ SocketPeer *remote = NULL; + SocketPeer sa, *i; + socklen_t salen = sizeof(sa.peer); + int r; + + assert(fd >= 0); + assert(s); + + r = getpeername(fd, &sa.peer.sa, &salen); + if (r < 0) + return log_error_errno(errno, "getpeername failed: %m"); + + i = hashmap_get(s->peers_by_address, &sa); + if (i) { + *p = i; + return 1; + } + + remote = socket_peer_new(); + if (!remote) + return log_oom(); + + memcpy(&remote->peer, &sa.peer, sizeof(union sockaddr_union)); + remote->socket = s; + + r = hashmap_put(s->peers_by_address, remote, remote); + if (r < 0) + return r; + + *p = remote; + remote = NULL; + + return 0; +} + static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { SocketPort *p = userdata; int cfd = -1; diff --git a/src/core/socket.h b/src/core/socket.h index 6c32d67bef..2fe38ef2aa 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -20,6 +20,7 @@ ***/ typedef struct Socket Socket; +typedef struct SocketPeer SocketPeer; #include "mount.h" #include "service.h" @@ -79,9 +80,12 @@ struct Socket { LIST_HEAD(SocketPort, ports); + Hashmap *peers_by_address; + unsigned n_accepted; unsigned n_connections; unsigned max_connections; + unsigned max_connections_per_source; unsigned backlog; unsigned keep_alive_cnt; @@ -164,6 +168,18 @@ struct Socket { RateLimit trigger_limit; }; +struct SocketPeer { + unsigned n_ref; + + Socket *socket; + union sockaddr_union peer; +}; + +SocketPeer *socket_peer_ref(SocketPeer *p); +SocketPeer *socket_peer_unref(SocketPeer *p); + +DEFINE_TRIVIAL_CLEANUP_FUNC(SocketPeer*, socket_peer_unref); + /* Called from the service code when collecting fds */ int socket_collect_fds(Socket *s, int **fds); -- cgit v1.2.3 From d5c3b228f007b3fbda00a8352ce9e90df8950910 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 3 Aug 2016 20:34:56 +1000 Subject: hwdb: add ID_INPUT_TRACKBALL as additional identifier (#3872) Whether a device is a trackball or not is a physical property so we should store this globally, in one place. The new property must be set in addition to ID_INPUT_MOUSE, otherwise existing clients won't detect the device. No actual code changes required, the default match rule is simply checking for "Trackball" in the name (in a few versions), other entries need to be added manually. --- hwdb/70-mouse.hwdb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/hwdb/70-mouse.hwdb b/hwdb/70-mouse.hwdb index a5b39dc41e..b68c01066e 100644 --- a/hwdb/70-mouse.hwdb +++ b/hwdb/70-mouse.hwdb @@ -44,10 +44,18 @@ # udevadm info /dev/input/eventXX. # # Allowed properties are: +# ID_INPUT_TRACKBALL # MOUSE_DPI # MOUSE_WHEEL_CLICK_ANGLE # ######################################### +# ID_INPUT_TRACKBALL # +######################################### +# +# Specified *in additition* to ID_INPUT_MOUSE if the device is a trackball. +# Removing ID_INPUT_MOUSE will break backwards compatibility. +# +######################################### # MOUSE_DPI # ######################################### # @@ -101,6 +109,14 @@ # Sort by brand, type (usb, bluetooth), DPI, frequency. # For mice with switchable resolution, sort by the starred entry. +########################################## +# Generic +########################################## +mouse:*:name:*Trackball*: +mouse:*:name:*trackball*: +mouse:*:name:*TrackBall*: + ID_INPUT_TRACKBALL=1 + ########################################## # Apple ########################################## @@ -231,6 +247,7 @@ mouse:usb:v04b3p310c:name:USB Optical Mouse: # Logitech M570 trackball mouse:usb:v046dp1028:name:Logitech M570: MOUSE_DPI=540@167 + ID_INPUT_TRACKBALL=1 # Logitech USB-PS/2 M-BZ96C mouse:usb:v046dpc045:name:Logitech USB-PS/2 Optical Mouse: -- cgit v1.2.3 From e5bc3f4fdc2ac2f68b781adc958e9570e577538b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 3 Aug 2016 21:12:37 +1000 Subject: hwdb: compress the various Lenovo *40, *50 and *60 series (#3877) Each series has identical hardware, let's use a glob instead of listing them one by one. --- hwdb/70-pointingstick.hwdb | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb index 8a74573904..ff52f11e45 100644 --- a/hwdb/70-pointingstick.hwdb +++ b/hwdb/70-pointingstick.hwdb @@ -95,30 +95,12 @@ evdev:name:*DualPoint Stick:dmi:bvn*:bvr*:bd*:svnDellInc.:pnLatitudeE6400*:pvr* evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX230:* # Lenovo Thinkpad X230 tablet evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX230Tablet:* -# Lenovo Thinkpad X240 -evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX240:* -# Lenovo Thinkpad T440p -evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT440p:* -# Lenovo Thinkpad T440s -evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT440s:* -# Lenovo Thinkpad T540p -evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT540p:* -# Lenovo Thinkpad L450 -evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadL450:* -# Lenovo Thinkpad T450s -evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT450s:* -# Lenovo Thinkpad T550 / W550s -evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT550:* -# Lenovo Thinkpad X250 -evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX250:* -# Lenovo Thinkpad L460 -evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadL460:* -# Lenovo Thinkpad T460s -evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT460s:* -# Lenovo Thinkpad T560 -evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT560:* -# Lenovo Thinkpad X260 -evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX260:* +# Lenovo Thinkpad *40 series +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??40?:* +# Lenovo Thinkpad *50 series +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??50?:* +# Lenovo Thinkpad *60 series +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??60?:* # Lenovo Thinkpad X1 Carbon 3rd gen evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Carbon3rd:* # Lenovo Thinkpad X1 Carbon 4th gen -- cgit v1.2.3 From 3539724c26a1b2b00c4eb3c004b635a4b8647de6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Jul 2016 14:50:45 +0200 Subject: nspawn: try to bind mount resolved's resolv.conf snippet into the container This has the benefit that the container can follow the host's DNS server changes without us having to constantly update the container's resolv.conf settings. --- src/nspawn/nspawn.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 6cc1b9177d..05e1fc1ab7 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1254,24 +1254,39 @@ static int setup_resolv_conf(const char *dest) { /* Fix resolv.conf, if possible */ where = prefix_roota(dest, "/etc/resolv.conf"); + if (access("/usr/lib/systemd/resolv.conf", F_OK) >= 0) { + /* resolved is enabled on the host. In this, case bind mount its static resolv.conf file into the + * container, so that the container can use the host's resolver. Given that network namespacing is + * disabled it's only natural of the container also uses the host's resolver. It also has the big + * advantage that the container will be able to follow the host's DNS server configuration changes + * transparently. */ + + if (mount("/usr/lib/systemd/resolv.conf", where, NULL, MS_BIND, NULL) < 0) + log_warning_errno(errno, "Failed to mount /etc/resolv.conf in the container, ignoring: %m"); + else { + if (mount(NULL, where, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL) < 0) + return log_error_errno(errno, "Failed to remount /etc/resolv.conf read-only: %m"); + + return 0; + } + } + + /* If that didn't work, let's copy the file */ r = copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644, 0); if (r < 0) { - /* If the file already exists as symlink, let's - * suppress the warning, under the assumption that - * resolved or something similar runs inside and the - * symlink points there. + /* If the file already exists as symlink, let's suppress the warning, under the assumption that + * resolved or something similar runs inside and the symlink points there. * - * If the disk image is read-only, there's also no - * point in complaining. + * If the disk image is read-only, there's also no point in complaining. */ log_full_errno(IN_SET(r, -ELOOP, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to copy /etc/resolv.conf to %s: %m", where); + "Failed to copy /etc/resolv.conf to %s, ignoring: %m", where); return 0; } r = userns_lchown(where, 0, 0); if (r < 0) - log_warning_errno(r, "Failed to chown /etc/resolv.conf: %m"); + log_warning_errno(r, "Failed to chown /etc/resolv.conf, ignoring: %m"); return 0; } -- cgit v1.2.3 From a6b5216c7cb3055cd234bacac3f785f86dbaf873 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Jul 2016 14:56:17 +0200 Subject: nspawn: deprecate --share-system support This removes the --share-system switch: from the documentation, the --help text as well as the command line parsing. It's an ugly option, given that it kinda contradicts the whole concept of PID namespaces that nspawn implements. Since it's barely ever used, let's just deprecate it and remove it from the options. It might be useful as a debugging option, hence the functionality is kept around for now, exposed via an undocumented $SYSTEMD_NSPAWN_SHARE_SYSTEM environment variable. --- man/systemd-nspawn.xml | 27 +++------------------------ src/nspawn/nspawn.c | 10 +++++++--- 2 files changed, 10 insertions(+), 27 deletions(-) diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 9b623c8353..97b348b565 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -274,8 +274,7 @@ signals. It is recommended to use this mode to invoke arbitrary commands in containers, unless they have been modified to run correctly as PID 1. Or in other words: this switch should be used for pretty much all commands, except when the command refers to an init or shell implementation, as these are generally capable of running - correctly as PID 1. This option may not be combined with or - . + correctly as PID 1. This option may not be combined with . @@ -285,8 +284,7 @@ Automatically search for an init binary and invoke it as PID 1, instead of a shell or a user supplied program. If this option is used, arguments specified on the command line are used as arguments for the - init binary. This option may not be combined with or - . + init binary. This option may not be combined with . The following table explains the different modes of invocation and relationship to (see above): @@ -846,23 +844,6 @@ parameter may be used more than once. - - - - Allows the container to share certain system - facilities with the host. More specifically, this turns off - PID namespacing, UTS namespacing and IPC namespacing, and thus - allows the guest to see and interact more easily with - processes outside of the container. Note that using this - option makes it impossible to start up a full Operating System - in the container, as an init system cannot operate in this - mode. It is only useful to run specific programs or - applications this way, without involving an init system in the - container. This option implies . - This option may not be combined with - . - - @@ -877,9 +858,7 @@ and shown by tools such as ps1. If the container does not run an init system, it is - recommended to set this option to no. Note - that implies - . + recommended to set this option to no. diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 05e1fc1ab7..b7c2fe9e55 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -269,7 +269,6 @@ static void help(void) { " --overlay-ro=PATH[:PATH...]:PATH\n" " Similar, but creates a read-only overlay mount\n" " -E --setenv=NAME=VALUE Pass an environment variable to PID 1\n" - " --share-system Share system namespaces with host\n" " --register=BOOLEAN Register container as machine\n" " --keep-unit Do not register a scope for the machine, reuse\n" " the service unit nspawn is running in\n" @@ -405,7 +404,7 @@ static int parse_argv(int argc, char *argv[]) { { "selinux-context", required_argument, NULL, 'Z' }, { "selinux-apifs-context", required_argument, NULL, 'L' }, { "quiet", no_argument, NULL, 'q' }, - { "share-system", no_argument, NULL, ARG_SHARE_SYSTEM }, + { "share-system", no_argument, NULL, ARG_SHARE_SYSTEM }, /* not documented */ { "register", required_argument, NULL, ARG_REGISTER }, { "keep-unit", no_argument, NULL, ARG_KEEP_UNIT }, { "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE }, @@ -814,6 +813,8 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_SHARE_SYSTEM: + /* We don't officially support this anymore, except for compat reasons. People should use the + * $SYSTEMD_NSPAWN_SHARE_SYSTEM environment variable instead. */ arg_share_system = true; break; @@ -1018,6 +1019,9 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } + if (getenv_bool("SYSTEMD_NSPAWN_SHARE_SYSTEM") > 0) + arg_share_system = true; + if (arg_share_system) arg_register = false; @@ -1025,7 +1029,7 @@ static int parse_argv(int argc, char *argv[]) { arg_userns_chown = true; if (arg_start_mode != START_PID1 && arg_share_system) { - log_error("--boot and --share-system may not be combined."); + log_error("--boot and SYSTEMD_NSPAWN_SHARE_SYSTEM=1 may not be combined."); return -EINVAL; } -- cgit v1.2.3 From 43992e57e0bf479a583e90fa2e23f0f1aa2fc2fb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Jul 2016 15:12:42 +0200 Subject: core: drop spurious newline --- src/core/manager.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/manager.c b/src/core/manager.c index e41b65da50..c20e185d78 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -553,7 +553,6 @@ static int manager_default_environment(Manager *m) { return 0; } - int manager_new(UnitFileScope scope, bool test_run, Manager **_m) { Manager *m; int r; -- cgit v1.2.3 From 6af760f3b263d3ddfa80a4168ad0a0c5e59bae1f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Jul 2016 15:25:55 +0200 Subject: core: inherit TERM from PID 1 for all services started on /dev/console This way, invoking nspawn from a shell in the best case inherits the TERM setting all the way down into the login shell spawned in the container. Fixes: #3697 --- src/basic/terminal-util.c | 2 +- src/core/execute.c | 60 +++++++++++++++++++++++++++++++++-------------- src/core/main.c | 2 +- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index df56d85317..f0a46c48cf 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -785,7 +785,7 @@ bool tty_is_vc_resolve(const char *tty) { } const char *default_term_for_tty(const char *tty) { - return tty && tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220"; + return tty && tty_is_vc_resolve(tty) ? "linux" : "vt220"; } int fd_columns(int fd) { diff --git a/src/core/execute.c b/src/core/execute.c index 26e9cd5339..0bf80fc437 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -219,12 +219,36 @@ static void exec_context_tty_reset(const ExecContext *context, const ExecParamet (void) vt_disallocate(path); } +static bool is_terminal_input(ExecInput i) { + return IN_SET(i, + EXEC_INPUT_TTY, + EXEC_INPUT_TTY_FORCE, + EXEC_INPUT_TTY_FAIL); +} + static bool is_terminal_output(ExecOutput o) { - return - o == EXEC_OUTPUT_TTY || - o == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || - o == EXEC_OUTPUT_KMSG_AND_CONSOLE || - o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE; + return IN_SET(o, + EXEC_OUTPUT_TTY, + EXEC_OUTPUT_SYSLOG_AND_CONSOLE, + EXEC_OUTPUT_KMSG_AND_CONSOLE, + EXEC_OUTPUT_JOURNAL_AND_CONSOLE); +} + +static bool exec_context_needs_term(const ExecContext *c) { + assert(c); + + /* Return true if the execution context suggests we should set $TERM to something useful. */ + + if (is_terminal_input(c->std_input)) + return true; + + if (is_terminal_output(c->std_output)) + return true; + + if (is_terminal_output(c->std_error)) + return true; + + return !!c->tty_path; } static int open_null_as(int flags, int nfd) { @@ -363,13 +387,6 @@ static int open_terminal_as(const char *path, mode_t mode, int nfd) { return r; } -static bool is_terminal_input(ExecInput i) { - return - i == EXEC_INPUT_TTY || - i == EXEC_INPUT_TTY_FORCE || - i == EXEC_INPUT_TTY_FAIL; -} - static int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) { if (is_terminal_input(std_input) && !apply_tty_stdin) @@ -1444,12 +1461,21 @@ static int build_environment( our_env[n_env++] = x; } - if (is_terminal_input(c->std_input) || - c->std_output == EXEC_OUTPUT_TTY || - c->std_error == EXEC_OUTPUT_TTY || - c->tty_path) { + if (exec_context_needs_term(c)) { + const char *tty_path, *term = NULL; + + tty_path = exec_context_tty_path(c); + + /* If we are forked off PID 1 and we are supposed to operate on /dev/console, then let's try to inherit + * the $TERM set for PID 1. This is useful for containers so that the $TERM the container manager + * passes to PID 1 ends up all the way in the console login shown. */ + + if (path_equal(tty_path, "/dev/console") && getppid() == 1) + term = getenv("TERM"); + if (!term) + term = default_term_for_tty(tty_path); - x = strdup(default_term_for_tty(exec_context_tty_path(c))); + x = strappend("TERM=", term); if (!x) return -ENOMEM; our_env[n_env++] = x; diff --git a/src/core/main.c b/src/core/main.c index c46d886653..094bbef964 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1318,7 +1318,7 @@ static int fixup_environment(void) { return r; if (r == 0) { - term = strdup(default_term_for_tty("/dev/console") + 5); + term = strdup(default_term_for_tty("/dev/console")); if (!term) return -ENOMEM; } -- cgit v1.2.3 From 70493828032abc74e5134563a915c4a3ccdde7f2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Jul 2016 20:00:33 +0200 Subject: execute: don't set $SHELL and $HOME for services, if they don't contain interesting data --- src/core/execute.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/core/execute.c b/src/core/execute.c index 0bf80fc437..77a75245cb 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1724,6 +1724,17 @@ static int exec_child( *exit_status = EXIT_USER; return r; } + + /* Don't set $HOME or $SHELL if they are are not particularly enlightening anyway. */ + if (isempty(home) || path_equal(home, "/")) + home = NULL; + + if (isempty(shell) || PATH_IN_SET(shell, + "/bin/nologin", + "/sbin/nologin", + "/usr/bin/nologin", + "/usr/sbin/nologin")) + shell = NULL; } if (context->group) { -- cgit v1.2.3 From f7b7b3df9e24713464d9089f62958c8c5c3aac49 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Aug 2016 14:51:05 +0200 Subject: nspawn: if we can't mark the boot ID RO let's fail It's probably better to be safe here. --- src/nspawn/nspawn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index b7c2fe9e55..5c6605a08e 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1320,7 +1320,7 @@ static int setup_boot_id(const char *dest) { if (mount(from, to, NULL, MS_BIND, NULL) < 0) r = log_error_errno(errno, "Failed to bind mount boot id: %m"); else if (mount(NULL, to, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL) < 0) - log_warning_errno(errno, "Failed to make boot id read-only, ignoring: %m"); + r = log_error_errno(errno, "Failed to make boot id read-only: %m"); (void) unlink(from); return r; -- cgit v1.2.3 From 7f5da8bd4fb1ba49ba40195a74ca76bb5d4d1f81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Nykr=C3=BDn?= Date: Wed, 3 Aug 2016 17:08:37 +0200 Subject: systemctl: consider service running only when it is in active or reloading state (#3874) Otherwise for example services that are failing on start and have Restart=on-failure and bigger RestartSec systemctl status will return 0. Fixes: #3864 --- src/systemctl/systemctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 782824ff38..4fa7adfc41 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -4767,7 +4767,7 @@ static int show_one( else if (streq(verb, "status")) { print_status_info(bus, &info, ellipsized); - if (info.active_state && STR_IN_SET(info.active_state, "inactive", "failed")) + if (info.active_state && !STR_IN_SET(info.active_state, "active", "reloading")) r = EXIT_PROGRAM_NOT_RUNNING; else r = EXIT_PROGRAM_RUNNING_OR_SERVICE_OK; -- cgit v1.2.3 From d251207d555a1a0d97924980e49b0ba563b9fc67 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Aug 2016 18:44:51 +0200 Subject: core: add new PrivateUsers= option to service execution This setting adds minimal user namespacing support to a service. When set the invoked processes will run in their own user namespace. Only a trivial mapping will be set up: the root user/group is mapped to root, and the user/group of the service will be mapped to itself, everything else is mapped to nobody. If this setting is used the service runs with no capabilities on the host, but configurable capabilities within the service. This setting is particularly useful in conjunction with RootDirectory= as the need to synchronize /etc/passwd and /etc/group between the host and the service OS tree is reduced, as only three UID/GIDs need to match: root, nobody and the user of the service itself. But even outside the RootDirectory= case this setting is useful to substantially reduce the attack surface of a service. Example command to test this: systemd-run -p PrivateUsers=1 -p User=foobar -t /bin/sh This runs a shell as user "foobar". When typing "ps" only processes owned by "root", by "foobar", and by "nobody" should be visible. --- man/systemd.exec.xml | 65 ++++++++----- src/core/dbus-execute.c | 7 +- src/core/execute.c | 168 +++++++++++++++++++++++++++++++++- src/core/execute.h | 1 + src/core/load-fragment-gperf.gperf.m4 | 3 +- src/shared/bus-unit-util.c | 2 +- 6 files changed, 215 insertions(+), 31 deletions(-) diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 58ba582911..2190da55d4 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -107,36 +107,29 @@ WorkingDirectory= - Takes a directory path relative to the service's root - directory specified by RootDirectory=, or the - special value ~. Sets the working directory - for executed processes. If set to ~, the - home directory of the user specified in - User= is used. If not set, defaults to the - root directory when systemd is running as a system instance - and the respective user's home directory if run as user. If - the setting is prefixed with the - - character, a missing working directory is not considered - fatal. If RootDirectory= is not set, then - WorkingDirectory= is relative to the root of - the system running the service manager. - Note that setting this parameter might result in - additional dependencies to be added to the unit (see - above). + Takes a directory path relative to the service's root directory specified by + RootDirectory=, or the special value ~. Sets the working directory for + executed processes. If set to ~, the home directory of the user specified in + User= is used. If not set, defaults to the root directory when systemd is running as a + system instance and the respective user's home directory if run as user. If the setting is prefixed with the + - character, a missing working directory is not considered fatal. If + RootDirectory= is not set, then WorkingDirectory= is relative to the root + of the system running the service manager. Note that setting this parameter might result in additional + dependencies to be added to the unit (see above). RootDirectory= - Takes a directory path relative to the host's root directory - (i.e. the root of the system running the service manager). Sets the - root directory for executed processes, with the chroot2 - system call. If this is used, it must be ensured that the - process binary and all its auxiliary files are available in - the chroot() jail. Note that setting this - parameter might result in additional dependencies to be added - to the unit (see above). + Takes a directory path relative to the host's root directory (i.e. the root of the system + running the service manager). Sets the root directory for executed processes, with the chroot2 system + call. If this is used, it must be ensured that the process binary and all its auxiliary files are available in + the chroot() jail. Note that setting this parameter might result in additional + dependencies to be added to the unit (see above). + + The PrivateUsers= setting is particularly useful in conjunction with + RootDirectory=. For details, see below. @@ -998,6 +991,28 @@ accessible). + + PrivateUsers= + + Takes a boolean argument. If true, sets up a new user namespace for the executed processes and + configures a minimal user and group mapping, that maps the root user and group as well as + the unit's own user and group to themselves and everything else to the nobody user and + group. This is useful to securely detach the user and group databases used by the unit from the rest of the + system, and thus to create an effective sandbox environment. All files, directories, processes, IPC objects and + other resources owned by users/groups not equalling root or the unit's own will stay visible + from within the unit but appear owned by the nobody user and group. If this mode is enabled, + all unit processes are run without privileges in the host user namespace (regardless if the unit's own + user/group is root or not). Specifically this means that the process will have zero process + capabilities on the host's user namespace, but full capabilities within the service's user namespace. Settings + such as CapabilityBoundingSet= will affect only the latter, and there's no way to acquire + additional capabilities in the host's user namespace. Defaults to off. + + This setting is particularly useful in conjunction with RootDirectory=, as the need to + synchronize the user and group databases in the root directory and on the host is reduced, as the only users + and groups who need to be matched are root, nobody and the unit's own + user and group. + + ProtectSystem= diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 9c50cd93e5..4b3bbfbc7d 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -705,8 +705,9 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("InaccessiblePaths", "as", NULL, offsetof(ExecContext, inaccessible_paths), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MountFlags", "t", bus_property_get_ulong, offsetof(ExecContext, mount_flags), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PrivateTmp", "b", bus_property_get_bool, offsetof(ExecContext, private_tmp), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("PrivateNetwork", "b", bus_property_get_bool, offsetof(ExecContext, private_network), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PrivateDevices", "b", bus_property_get_bool, offsetof(ExecContext, private_devices), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PrivateNetwork", "b", bus_property_get_bool, offsetof(ExecContext, private_network), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PrivateUsers", "b", bus_property_get_bool, offsetof(ExecContext, private_users), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ProtectHome", "s", bus_property_get_protect_home, offsetof(ExecContext, protect_home), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ProtectSystem", "s", bus_property_get_protect_system, offsetof(ExecContext, protect_system), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SameProcessGroup", "b", bus_property_get_bool, offsetof(ExecContext, same_pgrp), SD_BUS_VTABLE_PROPERTY_CONST), @@ -1068,7 +1069,7 @@ int bus_exec_context_set_transient_property( } else if (STR_IN_SET(name, "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", - "PrivateTmp", "PrivateDevices", "PrivateNetwork", + "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser")) { int b; @@ -1090,6 +1091,8 @@ int bus_exec_context_set_transient_property( c->private_devices = b; else if (streq(name, "PrivateNetwork")) c->private_network = b; + else if (streq(name, "PrivateUsers")) + c->private_users = b; else if (streq(name, "NoNewPrivileges")) c->no_new_privileges = b; else if (streq(name, "SyslogLevelPrefix")) diff --git a/src/core/execute.c b/src/core/execute.c index 26e9cd5339..cec3b3cf40 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -1526,6 +1527,159 @@ static bool exec_needs_mount_namespace( return false; } +static int setup_private_users(uid_t uid, gid_t gid) { + _cleanup_free_ char *uid_map = NULL, *gid_map = NULL; + _cleanup_close_pair_ int errno_pipe[2] = { -1, -1 }; + _cleanup_close_ int unshare_ready_fd = -1; + _cleanup_(sigkill_waitp) pid_t pid = 0; + uint64_t c = 1; + siginfo_t si; + ssize_t n; + int r; + + /* Set up a user namespace and map root to root, the selected UID/GID to itself, and everything else to + * nobody. In order to be able to write this mapping we need CAP_SETUID in the original user namespace, which + * we however lack after opening the user namespace. To work around this we fork() a temporary child process, + * which waits for the parent to create the new user namespace while staying in the original namespace. The + * child then writes the UID mapping, under full privileges. The parent waits for the child to finish and + * continues execution normally. */ + + if (uid != 0 && uid_is_valid(uid)) + asprintf(&uid_map, + "0 0 1\n" /* Map root → root */ + UID_FMT " " UID_FMT " 1\n", /* Map $UID → $UID */ + uid, uid); /* The case where the above is the same */ + else + uid_map = strdup("0 0 1\n"); + if (!uid_map) + return -ENOMEM; + + if (gid != 0 && gid_is_valid(gid)) + asprintf(&gid_map, + "0 0 1\n" /* Map root → root */ + GID_FMT " " GID_FMT " 1\n", /* Map $GID → $GID */ + gid, gid); + else + gid_map = strdup("0 0 1\n"); /* The case where the above is the same */ + if (!gid_map) + return -ENOMEM; + + /* Create a communication channel so that the parent can tell the child when it finished creating the user + * namespace. */ + unshare_ready_fd = eventfd(0, EFD_CLOEXEC); + if (unshare_ready_fd < 0) + return -errno; + + /* Create a communication channel so that the child can tell the parent a proper error code in case it + * failed. */ + if (pipe2(errno_pipe, O_CLOEXEC) < 0) + return -errno; + + pid = fork(); + if (pid < 0) + return -errno; + + if (pid == 0) { + _cleanup_close_ int fd = -1; + const char *a; + pid_t ppid; + + /* Child process, running in the original user namespace. Let's update the parent's UID/GID map from + * here, after the parent opened its own user namespace. */ + + ppid = getppid(); + errno_pipe[0] = safe_close(errno_pipe[0]); + + /* Wait until the parent unshared the user namespace */ + if (read(unshare_ready_fd, &c, sizeof(c)) < 0) { + r = -errno; + goto child_fail; + } + + /* Disable the setgroups() system call in the child user namespace, for good. */ + a = procfs_file_alloca(ppid, "setgroups"); + fd = open(a, O_WRONLY|O_CLOEXEC); + if (fd < 0) { + if (errno != ENOENT) { + r = -errno; + goto child_fail; + } + + /* If the file is missing the kernel is too old, let's continue anyway. */ + } else { + if (write(fd, "deny\n", 5) < 0) { + r = -errno; + goto child_fail; + } + + fd = safe_close(fd); + } + + /* First write the GID map */ + a = procfs_file_alloca(ppid, "gid_map"); + fd = open(a, O_WRONLY|O_CLOEXEC); + if (fd < 0) { + r = -errno; + goto child_fail; + } + if (write(fd, gid_map, strlen(gid_map)) < 0) { + r = -errno; + goto child_fail; + } + fd = safe_close(fd); + + /* The write the UID map */ + a = procfs_file_alloca(ppid, "uid_map"); + fd = open(a, O_WRONLY|O_CLOEXEC); + if (fd < 0) { + r = -errno; + goto child_fail; + } + if (write(fd, uid_map, strlen(uid_map)) < 0) { + r = -errno; + goto child_fail; + } + + _exit(EXIT_SUCCESS); + + child_fail: + (void) write(errno_pipe[1], &r, sizeof(r)); + _exit(EXIT_FAILURE); + } + + errno_pipe[1] = safe_close(errno_pipe[1]); + + if (unshare(CLONE_NEWUSER) < 0) + return -errno; + + /* Let the child know that the namespace is ready now */ + if (write(unshare_ready_fd, &c, sizeof(c)) < 0) + return -errno; + + /* Try to read an error code from the child */ + n = read(errno_pipe[0], &r, sizeof(r)); + if (n < 0) + return -errno; + if (n == sizeof(r)) { /* an error code was sent to us */ + if (r < 0) + return r; + return -EIO; + } + if (n != 0) /* on success we should have read 0 bytes */ + return -EIO; + + r = wait_for_terminate(pid, &si); + if (r < 0) + return r; + pid = 0; + + /* If something strange happened with the child, let's consider this fatal, too */ + if (si.si_code != CLD_EXITED || si.si_status != 0) + return -EIO; + + return 0; +} + static void append_socket_pair(int *array, unsigned *n, int pair[2]) { assert(array); assert(n); @@ -2037,6 +2191,14 @@ static int exec_child( } #endif + if (params->apply_permissions && context->private_users) { + r = setup_private_users(uid, gid); + if (r < 0) { + *exit_status = EXIT_USER; + return r; + } + } + /* We repeat the fd closing here, to make sure that * nothing is leaked from the PAM modules. Note that * we are more aggressive this time since socket_fd @@ -2598,8 +2760,9 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { "%sRootDirectory: %s\n" "%sNonBlocking: %s\n" "%sPrivateTmp: %s\n" - "%sPrivateNetwork: %s\n" "%sPrivateDevices: %s\n" + "%sPrivateNetwork: %s\n" + "%sPrivateUsers: %s\n" "%sProtectHome: %s\n" "%sProtectSystem: %s\n" "%sIgnoreSIGPIPE: %s\n" @@ -2610,8 +2773,9 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { prefix, c->root_directory ? c->root_directory : "/", prefix, yes_no(c->non_blocking), prefix, yes_no(c->private_tmp), - prefix, yes_no(c->private_network), prefix, yes_no(c->private_devices), + prefix, yes_no(c->private_network), + prefix, yes_no(c->private_users), prefix, protect_home_to_string(c->protect_home), prefix, protect_system_to_string(c->protect_system), prefix, yes_no(c->ignore_sigpipe), diff --git a/src/core/execute.h b/src/core/execute.h index 48cc18fbb3..5fac3e85e8 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -171,6 +171,7 @@ struct ExecContext { bool private_tmp; bool private_network; bool private_devices; + bool private_users; ProtectSystem protect_system; ProtectHome protect_home; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 396f847213..251155b428 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -88,8 +88,9 @@ $1.ReadWritePaths, config_parse_namespace_path_strv, 0, $1.ReadOnlyPaths, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_only_paths) $1.InaccessiblePaths, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.inaccessible_paths) $1.PrivateTmp, config_parse_bool, 0, offsetof($1, exec_context.private_tmp) -$1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network) $1.PrivateDevices, config_parse_bool, 0, offsetof($1, exec_context.private_devices) +$1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network) +$1.PrivateUsers, config_parse_bool, 0, offsetof($1, exec_context.private_users) $1.ProtectSystem, config_parse_protect_system, 0, offsetof($1, exec_context) $1.ProtectHome, config_parse_protect_home, 0, offsetof($1, exec_context) $1.MountFlags, config_parse_exec_mount_flags, 0, offsetof($1, exec_context) diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 14bf8ad627..9d8061b539 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -202,7 +202,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting", "TasksAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", - "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges", + "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges", "SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser")) { -- cgit v1.2.3 From 8ce9b83a8f0316b42143ad01c10e4944acc85e87 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Aug 2016 18:40:48 +0200 Subject: update TODO --- TODO | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/TODO b/TODO index bb36522bf9..0199d9d509 100644 --- a/TODO +++ b/TODO @@ -66,11 +66,10 @@ Features: * ProtectKeyRing= to take keyring calls away -* PrivateUsers= which maps the all user ids except root and the one specified - in User= to nobody - * ProtectControlGroups= which mounts all of /sys/fs/cgroup read-only +* RemoveKeyRing= to remove all keyring entries of the specified user + * Add DataDirectory=, CacheDirectory= and LogDirectory= to match RuntimeDirectory=, and create it as necessary when starting a service, owned by the right user. @@ -90,6 +89,11 @@ Features: * expose the "privileged" flag of ExecCommand on the bus, and open it up to transient units +* in nss-systemd, if we run inside of RootDirectory= with PrivateUsers= set, + find a way to map the User=/Group= of the service to the right name. This way + a user/group for a service only has to exist on the host for the right + mapping to work. + * allow attaching additional journald log fields to cgroups * rework fopen_temporary() to make use of open_tmpfile_linkable() (problem: the -- cgit v1.2.3 From 21b3a0fcd1fc4e4c668c4d34115e2e411dc0dceb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 4 Aug 2016 01:04:53 +0200 Subject: util-lib: make timestamp generation and parsing reversible (#3869) This patch improves parsing and generation of timestamps and calendar specifications in two ways: - The week day is now always printed in the abbreviated English form, instead of the locale's setting. This makes sure we can always parse the week day again, even if the locale is changed. Given that we don't follow locale settings for printing timestamps in any other way either (for example, we always use 24h syntax in order to make uniform parsing possible), it only makes sense to also stick to a generic, non-localized form for the timestamp, too. - When parsing a timestamp, the local timezone (in its DST or non-DST name) may be specified, in addition to "UTC". Other timezones are still not supported however (not because we wouldn't want to, but mostly because libc offers no nice API for that). In itself this brings no new features, however it ensures that any locally formatted timestamp's timezone is also parsable again. These two changes ensure that the output of format_timestamp() may always be passed to parse_timestamp() and results in the original input. The related flavours for usec/UTC also work accordingly. Calendar specifications are extended in a similar way. The man page is updated accordingly, in particular this removes the claim that timestamps systemd prints wouldn't be parsable by systemd. They are now. The man page previously showed invalid timestamps as examples. This has been removed, as the man page shouldn't be a unit test, where such negative examples would be useful. The man page also no longer mentions the names of internal functions, such as format_timestamp_us() or UNIX error codes such as EINVAL. --- man/systemd.time.xml | 150 +++++++++++++++++++++---------------------- src/basic/calendarspec.c | 43 ++++++++++++- src/basic/calendarspec.h | 1 + src/basic/time-util.c | 146 ++++++++++++++++++++++++++++++++++------- src/basic/time-util.h | 4 +- src/test/test-calendarspec.c | 23 +++++++ src/test/test-time.c | 44 +++++++++++++ 7 files changed, 307 insertions(+), 104 deletions(-) diff --git a/man/systemd.time.xml b/man/systemd.time.xml index aae3accb6c..47229b4a4e 100644 --- a/man/systemd.time.xml +++ b/man/systemd.time.xml @@ -57,14 +57,13 @@ Displaying Time Spans - Time spans refer to time durations. On display, systemd will - present time spans as a space-separated series of time values each - suffixed by a time unit. + Time spans refer to time durations. On display, systemd will present time spans as a space-separated series + of time values each suffixed by a time unit. Example: 2h 30min - All specified time values are meant to be added up. The - above hence refers to 150 minutes. + All specified time values are meant to be added up. The above hence refers to 150 minutes. Display is + locale-independent, only English names for the time units are used. @@ -83,13 +82,13 @@ days, day, d weeks, week, w months, month, M (defined as 30.44 days) - years, year, y (define as 365.25 days) + years, year, y (defined as 365.25 days) - If no time unit is specified, generally seconds are assumed, - but some exceptions exist and are marked as such. In a few cases - ns, nsec is accepted too, - where the granularity of the time span allows for this. + If no time unit is specified, generally seconds are assumed, but some exceptions exist and are marked as + such. In a few cases ns, nsec is accepted too, where the granularity of the + time span permits this. Parsing is generally locale-independent, non-English names for the time units are not + accepted. Examples for valid time span specifications: @@ -110,30 +109,29 @@ Fri 2012-11-23 23:02:15 CET - The weekday is printed according to the locale choice of the - user. + The weekday is printed in the abbreviated English language form. The formatting is locale-independent. + + In some cases timestamps are shown in the UTC timezone instead of the local timezone, which is indicated via + the UTC timezone specifier in the output. + + In some cases timestamps are shown with microsecond granularity. In this case the sub-second remainder is + separated by a full stop from the seconds component. Parsing Timestamps - When parsing, systemd will accept a similar syntax, but - expects no timezone specification, unless it is given as the - literal string "UTC". In this case, the time is considered in UTC, - otherwise in the local timezone. The weekday specification is - optional, but when the weekday is specified, it must either be in - the abbreviated (Wed) or non-abbreviated - (Wednesday) English language form (case does - not matter), and is not subject to the locale choice of the user. - Either the date, or the time part may be omitted, in which case - the current date or 00:00:00, respectively, is assumed. The seconds - component of the time may also be omitted, in which case ":00" is - assumed. Year numbers may be specified in full or may be - abbreviated (omitting the century). - - A timestamp is considered invalid if a weekday is specified - and the date does not actually match the specified day of the - week. + When parsing, systemd will accept a similar syntax, but expects no timezone specification, unless it is given + as the literal string UTC (for the UTC timezone) or is specified to be the locally configured + timezone. Other timezones than the local and UTC are not supported. The weekday specification is optional, but when + the weekday is specified, it must either be in the abbreviated (Wed) or non-abbreviated + (Wednesday) English language form (case does not matter), and is not subject to the locale + choice of the user. Either the date, or the time part may be omitted, in which case the current date or 00:00:00, + respectively, is assumed. The seconds component of the time may also be omitted, in which case ":00" is + assumed. Year numbers may be specified in full or may be abbreviated (omitting the century). + + A timestamp is considered invalid if a weekday is specified and the date does not match the specified day of + the week. When parsing, systemd will also accept a few special placeholders instead of timestamps: now may be @@ -167,8 +165,6 @@ 2012-11-23 → Fri 2012-11-23 00:00:00 12-11-23 → Fri 2012-11-23 00:00:00 11:12:13 → Fri 2012-11-23 11:12:13 - 11:12:13.9900009 → Fri 2012-11-23 11:12:13 - format_timestamp_us: Fri 2012-11-23 11:12:13.990000 11:12 → Fri 2012-11-23 11:12:00 now → Fri 2012-11-23 18:15:22 today → Fri 2012-11-23 00:00:00 @@ -176,28 +172,25 @@ yesterday → Fri 2012-11-22 00:00:00 tomorrow → Fri 2012-11-24 00:00:00 +3h30min → Fri 2012-11-23 21:45:22 - +3h30min UTC → -EINVAL -5s → Fri 2012-11-23 18:15:17 11min ago → Fri 2012-11-23 18:04:22 - 11min ago UTC → -EINVAL @1395716396 → Tue 2014-03-25 03:59:56 - Note that timestamps printed by systemd will not be parsed - correctly by systemd, as the timezone specification is not - accepted, and printing timestamps is subject to locale settings - for the weekday, while parsing only accepts English weekday - names. + Note that timestamps displayed by remote systems with a non-matching timezone are usually not parsable + locally, as the timezone component is not understood (unless it happens to be UTC). - In some cases, systemd will display a relative timestamp - (relative to the current time, or the time of invocation of the - command) instead or in addition to an absolute timestamp as - described above. A relative timestamp is formatted as - follows: + Timestamps may also be specified with microsecond granularity. The sub-second remainder is expected separated + by a full stop from the seconds component. Example: + + 2014-03-25 03:59:56.654563 + + In some cases, systemd will display a relative timestamp (relative to the current time, or the time of + invocation of the command) instead of or in addition to an absolute timestamp as described above. A relative + timestamp is formatted as follows: - 2 months 5 days ago + 2 months 5 days ago - Note that any relative timestamp will also parse correctly - where a timestamp is expected. (see above) + Note that a relative timestamp is also accepted where a timestamp is expected (see above). @@ -239,8 +232,9 @@ second component is not specified, :00 is assumed. - A timezone specification is not expected, unless it is given - as the literal string "UTC", similarly to timestamps. + A timezone specification is not expected, unless it is given as the literal string UTC, or + the local timezone, similar to the supported syntax of timestamps (see above). Non-local timezones except for UTC + are not supported. The special expressions minutely, @@ -263,38 +257,38 @@ Examples for valid timestamps and their normalized form: - Sat,Thu,Mon..Wed,Sat..Sun → Mon..Thu,Sat,Sun *-*-* 00:00:00 - Mon,Sun 12-*-* 2,1:23 → Mon,Sun 2012-*-* 01,02:23:00 - Wed *-1 → Wed *-*-01 00:00:00 + Sat,Thu,Mon..Wed,Sat..Sun → Mon..Thu,Sat,Sun *-*-* 00:00:00 + Mon,Sun 12-*-* 2,1:23 → Mon,Sun 2012-*-* 01,02:23:00 + Wed *-1 → Wed *-*-01 00:00:00 Wed..Wed,Wed *-1 → Wed *-*-01 00:00:00 - Wed, 17:48 → Wed *-*-* 17:48:00 + Wed, 17:48 → Wed *-*-* 17:48:00 Wed..Sat,Tue 12-10-15 1:2:3 → Tue..Sat 2012-10-15 01:02:03 - *-*-7 0:0:0 → *-*-07 00:00:00 - 10-15 → *-10-15 00:00:00 - monday *-12-* 17:00 → Mon *-12-* 17:00:00 - Mon,Fri *-*-3,1,2 *:30:45 → Mon,Fri *-*-01,02,03 *:30:45 - 12,14,13,12:20,10,30 → *-*-* 12,13,14:10,20,30:00 - 12..14:10,20,30 → *-*-* 12,13,14:10,20,30:00 - mon,fri *-1/2-1,3 *:30:45 → Mon,Fri *-01/2-01,03 *:30:45 - 03-05 08:05:40 → *-03-05 08:05:40 - 08:05:40 → *-*-* 08:05:40 - 05:40 → *-*-* 05:40:00 - Sat,Sun 12-05 08:05:40 → Sat,Sun *-12-05 08:05:40 - Sat,Sun 08:05:40 → Sat,Sun *-*-* 08:05:40 - 2003-03-05 05:40 → 2003-03-05 05:40:00 -05:40:23.4200004/3.1700005 → 05:40:23.420000/3.170001 - 2003-02..04-05 → 2003-02,03,04-05 00:00:00 - 2003-03-05 05:40 UTC → 2003-03-05 05:40:00 UTC - 2003-03-05 → 2003-03-05 00:00:00 - 03-05 → *-03-05 00:00:00 - hourly → *-*-* *:00:00 - daily → *-*-* 00:00:00 - daily UTC → *-*-* 00:00:00 UTC - monthly → *-*-01 00:00:00 - weekly → Mon *-*-* 00:00:00 - yearly → *-01-01 00:00:00 - annually → *-01-01 00:00:00 - *:2/3 → *-*-* *:02/3:00 + *-*-7 0:0:0 → *-*-07 00:00:00 + 10-15 → *-10-15 00:00:00 + monday *-12-* 17:00 → Mon *-12-* 17:00:00 + Mon,Fri *-*-3,1,2 *:30:45 → Mon,Fri *-*-01,02,03 *:30:45 + 12,14,13,12:20,10,30 → *-*-* 12,13,14:10,20,30:00 + 12..14:10,20,30 → *-*-* 12,13,14:10,20,30:00 + mon,fri *-1/2-1,3 *:30:45 → Mon,Fri *-01/2-01,03 *:30:45 + 03-05 08:05:40 → *-03-05 08:05:40 + 08:05:40 → *-*-* 08:05:40 + 05:40 → *-*-* 05:40:00 + Sat,Sun 12-05 08:05:40 → Sat,Sun *-12-05 08:05:40 + Sat,Sun 08:05:40 → Sat,Sun *-*-* 08:05:40 + 2003-03-05 05:40 → 2003-03-05 05:40:00 + 05:40:23.4200004/3.1700005 → 05:40:23.420000/3.170001 + 2003-02..04-05 → 2003-02,03,04-05 00:00:00 + 2003-03-05 05:40 UTC → 2003-03-05 05:40:00 UTC + 2003-03-05 → 2003-03-05 00:00:00 + 03-05 → *-03-05 00:00:00 + hourly → *-*-* *:00:00 + daily → *-*-* 00:00:00 + daily UTC → *-*-* 00:00:00 UTC + monthly → *-*-01 00:00:00 + weekly → Mon *-*-* 00:00:00 + yearly → *-01-01 00:00:00 + annually → *-01-01 00:00:00 + *:2/3 → *-*-* *:02/3:00 Calendar events are used by timer units, see systemd.timer5 diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index e4cfab364e..fda293fcb9 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -302,6 +302,17 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) { if (c->utc) fputs(" UTC", f); + else if (IN_SET(c->dst, 0, 1)) { + + /* If daylight saving is explicitly on or off, let's show the used timezone. */ + + tzset(); + + if (!isempty(tzname[c->dst])) { + fputc(' ', f); + fputs(tzname[c->dst], f); + } + } r = fflush_and_check(f); if (r < 0) { @@ -747,9 +758,9 @@ fail: } int calendar_spec_from_string(const char *p, CalendarSpec **spec) { + const char *utc; CalendarSpec *c; int r; - const char *utc; assert(p); assert(spec); @@ -760,11 +771,39 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { c = new0(CalendarSpec, 1); if (!c) return -ENOMEM; + c->dst = -1; utc = endswith_no_case(p, " UTC"); if (utc) { c->utc = true; p = strndupa(p, utc - p); + } else { + const char *e = NULL; + int j; + + tzset(); + + /* Check if the local timezone was specified? */ + for (j = 0; j <= 1; j++) { + if (isempty(tzname[j])) + continue; + + e = endswith_no_case(p, tzname[j]); + if(!e) + continue; + if (e == p) + continue; + if (e[-1] != ' ') + continue; + + break; + } + + /* Found one of the two timezones specified? */ + if (IN_SET(j, 0, 1)) { + p = strndupa(p, e - p - 1); + c->dst = j; + } } if (strcaseeq(p, "minutely")) { @@ -1017,7 +1056,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { for (;;) { /* Normalize the current date */ (void) mktime_or_timegm(&c, spec->utc); - c.tm_isdst = -1; + c.tm_isdst = spec->dst; c.tm_year += 1900; r = find_matching_component(spec->year, &c.tm_year); diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h index f6472c1244..c6087228fd 100644 --- a/src/basic/calendarspec.h +++ b/src/basic/calendarspec.h @@ -37,6 +37,7 @@ typedef struct CalendarComponent { typedef struct CalendarSpec { int weekdays_bits; bool utc; + int dst; CalendarComponent *year; CalendarComponent *month; diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 24e681bf85..0ef1f6393e 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -254,32 +254,95 @@ struct timeval *timeval_store(struct timeval *tv, usec_t u) { return tv; } -static char *format_timestamp_internal(char *buf, size_t l, usec_t t, - bool utc, bool us) { +static char *format_timestamp_internal( + char *buf, + size_t l, + usec_t t, + bool utc, + bool us) { + + /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our + * generated timestamps may be parsed with parse_timestamp(), and always read the same. */ + static const char * const weekdays[] = { + [0] = "Sun", + [1] = "Mon", + [2] = "Tue", + [3] = "Wed", + [4] = "Thu", + [5] = "Fri", + [6] = "Sat", + }; + struct tm tm; time_t sec; - int k; + size_t n; assert(buf); - assert(l > 0); + if (l < + 3 + /* week day */ + 1 + 10 + /* space and date */ + 1 + 8 + /* space and time */ + (us ? 1 + 6 : 0) + /* "." and microsecond part */ + 1 + 1 + /* space and shortest possible zone */ + 1) + return NULL; /* Not enough space even for the shortest form. */ if (t <= 0 || t == USEC_INFINITY) + return NULL; /* Timestamp is unset */ + + sec = (time_t) (t / USEC_PER_SEC); /* Round down */ + if ((usec_t) sec != (t / USEC_PER_SEC)) + return NULL; /* overflow? */ + + if (!localtime_or_gmtime_r(&sec, &tm, utc)) return NULL; - sec = (time_t) (t / USEC_PER_SEC); - localtime_or_gmtime_r(&sec, &tm, utc); + /* Start with the week day */ + assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays)); + memcpy(buf, weekdays[tm.tm_wday], 4); - if (us) - k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm); - else - k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm); + /* Add the main components */ + if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0) + return NULL; /* Doesn't fit */ - if (k <= 0) - return NULL; + /* Append the microseconds part, if that's requested */ if (us) { - snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC)); - if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0) - return NULL; + n = strlen(buf); + if (n + 8 > l) + return NULL; /* Microseconds part doesn't fit. */ + + sprintf(buf + n, ".%06llu", (unsigned long long) (t % USEC_PER_SEC)); + } + + /* Append the timezone */ + n = strlen(buf); + if (utc) { + /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the + * obsolete "GMT" instead. */ + if (n + 5 > l) + return NULL; /* "UTC" doesn't fit. */ + + strcpy(buf + n, " UTC"); + + } else if (!isempty(tm.tm_zone)) { + size_t tn; + + /* An explicit timezone is specified, let's use it, if it fits */ + tn = strlen(tm.tm_zone); + if (n + 1 + tn + 1 > l) { + /* The full time zone does not fit in. Yuck. */ + + if (n + 1 + _POSIX_TZNAME_MAX + 1 > l) + return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */ + + /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX + * minimum time zone length. In this case suppress the timezone entirely, in order not to dump + * an overly long, hard to read string on the user. This should be safe, because the user will + * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */ + } else { + buf[n++] = ' '; + strcpy(buf + n, tm.tm_zone); + } } return buf; @@ -539,12 +602,11 @@ int parse_timestamp(const char *t, usec_t *usec) { { "Sat", 6 }, }; - const char *k; - const char *utc; + const char *k, *utc, *tzn = NULL; struct tm tm, copy; time_t x; usec_t x_usec, plus = 0, minus = 0, ret; - int r, weekday = -1; + int r, weekday = -1, dst = -1; unsigned i; /* @@ -609,15 +671,55 @@ int parse_timestamp(const char *t, usec_t *usec) { goto finish; } + /* See if the timestamp is suffixed with UTC */ utc = endswith_no_case(t, " UTC"); if (utc) t = strndupa(t, utc - t); + else { + const char *e = NULL; + int j; + + tzset(); + + /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only + * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because + * there are no nice APIs available to cover this. By accepting the local time zone strings, we make + * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't + * support arbitrary timezone specifications. */ + + for (j = 0; j <= 1; j++) { + + if (isempty(tzname[j])) + continue; + + e = endswith_no_case(t, tzname[j]); + if (!e) + continue; + if (e == t) + continue; + if (e[-1] != ' ') + continue; + + break; + } - x = ret / USEC_PER_SEC; + if (IN_SET(j, 0, 1)) { + /* Found one of the two timezones specified. */ + t = strndupa(t, e - t - 1); + dst = j; + tzn = tzname[j]; + } + } + + x = (time_t) (ret / USEC_PER_SEC); x_usec = 0; - assert_se(localtime_or_gmtime_r(&x, &tm, utc)); - tm.tm_isdst = -1; + if (!localtime_or_gmtime_r(&x, &tm, utc)) + return -EINVAL; + + tm.tm_isdst = dst; + if (tzn) + tm.tm_zone = tzn; if (streq(t, "today")) { tm.tm_sec = tm.tm_min = tm.tm_hour = 0; @@ -634,7 +736,6 @@ int parse_timestamp(const char *t, usec_t *usec) { goto from_tm; } - for (i = 0; i < ELEMENTSOF(day_nr); i++) { size_t skip; @@ -727,7 +828,6 @@ parse_usec: return -EINVAL; x_usec = add; - } from_tm: diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 1b058f0e49..99be5ce6ee 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -68,7 +68,9 @@ typedef struct triple_timestamp { #define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC)) #define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC)) -#define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1) /* weekdays can be unicode */ +/* We assume a maximum timezone length of 6. TZNAME_MAX is not defined on Linux, but glibc internally initializes this + * to 6. Let's rely on that. */ +#define FORMAT_TIMESTAMP_MAX (3+1+10+1+8+1+6+1+6+1) #define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */ #define FORMAT_TIMESTAMP_RELATIVE_MAX 256 #define FORMAT_TIMESPAN_MAX 64 diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c index 4a2b93de59..57d9da4855 100644 --- a/src/test/test-calendarspec.c +++ b/src/test/test-calendarspec.c @@ -88,6 +88,27 @@ static void test_next(const char *input, const char *new_tz, usec_t after, usec_ tzset(); } +static void test_timestamp(void) { + char buf[FORMAT_TIMESTAMP_MAX]; + _cleanup_free_ char *t = NULL; + CalendarSpec *c; + usec_t x, y; + + /* Ensure that a timestamp is also a valid calendar specification. Convert forth and back */ + + x = now(CLOCK_REALTIME); + + assert_se(format_timestamp_us(buf, sizeof(buf), x)); + printf("%s\n", buf); + assert_se(calendar_spec_from_string(buf, &c) >= 0); + assert_se(calendar_spec_to_string(c, &t) >= 0); + calendar_spec_free(c); + printf("%s\n", t); + + assert_se(parse_timestamp(t, &y) >= 0); + assert_se(y == x); +} + int main(int argc, char* argv[]) { CalendarSpec *c; @@ -155,5 +176,7 @@ int main(int argc, char* argv[]) { assert_se(calendar_spec_from_string("00:00:00/0.00000001", &c) < 0); assert_se(calendar_spec_from_string("00:00:00.0..00.9", &c) < 0); + test_timestamp(); + return 0; } diff --git a/src/test/test-time.c b/src/test/test-time.c index ee7d55c5ab..7078a0374d 100644 --- a/src/test/test-time.c +++ b/src/test/test-time.c @@ -19,6 +19,7 @@ #include "strv.h" #include "time-util.h" +#include "random-util.h" static void test_parse_sec(void) { usec_t u; @@ -201,6 +202,48 @@ static void test_usec_sub(void) { assert_se(usec_sub(USEC_INFINITY, 5) == USEC_INFINITY); } +static void test_format_timestamp(void) { + unsigned i; + + for (i = 0; i < 100; i++) { + char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESPAN_MAX)]; + usec_t x, y; + + random_bytes(&x, sizeof(x)); + x = x % (2147483600 * USEC_PER_SEC) + 1; + + assert_se(format_timestamp(buf, sizeof(buf), x)); + log_info("%s", buf); + assert_se(parse_timestamp(buf, &y) >= 0); + assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC); + + assert_se(format_timestamp_utc(buf, sizeof(buf), x)); + log_info("%s", buf); + assert_se(parse_timestamp(buf, &y) >= 0); + assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC); + + assert_se(format_timestamp_us(buf, sizeof(buf), x)); + log_info("%s", buf); + assert_se(parse_timestamp(buf, &y) >= 0); + assert_se(x == y); + + assert_se(format_timestamp_us_utc(buf, sizeof(buf), x)); + log_info("%s", buf); + assert_se(parse_timestamp(buf, &y) >= 0); + assert_se(x == y); + + assert_se(format_timestamp_relative(buf, sizeof(buf), x)); + log_info("%s", buf); + assert_se(parse_timestamp(buf, &y) >= 0); + + /* The two calls above will run with a slightly different local time. Make sure we are in the same + * range however, but give enough leeway that this is unlikely to explode. And of course, + * format_timestamp_relative() scales the accuracy with the distance from the current time up to one + * month, cover for that too. */ + assert_se(y > x ? y - x : x - y <= USEC_PER_MONTH + USEC_PER_DAY); + } +} + int main(int argc, char *argv[]) { uintmax_t x; @@ -214,6 +257,7 @@ int main(int argc, char *argv[]) { test_get_timezones(); test_usec_add(); test_usec_sub(); + test_format_timestamp(); /* Ensure time_t is signed */ assert_cc((time_t) -1 < (time_t) 1); -- cgit v1.2.3 From 29a753df7682424a0ea505698d548f85c514fad5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 4 Aug 2016 01:45:07 +0200 Subject: journalctl: add new output mode "short-full" (#3880) This new output mode formats all timestamps using the usual format_timestamp() call we use pretty much everywhere else. Timestamps formatted this way are some ways more useful than traditional syslog timestamps as they include weekday, month and timezone information, while not being much longer. They are also not locale-dependent. The primary advantage however is that they may be passed directly to journalctl's --since= and --until= switches as soon as #3869 is merged. While we are at it, let's also add "short-unix" to shell completion. --- man/journalctl.xml | 41 ++++---- shell-completion/bash/journalctl | 2 +- shell-completion/bash/systemctl.in | 2 +- shell-completion/zsh/_sd_outputmodes | 2 +- src/shared/logs-show.c | 183 +++++++++++++++++++++-------------- src/shared/output-mode.c | 1 + src/shared/output-mode.h | 1 + 7 files changed, 140 insertions(+), 92 deletions(-) diff --git a/man/journalctl.xml b/man/journalctl.xml index e77621d7b3..c448a29a51 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -248,6 +248,18 @@ + + + + + + is very similar, but shows timestamps in the format the and + options accept. Unlike the timestamp information shown in + output mode this mode includes weekday, year and timezone information in the + output, and is locale-independent. + + + @@ -572,24 +584,17 @@ - Start showing entries on or newer than the - specified date, or on or older than the specified date, - respectively. Date specifications should be of the format - 2012-10-30 18:17:16. If the time part is - omitted, 00:00:00 is assumed. If only the - seconds component is omitted, :00 is - assumed. If the date component is omitted, the current day is - assumed. Alternatively the strings - yesterday, today, - tomorrow are understood, which refer to - 00:00:00 of the day before the current day, the current day, - or the day after the current day, - respectively. now refers to the current - time. Finally, relative times may be specified, prefixed with - - or +, referring to - times before or after the current time, respectively. For complete - time and date specification, see - systemd.time7. + Start showing entries on or newer than the specified date, or on or older than the specified + date, respectively. Date specifications should be of the format 2012-10-30 18:17:16. If the + time part is omitted, 00:00:00 is assumed. If only the seconds component is omitted, + :00 is assumed. If the date component is omitted, the current day is assumed. Alternatively + the strings yesterday, today, tomorrow are understood, + which refer to 00:00:00 of the day before the current day, the current day, or the day after the current day, + respectively. now refers to the current time. Finally, relative times may be specified, + prefixed with - or +, referring to times before or after the current + time, respectively. For complete time and date specification, see + systemd.time7. Note that + prints timestamps that follow precisely this format. diff --git a/shell-completion/bash/journalctl b/shell-completion/bash/journalctl index 53bedcd92e..a999a10df1 100644 --- a/shell-completion/bash/journalctl +++ b/shell-completion/bash/journalctl @@ -65,7 +65,7 @@ _journalctl() { compopt -o filenames ;; --output|-o) - comps='short short-iso short-precise short-monotonic verbose export json json-pretty json-sse cat' + comps='short short-full short-iso short-precise short-monotonic short-unix verbose export json json-pretty json-sse cat' ;; --field|-F) comps=$(journalctl --fields | sort 2>/dev/null) diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in index 6f2b3f122c..2a45dcbba0 100644 --- a/shell-completion/bash/systemctl.in +++ b/shell-completion/bash/systemctl.in @@ -145,7 +145,7 @@ _systemctl () { comps='full enable-only disable-only' ;; --output|-o) - comps='short short-iso short-precise short-monotonic verbose export json + comps='short short-full short-iso short-precise short-monotonic short-unix verbose export json json-pretty json-sse cat' ;; --machine|-M) diff --git a/shell-completion/zsh/_sd_outputmodes b/shell-completion/zsh/_sd_outputmodes index 3836f79b73..52617c6b7a 100644 --- a/shell-completion/zsh/_sd_outputmodes +++ b/shell-completion/zsh/_sd_outputmodes @@ -1,5 +1,5 @@ #autoload local -a _output_opts -_output_opts=(short short-iso short-precise short-monotonic verbose export json json-pretty json-sse cat) +_output_opts=(short short-full short-iso short-precise short-monotonic short-unix verbose export json json-pretty json-sse cat) _describe -t output 'output mode' _output_opts || compadd "$@" diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index d04728f505..f9d9c4ed62 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -45,6 +45,7 @@ #include "parse-util.h" #include "process-util.h" #include "sparse-endian.h" +#include "stdio-util.h" #include "string-table.h" #include "string-util.h" #include "terminal-util.h" @@ -206,6 +207,108 @@ static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, Output return ellipsized; } +static int output_timestamp_monotonic(FILE *f, sd_journal *j, const char *monotonic) { + sd_id128_t boot_id; + uint64_t t; + int r; + + assert(f); + assert(j); + + r = -ENXIO; + if (monotonic) + r = safe_atou64(monotonic, &t); + if (r < 0) + r = sd_journal_get_monotonic_usec(j, &t, &boot_id); + if (r < 0) + return log_error_errno(r, "Failed to get monotonic timestamp: %m"); + + fprintf(f, "[%5llu.%06llu]", + (unsigned long long) (t / USEC_PER_SEC), + (unsigned long long) (t % USEC_PER_SEC)); + + return 1 + 5 + 1 + 6 + 1; +} + +static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, OutputFlags flags, const char *realtime) { + char buf[MAX(FORMAT_TIMESTAMP_MAX, 64)]; + struct tm *(*gettime_r)(const time_t *, struct tm *); + struct tm tm; + uint64_t x; + time_t t; + int r; + + assert(f); + assert(j); + + r = -ENXIO; + if (realtime) + r = safe_atou64(realtime, &x); + if (r < 0) + r = sd_journal_get_realtime_usec(j, &x); + if (r < 0) + return log_error_errno(r, "Failed to get realtime timestamp: %m"); + + if (mode == OUTPUT_SHORT_FULL) { + const char *k; + + if (flags & OUTPUT_UTC) + k = format_timestamp_utc(buf, sizeof(buf), x); + else + k = format_timestamp(buf, sizeof(buf), x); + if (!k) { + log_error("Failed to format timestamp."); + return -EINVAL; + } + + } else { + gettime_r = (flags & OUTPUT_UTC) ? gmtime_r : localtime_r; + t = (time_t) (x / USEC_PER_SEC); + + switch (mode) { + + case OUTPUT_SHORT_UNIX: + xsprintf(buf, "%10llu.%06llu", (unsigned long long) t, (unsigned long long) (x % USEC_PER_SEC)); + break; + + case OUTPUT_SHORT_ISO: + if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)) <= 0) { + log_error("Failed for format ISO time"); + return -EINVAL; + } + break; + + case OUTPUT_SHORT: + case OUTPUT_SHORT_PRECISE: + + if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)) <= 0) { + log_error("Failed to format syslog time"); + return -EINVAL; + } + + if (mode == OUTPUT_SHORT_PRECISE) { + size_t k; + + assert(sizeof(buf) > strlen(buf)); + k = sizeof(buf) - strlen(buf); + + r = snprintf(buf + strlen(buf), k, ".%06llu", (unsigned long long) (x % USEC_PER_SEC)); + if (r <= 0 || (size_t) r >= k) { /* too long? */ + log_error("Failed to format precise time"); + return -EINVAL; + } + } + break; + + default: + assert_not_reached("Unknown time format"); + } + } + + fputs(buf, f); + return (int) strlen(buf); +} + static int output_short( FILE *f, sd_journal *j, @@ -305,78 +408,15 @@ static int output_short( if (priority_len == 1 && *priority >= '0' && *priority <= '7') p = *priority - '0'; - if (mode == OUTPUT_SHORT_MONOTONIC) { - uint64_t t; - sd_id128_t boot_id; - - r = -ENOENT; - - if (monotonic) - r = safe_atou64(monotonic, &t); - - if (r < 0) - r = sd_journal_get_monotonic_usec(j, &t, &boot_id); - - if (r < 0) - return log_error_errno(r, "Failed to get monotonic timestamp: %m"); - - fprintf(f, "[%5llu.%06llu]", - (unsigned long long) (t / USEC_PER_SEC), - (unsigned long long) (t % USEC_PER_SEC)); - - n += 1 + 5 + 1 + 6 + 1; - - } else { - char buf[64]; - uint64_t x; - time_t t; - struct tm tm; - struct tm *(*gettime_r)(const time_t *, struct tm *); - - r = -ENOENT; - gettime_r = (flags & OUTPUT_UTC) ? gmtime_r : localtime_r; - - if (realtime) - r = safe_atou64(realtime, &x); - - if (r < 0) - r = sd_journal_get_realtime_usec(j, &x); - - if (r < 0) - return log_error_errno(r, "Failed to get realtime timestamp: %m"); - - t = (time_t) (x / USEC_PER_SEC); - - switch (mode) { - - case OUTPUT_SHORT_UNIX: - r = snprintf(buf, sizeof(buf), "%10llu.%06llu", (unsigned long long) t, (unsigned long long) (x % USEC_PER_SEC)); - break; - - case OUTPUT_SHORT_ISO: - r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)); - break; - - case OUTPUT_SHORT_PRECISE: - r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)); - if (r > 0) - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%06llu", (unsigned long long) (x % USEC_PER_SEC)); - break; - - default: - r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)); - } - - if (r <= 0) { - log_error("Failed to format time."); - return -EINVAL; - } - - fputs(buf, f); - n += strlen(buf); - } + if (mode == OUTPUT_SHORT_MONOTONIC) + r = output_timestamp_monotonic(f, j, monotonic); + else + r = output_timestamp_realtime(f, j, mode, flags, realtime); + if (r < 0) + return r; + n += r; - if (hostname && (flags & OUTPUT_NO_HOSTNAME)) { + if (flags & OUTPUT_NO_HOSTNAME) { /* Suppress display of the hostname if this is requested. */ hostname = NULL; hostname_len = 0; @@ -910,6 +950,7 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])( [OUTPUT_SHORT_PRECISE] = output_short, [OUTPUT_SHORT_MONOTONIC] = output_short, [OUTPUT_SHORT_UNIX] = output_short, + [OUTPUT_SHORT_FULL] = output_short, [OUTPUT_VERBOSE] = output_verbose, [OUTPUT_EXPORT] = output_export, [OUTPUT_JSON] = output_json, diff --git a/src/shared/output-mode.c b/src/shared/output-mode.c index bec53ee0ae..67d8208ad2 100644 --- a/src/shared/output-mode.c +++ b/src/shared/output-mode.c @@ -22,6 +22,7 @@ static const char *const output_mode_table[_OUTPUT_MODE_MAX] = { [OUTPUT_SHORT] = "short", + [OUTPUT_SHORT_FULL] = "short-full", [OUTPUT_SHORT_ISO] = "short-iso", [OUTPUT_SHORT_PRECISE] = "short-precise", [OUTPUT_SHORT_MONOTONIC] = "short-monotonic", diff --git a/src/shared/output-mode.h b/src/shared/output-mode.h index f37189e57f..ff29dafcb5 100644 --- a/src/shared/output-mode.h +++ b/src/shared/output-mode.h @@ -23,6 +23,7 @@ typedef enum OutputMode { OUTPUT_SHORT, + OUTPUT_SHORT_FULL, OUTPUT_SHORT_ISO, OUTPUT_SHORT_PRECISE, OUTPUT_SHORT_MONOTONIC, -- cgit v1.2.3 From d26d41f36c598aeddcb25cca19ee3526f771c045 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 4 Aug 2016 09:57:36 +1000 Subject: hwdb: fix hwdb entry for numeric-only Lenovo *40, *50 and *60 series Commit e5bc3f4fdc matches on e.g. a T440s, but not a T440 (i.e. the one without a suffix). --- hwdb/70-pointingstick.hwdb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb index ff52f11e45..c0ec8ffbe0 100644 --- a/hwdb/70-pointingstick.hwdb +++ b/hwdb/70-pointingstick.hwdb @@ -96,10 +96,13 @@ evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX230 # Lenovo Thinkpad X230 tablet evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX230Tablet:* # Lenovo Thinkpad *40 series +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??40:* evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??40?:* # Lenovo Thinkpad *50 series +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??50:* evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??50?:* # Lenovo Thinkpad *60 series +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??60:* evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??60?:* # Lenovo Thinkpad X1 Carbon 3rd gen evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Carbon3rd:* -- cgit v1.2.3 From 5124866d7355b685c08aef6abc618e03367e4b93 Mon Sep 17 00:00:00 2001 From: David Michael Date: Thu, 4 Aug 2016 04:09:54 -0700 Subject: util-lib: add parse_percent_unbounded() for percentages over 100% (#3886) This permits CPUQuota to accept greater values as documented. --- src/basic/parse-util.c | 11 +++++++++-- src/basic/parse-util.h | 1 + src/core/load-fragment.c | 2 +- src/shared/bus-unit-util.c | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 503a895731..11849ade0b 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -533,7 +533,7 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { return 0; } -int parse_percent(const char *p) { +int parse_percent_unbounded(const char *p) { const char *pc, *n; unsigned v; int r; @@ -546,8 +546,15 @@ int parse_percent(const char *p) { r = safe_atou(n, &v); if (r < 0) return r; + + return (int) v; +} + +int parse_percent(const char *p) { + int v = parse_percent_unbounded(p); + if (v > 100) return -ERANGE; - return (int) v; + return v; } diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index 73441bb6fd..f0fa5f9752 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -106,4 +106,5 @@ int safe_atod(const char *s, double *ret_d); int parse_fractional_part_u(const char **s, size_t digits, unsigned *res); +int parse_percent_unbounded(const char *p); int parse_percent(const char *p); diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index e8cb3a4249..d5f035b67f 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -2903,7 +2903,7 @@ int config_parse_cpu_quota( return 0; } - r = parse_percent(rvalue); + r = parse_percent_unbounded(rvalue); if (r <= 0) { log_syntax(unit, LOG_ERR, filename, line, r, "CPU quota '%s' invalid. Ignoring.", rvalue); return 0; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 14bf8ad627..589f9d46e9 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -84,7 +84,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (isempty(eq)) r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY); else { - r = parse_percent(eq); + r = parse_percent_unbounded(eq); if (r <= 0) { log_error_errno(r, "CPU quota '%s' invalid.", eq); return -EINVAL; -- cgit v1.2.3 From 584c6e70509ef0792a22d456e96718d3ea9cb30d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 3 Aug 2016 19:52:28 -0400 Subject: journalctl,systemctl: add "short-full", "short-unix" mode to --help --- src/journal/journalctl.c | 6 +++--- src/systemctl/systemctl.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 53c6180864..6f841efb69 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -297,9 +297,9 @@ static void help(void) { " -n --lines[=INTEGER] Number of journal entries to show\n" " --no-tail Show all lines, even in follow mode\n" " -r --reverse Show the newest entries first\n" - " -o --output=STRING Change journal output mode (short, short-iso,\n" - " short-precise, short-monotonic, verbose,\n" - " export, json, json-pretty, json-sse, cat)\n" + " -o --output=STRING Change journal output mode (short, short-precise,\n" + " short-iso, short-full, short-monotonic, short-unix,\n" + " verbose, export, json, json-pretty, json-sse, cat)\n" " --utc Express time in Coordinated Universal Time (UTC)\n" " -x --catalog Add message explanations where available\n" " --no-full Ellipsize fields\n" diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 4fa7adfc41..b4ce6fba5a 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -6591,9 +6591,9 @@ static void systemctl_help(void) { " --preset-mode= Apply only enable, only disable, or all presets\n" " --root=PATH Enable unit files in the specified root directory\n" " -n --lines=INTEGER Number of journal entries to show\n" - " -o --output=STRING Change journal output mode (short, short-iso,\n" - " short-precise, short-monotonic, verbose,\n" - " export, json, json-pretty, json-sse, cat)\n" + " -o --output=STRING Change journal output mode (short, short-precise,\n" + " short-iso, short-full, short-monotonic, short-unix,\n" + " verbose, export, json, json-pretty, json-sse, cat)\n" " --firmware-setup Tell the firmware to show the setup menu on next boot\n" " --plain Print unit dependencies as a list instead of a tree\n\n" "Unit Commands:\n" -- cgit v1.2.3 From 90b4a64d77ad9395d940b095a27cf9f1cb65e5cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 3 Aug 2016 20:05:37 -0400 Subject: nspawn,resolve: short --help output to fit within 80 columns make dist-check-help FTW! --- src/nspawn/nspawn.c | 18 ++++++++---------- src/resolve/resolve-tool.c | 2 +- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 6cc1b9177d..3d131863f7 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -216,10 +216,10 @@ static void help(void) { " --uuid=UUID Set a specific machine UUID for the container\n" " -S --slice=SLICE Place the container in the specified slice\n" " --property=NAME=VALUE Set scope unit property\n" - " -U --private-users=pick Run within user namespace, pick UID/GID range automatically\n" + " -U --private-users=pick Run within user namespace, autoselect UID/GID range\n" " --private-users[=UIDBASE[:NUIDS]]\n" - " Run within user namespace, user configured UID/GID range\n" - " --private-user-chown Adjust OS tree file ownership for private UID/GID range\n" + " Similar, but with user configured UID/GID range\n" + " --private-user-chown Adjust OS tree ownership to private UID/GID range\n" " --private-network Disable network in container\n" " --network-interface=INTERFACE\n" " Assign an existing network interface to the\n" @@ -236,11 +236,10 @@ static void help(void) { " Add an additional virtual Ethernet link between\n" " host and container\n" " --network-bridge=INTERFACE\n" - " Add a virtual Ethernet connection between host\n" - " and container and add it to an existing bridge on\n" - " the host\n" - " --network-zone=NAME Add a virtual Ethernet connection to the container,\n" - " and add it to an automatically managed bridge interface\n" + " Add a virtual Ethernet connection to the container\n" + " and attach it to an existing bridge on the host\n" + " --network-zone=NAME Similar, but attach the new interface to an\n" + " an automatically managed bridge interface\n" " -p --port=[PROTOCOL:]HOSTPORT[:CONTAINERPORT]\n" " Expose a container IP port on the host\n" " -Z --selinux-context=SECLABEL\n" @@ -275,8 +274,7 @@ static void help(void) { " the service unit nspawn is running in\n" " --volatile[=MODE] Run the system in volatile mode\n" " --settings=BOOLEAN Load additional settings from .nspawn file\n" - " --notify-ready=BOOLEAN Receive notifications from the container's init process,\n" - " accepted values: yes and no\n" + " --notify-ready=BOOLEAN Receive notifications from the child init process\n" , program_invocation_short_name); } diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c index 6ae3750417..07e4cd7d1d 100644 --- a/src/resolve/resolve-tool.c +++ b/src/resolve/resolve-tool.c @@ -1542,7 +1542,7 @@ static void help(void) { "%1$s [OPTIONS...] --statistics\n" "%1$s [OPTIONS...] --reset-statistics\n" "\n" - "Resolve domain names, IPv4 and IPv6 addresses, DNS resource records, and services.\n\n" + "Resolve domain names, IPv4 and IPv6 addresses, DNS records, and services.\n\n" " -h --help Show this help\n" " --version Show package version\n" " --no-pager Do not pipe output into a pager\n" -- cgit v1.2.3 From e9fbae3f680ccd8dc3d6d4e7a3d0f7d5de1b28ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 3 Aug 2016 23:49:18 -0400 Subject: man: describe list-dependencies --all Meaning of --all was mentioned in list-dependencies description, but the this effect should also be mentioned in the description of the option itself. --- man/systemctl.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/man/systemctl.xml b/man/systemctl.xml index e7880d24f7..0ad0ad6d7e 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -156,6 +156,10 @@ To list all units installed in the file system, use the list-unit-files command instead. + + When listing units with list-dependencies, recursively show + dependencies of all dependent units (by default only dependencies of target units are + shown). -- cgit v1.2.3 From 1898a1a5c37ba019c7a5a48505befd64f78071dc Mon Sep 17 00:00:00 2001 From: Jonathan Boulle Date: Thu, 4 Aug 2016 15:54:10 +0200 Subject: src/test: add tests for parse_percent_unbounded (#3889) --- src/test/test-parse-util.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c index 0a76308f72..097c464229 100644 --- a/src/test/test-parse-util.c +++ b/src/test/test-parse-util.c @@ -493,6 +493,11 @@ static void test_parse_percent(void) { assert_se(parse_percent("1%%") == -EINVAL); } +static void test_parse_percent_unbounded(void) { + assert_se(parse_percent_unbounded("101%") == 101); + assert_se(parse_percent_unbounded("400%") == 400); +} + int main(int argc, char *argv[]) { log_parse_environment(); log_open(); @@ -507,6 +512,7 @@ int main(int argc, char *argv[]) { test_safe_atoi16(); test_safe_atod(); test_parse_percent(); + test_parse_percent_unbounded(); return 0; } -- cgit v1.2.3 From 3104883ddc2452ae210de3c70576c03023250a51 Mon Sep 17 00:00:00 2001 From: Susant Sahani Date: Thu, 4 Aug 2016 19:26:39 +0530 Subject: networkd: remove route if carrier is lost (#3831) Fixes #3669. --- src/network/networkd-link.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index a0da697707..99784b0ebe 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2314,6 +2314,35 @@ static int link_drop_foreign_config(Link *link) { return 0; } +static int link_drop_config(Link *link) { + Address *address; + Route *route; + Iterator i; + int r; + + SET_FOREACH(address, link->addresses, i) { + /* we consider IPv6LL addresses to be managed by the kernel */ + if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1) + continue; + + r = address_remove(address, link, link_address_remove_handler); + if (r < 0) + return r; + } + + SET_FOREACH(route, link->routes, i) { + /* do not touch routes managed by the kernel */ + if (route->protocol == RTPROT_KERNEL) + continue; + + r = route_remove(route, link, link_route_remove_handler); + if (r < 0) + return r; + } + + return 0; +} + static int link_update_lldp(Link *link) { int r; @@ -2860,6 +2889,14 @@ static int link_carrier_lost(Link *link) { return r; } + r = link_drop_config(link); + if (r < 0) + return r; + + r = link_drop_foreign_config(link); + if (r < 0) + return r; + r = link_handle_bound_by_list(link); if (r < 0) return r; -- cgit v1.2.3 From 99d2baa2cab0706d89beff596f997d4e3a1c833f Mon Sep 17 00:00:00 2001 From: Susant Sahani Date: Thu, 4 Aug 2016 19:30:58 +0530 Subject: networkd: add support to configure NOARP/ARP for interface (#3854) https://lists.freedesktop.org/archives/systemd-devel/2016-August/037268.html --- man/systemd.network.xml | 11 ++++++ src/network/networkd-link.c | 63 ++++++++++++++++++++++++++++++++ src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.c | 1 + src/network/networkd-network.h | 1 + 5 files changed, 77 insertions(+) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 4541a55490..c332cd7bdc 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -212,6 +212,17 @@ below 1280 (the minimum MTU for IPv6) it will automatically be increased to this value. + + ARP= + + A boolean. Enables or disables the ARP (low-level Address Resolution Protocol) + for this interface. Defaults to unset, which means that the kernel default will be used. + For example, disabling ARP is useful when creating multiple MACVLAN or VLAN virtual + interfaces atop a single lower-level physical interface, which will then only serve as a + link/"bridge" device aggregating traffic to the same physical link and not participate in + the network otherwise. + + diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 99784b0ebe..3e10ab1e04 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1314,6 +1314,65 @@ int link_set_mtu(Link *link, uint32_t mtu) { return 0; } +static int set_flags_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(m); + assert(link); + assert(link->ifname); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0) + log_link_warning_errno(link, r, "Could not set link flags: %m"); + + return 1; +} + +static int link_set_flags(Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + unsigned ifi_change = 0; + unsigned ifi_flags = 0; + int r; + + assert(link); + assert(link->manager); + assert(link->manager->rtnl); + + if (link->flags & IFF_LOOPBACK) + return 0; + + if (!link->network) + return 0; + + if (link->network->arp < 0) + return 0; + + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); + + if (link->network->arp >= 0) { + ifi_change |= IFF_NOARP; + ifi_flags |= IFF_NOARP; + } + + r = sd_rtnl_message_link_set_flags(req, ifi_flags, ifi_change); + if (r < 0) + return log_link_error_errno(link, r, "Could not set link flags: %m"); + + r = sd_netlink_call_async(link->manager->rtnl, req, set_flags_handler, link, 0, NULL); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + return 0; +} + static int link_set_bridge(Link *link) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -2411,6 +2470,10 @@ static int link_configure(Link *link) { if (r < 0) return r; + r = link_set_flags(link); + if (r < 0) + return r; + if (link_ipv4ll_enabled(link)) { r = ipv4ll_configure(link); if (r < 0) diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 5172a7b5e9..19adac66b8 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -28,6 +28,7 @@ Match.KernelCommandLine, config_parse_net_condition, Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, match_arch) Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac) Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu) +Link.ARP, config_parse_tristate, 0, offsetof(Network, arp) Network.Description, config_parse_string, 0, offsetof(Network, description) Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge) Network.Bond, config_parse_netdev, 0, offsetof(Network, bond) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 2b764d4f24..17bbe5de9f 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -134,6 +134,7 @@ static int network_load_one(Manager *manager, const char *filename) { network->ipv6_hop_limit = -1; network->duid.type = _DUID_TYPE_INVALID; network->proxy_arp = -1; + network->arp = -1; network->ipv6_accept_ra_use_dns = true; r = config_parse(NULL, filename, file, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 08ee939faa..7c0bdc1e4a 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -171,6 +171,7 @@ struct Network { struct ether_addr *mac; unsigned mtu; + int arp; uint32_t iaid; DUID duid; -- cgit v1.2.3 From 992e8f224b91cacc3d6589bea7882c7ab9c0911b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 26 Jul 2016 17:23:28 +0200 Subject: util-lib: rework /tmp and /var/tmp handling code Beef up the existing var_tmp() call, rename it to var_tmp_dir() and add a matching tmp_dir() call (the former looks for the place for /var/tmp, the latter for /tmp). Both calls check $TMPDIR, $TEMP, $TMP, following the algorithm Python3 uses. All dirs are validated before use. secure_getenv() is used in order to limite exposure in suid binaries. This also ports a couple of users over to these new APIs. The var_tmp() return parameter is changed from an allocated buffer the caller will own to a const string either pointing into environ[], or into a static const buffer. Given that environ[] is mostly considered constant (and this is exposed in the very well-known getenv() call), this should be OK behaviour and allows us to avoid memory allocations in most cases. Note that $TMPDIR and friends override both /var/tmp and /tmp usage if set. --- src/basic/fileio.c | 17 ++++++-- src/basic/fs-util.c | 98 +++++++++++++++++++++++++++++++++++--------- src/basic/fs-util.h | 3 +- src/coredump/coredumpctl.c | 9 +++- src/journal/journal-verify.c | 4 +- src/machine/machined-dbus.c | 2 +- src/test/test-fs-util.c | 54 ++++++++++-------------- 7 files changed, 126 insertions(+), 61 deletions(-) diff --git a/src/basic/fileio.c b/src/basic/fileio.c index f183de4999..6114bf3315 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -1161,8 +1161,8 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) { char *t, *x; uint64_t u; unsigned i; + int r; - assert(p); assert(ret); /* Turns this: @@ -1171,6 +1171,12 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) { * /foo/bar/waldo/.#3c2b6219aa75d7d0 */ + if (!p) { + r = tmp_dir(&p); + if (r < 0) + return r; + } + if (!extra) extra = ""; @@ -1257,10 +1263,13 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) int open_tmpfile_unlinkable(const char *directory, int flags) { char *p; - int fd; + int fd, r; - if (!directory) - directory = "/tmp"; + if (!directory) { + r = tmp_dir(&directory); + if (r < 0) + return r; + } /* Returns an unlinked temporary file that cannot be linked into the file system anymore */ diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index f0c6f3265e..ce87257bc1 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -496,34 +496,94 @@ int get_files_in_directory(const char *path, char ***list) { return n; } -int var_tmp(char **ret) { - const char *tmp_dir = NULL; - const char *env_tmp_dir = NULL; - char *c = NULL; - int r; +static int getenv_tmp_dir(const char **ret_path) { + const char *n; + int r, ret = 0; - assert(ret); + assert(ret_path); - env_tmp_dir = getenv("TMPDIR"); - if (env_tmp_dir != NULL) { - r = is_dir(env_tmp_dir, true); - if (r < 0 && r != -ENOENT) - return r; - if (r > 0) - tmp_dir = env_tmp_dir; + /* We use the same order of environment variables python uses in tempfile.gettempdir(): + * https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */ + FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") { + const char *e; + + e = secure_getenv(n); + if (!e) + continue; + if (!path_is_absolute(e)) { + r = -ENOTDIR; + goto next; + } + if (!path_is_safe(e)) { + r = -EPERM; + goto next; + } + + r = is_dir(e, true); + if (r < 0) + goto next; + if (r == 0) { + r = -ENOTDIR; + goto next; + } + + *ret_path = e; + return 1; + + next: + /* Remember first error, to make this more debuggable */ + if (ret >= 0) + ret = r; } - if (!tmp_dir) - tmp_dir = "/var/tmp"; + if (ret < 0) + return ret; - c = strdup(tmp_dir); - if (!c) - return -ENOMEM; - *ret = c; + *ret_path = NULL; + return ret; +} +static int tmp_dir_internal(const char *def, const char **ret) { + const char *e; + int r, k; + + assert(def); + assert(ret); + + r = getenv_tmp_dir(&e); + if (r > 0) { + *ret = e; + return 0; + } + + k = is_dir(def, true); + if (k == 0) + k = -ENOTDIR; + if (k < 0) + return r < 0 ? r : k; + + *ret = def; return 0; } +int var_tmp_dir(const char **ret) { + + /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus + * even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is + * returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR, + * making it a variable that overrides all temporary file storage locations. */ + + return tmp_dir_internal("/var/tmp", ret); +} + +int tmp_dir(const char **ret) { + + /* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually + * backed by an in-memory file system: /tmp. */ + + return tmp_dir_internal("/tmp", ret); +} + int inotify_add_watch_fd(int fd, int what, uint32_t mask) { char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; int r; diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 075e5942b1..2c3b9a1c74 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -61,7 +61,8 @@ int mkfifo_atomic(const char *path, mode_t mode); int get_files_in_directory(const char *path, char ***list); -int var_tmp(char **ret); +int tmp_dir(const char **ret); +int var_tmp_dir(const char **ret); #define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1) diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 27b1e0fb3f..bbf8793e57 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -30,6 +30,7 @@ #include "compress.h" #include "fd-util.h" #include "fileio.h" +#include "fs-util.h" #include "journal-internal.h" #include "log.h" #include "macro.h" @@ -609,7 +610,13 @@ static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) { char *temp = NULL; if (fd < 0) { - temp = strdup("/var/tmp/coredump-XXXXXX"); + const char *vt; + + r = var_tmp_dir(&vt); + if (r < 0) + return log_error_errno(r, "Failed to acquire temporary directory path: %m"); + + temp = strjoin(vt, "/coredump-XXXXXX", NULL); if (!temp) return log_oom(); diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index f61f158e8a..4105abfccc 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -826,7 +826,7 @@ int journal_file_verify( int data_fd = -1, entry_fd = -1, entry_array_fd = -1; unsigned i; bool found_last = false; - _cleanup_free_ char *tmp_dir = NULL; + const char *tmp_dir = NULL; #ifdef HAVE_GCRYPT uint64_t last_tag = 0; @@ -846,7 +846,7 @@ int journal_file_verify( } else if (f->seal) return -ENOKEY; - r = var_tmp(&tmp_dir); + r = var_tmp_dir(&tmp_dir); if (r < 0) { log_error_errno(r, "Failed to determine temporary directory: %m"); goto fail; diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 1923e8b971..5e2462cba2 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -954,7 +954,7 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err /* Create a temporary file we can dump information about deleted images into. We use a temporary file for this * instead of a pipe or so, since this might grow quit large in theory and we don't want to process this * continuously */ - result_fd = open_tmpfile_unlinkable("/tmp/", O_RDWR|O_CLOEXEC); + result_fd = open_tmpfile_unlinkable(NULL, O_RDWR|O_CLOEXEC); if (result_fd < 0) return -errno; diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index e0c040f39b..93eec3ef9c 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -83,47 +83,35 @@ static void test_get_files_in_directory(void) { } static void test_var_tmp(void) { - char *tmp_dir = NULL; - char *tmpdir_backup = NULL; - const char *default_var_tmp = NULL; - const char *var_name; - bool do_overwrite = true; - - default_var_tmp = "/var/tmp"; - var_name = "TMPDIR"; - - if (getenv(var_name) != NULL) { - tmpdir_backup = strdup(getenv(var_name)); - assert_se(tmpdir_backup != NULL); - } - - unsetenv(var_name); + _cleanup_free_ char *tmpdir_backup = NULL; + const char *tmp_dir = NULL, *t; - var_tmp(&tmp_dir); - assert_se(!strcmp(tmp_dir, default_var_tmp)); - - free(tmp_dir); + t = getenv("TMPDIR"); + if (t) { + tmpdir_backup = strdup(t); + assert_se(tmpdir_backup); + } - setenv(var_name, "/tmp", do_overwrite); - assert_se(!strcmp(getenv(var_name), "/tmp")); + assert(unsetenv("TMPDIR") >= 0); - var_tmp(&tmp_dir); - assert_se(!strcmp(tmp_dir, "/tmp")); + assert_se(var_tmp_dir(&tmp_dir) >= 0); + assert_se(streq(tmp_dir, "/var/tmp")); - free(tmp_dir); + assert_se(setenv("TMPDIR", "/tmp", true) >= 0); + assert_se(streq(getenv("TMPDIR"), "/tmp")); - setenv(var_name, "/88_does_not_exist_88", do_overwrite); - assert_se(!strcmp(getenv(var_name), "/88_does_not_exist_88")); + assert_se(var_tmp_dir(&tmp_dir) >= 0); + assert_se(streq(tmp_dir, "/tmp")); - var_tmp(&tmp_dir); - assert_se(!strcmp(tmp_dir, default_var_tmp)); + assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0); + assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88")); - free(tmp_dir); + assert_se(var_tmp_dir(&tmp_dir) >= 0); + assert_se(streq(tmp_dir, "/var/tmp")); - if (tmpdir_backup != NULL) { - setenv(var_name, tmpdir_backup, do_overwrite); - assert_se(!strcmp(getenv(var_name), tmpdir_backup)); - free(tmpdir_backup); + if (tmpdir_backup) { + assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0); + assert_se(streq(getenv("TMPDIR"), tmpdir_backup)); } } -- cgit v1.2.3 From c39f1ce24ddb1aa683991c5099dcc2afbfcbc57c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 26 Jul 2016 17:40:35 +0200 Subject: core: turn various execution flags into a proper flags parameter The ExecParameters structure contains a number of bit-flags, that were so far exposed as bool:1, change this to a proper, single binary bit flag field. This makes things a bit more expressive, and is helpful as we add more flags, since these booleans are passed around in various callers, for example service_spawn(), whose signature can be made much shorter now. Not all bit booleans from ExecParameters are moved into the flags field for now, but this can be added later. --- src/core/execute.c | 21 ++++++++----- src/core/execute.h | 17 ++++++++--- src/core/mount.c | 12 +++----- src/core/service.c | 90 +++++++++++++++++++----------------------------------- src/core/socket.c | 12 +++----- src/core/swap.c | 12 +++----- 6 files changed, 72 insertions(+), 92 deletions(-) diff --git a/src/core/execute.c b/src/core/execute.c index 77a75245cb..bc0fd27402 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -427,7 +427,7 @@ static int setup_input( return STDIN_FILENO; } - i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin); + i = fixup_input(context->std_input, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN); switch (i) { @@ -502,7 +502,7 @@ static int setup_output( return STDERR_FILENO; } - i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin); + i = fixup_input(context->std_input, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN); o = fixup_output(context->std_output, socket_fd); if (fileno == STDERR_FILENO) { @@ -1675,7 +1675,7 @@ static int exec_child( exec_context_tty_reset(context, params); - if (params->confirm_spawn) { + if (params->flags & EXEC_CONFIRM_SPAWN) { char response; r = ask_for_confirmation(&response, argv); @@ -1940,7 +1940,7 @@ static int exec_child( umask(context->umask); - if (params->apply_permissions && !command->privileged) { + if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) { r = enforce_groups(context, username, gid); if (r < 0) { *exit_status = EXIT_GROUP; @@ -2010,7 +2010,7 @@ static int exec_child( } r = setup_namespace( - params->apply_chroot ? context->root_directory : NULL, + (params->flags & EXEC_APPLY_CHROOT) ? context->root_directory : NULL, context->read_write_paths, context->read_only_paths, context->inaccessible_paths, @@ -2041,7 +2041,7 @@ static int exec_child( else wd = "/"; - if (params->apply_chroot) { + if (params->flags & EXEC_APPLY_CHROOT) { if (!needs_mount_namespace && context->root_directory) if (chroot(context->root_directory) < 0) { *exit_status = EXIT_CHROOT; @@ -2065,7 +2065,12 @@ static int exec_child( } #ifdef HAVE_SELINUX - if (params->apply_permissions && mac_selinux_use() && params->selinux_context_net && socket_fd >= 0 && !command->privileged) { + if ((params->flags & EXEC_APPLY_PERMISSIONS) && + mac_selinux_use() && + params->selinux_context_net && + socket_fd >= 0 && + !command->privileged) { + r = mac_selinux_get_child_mls_label(socket_fd, command->path, context->selinux_context, &mac_selinux_context_net); if (r < 0) { *exit_status = EXIT_SELINUX_CONTEXT; @@ -2090,7 +2095,7 @@ static int exec_child( return r; } - if (params->apply_permissions && !command->privileged) { + if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) { bool use_address_families = context->address_families_whitelist || !set_isempty(context->address_families); diff --git a/src/core/execute.h b/src/core/execute.h index 48cc18fbb3..77418ea2ad 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -208,6 +208,17 @@ struct ExecContext { bool no_new_privileges_set:1; }; +typedef enum ExecFlags { + EXEC_CONFIRM_SPAWN = 1U << 0, + EXEC_APPLY_PERMISSIONS = 1U << 1, + EXEC_APPLY_CHROOT = 1U << 2, + EXEC_APPLY_TTY_STDIN = 1U << 3, + + /* The following are not usec by execute.c, but by consumers internally */ + EXEC_PASS_FDS = 1U << 4, + EXEC_IS_CONTROL = 1U << 5, +} ExecFlags; + struct ExecParameters { char **argv; char **environment; @@ -216,11 +227,7 @@ struct ExecParameters { char **fd_names; unsigned n_fds; - bool apply_permissions:1; - bool apply_chroot:1; - bool apply_tty_stdin:1; - - bool confirm_spawn:1; + ExecFlags flags; bool selinux_context_net:1; bool cgroup_delegate:1; diff --git a/src/core/mount.c b/src/core/mount.c index afb20af9e2..3f6ac7fcf9 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -701,12 +701,10 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { pid_t pid; int r; ExecParameters exec_params = { - .apply_permissions = true, - .apply_chroot = true, - .apply_tty_stdin = true, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, + .flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, }; assert(m); @@ -732,7 +730,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { return r; exec_params.environment = UNIT(m)->manager->environment; - exec_params.confirm_spawn = UNIT(m)->manager->confirm_spawn; + exec_params.flags |= UNIT(m)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; exec_params.cgroup_supported = UNIT(m)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(m)->cgroup_path; exec_params.cgroup_delegate = m->cgroup_context.delegate; diff --git a/src/core/service.c b/src/core/service.c index eb125cb999..b4db7d17ed 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1152,11 +1152,7 @@ static int service_spawn( Service *s, ExecCommand *c, usec_t timeout, - bool pass_fds, - bool apply_permissions, - bool apply_chroot, - bool apply_tty_stdin, - bool is_control, + ExecFlags flags, pid_t *_pid) { _cleanup_strv_free_ char **argv = NULL, **final_env = NULL, **our_env = NULL, **fd_names = NULL; @@ -1166,12 +1162,10 @@ static int service_spawn( pid_t pid; ExecParameters exec_params = { - .apply_permissions = apply_permissions, - .apply_chroot = apply_chroot, - .apply_tty_stdin = apply_tty_stdin, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, + .flags = flags, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, }; int r; @@ -1194,7 +1188,7 @@ static int service_spawn( if (r < 0) return r; - if (pass_fds || + if ((flags & EXEC_PASS_FDS) || s->exec_context.std_input == EXEC_INPUT_SOCKET || s->exec_context.std_output == EXEC_OUTPUT_SOCKET || s->exec_context.std_error == EXEC_OUTPUT_SOCKET) { @@ -1218,7 +1212,7 @@ static int service_spawn( if (!our_env) return -ENOMEM; - if (is_control ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE) + if ((flags & EXEC_IS_CONTROL) ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE) if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0) return -ENOMEM; @@ -1226,7 +1220,7 @@ static int service_spawn( if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0) return -ENOMEM; - if (!MANAGER_IS_SYSTEM(UNIT(s)->manager)) + if (MANAGER_IS_USER(UNIT(s)->manager)) if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0) return -ENOMEM; @@ -1266,18 +1260,18 @@ static int service_spawn( if (!final_env) return -ENOMEM; - if (is_control && UNIT(s)->cgroup_path) { + if ((flags & EXEC_IS_CONTROL) && UNIT(s)->cgroup_path) { path = strjoina(UNIT(s)->cgroup_path, "/control"); (void) cg_create(SYSTEMD_CGROUP_CONTROLLER, path); } else path = UNIT(s)->cgroup_path; exec_params.argv = argv; + exec_params.environment = final_env; exec_params.fds = fds; exec_params.fd_names = fd_names; exec_params.n_fds = n_fds; - exec_params.environment = final_env; - exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; + exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = path; exec_params.cgroup_delegate = s->cgroup_context.delegate; @@ -1465,11 +1459,9 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { r = service_spawn(s, s->control_command, s->timeout_stop_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - true, - true, + (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS) | + (s->root_directory_start_only ? 0 : EXEC_APPLY_CHROOT) | + EXEC_APPLY_TTY_STDIN | EXEC_IS_CONTROL, &s->control_pid); if (r < 0) goto fail; @@ -1580,11 +1572,9 @@ static void service_enter_stop(Service *s, ServiceResult f) { r = service_spawn(s, s->control_command, s->timeout_stop_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - true, + (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS) | + (s->root_directory_start_only ? 0 : EXEC_APPLY_CHROOT) | + EXEC_IS_CONTROL, &s->control_pid); if (r < 0) goto fail; @@ -1661,11 +1651,9 @@ static void service_enter_start_post(Service *s) { r = service_spawn(s, s->control_command, s->timeout_start_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - true, + (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS)| + (s->root_directory_start_only ? 0 : EXEC_APPLY_CHROOT)| + EXEC_IS_CONTROL, &s->control_pid); if (r < 0) goto fail; @@ -1735,11 +1723,7 @@ static void service_enter_start(Service *s) { r = service_spawn(s, c, timeout, - true, - true, - true, - true, - false, + EXEC_PASS_FDS|EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, &pid); if (r < 0) goto fail; @@ -1798,11 +1782,9 @@ static void service_enter_start_pre(Service *s) { r = service_spawn(s, s->control_command, s->timeout_start_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - true, - true, + (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS) | + (s->root_directory_start_only ? 0: EXEC_APPLY_CHROOT) | + EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN, &s->control_pid); if (r < 0) goto fail; @@ -1877,11 +1859,9 @@ static void service_enter_reload(Service *s) { r = service_spawn(s, s->control_command, s->timeout_start_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - true, + (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS) | + (s->root_directory_start_only ? 0 : EXEC_APPLY_CHROOT) | + EXEC_IS_CONTROL, &s->control_pid); if (r < 0) goto fail; @@ -1919,12 +1899,10 @@ static void service_run_next_control(Service *s) { r = service_spawn(s, s->control_command, timeout, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - s->control_command_id == SERVICE_EXEC_START_PRE || - s->control_command_id == SERVICE_EXEC_STOP_POST, - true, + (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS) | + (s->root_directory_start_only ? 0 : EXEC_APPLY_CHROOT) | + (IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0)| + EXEC_IS_CONTROL, &s->control_pid); if (r < 0) goto fail; @@ -1962,11 +1940,7 @@ static void service_run_next_main(Service *s) { r = service_spawn(s, s->main_command, s->timeout_start_usec, - true, - true, - true, - true, - false, + EXEC_PASS_FDS|EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, &pid); if (r < 0) goto fail; diff --git a/src/core/socket.c b/src/core/socket.c index ff55885fb3..82363e2157 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1664,12 +1664,10 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { pid_t pid; int r; ExecParameters exec_params = { - .apply_permissions = true, - .apply_chroot = true, - .apply_tty_stdin = true, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, + .flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, }; assert(s); @@ -1700,7 +1698,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { exec_params.argv = argv; exec_params.environment = UNIT(s)->manager->environment; - exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; + exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(s)->cgroup_path; exec_params.cgroup_delegate = s->cgroup_context.delegate; diff --git a/src/core/swap.c b/src/core/swap.c index 66a318d01f..0ba4c4d881 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -611,12 +611,10 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { pid_t pid; int r; ExecParameters exec_params = { - .apply_permissions = true, - .apply_chroot = true, - .apply_tty_stdin = true, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, + .flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, }; assert(s); @@ -642,7 +640,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { goto fail; exec_params.environment = UNIT(s)->manager->environment; - exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; + exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(s)->cgroup_path; exec_params.cgroup_delegate = s->cgroup_context.delegate; -- cgit v1.2.3 From 9c1a61adba1ed61a405bc30675f08b8442eefd70 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 26 Jul 2016 17:53:07 +0200 Subject: core: move masking of chroot/permission masking into service_spawn() Let's fix up the flags fields in service_spawn() rather than its callers, in order to simplify things a bit. --- src/core/execute.h | 2 +- src/core/service.c | 34 +++++++++++++++------------------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/core/execute.h b/src/core/execute.h index 77418ea2ad..8d659ca178 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -214,7 +214,7 @@ typedef enum ExecFlags { EXEC_APPLY_CHROOT = 1U << 2, EXEC_APPLY_TTY_STDIN = 1U << 3, - /* The following are not usec by execute.c, but by consumers internally */ + /* The following are not used by execute.c, but by consumers internally */ EXEC_PASS_FDS = 1U << 4, EXEC_IS_CONTROL = 1U << 5, } ExecFlags; diff --git a/src/core/service.c b/src/core/service.c index b4db7d17ed..32b8e7d2c5 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1174,6 +1174,14 @@ static int service_spawn( assert(c); assert(_pid); + if (flags & EXEC_IS_CONTROL) { + /* If this is a control process, mask the permissions/chroot application if this is requested. */ + if (s->permissions_start_only) + exec_params.flags &= ~EXEC_APPLY_PERMISSIONS; + if (s->root_directory_start_only) + exec_params.flags &= ~EXEC_APPLY_CHROOT; + } + (void) unit_realize_cgroup(UNIT(s)); if (s->reset_cpu_usage) { (void) unit_reset_cpu_usage(UNIT(s)); @@ -1459,9 +1467,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { r = service_spawn(s, s->control_command, s->timeout_stop_usec, - (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS) | - (s->root_directory_start_only ? 0 : EXEC_APPLY_CHROOT) | - EXEC_APPLY_TTY_STDIN | EXEC_IS_CONTROL, + EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_IS_CONTROL, &s->control_pid); if (r < 0) goto fail; @@ -1572,9 +1578,7 @@ static void service_enter_stop(Service *s, ServiceResult f) { r = service_spawn(s, s->control_command, s->timeout_stop_usec, - (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS) | - (s->root_directory_start_only ? 0 : EXEC_APPLY_CHROOT) | - EXEC_IS_CONTROL, + EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL, &s->control_pid); if (r < 0) goto fail; @@ -1651,9 +1655,7 @@ static void service_enter_start_post(Service *s) { r = service_spawn(s, s->control_command, s->timeout_start_usec, - (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS)| - (s->root_directory_start_only ? 0 : EXEC_APPLY_CHROOT)| - EXEC_IS_CONTROL, + EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL, &s->control_pid); if (r < 0) goto fail; @@ -1782,9 +1784,7 @@ static void service_enter_start_pre(Service *s) { r = service_spawn(s, s->control_command, s->timeout_start_usec, - (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS) | - (s->root_directory_start_only ? 0: EXEC_APPLY_CHROOT) | - EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN, + EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN, &s->control_pid); if (r < 0) goto fail; @@ -1859,9 +1859,7 @@ static void service_enter_reload(Service *s) { r = service_spawn(s, s->control_command, s->timeout_start_usec, - (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS) | - (s->root_directory_start_only ? 0 : EXEC_APPLY_CHROOT) | - EXEC_IS_CONTROL, + EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL, &s->control_pid); if (r < 0) goto fail; @@ -1899,10 +1897,8 @@ static void service_run_next_control(Service *s) { r = service_spawn(s, s->control_command, timeout, - (s->permissions_start_only ? 0 : EXEC_APPLY_PERMISSIONS) | - (s->root_directory_start_only ? 0 : EXEC_APPLY_CHROOT) | - (IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0)| - EXEC_IS_CONTROL, + EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL| + (IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0), &s->control_pid); if (r < 0) goto fail; -- cgit v1.2.3 From af9d16e10a23899b821af19e54e339486a86bd82 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Jul 2016 11:50:37 +0200 Subject: core: use the correct APIs to determine whether a dual timestamp is initialized --- src/core/execute.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/execute.c b/src/core/execute.c index bc0fd27402..9028139723 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2969,12 +2969,12 @@ void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) { "%sPID: "PID_FMT"\n", prefix, s->pid); - if (s->start_timestamp.realtime > 0) + if (dual_timestamp_is_set(&s->start_timestamp)) fprintf(f, "%sStart Timestamp: %s\n", prefix, format_timestamp(buf, sizeof(buf), s->start_timestamp.realtime)); - if (s->exit_timestamp.realtime > 0) + if (dual_timestamp_is_set(&s->exit_timestamp)) fprintf(f, "%sExit Timestamp: %s\n" "%sExit Code: %s\n" -- cgit v1.2.3 From ffff9abe1d4dc61d6f9a47ce0f3f85a3854373cf Mon Sep 17 00:00:00 2001 From: Tobias Jungel Date: Thu, 4 Aug 2016 17:11:13 +0200 Subject: networkd: apply bridge vlan configuration correct bridge vlan configuration was applied even if it wasn't configured. fixes #3876 --- src/network/networkd-brvlan.c | 20 ++++++++++++++++++++ src/network/networkd-brvlan.h | 1 + src/network/networkd-link.c | 3 ++- src/network/networkd-network-gperf.gperf | 2 +- src/network/networkd-network.h | 1 + 5 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/network/networkd-brvlan.c b/src/network/networkd-brvlan.c index 8bc330ebae..18ecd86858 100644 --- a/src/network/networkd-brvlan.c +++ b/src/network/networkd-brvlan.c @@ -257,6 +257,24 @@ static int parse_vid_range(const char *rvalue, uint16_t *vid, uint16_t *vid_end) return r; } +int config_parse_brvlan_pvid(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; + int r; + uint16_t pvid; + r = parse_vlanid(rvalue, &pvid); + if (r < 0) + return r; + + network->pvid = pvid; + network->use_br_vlan = true; + + return 0; +} + int config_parse_brvlan_vlan(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, @@ -288,6 +306,7 @@ int config_parse_brvlan_vlan(const char *unit, const char *filename, for (; vid <= vid_end; vid++) set_bit(vid, network->br_vid_bitmap); } + network->use_br_vlan = true; return 0; } @@ -325,5 +344,6 @@ int config_parse_brvlan_untagged(const char *unit, const char *filename, set_bit(vid, network->br_untagged_bitmap); } } + network->use_br_vlan = true; return 0; } diff --git a/src/network/networkd-brvlan.h b/src/network/networkd-brvlan.h index 6aa6883bfc..b37633f94f 100644 --- a/src/network/networkd-brvlan.h +++ b/src/network/networkd-brvlan.h @@ -25,5 +25,6 @@ typedef struct Link Link; int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap); +int config_parse_brvlan_pvid(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); int config_parse_brvlan_vlan(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); int config_parse_brvlan_untagged(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); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 3e10ab1e04..e1d07eb4b0 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2060,7 +2060,8 @@ static int link_joined(Link *link) { log_link_error_errno(link, r, "Could not set bridge message: %m"); } - if (link->network->bridge || streq_ptr("bridge", link->kind)) { + if (link->network->use_br_vlan && + (link->network->bridge || streq_ptr("bridge", link->kind))) { r = link_set_bridge_vlan(link); if (r < 0) log_link_error_errno(link, r, "Could not set bridge vlan: %m"); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 19adac66b8..b96f0b7210 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -115,7 +115,7 @@ Bridge.AllowPortToBeRoot, config_parse_bool, Bridge.UnicastFlood, config_parse_bool, 0, offsetof(Network, unicast_flood) BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0 BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0 -BridgeVLAN.PVID, config_parse_vlanid, 0, offsetof(Network, pvid) +BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0 BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0 BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0 /* backwards compatibility: do not add new entries to this section */ diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 7c0bdc1e4a..5460eb4d1c 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -151,6 +151,7 @@ struct Network { bool unicast_flood; unsigned cost; + bool use_br_vlan; uint16_t pvid; uint32_t br_vid_bitmap[BRIDGE_VLAN_BITMAP_LEN]; uint32_t br_untagged_bitmap[BRIDGE_VLAN_BITMAP_LEN]; -- cgit v1.2.3 From c2d11a630287631d3b401a5cf300ea3655e21fd1 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Thu, 4 Aug 2016 13:52:02 -0700 Subject: fileio: fix read_full_stream() bugs (#3887) read_full_stream() _always_ allocated twice the memory needed, due to only breaking the realloc() && fread() loop when fread() returned 0, requiring another iteration and exponentially enlarged buffer just to discover the EOF condition. This also caused file sizes >2MiB && <= 4MiB to erroneously be treated as E2BIG, due to the inappropriately doubled buffer size exceeding 4*1024*1024. Also made the 4*1024*1024 magic number a READ_FULL_BYTES_MAX constant. --- src/basic/fileio.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/basic/fileio.c b/src/basic/fileio.c index f183de4999..c18ac39e03 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -47,6 +47,8 @@ #include "umask-util.h" #include "utf8.h" +#define READ_FULL_BYTES_MAX (4U*1024U*1024U) + int write_string_stream(FILE *f, const char *line, bool enforce_newline) { assert(f); @@ -230,7 +232,7 @@ int read_full_stream(FILE *f, char **contents, size_t *size) { if (S_ISREG(st.st_mode)) { /* Safety check */ - if (st.st_size > 4*1024*1024) + if (st.st_size > READ_FULL_BYTES_MAX) return -E2BIG; /* Start with the right file size, but be prepared for @@ -245,26 +247,31 @@ int read_full_stream(FILE *f, char **contents, size_t *size) { char *t; size_t k; - t = realloc(buf, n+1); + t = realloc(buf, n + 1); if (!t) return -ENOMEM; buf = t; k = fread(buf + l, 1, n - l, f); + if (k > 0) + l += k; - if (k <= 0) { - if (ferror(f)) - return -errno; + if (ferror(f)) + return -errno; + if (feof(f)) break; - } - l += k; - n *= 2; + /* We aren't expecting fread() to return a short read outside + * of (error && eof), assert buffer is full and enlarge buffer. + */ + assert(l == n); /* Safety check */ - if (n > 4*1024*1024) + if (n >= READ_FULL_BYTES_MAX) return -E2BIG; + + n = MAX(n * 2, READ_FULL_BYTES_MAX); } buf[l] = 0; -- cgit v1.2.3 From 13811bf909d99b24411d3d5fbea790c9f81d08b7 Mon Sep 17 00:00:00 2001 From: 0xAX <0xAX@users.noreply.github.com> Date: Thu, 4 Aug 2016 23:52:24 +0300 Subject: main: use pager for --dump-configuration-items (#3894) --- src/core/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/main.c b/src/core/main.c index 094bbef964..02324d325e 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1614,6 +1614,7 @@ int main(int argc, char *argv[]) { retval = version(); goto finish; } else if (arg_action == ACTION_DUMP_CONFIGURATION_ITEMS) { + pager_open(arg_no_pager, false); unit_dump_config_items(stdout); retval = EXIT_SUCCESS; goto finish; -- cgit v1.2.3 From 136dc4c4350dfcb1d61ceaac982d0d0a8e6e3863 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Jul 2016 11:51:11 +0200 Subject: core: set $SERVICE_RESULT, $EXIT_CODE and $EXIT_STATUS in ExecStop=/ExecStopPost= commands This should simplify monitoring tools for services, by passing the most basic information about service result/exit information via environment variables, thus making it unnecessary to retrieve them explicitly via the bus. --- man/systemd.exec.xml | 37 +++++++++++++++++++++++++++++++++++++ man/systemd.service.xml | 8 +++++++- src/core/execute.h | 1 + src/core/service.c | 27 +++++++++++++++++++++++---- 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 58ba582911..0fc658f180 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1602,6 +1602,43 @@ functions) if their standard output or standard error output is connected to the journal anyway, thus enabling delivery of structured metadata along with logged messages. + + + $SERVICE_RESULT + + Only defined for the service unit type, this environment variable is passed to all + ExecStop= and ExecStopPost= processes, and encodes the service + "result". Currently, the following values are defined: timeout (in case of an operation + timeout), exit-code (if a service process exited with a non-zero exit code; see + $EXIT_STATUS below for the actual exit status returned), signal (if a + service process was terminated abnormally by a signal; see $EXIT_STATUS below for the actual + signal used for the termination), core-dump (if a service process terminated abnormally and + dumped core), watchdog (if the watchdog keep-alive ping was enabled for the service but it + missed the deadline), or resources (a catch-all condition in case a system operation + failed). + + This environment variable is useful to monitor failure or successful termination of a service. Even + though this variable is available in both ExecStop= and ExecStopPost=, it + is usually a better choice to place monitoring tools in the latter, as the former is only invoked for services + that managed to start up correctly, and the latter covers both services that failed during their start-up and + those which failed during their runtime. + + + + $EXIT_CODE + $EXIT_STATUS + + Only defined for the service unit type, these environment variables are passed to all + ExecStop=, ExecStopPost= processes and contain exit status/code + information of the main process of the service. For the precise definition of the exit code and status, see + wait2. $EXIT_CODE + is one of exited, killed, + dumped. $EXIT_STATUS contains the numeric exit code formatted as string + if $EXIT_CODE is exited, and the signal name in all other cases. Note + that these environment variables are only set if the service manager succeeded to start and identify the main + process of the service. + + Additional variables may be configured by the following diff --git a/man/systemd.service.xml b/man/systemd.service.xml index 875d368fcf..e82edbe93e 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -429,7 +429,13 @@ service failed to start up correctly. Commands configured with this setting need to be able to operate even if the service failed starting up half-way and left incompletely initialized data around. As the service's processes have been terminated already when the commands specified with this setting are executed they should - not attempt to communicate with them. + not attempt to communicate with them. + + Note that all commands that are configured with this setting are invoked with the result code of the + service, as well as the main process' exit code and status, set in the $SERVICE_RESULT, + $EXIT_CODE and $EXIT_STATUS environment variables, see + systemd.exec5 for + details. diff --git a/src/core/execute.h b/src/core/execute.h index 8d659ca178..2b4238ed7e 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -217,6 +217,7 @@ typedef enum ExecFlags { /* The following are not used by execute.c, but by consumers internally */ EXEC_PASS_FDS = 1U << 4, EXEC_IS_CONTROL = 1U << 5, + EXEC_SETENV_RESULT = 1U << 6, } ExecFlags; struct ExecParameters { diff --git a/src/core/service.c b/src/core/service.c index 32b8e7d2c5..0cbea52276 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1216,7 +1216,7 @@ static int service_spawn( if (r < 0) return r; - our_env = new0(char*, 6); + our_env = new0(char*, 9); if (!our_env) return -ENOMEM; @@ -1264,6 +1264,24 @@ static int service_spawn( } } + if (flags & EXEC_SETENV_RESULT) { + if (asprintf(our_env + n_env++, "SERVICE_RESULT=%s", service_result_to_string(s->result)) < 0) + return -ENOMEM; + + if (s->main_exec_status.pid > 0 && + dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) { + if (asprintf(our_env + n_env++, "EXIT_CODE=%s", sigchld_code_to_string(s->main_exec_status.code)) < 0) + return -ENOMEM; + + if (s->main_exec_status.code == CLD_EXITED) + r = asprintf(our_env + n_env++, "EXIT_STATUS=%i", s->main_exec_status.status); + else + r = asprintf(our_env + n_env++, "EXIT_STATUS=%s", signal_to_string(s->main_exec_status.status)); + if (r < 0) + return -ENOMEM; + } + } + final_env = strv_env_merge(2, UNIT(s)->manager->environment, our_env, NULL); if (!final_env) return -ENOMEM; @@ -1467,7 +1485,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { r = service_spawn(s, s->control_command, s->timeout_stop_usec, - EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_IS_CONTROL, + EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_IS_CONTROL|EXEC_SETENV_RESULT, &s->control_pid); if (r < 0) goto fail; @@ -1578,7 +1596,7 @@ static void service_enter_stop(Service *s, ServiceResult f) { r = service_spawn(s, s->control_command, s->timeout_stop_usec, - EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL, + EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_SETENV_RESULT, &s->control_pid); if (r < 0) goto fail; @@ -1898,7 +1916,8 @@ static void service_run_next_control(Service *s) { s->control_command, timeout, EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL| - (IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0), + (IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0)| + (IN_SET(s->control_command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_SETENV_RESULT : 0), &s->control_pid); if (r < 0) goto fail; -- cgit v1.2.3 From 2129011e92feee93383b450bb2d20008786b90a0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Jul 2016 13:14:01 +0200 Subject: nss-systemd: resolve root/nobody statically Let's extend nss-systemd to also synthesize user/group entries for the UIDs/GIDs 0 and 65534 which have special kernel meaning. Given that nss-systemd is listed in /etc/nsswitch.conf only very late any explicit listing in /etc/passwd or /etc/group takes precedence. This functionality is useful in minimal container-like setups that lack /etc/passwd files (or only have incompletely populated ones). --- configure.ac | 22 ++++++++- man/nss-systemd.xml | 4 ++ src/nss-systemd/nss-systemd.c | 101 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index a86deca471..4d1c96606f 100644 --- a/configure.ac +++ b/configure.ac @@ -556,12 +556,30 @@ AC_SUBST(CERTIFICATEROOT) AC_ARG_WITH([support-url], AS_HELP_STRING([--with-support-url=URL], - [Specify the supoport URL to show in catalog entries included in systemd]), + [Specify the support URL to show in catalog entries included in systemd]), [SUPPORT_URL="$withval"], [SUPPORT_URL=http://lists.freedesktop.org/mailman/listinfo/systemd-devel]) AC_SUBST(SUPPORT_URL) +AC_ARG_WITH([nobody-user], + AS_HELP_STRING([--with-nobody-user=NAME], + [Specify the name of the nobody user (the one with UID 65534)]), + [NOBODY_USER_NAME="$withval"], + [NOBODY_USER_NAME=nobody]) + +AC_SUBST(NOBODY_USER_NAME) +AC_DEFINE_UNQUOTED(NOBODY_USER_NAME, ["$NOBODY_USER_NAME"], [The name of the nobody user (the one with UID 65534)]) + +AC_ARG_WITH([nobody-group], + AS_HELP_STRING([--with-nobody-group=NAME], + [Specify the name of the nobody group (the one with GID 65534)]), + [NOBODY_GROUP_NAME="$withval"], + [NOBODY_GROUP_NAME=nobody]) + +AC_SUBST(NOBODY_GROUP_NAME) +AC_DEFINE_UNQUOTED(NOBODY_GROUP_NAME, ["$NOBODY_GROUP_NAME"], [The name of the nobody group (the one with GID 65534)]) + # ------------------------------------------------------------------------------ have_xz=no AC_ARG_ENABLE(xz, AS_HELP_STRING([--disable-xz], [Disable optional XZ support])) @@ -1677,6 +1695,8 @@ AC_MSG_RESULT([ Maximum System GID: ${SYSTEM_GID_MAX} Certificate root: ${CERTIFICATEROOT} Support URL: ${SUPPORT_URL} + Nobody User Name: ${NOBODY_USER_NAME} + Nobody Group Name: ${NOBODY_GROUP_NAME} CFLAGS: ${OUR_CFLAGS} ${CFLAGS} CPPFLAGS: ${OUR_CPPFLAGS} ${CPPFLAGS} diff --git a/man/nss-systemd.xml b/man/nss-systemd.xml index 4228372e51..56d26e7d1f 100644 --- a/man/nss-systemd.xml +++ b/man/nss-systemd.xml @@ -61,6 +61,10 @@ systemd.exec5 for details on this option. + This module also ensures that the root and nobody users and groups (i.e. the users/groups with the UIDs/GIDs + 0 and 65534) remain resolvable at all times, even if they aren't listed in /etc/passwd or + /etc/group, or if these files are missing. + To activate the NSS module, add systemd to the lines starting with passwd: and group: in /etc/nsswitch.conf. diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index e7a4393bb0..7078c0c50c 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -26,9 +26,52 @@ #include "macro.h" #include "nss-util.h" #include "signal-util.h" +#include "string-util.h" #include "user-util.h" #include "util.h" +#ifndef NOBODY_USER_NAME +#define NOBODY_USER_NAME "nobody" +#endif + +#ifndef NOBODY_GROUP_NAME +#define NOBODY_GROUP_NAME "nobody" +#endif + +static const struct passwd root_passwd = { + .pw_name = (char*) "root", + .pw_passwd = (char*) "x", /* see shadow file */ + .pw_uid = 0, + .pw_gid = 0, + .pw_gecos = (char*) "Super User", + .pw_dir = (char*) "/root", + .pw_shell = (char*) "/bin/sh", +}; + +static const struct passwd nobody_passwd = { + .pw_name = (char*) NOBODY_USER_NAME, + .pw_passwd = (char*) "*", /* locked */ + .pw_uid = 65534, + .pw_gid = 65534, + .pw_gecos = (char*) "User Nobody", + .pw_dir = (char*) "/", + .pw_shell = (char*) "/sbin/nologin", +}; + +static const struct group root_group = { + .gr_name = (char*) "root", + .gr_gid = 0, + .gr_passwd = (char*) "x", /* see shadow file */ + .gr_mem = (char*[]) { NULL }, +}; + +static const struct group nobody_group = { + .gr_name = (char*) NOBODY_GROUP_NAME, + .gr_gid = 65534, + .gr_passwd = (char*) "*", /* locked */ + .gr_mem = (char*[]) { NULL }, +}; + NSS_GETPW_PROTOTYPES(systemd); NSS_GETGR_PROTOTYPES(systemd); @@ -50,6 +93,23 @@ enum nss_status _nss_systemd_getpwnam_r( assert(name); assert(pwd); + if (!valid_user_group_name(name)) { + r = -EINVAL; + goto fail; + } + + /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */ + if (streq(name, root_passwd.pw_name)) { + *pwd = root_passwd; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (streq(name, nobody_passwd.pw_name)) { + *pwd = nobody_passwd; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */ if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) goto not_found; @@ -126,6 +186,18 @@ enum nss_status _nss_systemd_getpwuid_r( goto fail; } + /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */ + if (uid == root_passwd.pw_uid) { + *pwd = root_passwd; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (uid == nobody_passwd.pw_uid) { + *pwd = nobody_passwd; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (uid <= SYSTEM_UID_MAX) goto not_found; @@ -202,6 +274,23 @@ enum nss_status _nss_systemd_getgrnam_r( assert(name); assert(gr); + if (!valid_user_group_name(name)) { + r = -EINVAL; + goto fail; + } + + /* Synthesize records for root and nobody, in case they are missing form /etc/group */ + if (streq(name, root_group.gr_name)) { + *gr = root_group; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (streq(name, nobody_group.gr_name)) { + *gr = nobody_group; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) goto not_found; @@ -275,6 +364,18 @@ enum nss_status _nss_systemd_getgrgid_r( goto fail; } + /* Synthesize records for root and nobody, in case they are missing from /etc/group */ + if (gid == root_group.gr_gid) { + *gr = root_group; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (gid == nobody_group.gr_gid) { + *gr = nobody_group; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (gid <= SYSTEM_GID_MAX) goto not_found; -- cgit v1.2.3 From d47f681b285b1dfb7ce68200205bfe8b835657a2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Jul 2016 13:30:58 +0200 Subject: update TODO --- TODO | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/TODO b/TODO index bb36522bf9..723292cde0 100644 --- a/TODO +++ b/TODO @@ -35,27 +35,17 @@ Features: * RemoveIPC= in unit files for removing POSIX/SysV IPC objects -* Set SERVICE_RESULT= as env var while running ExecStop= - * Introduce ProtectSystem=strict for making the entire OS hierarchy read-only except for a select few * nspawn: start UID allocation loop from hash of container name -* in the DynamicUser=1 nss module, also map "nobody" and "root" statically - -* pid1: log about all processes we kill with with SIGKILL or in abandoned scopes, as this should normally not happen - * nspawn: support that /proc, /sys/, /dev are pre-mounted -* nspawn: mount esp, so that bootctl can work - * define gpt header bits to select volatility mode * nspawn: mount loopback filesystems with "discard" -* Make TasksMax= take percentages, taken relative to the pids_max sysctl and pids.max cgroup limit - * ProtectKernelLogs= (drops CAP_SYSLOG, add seccomp for syslog() syscall, and DeviceAllow to /dev/kmsg) in service files * ProtectClock= (drops CAP_SYS_TIMES, adds seecomp filters for settimeofday, adjtimex), sets DeviceAllow o /dev/rtc -- cgit v1.2.3 From a0fef983ab200db4e2b151beb06c9cf8fef6c5ab Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 4 Aug 2016 21:14:27 +0200 Subject: core: remember first unit failure, not last unit failure Previously, the result value of a unit was overriden with each failure that took place, so that the result always reported the last failure that took place. With this commit this is changed, so that the first failure taking place is stored instead. This should normally not matter much as multiple failures are sufficiently uncommon. However, it improves one behaviour: if we send SIGABRT to a service due to a watchdog timeout, then this currently would be reported as "coredump" failure, rather than the "watchodg" failure it really is. Hence, in order to report information about the type of the failure, and not about the effect of it, let's change this from all unit type to store the first, not the last failure. This addresses the issue pointed out here: https://github.com/systemd/systemd/pull/3818#discussion_r73433520 --- src/core/automount.c | 2 +- src/core/busname.c | 6 +++--- src/core/mount.c | 8 ++++---- src/core/path.c | 2 +- src/core/scope.c | 4 ++-- src/core/service.c | 14 +++++++------- src/core/socket.c | 10 +++++----- src/core/swap.c | 8 ++++---- src/core/timer.c | 2 +- 9 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/core/automount.c b/src/core/automount.c index 20a73c76f9..00295cf769 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -301,7 +301,7 @@ static void automount_dump(Unit *u, FILE *f, const char *prefix) { static void automount_enter_dead(Automount *a, AutomountResult f) { assert(a); - if (f != AUTOMOUNT_SUCCESS) + if (a->result == AUTOMOUNT_SUCCESS) a->result = f; automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD); diff --git a/src/core/busname.c b/src/core/busname.c index 730be2ee14..7952cd31aa 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -442,7 +442,7 @@ fail: static void busname_enter_dead(BusName *n, BusNameResult f) { assert(n); - if (f != BUSNAME_SUCCESS) + if (n->result == BUSNAME_SUCCESS) n->result = f; busname_set_state(n, n->result != BUSNAME_SUCCESS ? BUSNAME_FAILED : BUSNAME_DEAD); @@ -454,7 +454,7 @@ static void busname_enter_signal(BusName *n, BusNameState state, BusNameResult f assert(n); - if (f != BUSNAME_SUCCESS) + if (n->result == BUSNAME_SUCCESS) n->result = f; kill_context_init(&kill_context); @@ -882,7 +882,7 @@ static void busname_sigchld_event(Unit *u, pid_t pid, int code, int status) { log_unit_full(u, f == BUSNAME_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0, "Control process exited, code=%s status=%i", sigchld_code_to_string(code), status); - if (f != BUSNAME_SUCCESS) + if (n->result == BUSNAME_SUCCESS) n->result = f; switch (n->state) { diff --git a/src/core/mount.c b/src/core/mount.c index 3f6ac7fcf9..f3ccf6d48a 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -759,7 +759,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { static void mount_enter_dead(Mount *m, MountResult f) { assert(m); - if (f != MOUNT_SUCCESS) + if (m->result == MOUNT_SUCCESS) m->result = f; mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD); @@ -775,7 +775,7 @@ static void mount_enter_dead(Mount *m, MountResult f) { static void mount_enter_mounted(Mount *m, MountResult f) { assert(m); - if (f != MOUNT_SUCCESS) + if (m->result == MOUNT_SUCCESS) m->result = f; mount_set_state(m, MOUNT_MOUNTED); @@ -786,7 +786,7 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) { assert(m); - if (f != MOUNT_SUCCESS) + if (m->result == MOUNT_SUCCESS) m->result = f; r = unit_kill_context( @@ -1158,7 +1158,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { else assert_not_reached("Unknown code"); - if (f != MOUNT_SUCCESS) + if (m->result == MOUNT_SUCCESS) m->result = f; if (m->control_command) { diff --git a/src/core/path.c b/src/core/path.c index 0dd0d375d8..10f9b06974 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -454,7 +454,7 @@ static int path_coldplug(Unit *u) { static void path_enter_dead(Path *p, PathResult f) { assert(p); - if (f != PATH_SUCCESS) + if (p->result == PATH_SUCCESS) p->result = f; path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD); diff --git a/src/core/scope.c b/src/core/scope.c index b45e238974..b278aed3d6 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -221,7 +221,7 @@ static void scope_dump(Unit *u, FILE *f, const char *prefix) { static void scope_enter_dead(Scope *s, ScopeResult f) { assert(s); - if (f != SCOPE_SUCCESS) + if (s->result == SCOPE_SUCCESS) s->result = f; scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD); @@ -233,7 +233,7 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) { assert(s); - if (f != SCOPE_SUCCESS) + if (s->result == SCOPE_SUCCESS) s->result = f; unit_watch_all_pids(UNIT(s)); diff --git a/src/core/service.c b/src/core/service.c index 0cbea52276..a6793b813b 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1423,7 +1423,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) int r; assert(s); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD); @@ -1472,7 +1472,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { int r; assert(s); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; service_unwatch_control_pid(s); @@ -1525,7 +1525,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f assert(s); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; unit_watch_all_pids(UNIT(s)); @@ -1583,7 +1583,7 @@ static void service_enter_stop(Service *s, ServiceResult f) { assert(s); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; service_unwatch_control_pid(s); @@ -1635,7 +1635,7 @@ static bool service_good(Service *s) { static void service_enter_running(Service *s, ServiceResult f) { assert(s); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; service_unwatch_control_pid(s); @@ -2609,7 +2609,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { "EXIT_STATUS=%i", status, NULL); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; if (s->main_command && @@ -2690,7 +2690,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { "Control process exited, code=%s status=%i", sigchld_code_to_string(code), status); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; /* Immediately get rid of the cgroup, so that the diff --git a/src/core/socket.c b/src/core/socket.c index 82363e2157..c919e89b7d 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1810,7 +1810,7 @@ fail: static void socket_enter_dead(Socket *s, SocketResult f) { assert(s); - if (f != SOCKET_SUCCESS) + if (s->result == SOCKET_SUCCESS) s->result = f; socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD); @@ -1829,7 +1829,7 @@ static void socket_enter_stop_post(Socket *s, SocketResult f) { int r; assert(s); - if (f != SOCKET_SUCCESS) + if (s->result == SOCKET_SUCCESS) s->result = f; socket_unwatch_control_pid(s); @@ -1857,7 +1857,7 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { assert(s); - if (f != SOCKET_SUCCESS) + if (s->result == SOCKET_SUCCESS) s->result = f; r = unit_kill_context( @@ -1901,7 +1901,7 @@ static void socket_enter_stop_pre(Socket *s, SocketResult f) { int r; assert(s); - if (f != SOCKET_SUCCESS) + if (s->result == SOCKET_SUCCESS) s->result = f; socket_unwatch_control_pid(s); @@ -2822,7 +2822,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { "Control process exited, code=%s status=%i", sigchld_code_to_string(code), status); - if (f != SOCKET_SUCCESS) + if (s->result == SOCKET_SUCCESS) s->result = f; if (s->control_command && diff --git a/src/core/swap.c b/src/core/swap.c index 0ba4c4d881..2c802da3b5 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -673,7 +673,7 @@ fail: static void swap_enter_dead(Swap *s, SwapResult f) { assert(s); - if (f != SWAP_SUCCESS) + if (s->result == SWAP_SUCCESS) s->result = f; swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD); @@ -689,7 +689,7 @@ static void swap_enter_dead(Swap *s, SwapResult f) { static void swap_enter_active(Swap *s, SwapResult f) { assert(s); - if (f != SWAP_SUCCESS) + if (s->result == SWAP_SUCCESS) s->result = f; swap_set_state(s, SWAP_ACTIVE); @@ -700,7 +700,7 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) { assert(s); - if (f != SWAP_SUCCESS) + if (s->result == SWAP_SUCCESS) s->result = f; r = unit_kill_context( @@ -997,7 +997,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { else assert_not_reached("Unknown code"); - if (f != SWAP_SUCCESS) + if (s->result == SWAP_SUCCESS) s->result = f; if (s->control_command) { diff --git a/src/core/timer.c b/src/core/timer.c index 3206296f09..e2b43f02f8 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -291,7 +291,7 @@ static int timer_coldplug(Unit *u) { static void timer_enter_dead(Timer *t, TimerResult f) { assert(t); - if (f != TIMER_SUCCESS) + if (t->result == TIMER_SUCCESS) t->result = f; timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD); -- cgit v1.2.3 From b08af3b12706f352f651e70e117f6d6dcf11a911 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 4 Aug 2016 22:11:29 +0200 Subject: core: only set the watchdog variables in ExecStart= lines --- src/core/execute.c | 2 +- src/core/execute.h | 1 + src/core/service.c | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/execute.c b/src/core/execute.c index 9028139723..7aafb1b058 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1425,7 +1425,7 @@ static int build_environment( our_env[n_env++] = x; } - if (p->watchdog_usec > 0) { + if ((p->flags & EXEC_SET_WATCHDOG) && p->watchdog_usec > 0) { if (asprintf(&x, "WATCHDOG_PID="PID_FMT, getpid()) < 0) return -ENOMEM; our_env[n_env++] = x; diff --git a/src/core/execute.h b/src/core/execute.h index 2b4238ed7e..2d05ca39aa 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -218,6 +218,7 @@ typedef enum ExecFlags { EXEC_PASS_FDS = 1U << 4, EXEC_IS_CONTROL = 1U << 5, EXEC_SETENV_RESULT = 1U << 6, + EXEC_SET_WATCHDOG = 1U << 7, } ExecFlags; struct ExecParameters { diff --git a/src/core/service.c b/src/core/service.c index a6793b813b..c21653f5fa 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1743,7 +1743,7 @@ static void service_enter_start(Service *s) { r = service_spawn(s, c, timeout, - EXEC_PASS_FDS|EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, + EXEC_PASS_FDS|EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG, &pid); if (r < 0) goto fail; @@ -1955,7 +1955,7 @@ static void service_run_next_main(Service *s) { r = service_spawn(s, s->main_command, s->timeout_start_usec, - EXEC_PASS_FDS|EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, + EXEC_PASS_FDS|EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG, &pid); if (r < 0) goto fail; -- cgit v1.2.3 From 163f561e2a1d2ef8ef3a1efa2ea8f00d90cc1246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 4 Aug 2016 20:36:10 -0400 Subject: basic/set: remove some spurious spaces --- src/basic/set.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/basic/set.h b/src/basic/set.h index 12f64a8c57..a5f8beb0c4 100644 --- a/src/basic/set.h +++ b/src/basic/set.h @@ -23,8 +23,8 @@ #include "hashmap.h" #include "macro.h" -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) +Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) static inline Set *set_free(Set *s) { internal_hashmap_free(HASHMAP_BASE(s)); @@ -42,8 +42,8 @@ static inline Set *set_copy(Set *s) { return (Set*) internal_hashmap_copy(HASHMAP_BASE(s)); } -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) int set_put(Set *s, const void *key); /* no set_update */ -- cgit v1.2.3 From 9a73653c3ed819c28a293a5df2da8bdac84dfdb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 4 Aug 2016 20:55:45 -0400 Subject: systemd: convert peers_by_address to a set --- src/core/socket.c | 16 ++++++++-------- src/core/socket.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/core/socket.c b/src/core/socket.c index ff55885fb3..d3b9a75547 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -151,10 +151,10 @@ static void socket_done(Unit *u) { socket_free_ports(s); - while ((p = hashmap_steal_first(s->peers_by_address))) + while ((p = set_steal_first(s->peers_by_address))) p->socket = NULL; - s->peers_by_address = hashmap_free(s->peers_by_address); + s->peers_by_address = set_free(s->peers_by_address); s->exec_runtime = exec_runtime_unref(s->exec_runtime); exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX); @@ -519,7 +519,7 @@ static int socket_load(Unit *u) { assert(u); assert(u->load_state == UNIT_STUB); - r = hashmap_ensure_allocated(&s->peers_by_address, &peer_address_hash_ops); + r = set_ensure_allocated(&s->peers_by_address, &peer_address_hash_ops); if (r < 0) return r; @@ -2366,7 +2366,7 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { } } - HASHMAP_FOREACH(k, s->peers_by_address, i) { + SET_FOREACH(k, s->peers_by_address, i) { _cleanup_free_ char *t = NULL; r = sockaddr_pretty(&k->peer.sa, FAMILY_ADDRESS_SIZE(k->peer.sa.sa_family), true, true, &t); @@ -2560,7 +2560,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, memcpy(&p->peer, &a.sockaddr, sizeof(a.sockaddr)); p->socket = s; - r = hashmap_put(s->peers_by_address, p, p); + r = set_put(s->peers_by_address, p); if (r < 0) return r; @@ -2696,7 +2696,7 @@ SocketPeer *socket_peer_unref(SocketPeer *p) { return NULL; if (p->socket) - (void) hashmap_remove(p->socket->peers_by_address, p); + set_remove(p->socket->peers_by_address, p); free(p); @@ -2716,7 +2716,7 @@ int socket_find_peer(Socket *s, int fd, SocketPeer **p) { if (r < 0) return log_error_errno(errno, "getpeername failed: %m"); - i = hashmap_get(s->peers_by_address, &sa); + i = set_get(s->peers_by_address, &sa); if (i) { *p = i; return 1; @@ -2729,7 +2729,7 @@ int socket_find_peer(Socket *s, int fd, SocketPeer **p) { memcpy(&remote->peer, &sa.peer, sizeof(union sockaddr_union)); remote->socket = s; - r = hashmap_put(s->peers_by_address, remote, remote); + r = set_put(s->peers_by_address, remote); if (r < 0) return r; diff --git a/src/core/socket.h b/src/core/socket.h index 2fe38ef2aa..edbe9df6b1 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -80,7 +80,7 @@ struct Socket { LIST_HEAD(SocketPort, ports); - Hashmap *peers_by_address; + Set *peers_by_address; unsigned n_accepted; unsigned n_connections; -- cgit v1.2.3 From 2604646c05fcb8a10dadc302ea5a376a56faaa73 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Thu, 4 Aug 2016 21:05:34 -0700 Subject: journal-remote: remove unnecessary gnutls includes (#3895) journal-(gatewayd,remote).c don't actually utilize libgnutls even when HAVE_GNUTLS is defined. --- src/journal-remote/journal-gatewayd.c | 3 --- src/journal-remote/journal-remote.c | 4 ---- 2 files changed, 7 deletions(-) diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 4ad9184993..a1627fab39 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -19,9 +19,6 @@ #include #include -#ifdef HAVE_GNUTLS -#include -#endif #include #include #include diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index f1ef90ed7a..80e2adb100 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -27,10 +27,6 @@ #include #include -#ifdef HAVE_GNUTLS -#include -#endif - #include "sd-daemon.h" #include "alloc-util.h" -- cgit v1.2.3 From 82b103a7ce299effbb151a2cd7f0b3c7d29aefad Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Thu, 4 Aug 2016 21:09:23 -0700 Subject: fileio: fix MIN/MAX mixup (#3896) The intention is to clamp the value to READ_FULL_BYTES_MAX, which would be the minimum of the two. --- src/basic/fileio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/basic/fileio.c b/src/basic/fileio.c index c18ac39e03..588eacd77c 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -271,7 +271,7 @@ int read_full_stream(FILE *f, char **contents, size_t *size) { if (n >= READ_FULL_BYTES_MAX) return -E2BIG; - n = MAX(n * 2, READ_FULL_BYTES_MAX); + n = MIN(n * 2, READ_FULL_BYTES_MAX); } buf[l] = 0; -- cgit v1.2.3 From 1ed1f50f8277df07918e13cba3331a114eaa6fe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 5 Aug 2016 04:37:10 -0400 Subject: networkd: do not set NOARP unconditionally (#3891) Fixes #3890. --- src/network/networkd-link.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index e1d07eb4b0..69ee7424ce 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1357,7 +1357,7 @@ static int link_set_flags(Link *link) { if (link->network->arp >= 0) { ifi_change |= IFF_NOARP; - ifi_flags |= IFF_NOARP; + ifi_flags |= link->network->arp ? 0 : IFF_NOARP; } r = sd_rtnl_message_link_set_flags(req, ifi_flags, ifi_change); -- cgit v1.2.3 From 41bf0590cc89438f1d319465190b1c00809c78fe Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 5 Aug 2016 11:17:08 +0200 Subject: util-lib: unify parsing of nice level values This adds parse_nice() that parses a nice level and ensures it is in the right range, via a new nice_is_valid() helper. It then ports over a number of users to this. No functional changes. --- src/basic/parse-util.c | 18 +++++++++++++++++- src/basic/parse-util.h | 2 ++ src/basic/process-util.h | 5 +++++ src/core/dbus-execute.c | 2 +- src/core/load-fragment.c | 32 ++++++++++++++++---------------- src/run/run.c | 8 +++----- src/shared/bus-unit-util.c | 12 +++++------- src/test/test-parse-util.c | 29 +++++++++++++++++++++++++++++ 8 files changed, 78 insertions(+), 30 deletions(-) diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 11849ade0b..c98815b9bc 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -29,6 +29,7 @@ #include "extract-word.h" #include "macro.h" #include "parse-util.h" +#include "process-util.h" #include "string-util.h" int parse_boolean(const char *v) { @@ -551,10 +552,25 @@ int parse_percent_unbounded(const char *p) { } int parse_percent(const char *p) { - int v = parse_percent_unbounded(p); + int v; + v = parse_percent_unbounded(p); if (v > 100) return -ERANGE; return v; } + +int parse_nice(const char *p, int *ret) { + int n, r; + + r = safe_atoi(p, &n); + if (r < 0) + return r; + + if (!nice_is_valid(n)) + return -ERANGE; + + *ret = n; + return 0; +} diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index f0fa5f9752..461e1cd4d8 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -108,3 +108,5 @@ int parse_fractional_part_u(const char **s, size_t digits, unsigned *res); int parse_percent_unbounded(const char *p); int parse_percent(const char *p); + +int parse_nice(const char *p, int *ret); diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 9f75088796..2568e3834f 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "formats-util.h" #include "macro.h" @@ -103,3 +104,7 @@ int sched_policy_from_string(const char *s); void valgrind_summary_hack(void); int pid_compare_func(const void *a, const void *b); + +static inline bool nice_is_valid(int n) { + return n >= PRIO_MIN && n < PRIO_MAX; +} diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 9c50cd93e5..346c8b973e 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -935,7 +935,7 @@ int bus_exec_context_set_transient_property( if (r < 0) return r; - if (n < PRIO_MIN || n >= PRIO_MAX) + if (!nice_is_valid(n)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Nice value out of range"); if (mode != UNIT_CHECK) { diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index d5f035b67f..420f368689 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -491,16 +491,17 @@ int config_parse_socket_bind(const char *unit, return 0; } -int config_parse_exec_nice(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) { +int config_parse_exec_nice( + 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) { ExecContext *c = data; int priority, r; @@ -510,14 +511,13 @@ int config_parse_exec_nice(const char *unit, assert(rvalue); assert(data); - r = safe_atoi(rvalue, &priority); + r = parse_nice(rvalue, &priority); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue); - return 0; - } + if (r == -ERANGE) + log_syntax(unit, LOG_ERR, filename, line, r, "Nice priority out of range, ignoring: %s", rvalue); + else + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue); - if (priority < PRIO_MIN || priority >= PRIO_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Nice priority out of range, ignoring: %s", rvalue); return 0; } diff --git a/src/run/run.c b/src/run/run.c index 58fa49a4d1..1917ffd857 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -257,11 +257,9 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_NICE: - r = safe_atoi(optarg, &arg_nice); - if (r < 0 || arg_nice < PRIO_MIN || arg_nice >= PRIO_MAX) { - log_error("Failed to parse nice value"); - return -EINVAL; - } + r = parse_nice(optarg, &arg_nice); + if (r < 0) + return log_error_errno(r, "Failed to parse nice value: %s", optarg); arg_nice_set = true; break; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 589f9d46e9..c3a5233532 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -366,15 +366,13 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen } } else if (streq(field, "Nice")) { - int32_t i; + int n; - r = safe_atoi32(eq, &i); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } + r = parse_nice(eq, &n); + if (r < 0) + return log_error_errno(r, "Failed to parse nice value: %s", eq); - r = sd_bus_message_append(m, "v", "i", i); + r = sd_bus_message_append(m, "v", "i", (int32_t) n); } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) { const char *p; diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c index 097c464229..d08014100b 100644 --- a/src/test/test-parse-util.c +++ b/src/test/test-parse-util.c @@ -498,6 +498,34 @@ static void test_parse_percent_unbounded(void) { assert_se(parse_percent_unbounded("400%") == 400); } +static void test_parse_nice(void) { + int n; + + assert_se(parse_nice("0", &n) >= 0 && n == 0); + assert_se(parse_nice("+0", &n) >= 0 && n == 0); + assert_se(parse_nice("-1", &n) >= 0 && n == -1); + assert_se(parse_nice("-2", &n) >= 0 && n == -2); + assert_se(parse_nice("1", &n) >= 0 && n == 1); + assert_se(parse_nice("2", &n) >= 0 && n == 2); + assert_se(parse_nice("+1", &n) >= 0 && n == 1); + assert_se(parse_nice("+2", &n) >= 0 && n == 2); + assert_se(parse_nice("-20", &n) >= 0 && n == -20); + assert_se(parse_nice("19", &n) >= 0 && n == 19); + assert_se(parse_nice("+19", &n) >= 0 && n == 19); + + + assert_se(parse_nice("", &n) == -EINVAL); + assert_se(parse_nice("-", &n) == -EINVAL); + assert_se(parse_nice("+", &n) == -EINVAL); + assert_se(parse_nice("xx", &n) == -EINVAL); + assert_se(parse_nice("-50", &n) == -ERANGE); + assert_se(parse_nice("50", &n) == -ERANGE); + assert_se(parse_nice("+50", &n) == -ERANGE); + assert_se(parse_nice("-21", &n) == -ERANGE); + assert_se(parse_nice("20", &n) == -ERANGE); + assert_se(parse_nice("+20", &n) == -ERANGE); +} + int main(int argc, char *argv[]) { log_parse_environment(); log_open(); @@ -513,6 +541,7 @@ int main(int argc, char *argv[]) { test_safe_atod(); test_parse_percent(); test_parse_percent_unbounded(); + test_parse_nice(); return 0; } -- cgit v1.2.3 From 166cf510c2ba5aed15dcb807ec263ea2201dcb28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 4 Aug 2016 21:42:23 -0400 Subject: core/socket: rework SocketPeer refcounting Make functions and definitions that don't need to be shared local to socket.c. --- src/core/socket.c | 194 ++++++++++++++++++++++++++++-------------------------- src/core/socket.h | 7 -- 2 files changed, 100 insertions(+), 101 deletions(-) diff --git a/src/core/socket.c b/src/core/socket.c index d3b9a75547..972d494dbc 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -59,6 +59,13 @@ #include "user-util.h" #include "in-addr-util.h" +struct SocketPeer { + unsigned n_ref; + + Socket *socket; + union sockaddr_union peer; +}; + static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { [SOCKET_DEAD] = UNIT_INACTIVE, [SOCKET_START_PRE] = UNIT_ACTIVATING, @@ -78,9 +85,6 @@ static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); -SocketPeer *socket_peer_new(void); -int socket_find_peer(Socket *s, int fd, SocketPeer **p); - static void socket_init(Unit *u) { Socket *s = SOCKET(u); @@ -482,10 +486,11 @@ static void peer_address_hash_func(const void *p, struct siphash *state) { const SocketPeer *s = p; assert(s); + assert(IN_SET(s->peer.sa.sa_family, AF_INET, AF_INET6)); if (s->peer.sa.sa_family == AF_INET) siphash24_compress(&s->peer.in.sin_addr, sizeof(s->peer.in.sin_addr), state); - else if (s->peer.sa.sa_family == AF_INET6) + else siphash24_compress(&s->peer.in6.sin6_addr, sizeof(s->peer.in6.sin6_addr), state); } @@ -503,8 +508,7 @@ static int peer_address_compare_func(const void *a, const void *b) { case AF_INET6: return memcmp(&x->peer.in6.sin6_addr, &y->peer.in6.sin6_addr, sizeof(x->peer.in6.sin6_addr)); } - - return -1; + assert_not_reached("Black sheep in the family!"); } const struct hash_ops peer_address_hash_ops = { @@ -537,6 +541,87 @@ static int socket_load(Unit *u) { return socket_verify(s); } +static SocketPeer *socket_peer_new(void) { + SocketPeer *p; + + p = new0(SocketPeer, 1); + if (!p) + return NULL; + + p->n_ref = 1; + + return p; +} + +SocketPeer *socket_peer_ref(SocketPeer *p) { + if (!p) + return NULL; + + assert(p->n_ref > 0); + p->n_ref++; + + return p; +} + +SocketPeer *socket_peer_unref(SocketPeer *p) { + if (!p) + return NULL; + + assert(p->n_ref > 0); + + p->n_ref--; + + if (p->n_ref > 0) + return NULL; + + if (p->socket) + set_remove(p->socket->peers_by_address, p); + + return mfree(p); +} + +static int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) { + _cleanup_(socket_peer_unrefp) SocketPeer *remote = NULL; + SocketPeer sa = {}, *i; + socklen_t salen = sizeof(sa.peer); + int r; + + assert(fd >= 0); + assert(s); + + r = getpeername(fd, &sa.peer.sa, &salen); + if (r < 0) + return log_error_errno(errno, "getpeername failed: %m"); + + if (!IN_SET(sa.peer.sa.sa_family, AF_INET, AF_INET6)) { + *p = NULL; + return 0; + } + + i = set_get(s->peers_by_address, &sa); + if (i) { + *p = socket_peer_ref(i); + return 1; + } + + remote = socket_peer_new(); + if (!remote) + return log_oom(); + + remote->peer = sa.peer; + + r = set_put(s->peers_by_address, remote); + if (r < 0) + return r; + + remote->socket = s; + + *p = remote; + remote = NULL; + + return 1; +} + _const_ static const char* listen_lookup(int family, int type) { if (family == AF_NETLINK) @@ -2102,22 +2187,22 @@ static void socket_enter_running(Socket *s, int cfd) { Service *service; if (s->n_connections >= s->max_connections) { - log_unit_warning(UNIT(s), "Too many incoming connections (%u), refusing connection attempt.", s->n_connections); + log_unit_warning(UNIT(s), "Too many incoming connections (%u), refusing connection attempt.", + s->n_connections); safe_close(cfd); return; } if (s->max_connections_per_source > 0) { - r = socket_find_peer(s, cfd, &p); + r = socket_acquire_peer(s, cfd, &p); if (r < 0) { safe_close(cfd); return; - } - - if (p->n_ref > s->max_connections_per_source) { - log_unit_warning(UNIT(s), "Too many incoming connections (%u) from source, refusing connection attempt.", p->n_ref); + } else if (r > 0 && p->n_ref > s->max_connections_per_source) { + log_unit_warning(UNIT(s), + "Too many incoming connections (%u) from source, refusing connection attempt.", + p->n_ref); safe_close(cfd); - p = NULL; return; } } @@ -2163,10 +2248,8 @@ static void socket_enter_running(Socket *s, int cfd) { cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */ s->n_connections++; - if (s->max_connections_per_source > 0) { - service->peer = socket_peer_ref(p); - p = NULL; - } + service->peer = p; /* Pass ownership of the peer reference */ + p = NULL; r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL); if (r < 0) { @@ -2662,83 +2745,6 @@ _pure_ static bool socket_check_gc(Unit *u) { return s->n_connections > 0; } -SocketPeer *socket_peer_new(void) { - SocketPeer *p; - - p = new0(SocketPeer, 1); - if (!p) - return NULL; - - p->n_ref = 1; - - return p; -} - -SocketPeer *socket_peer_ref(SocketPeer *p) { - if (!p) - return NULL; - - assert(p->n_ref > 0); - p->n_ref++; - - return p; -} - -SocketPeer *socket_peer_unref(SocketPeer *p) { - if (!p) - return NULL; - - assert(p->n_ref > 0); - - p->n_ref--; - - if (p->n_ref > 0) - return NULL; - - if (p->socket) - set_remove(p->socket->peers_by_address, p); - - free(p); - - return NULL; -} - -int socket_find_peer(Socket *s, int fd, SocketPeer **p) { - _cleanup_free_ SocketPeer *remote = NULL; - SocketPeer sa, *i; - socklen_t salen = sizeof(sa.peer); - int r; - - assert(fd >= 0); - assert(s); - - r = getpeername(fd, &sa.peer.sa, &salen); - if (r < 0) - return log_error_errno(errno, "getpeername failed: %m"); - - i = set_get(s->peers_by_address, &sa); - if (i) { - *p = i; - return 1; - } - - remote = socket_peer_new(); - if (!remote) - return log_oom(); - - memcpy(&remote->peer, &sa.peer, sizeof(union sockaddr_union)); - remote->socket = s; - - r = set_put(s->peers_by_address, remote); - if (r < 0) - return r; - - *p = remote; - remote = NULL; - - return 0; -} - static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { SocketPort *p = userdata; int cfd = -1; diff --git a/src/core/socket.h b/src/core/socket.h index edbe9df6b1..6a78fd322d 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -168,13 +168,6 @@ struct Socket { RateLimit trigger_limit; }; -struct SocketPeer { - unsigned n_ref; - - Socket *socket; - union sockaddr_union peer; -}; - SocketPeer *socket_peer_ref(SocketPeer *p); SocketPeer *socket_peer_unref(SocketPeer *p); -- cgit v1.2.3 From 9dfb64f87da718284f5c840ac6176dccbfd7dc3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 4 Aug 2016 23:26:31 -0400 Subject: core/service: serialize and deserialize accept_socket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes an issue during reexec — the count of connections would be lost: [zbyszek@fedora-rawhide ~]$ systemctl status testlimit.socket | grep Connected Accepted: 1; Connected: 1 [zbyszek@fedora-rawhide ~]$ sudo systemctl daemon-reexec [zbyszek@fedora-rawhide ~]$ systemctl status testlimit.socket | grep Connected Accepted: 1; Connected: 0 With the patch, Connected count is preserved. Also add "Accept Socket" to the dump output for services. --- src/core/service.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/core/service.c b/src/core/service.c index eb125cb999..1629e1ce44 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -761,6 +761,11 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { prefix, s->bus_name, prefix, yes_no(s->bus_name_good)); + if (UNIT_ISSET(s->accept_socket)) + fprintf(f, + "%sAccept Socket: %s\n", + prefix, UNIT_DEREF(s->accept_socket)->id); + kill_context_dump(&s->kill_context, f, prefix); exec_context_dump(&s->exec_context, f, prefix); @@ -2130,6 +2135,12 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { if (r < 0) return r; + if (UNIT_ISSET(s->accept_socket)) { + r = unit_serialize_item(u, f, "accept-socket", UNIT_DEREF(s->accept_socket)->id); + if (r < 0) + return r; + } + r = unit_serialize_item_fd(u, f, fds, "socket-fd", s->socket_fd); if (r < 0) return r; @@ -2260,6 +2271,17 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, s->control_command_id = id; s->control_command = s->exec_command[id]; } + } else if (streq(key, "accept-socket")) { + Unit *socket; + + r = manager_load_unit(u->manager, value, NULL, NULL, &socket); + if (r < 0) + log_unit_debug_errno(u, r, "Failed to load accept-socket unit: %s", value); + else { + unit_ref_set(&s->accept_socket, socket); + SOCKET(socket)->n_connections++; + } + } else if (streq(key, "socket-fd")) { int fd; -- cgit v1.2.3 From 3ebcd323bdeeb55bb963fe5e9d97a87f96fd8879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 4 Aug 2016 23:42:27 -0400 Subject: systemd: do not serialize peer, bump count when deserializing socket instead --- src/core/service.c | 14 ++++++++++++++ src/core/socket.c | 42 +----------------------------------------- src/core/socket.h | 1 + 3 files changed, 16 insertions(+), 41 deletions(-) diff --git a/src/core/service.c b/src/core/service.c index 1629e1ce44..3c9455a5f8 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1041,6 +1041,20 @@ static int service_coldplug(Unit *u) { if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) (void) unit_setup_dynamic_creds(u); + if (UNIT_ISSET(s->accept_socket)) { + Socket* socket = SOCKET(UNIT_DEREF(s->accept_socket)); + + if (socket->max_connections_per_source > 0) { + SocketPeer *peer; + + /* Make a best-effort attempt at bumping the connection count */ + if (socket_acquire_peer(socket, s->socket_fd, &peer) > 0) { + socket_peer_unref(s->peer); + s->peer = peer; + } + } + } + service_set_state(s, s->deserialized_state); return 0; } diff --git a/src/core/socket.c b/src/core/socket.c index 972d494dbc..0d77694251 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -580,7 +580,7 @@ SocketPeer *socket_peer_unref(SocketPeer *p) { return mfree(p); } -static int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) { +int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) { _cleanup_(socket_peer_unrefp) SocketPeer *remote = NULL; SocketPeer sa = {}, *i; socklen_t salen = sizeof(sa.peer); @@ -2396,9 +2396,7 @@ static int socket_stop(Unit *u) { static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { Socket *s = SOCKET(u); - SocketPeer *k; SocketPort *p; - Iterator i; int r; assert(u); @@ -2449,16 +2447,6 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { } } - SET_FOREACH(k, s->peers_by_address, i) { - _cleanup_free_ char *t = NULL; - - r = sockaddr_pretty(&k->peer.sa, FAMILY_ADDRESS_SIZE(k->peer.sa.sa_family), true, true, &t); - if (r < 0) - return r; - - unit_serialize_item_format(u, f, "peer", "%u %s", k->n_ref, t); - } - return 0; } @@ -2574,7 +2562,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse socket value: %s", value); else { - LIST_FOREACH(port, p, s->ports) if (socket_address_is(&p->address, value+skip, type)) break; @@ -2622,33 +2609,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, } } - } else if (streq(key, "peer")) { - _cleanup_(socket_peer_unrefp) SocketPeer *p; - int n_ref, skip = 0; - SocketAddress a; - int r; - - if (sscanf(value, "%u %n", &n_ref, &skip) < 1 || n_ref < 1) - log_unit_debug(u, "Failed to parse socket peer value: %s", value); - else { - r = socket_address_parse(&a, value+skip); - if (r < 0) - return r; - - p = socket_peer_new(); - if (!p) - return log_oom(); - - p->n_ref = n_ref; - memcpy(&p->peer, &a.sockaddr, sizeof(a.sockaddr)); - p->socket = s; - - r = set_put(s->peers_by_address, p); - if (r < 0) - return r; - - p = NULL; - } } else log_unit_debug(UNIT(s), "Unknown serialization key: %s", key); diff --git a/src/core/socket.h b/src/core/socket.h index 6a78fd322d..89f4664510 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -170,6 +170,7 @@ struct Socket { SocketPeer *socket_peer_ref(SocketPeer *p); SocketPeer *socket_peer_unref(SocketPeer *p); +int socket_acquire_peer(Socket *s, int fd, SocketPeer **p); DEFINE_TRIVIAL_CLEANUP_FUNC(SocketPeer*, socket_peer_unref); -- cgit v1.2.3 From ea8f50f8085ce9385676385006030e8886cabaa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 4 Aug 2016 23:51:47 -0400 Subject: core/socket: include remote address in the message when dropping connection Without the address the message is not very useful. Aug 04 23:52:21 rawhide systemd[1]: testlimit.socket: Too many incoming connections (4) from source ::1, dropping connection. --- src/core/socket.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/socket.c b/src/core/socket.c index 0d77694251..b8a2f7fff8 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2187,7 +2187,7 @@ static void socket_enter_running(Socket *s, int cfd) { Service *service; if (s->n_connections >= s->max_connections) { - log_unit_warning(UNIT(s), "Too many incoming connections (%u), refusing connection attempt.", + log_unit_warning(UNIT(s), "Too many incoming connections (%u), dropping connection.", s->n_connections); safe_close(cfd); return; @@ -2199,9 +2199,13 @@ static void socket_enter_running(Socket *s, int cfd) { safe_close(cfd); return; } else if (r > 0 && p->n_ref > s->max_connections_per_source) { + _cleanup_free_ char *t = NULL; + + sockaddr_pretty(&p->peer.sa, FAMILY_ADDRESS_SIZE(p->peer.sa.sa_family), true, false, &t); + log_unit_warning(UNIT(s), - "Too many incoming connections (%u) from source, refusing connection attempt.", - p->n_ref); + "Too many incoming connections (%u) from source %s, dropping connection.", + p->n_ref, strnull(t)); safe_close(cfd); return; } -- cgit v1.2.3 From 80a58668d989c2316bcf1079b3e98ae526e633fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 5 Aug 2016 08:24:00 -0400 Subject: socket: add helper function to remove code duplication --- src/core/socket.c | 82 ++++++++++++++++++++----------------------------------- 1 file changed, 29 insertions(+), 53 deletions(-) diff --git a/src/core/socket.c b/src/core/socket.c index b8a2f7fff8..7f3f154a16 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2454,6 +2454,11 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { return 0; } +static void socket_port_take_fd(SocketPort *p, FDSet *fds, int fd) { + safe_close(p->fd); + p->fd = fdset_remove(fds, fd); +} + static int socket_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { Socket *s = SOCKET(u); @@ -2508,18 +2513,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse fifo value: %s", value); - else { - + else LIST_FOREACH(port, p, s->ports) if (p->type == SOCKET_FIFO && - path_equal_or_files_same(p->path, value+skip)) + path_equal_or_files_same(p->path, value+skip)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else if (streq(key, "special")) { int fd, skip = 0; @@ -2527,18 +2527,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse special value: %s", value); - else { - + else LIST_FOREACH(port, p, s->ports) if (p->type == SOCKET_SPECIAL && - path_equal_or_files_same(p->path, value+skip)) + path_equal_or_files_same(p->path, value+skip)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else if (streq(key, "mqueue")) { int fd, skip = 0; @@ -2546,18 +2541,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse mqueue value: %s", value); - else { - + else LIST_FOREACH(port, p, s->ports) if (p->type == SOCKET_MQUEUE && - streq(p->path, value+skip)) + streq(p->path, value+skip)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else if (streq(key, "socket")) { int fd, type, skip = 0; @@ -2565,16 +2555,12 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse socket value: %s", value); - else { + else LIST_FOREACH(port, p, s->ports) - if (socket_address_is(&p->address, value+skip, type)) + if (socket_address_is(&p->address, value+skip, type)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else if (streq(key, "netlink")) { int fd, skip = 0; @@ -2582,17 +2568,12 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse socket value: %s", value); - else { - + else LIST_FOREACH(port, p, s->ports) - if (socket_address_is_netlink(&p->address, value+skip)) + if (socket_address_is_netlink(&p->address, value+skip)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else if (streq(key, "ffs")) { int fd, skip = 0; @@ -2600,18 +2581,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse ffs value: %s", value); - else { - + else LIST_FOREACH(port, p, s->ports) if (p->type == SOCKET_USB_FUNCTION && - path_equal_or_files_same(p->path, value+skip)) + path_equal_or_files_same(p->path, value+skip)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else log_unit_debug(UNIT(s), "Unknown serialization key: %s", key); -- cgit v1.2.3 From b760a9af902c73ae76a05e17374e12e8e6c4e629 Mon Sep 17 00:00:00 2001 From: Susant Sahani Date: Sat, 6 Aug 2016 05:14:57 +0530 Subject: networkd: add support to set STP (#3903) fixes #3881 --- man/systemd.netdev.xml | 9 ++++++++- src/network/networkd-netdev-bridge.c | 7 +++++++ src/network/networkd-netdev-bridge.h | 1 + src/network/networkd-netdev-gperf.gperf | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index a5c6f0fa40..e56708a648 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -343,8 +343,15 @@ + + STP= + + A boolean. This enables the bridge's Spanning Tree Protocol (STP). When unset, + the kernel's default setting applies. + + + - diff --git a/src/network/networkd-netdev-bridge.c b/src/network/networkd-netdev-bridge.c index a5085d2b19..12b0fe972f 100644 --- a/src/network/networkd-netdev-bridge.c +++ b/src/network/networkd-netdev-bridge.c @@ -108,6 +108,12 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_VLAN_FILTERING attribute: %m"); } + if (b->stp >= 0) { + r = sd_netlink_message_append_u32(req, IFLA_BR_STP_STATE, b->stp); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_STP_STATE attribute: %m"); + } + r = sd_netlink_message_close_container(req); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); @@ -135,6 +141,7 @@ static void bridge_init(NetDev *n) { b->mcast_querier = -1; b->mcast_snooping = -1; b->vlan_filtering = -1; + b->stp = -1; } const NetDevVTable bridge_vtable = { diff --git a/src/network/networkd-netdev-bridge.h b/src/network/networkd-netdev-bridge.h index a637aea0a3..4ce0fbb6f9 100644 --- a/src/network/networkd-netdev-bridge.h +++ b/src/network/networkd-netdev-bridge.h @@ -27,6 +27,7 @@ typedef struct Bridge { int mcast_querier; int mcast_snooping; int vlan_filtering; + int stp; usec_t forward_delay; usec_t hello_time; diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf index 9d69f61376..a1ca1a3d4e 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/networkd-netdev-gperf.gperf @@ -106,4 +106,5 @@ Bridge.ForwardDelaySec, config_parse_sec, 0, Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier) Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping) Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering) +Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp) VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table_id) -- cgit v1.2.3 From d6d46cddee404cec266a63fe83b8224b8532e9eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristian=20Rodr=C3=ADguez?= Date: Thu, 4 Aug 2016 16:49:19 +0000 Subject: test: test-sigbus must be skipped when -fsanitize=address is used ASAN is unable to handle it. --- src/test/test-sigbus.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/test-sigbus.c b/src/test/test-sigbus.c index 17b81747be..02b8e24308 100644 --- a/src/test/test-sigbus.c +++ b/src/test/test-sigbus.c @@ -29,6 +29,9 @@ int main(int argc, char *argv[]) { void *addr = NULL; uint8_t *p; +#ifdef __SANITIZE_ADDRESS__ + return EXIT_TEST_SKIP; +#endif sigbus_install(); assert_se(sigbus_pop(&addr) == 0); -- cgit v1.2.3 From ba8868f58a1030d8d71ecd0edb1c90fe23b914d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristian=20Rodr=C3=ADguez?= Date: Fri, 5 Aug 2016 16:07:49 +0000 Subject: test: fix stack overflow reported by ASAN It was meant to write to q instead of t FAIL: test-id128 ================ ================================================================= ==125770==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd4615bd31 at pc 0x7a2f41b1bf33 bp 0x7ffd4615b750 sp 0x7ffd4615b748 WRITE of size 1 at 0x7ffd4615bd31 thread T0 #0 0x7a2f41b1bf32 in id128_to_uuid_string src/libsystemd/sd-id128/id128-util.c:42 #1 0x401f73 in main src/test/test-id128.c:147 #2 0x7a2f41336341 in __libc_start_main (/lib64/libc.so.6+0x20341) #3 0x401129 in _start (/home/crrodriguez/scm/systemd/.libs/test-id128+0x401129) Address 0x7ffd4615bd31 is located in stack of thread T0 at offset 1409 in frame #0 0x401205 in main src/test/test-id128.c:37 This frame has 23 object(s): [32, 40) 'b' [96, 112) 'id' [160, 176) 'id2' [224, 240) 'a' [288, 304) 'b' [352, 368) 'a' [416, 432) 'b' [480, 496) 'a' [544, 560) 'b' [608, 624) 'a' [672, 688) 'b' [736, 752) 'a' [800, 816) 'b' [864, 880) 'a' [928, 944) 'b' [992, 1008) 'a' [1056, 1072) 'b' [1120, 1136) 'a' [1184, 1200) 'b' [1248, 1264) 'a' [1312, 1328) 'b' [1376, 1409) 't' <== Memory access at offset 1409 overflows this variable [1472, 1509) 'q' HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext (longjmp and C++ exceptions *are* supported) SUMMARY: AddressSanitizer: stack-buffer-overflow src/libsystemd/sd-id128/id128-util.c:42 in id128_to_uuid_string Shadow bytes around the buggy address: 0x100028c23750: f2 f2 00 00 f4 f4 f2 f2 f2 f2 00 00 f4 f4 f2 f2 0x100028c23760: f2 f2 00 00 f4 f4 f2 f2 f2 f2 00 00 f4 f4 f2 f2 0x100028c23770: f2 f2 00 00 f4 f4 f2 f2 f2 f2 00 00 f4 f4 f2 f2 0x100028c23780: f2 f2 00 00 f4 f4 f2 f2 f2 f2 00 00 f4 f4 f2 f2 0x100028c23790: f2 f2 00 00 f4 f4 f2 f2 f2 f2 00 00 f4 f4 f2 f2 =>0x100028c237a0: f2 f2 00 00 00 00[01]f4 f4 f4 f2 f2 f2 f2 00 00 0x100028c237b0: 00 00 05 f4 f4 f4 00 00 00 00 00 00 00 00 00 00 0x100028c237c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x100028c237d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x100028c237e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x100028c237f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==125770==ABORTING FAIL test-id128 (exit status: 1) --- src/test/test-id128.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/test-id128.c b/src/test/test-id128.c index f01fbdd6b2..1c8e5549da 100644 --- a/src/test/test-id128.c +++ b/src/test/test-id128.c @@ -144,7 +144,7 @@ int main(int argc, char *argv[]) { assert_se(ftruncate(fd, 0) >= 0); assert_se(sd_id128_randomize(&id) >= 0); - assert_se(write(fd, id128_to_uuid_string(id, t), 36) == 36); + assert_se(write(fd, id128_to_uuid_string(id, q), 36) == 36); assert_se(lseek(fd, 0, SEEK_SET) == 0); assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) == -EINVAL); -- cgit v1.2.3 From 7dbe0b72c5666a0e0a73cefac2b1c253bce5deef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristian=20Rodr=C3=ADguez?= Date: Fri, 5 Aug 2016 16:10:41 +0000 Subject: buildsys,journal: allow -fsanitize=address without VALGRIND defined Fixed (master) versions of libtool pass -fsanitize=address correctly into CFLAGS and LDFLAGS allowing ASAN to be used without any special configure tricks..however ASAN triggers in lookup3.c for the same reasons valgrind does. take the alternative codepath if __SANITIZE_ADDRESS__ is defined as well. --- src/journal/lookup3.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/journal/lookup3.c b/src/journal/lookup3.c index 3d791234f4..d8f1a4977d 100644 --- a/src/journal/lookup3.c +++ b/src/journal/lookup3.c @@ -317,7 +317,7 @@ uint32_t jenkins_hashlittle( const void *key, size_t length, uint32_t initval) * still catch it and complain. The masking trick does make the hash * noticeably faster for short strings (like English words). */ -#ifndef VALGRIND +#if !defined(VALGRIND) && !defined(__SANITIZE_ADDRESS__) switch(length) { @@ -503,7 +503,7 @@ void jenkins_hashlittle2( * still catch it and complain. The masking trick does make the hash * noticeably faster for short strings (like English words). */ -#ifndef VALGRIND +#if !defined(VALGRIND) && !defined(__SANITIZE_ADDRESS__) switch(length) { @@ -681,7 +681,7 @@ uint32_t jenkins_hashbig( const void *key, size_t length, uint32_t initval) * still catch it and complain. The masking trick does make the hash * noticeably faster for short strings (like English words). */ -#ifndef VALGRIND +#if !defined(VALGRIND) && !defined(__SANITIZE_ADDRESS__) switch(length) { -- cgit v1.2.3 From 1aa1e59c7f34acdbd1af7c18e1fc471e5b92ed85 Mon Sep 17 00:00:00 2001 From: Yi EungJun Date: Sun, 7 Aug 2016 02:00:31 +0900 Subject: journal-gatewayd: add --directory option (#3913) Serve journals in the specified directory instead of default journals. --- man/systemd-journal-gatewayd.service.xml | 10 ++++++++++ src/journal-remote/journal-gatewayd.c | 23 ++++++++++++++++------- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/man/systemd-journal-gatewayd.service.xml b/man/systemd-journal-gatewayd.service.xml index 9ed85c3950..2cb114f6e3 100644 --- a/man/systemd-journal-gatewayd.service.xml +++ b/man/systemd-journal-gatewayd.service.xml @@ -100,6 +100,16 @@ with . + + + + + Takes a directory path as argument. If + specified, systemd-journal-gatewayd will serve the + specified journal directory DIR instead of + the default runtime and system journal paths. + + diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index a1627fab39..4d785c819f 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -45,6 +45,7 @@ static char *arg_key_pem = NULL; static char *arg_cert_pem = NULL; static char *arg_trust_pem = NULL; +static char *arg_directory = NULL; typedef struct RequestMeta { sd_journal *journal; @@ -115,7 +116,10 @@ static int open_journal(RequestMeta *m) { if (m->journal) return 0; - return sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM); + if (arg_directory) + return sd_journal_open_directory(&m->journal, arg_directory, 0); + else + return sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM); } static int request_meta_ensure_tmp(RequestMeta *m) { @@ -878,7 +882,8 @@ static void help(void) { " --version Show package version\n" " --cert=CERT.PEM Server certificate in PEM format\n" " --key=KEY.PEM Server key in PEM format\n" - " --trust=CERT.PEM Certificat authority certificate in PEM format\n", + " --trust=CERT.PEM Certificat authority certificate in PEM format\n" + " -D --directory=PATH Serve journal files in directory\n", program_invocation_short_name); } @@ -893,11 +898,12 @@ static int parse_argv(int argc, char *argv[]) { int r, c; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "key", required_argument, NULL, ARG_KEY }, - { "cert", required_argument, NULL, ARG_CERT }, - { "trust", required_argument, NULL, ARG_TRUST }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "key", required_argument, NULL, ARG_KEY }, + { "cert", required_argument, NULL, ARG_CERT }, + { "trust", required_argument, NULL, ARG_TRUST }, + { "directory", required_argument, NULL, 'D' }, {} }; @@ -951,6 +957,9 @@ static int parse_argv(int argc, char *argv[]) { #else log_error("Option --trust is not available."); #endif + case 'D': + arg_directory = optarg; + break; case '?': return -EINVAL; -- cgit v1.2.3 From 3475fc5899db8c8c9198573912429b85213e4862 Mon Sep 17 00:00:00 2001 From: Yi EungJun Date: Sun, 7 Aug 2016 05:39:13 +0900 Subject: journal-gatewayd: fix segfault with certain request (#3893) When client requests to get logs with `follow` and `KEY=match` that doesn't match any log entry, journal-gatewayd segfaulted. Make request_reader_entries to return zero in such case to wait for matching entries. This fixes https://github.com/systemd/systemd/issues/3873. --- src/journal-remote/journal-gatewayd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 4d785c819f..05c8698f78 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -240,6 +240,9 @@ static ssize_t request_reader_entries( m->size = (uint64_t) sz; } + if (m->tmp == NULL && m->follow) + return 0; + if (fseeko(m->tmp, pos, SEEK_SET) < 0) { log_error_errno(errno, "Failed to seek to position: %m"); return MHD_CONTENT_READER_END_WITH_ERROR; -- cgit v1.2.3 From 0a07667d8dd7e931ab425b3beb4fef01d8238f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 6 Aug 2016 16:36:51 -0400 Subject: man: provide html links to a bunch of external man pages --- man/localectl.xml | 2 +- man/systemd-resolved.service.xml | 8 ++++---- man/systemd.exec.xml | 2 +- man/systemd.mount.xml | 2 +- man/systemd.offline-updates.xml | 2 +- man/tmpfiles.d.xml | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/man/localectl.xml b/man/localectl.xml index 8d2becb5d9..31238272f3 100644 --- a/man/localectl.xml +++ b/man/localectl.xml @@ -223,7 +223,7 @@ systemctl1, systemd-localed.service8, systemd-firstboot1, - mkinitrd8 + mkinitrd8 diff --git a/man/systemd-resolved.service.xml b/man/systemd-resolved.service.xml index aa1c2365e5..56f67960ce 100644 --- a/man/systemd-resolved.service.xml +++ b/man/systemd-resolved.service.xml @@ -68,12 +68,12 @@ link-local networking). The glibc - getaddrinfo3 API as defined + getaddrinfo3 API as defined by RFC3493 and its related resolver functions, - including gethostbyname3. This + including gethostbyname3. This API is widely supported, including beyond the Linux platform. In its current form it does not expose DNSSEC validation status information however, and is synchronous only. This API is backed by the glibc Name Service - Switch (nss5). Usage of the + Switch (nss5). Usage of the glibc NSS module nss-resolve8 is required in order to allow glibc's NSS resolver functions to resolve host names via systemd-resolved. @@ -164,7 +164,7 @@ <filename>/etc/resolv.conf</filename> Three modes of handling /etc/resolv.conf (see - resolv.conf5) are + resolv.conf5) are supported: diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 0fc658f180..f530acac8f 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1449,7 +1449,7 @@ Takes a boolean argument. If set, any attempts to enable realtime scheduling in a process of the unit are refused. This restricts access to realtime task scheduling policies such as SCHED_FIFO, SCHED_RR or SCHED_DEADLINE. See - sched7 for details about + sched7 for details about these scheduling policies. Realtime scheduling policies may be used to monopolize CPU time for longer periods of time, and may hence be used to lock up or otherwise trigger Denial-of-Service situations on the system. It is hence recommended to restrict access to realtime scheduling to the few programs that actually require diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index 66cddd72e0..a38165f9b9 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -160,7 +160,7 @@ for details about the conversion. The NFS mount option for NFS background mounts - as documented in nfs5 + as documented in nfs5 is not supported in /etc/fstab entries. The systemd mount option provides similar functionality and should be used instead. diff --git a/man/systemd.offline-updates.xml b/man/systemd.offline-updates.xml index ae53b8552d..ba35a1786a 100644 --- a/man/systemd.offline-updates.xml +++ b/man/systemd.offline-updates.xml @@ -163,7 +163,7 @@ systemd1, systemd.generator7, systemd-update-generator8, - dnf.plugin.system-upgrade8 + dnf.plugin.system-upgrade8 diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml index 957475d2bd..75fb901102 100644 --- a/man/tmpfiles.d.xml +++ b/man/tmpfiles.d.xml @@ -626,7 +626,7 @@ Create directories with specific mode and ownership - screen1, + screen1, needs two directories created at boot with specific modes and ownership: # /usr/lib/tmpfiles.d/screen.conf @@ -652,7 +652,7 @@ t /run/cups - - - - security.SMACK64=printing user.attr-with-spaces="foo bar" Create a directory and prevent its contents from cleanup - abrt1, + abrt1, needs a directory created at boot with specific mode and ownership and its content should be preserved from the automatic cleanup applied to the contents of /var/tmp: -- cgit v1.2.3 From 2ac5fe9525eaaff33549b15ac3d12982aedb3d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 6 Aug 2016 16:38:09 -0400 Subject: man: fix some internal man page references sd_journal-query_enumerate was an early draft, the name was changed to sd_j_enumerate_fields. --- man/networkd.conf.xml | 2 +- man/sd-journal.xml | 2 -- man/systemd.offline-updates.xml | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/man/networkd.conf.xml b/man/networkd.conf.xml index 4bfc4f773a..57e647a31b 100644 --- a/man/networkd.conf.xml +++ b/man/networkd.conf.xml @@ -120,7 +120,7 @@ The DUID value specified here overrides the DUID that systemd-networkd generates using the machine-id from the /etc/machine-id file. To configure DUID per-network, see - systemd.network 5. + systemd.network5. The configured DHCP DUID should conform to the specification in RFC 3315, RFC 6355. To configure IAID, see diff --git a/man/sd-journal.xml b/man/sd-journal.xml index 09747a480c..936a83acf7 100644 --- a/man/sd-journal.xml +++ b/man/sd-journal.xml @@ -77,7 +77,6 @@ sd_journal_get_realtime_usec3, sd_journal_add_match3, sd_journal_seek_head3, - sd_journal_query_enumerate3, sd_journal_enumerate_fields3, sd_journal_get_cursor3, sd_journal_get_cutoff_realtime_usec3, @@ -113,7 +112,6 @@ sd_journal_get_realtime_usec3, sd_journal_add_match3, sd_journal_seek_head3, - sd_journal_query_enumerate3, sd_journal_enumerate_fields3, sd_journal_get_cursor3, sd_journal_get_cutoff_realtime_usec3, diff --git a/man/systemd.offline-updates.xml b/man/systemd.offline-updates.xml index ba35a1786a..f404c8d72f 100644 --- a/man/systemd.offline-updates.xml +++ b/man/systemd.offline-updates.xml @@ -77,7 +77,7 @@ Very early in the new boot - systemd-update-generator8 + systemd-system-update-generator8 checks whether /system-update exists. If so, it (temporarily and for this boot only) redirects (i.e. symlinks) default.target to system-update.target, a special target that is pulls in the base system @@ -162,7 +162,7 @@ Implementing Offline System Updates, systemd1, systemd.generator7, - systemd-update-generator8, + systemd-system-update-generator8, dnf.plugin.system-upgrade8 -- cgit v1.2.3 From e64e1bfd86534e542f7d5585459e78f88a6d9b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 7 Aug 2016 05:14:40 -0400 Subject: man: add a table of possible exit statuses (#3910) --- man/systemd.exec.xml | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 2495998295..6163709afd 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1651,9 +1651,68 @@ dumped. $EXIT_STATUS contains the numeric exit code formatted as string if $EXIT_CODE is exited, and the signal name in all other cases. Note that these environment variables are only set if the service manager succeeded to start and identify the main - process of the service. - + process of the service. + + + Summary of possible service result variable values + + + + + + + $SERVICE_RESULT + $EXIT_STATUS + $EXIT_CODE + + + + + + exit-code + exited + 0123255 + + + + signal + killed + HUPINTKILL + + + + core-dump + dumped + ABRTSEGVQUIT + + + watchdog + dumped + ABRT + + + killed + TERMKILL + + + exited + 0123255 + + + + resources + any of the above + any of the above + + + +
+ + + Additional variables may be configured by the following @@ -1689,4 +1748,5 @@ + -- cgit v1.2.3 From 2ad6b61049e13b93b1b3a92b4802e82ab99c86af Mon Sep 17 00:00:00 2001 From: Susant Sahani Date: Sun, 7 Aug 2016 14:53:32 +0530 Subject: networkd: remove duplicate call to manager_dirty (#3917) since link_dirty itself calls manager_dirty no need to call it separately . --- src/network/networkd-network.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 17bbe5de9f..49faba5b12 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -395,10 +395,8 @@ int network_apply(Manager *manager, Network *network, Link *link) { if (!strv_isempty(network->dns) || !strv_isempty(network->ntp) || !strv_isempty(network->search_domains) || - !strv_isempty(network->route_domains)) { - manager_dirty(manager); + !strv_isempty(network->route_domains)) link_dirty(link); - } return 0; } -- cgit v1.2.3 From 3f193af8807be50913b87e1a5b88c494f8985667 Mon Sep 17 00:00:00 2001 From: romanstingler Date: Sun, 7 Aug 2016 17:45:44 +0200 Subject: hwdb: add multimedia keys for Medion Akoya series (#3918) --- hwdb/60-keyboard.hwdb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hwdb/60-keyboard.hwdb b/hwdb/60-keyboard.hwdb index fd49b03493..1f96d9e3cc 100644 --- a/hwdb/60-keyboard.hwdb +++ b/hwdb/60-keyboard.hwdb @@ -797,6 +797,14 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMAXDATA:pnPro*7000*:pvr* # Medion ########################################################### +# Akoya +evdev:atkbd:dmi:bvn*:bvr*:svnMedion*:pnAkoya*:pvr* + KEYBOARD_KEY_a0=!mute + KEYBOARD_KEY_ae=!volumedown + KEYBOARD_KEY_b0=!volumeup + KEYBOARD_KEY_19=!p + KEYBOARD_KEY_df=sleep + # FID2060 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMEDION*:pn*FID2060*:pvr* KEYBOARD_KEY_6b=channeldown # Thottle Down -- cgit v1.2.3 From 87da8a864fd2173e176eb8b2f09b27711f1d3b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Mon, 8 Aug 2016 17:39:16 -0400 Subject: core: amend policy to open up dynamic user queries (#3920) --- src/core/org.freedesktop.systemd1.conf | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf index 3c64f20872..14f6aec029 100644 --- a/src/core/org.freedesktop.systemd1.conf +++ b/src/core/org.freedesktop.systemd1.conf @@ -108,6 +108,14 @@ send_interface="org.freedesktop.systemd1.Manager" send_member="GetDefaultTarget"/> + + + + Date: Tue, 9 Aug 2016 08:58:19 +1000 Subject: hwdb: fix duplicate entry for Apple touchpads (#3921) Copy-paste error, correct IDs from the kernel's drivers/input/mouse/bcm5974.c Fixes: https://github.com/systemd/systemd/pull/3906/ --- hwdb/60-evdev.hwdb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hwdb/60-evdev.hwdb b/hwdb/60-evdev.hwdb index d4cd61c24d..4d14a6a2f4 100644 --- a/hwdb/60-evdev.hwdb +++ b/hwdb/60-evdev.hwdb @@ -76,9 +76,9 @@ evdev:input:b0003v05ACp0254* EVDEV_ABS_36=::92 # MacbookPro10,1 (unibody, June 2012) -evdev:input:b0003v05ACp0259* -evdev:input:b0003v05ACp025A* -evdev:input:b0003v05ACp025B* +evdev:input:b0003v05ACp0262* +evdev:input:b0003v05ACp0263* +evdev:input:b0003v05ACp0264* # MacbookPro10,2 (unibody, October 2012) evdev:input:b0003v05ACp0259* evdev:input:b0003v05ACp025A* -- cgit v1.2.3 From 5a507f8cce8b8557a916c68ec277851f311b035c Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Mon, 8 Aug 2016 21:35:07 -0700 Subject: journal-gatewayd: fix typo; s/Certificat/Certificate/ (#3931) --- src/journal-remote/journal-gatewayd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 05c8698f78..c2b5a5f205 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -885,7 +885,7 @@ static void help(void) { " --version Show package version\n" " --cert=CERT.PEM Server certificate in PEM format\n" " --key=KEY.PEM Server key in PEM format\n" - " --trust=CERT.PEM Certificat authority certificate in PEM format\n" + " --trust=CERT.PEM Certificate authority certificate in PEM format\n" " -D --directory=PATH Serve journal files in directory\n", program_invocation_short_name); } -- cgit v1.2.3 From 3d431503b3d2753dba8148896301228c3068e38c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 5 Aug 2016 10:52:21 -0400 Subject: hwdb: remove extra spaces --- hwdb/70-mouse.hwdb | 2 +- hwdb/70-pointingstick.hwdb | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hwdb/70-mouse.hwdb b/hwdb/70-mouse.hwdb index b68c01066e..62bb77a0ef 100644 --- a/hwdb/70-mouse.hwdb +++ b/hwdb/70-mouse.hwdb @@ -297,7 +297,7 @@ mouse:usb:v046dpc049:name:Logitech USB Gaming Mouse: mouse:usb:v046dpc24e:name:Logitech G500s Laser Gaming Mouse: MOUSE_DPI=400@500 *800@500 2000@500 - # Logitech G9 +# Logitech G9 mouse:usb:v046dpc048:name:Logitech G9 Laser Mouse: MOUSE_DPI=400@1000 800@1000 *1600@1000 diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb index c0ec8ffbe0..5e2ab393bd 100644 --- a/hwdb/70-pointingstick.hwdb +++ b/hwdb/70-pointingstick.hwdb @@ -77,15 +77,15 @@ # Latitude D620 evdev:name:*DualPoint Stick:dmi:bvn*:bvr*:bd*:svnDellInc.:pnLatitudeD620*:pvr* - POINTINGSTICK_CONST_ACCEL=0.5 + POINTINGSTICK_CONST_ACCEL=0.5 # Latitude E6320 evdev:name:*DualPoint Stick:dmi:bvn*:bvr*:bd*:svnDellInc.:pnLatitudeE6320*:pvr* - POINTINGSTICK_CONST_ACCEL=2.0 + POINTINGSTICK_CONST_ACCEL=2.0 # Latitude E6400 evdev:name:*DualPoint Stick:dmi:bvn*:bvr*:bd*:svnDellInc.:pnLatitudeE6400*:pvr* - POINTINGSTICK_CONST_ACCEL=1.5 + POINTINGSTICK_CONST_ACCEL=1.5 ######################################### # Lenovo @@ -108,13 +108,13 @@ evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??60 evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Carbon3rd:* # Lenovo Thinkpad X1 Carbon 4th gen evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Carbon4th:* - POINTINGSTICK_SENSITIVITY=200 - POINTINGSTICK_CONST_ACCEL=1.0 + POINTINGSTICK_SENSITIVITY=200 + POINTINGSTICK_CONST_ACCEL=1.0 # Lenovo Thinkpad X200s / X201s # Note these come with 2 revisions of keyboard, with the trackpoints having a # different sensitivity in the different revisions. 1.25 is a bit slow for the # least sensitive revision, but it is better to be a bit slow than too fast. evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX20?s:* - POINTINGSTICK_SENSITIVITY=200 - POINTINGSTICK_CONST_ACCEL=1.25 + POINTINGSTICK_SENSITIVITY=200 + POINTINGSTICK_CONST_ACCEL=1.25 -- cgit v1.2.3 From 40e7518b03408f643e1cf41b0808e3bd9ba2c00b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 5 Aug 2016 13:33:22 -0400 Subject: hwdb: indent commented properties This way it's clear that the property block does not end at the comment. The python checker will complain if this is not the case. We had a few bugs before where two match blocks were merged by mistake, and this change should help avoid that. --- hwdb/60-keyboard.hwdb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hwdb/60-keyboard.hwdb b/hwdb/60-keyboard.hwdb index 1f96d9e3cc..16f29b820a 100644 --- a/hwdb/60-keyboard.hwdb +++ b/hwdb/60-keyboard.hwdb @@ -482,8 +482,7 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnFalco:pvr* KEYBOARD_KEY_43=volumedown KEYBOARD_KEY_44=volumeup KEYBOARD_KEY_db=search # Same position as caps lock key on most keyboards -# KEYBOARD_KEY_3e=fullscreen, no defined key sym - + # KEYBOARD_KEY_3e=fullscreen, no defined key sym # HP EliteBook 725 G2 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPLicrice:pvr* -- cgit v1.2.3 From 0c9836c0cf93235c324e59235d7b4fe40e7e0594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 5 Aug 2016 10:53:06 -0400 Subject: tests: add python parser for hwdb grammar This works for hwdb/[67]0-*.hwdb. I also added code to parse hwdb/20-*, but those files are huge, and parsing them using this parser is annoyingly slow (about one minute for the biggest files). So I removed the support for hwdb/20-*, a much simpler hand-generated parser should suffice for those. Current output: hwdb/60-evdev.hwdb: 24 match groups, 35 matches, 88 properties, 0.19323015213012695s to parse Match 'evdev:input:b0003v05ACp0259*' is duplicated Match 'evdev:input:b0003v05ACp025A*' is duplicated Match 'evdev:input:b0003v05ACp025B*' is duplicated hwdb/60-keyboard.hwdb: 122 match groups, 188 matches, 638 properties, 1.0906572341918945s to parse Failed to parse: 'KEYBOARD_KEY_8F=switchvideomode' Failed to parse: 'KEYBOARD_KEY_C0183=media' Failed to parse: 'KEYBOARD_KEY_C0201=new' Failed to parse: 'KEYBOARD_KEY_C0289=reply' Failed to parse: 'KEYBOARD_KEY_C028B=forwardmail' Failed to parse: 'KEYBOARD_KEY_C028C=send' Failed to parse: 'KEYBOARD_KEY_C021A=undo' Failed to parse: 'KEYBOARD_KEY_C0279=redo' Failed to parse: 'KEYBOARD_KEY_C0208=print' Failed to parse: 'KEYBOARD_KEY_C0207=save' Failed to parse: 'KEYBOARD_KEY_C0194=file' Failed to parse: 'KEYBOARD_KEY_C01A7=documents' Failed to parse: 'KEYBOARD_KEY_C01B6=images' Failed to parse: 'KEYBOARD_KEY_C01B7=sound' Property KEYBOARD_KEY_c7 is duplicated Failed to parse: 'KEYBOARD_KEY_cF=end' hwdb/70-mouse.hwdb: 62 match groups, 93 matches, 68 properties, 0.34186625480651855s to parse Match 'mouse:usb:v046dpc51b:name:Logitech USB Receiver:' is duplicated hwdb/70-pointingstick.hwdb: 5 match groups, 14 matches, 7 properties, 0.06518816947937012s to parse hwdb/70-touchpad.hwdb: 3 match groups, 5 matches, 3 properties, 0.039690494537353516s to parse Subsequest commits will clean those issues up. --- hwdb/parse_hwdb.py | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 hwdb/parse_hwdb.py diff --git a/hwdb/parse_hwdb.py b/hwdb/parse_hwdb.py new file mode 100644 index 0000000000..773513eaf0 --- /dev/null +++ b/hwdb/parse_hwdb.py @@ -0,0 +1,177 @@ +# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */ +# +# This file is part of systemd. +# +# Copyright 2016 Zbigniew Jędrzejewski-Szmek +# +# 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 distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +import sys +import string +import functools + +try: + from pyparsing import (Word, White, Literal, ParserElement, Regex, + LineStart, LineEnd, + ZeroOrMore, OneOrMore, Combine, Or, Optional, Suppress, Group, + nums, alphanums, printables, + stringEnd, pythonStyleComment, + ParseBaseException) +except ImportError: + sys.exit('pyparsing is not available') + +try: + from evdev.ecodes import ecodes +except ImportError: + ecodes = None + print('WARNING: evdev is not available') + +EOL = LineEnd().suppress() +EMPTYLINE = LineStart() + LineEnd() +COMMENTLINE = pythonStyleComment + EOL +INTEGER = Word(nums) +REAL = Combine((INTEGER + Optional('.' + Optional(INTEGER))) ^ ('.' + INTEGER)) +UDEV_TAG = Word(string.ascii_uppercase, alphanums + '_') + +TYPES = {'mouse': ('usb', 'bluetooth', 'ps2', '*'), + 'evdev': ('name', 'atkbd', 'input'), + 'touchpad': ('i8042', 'rmi', 'bluetooth', 'usb'), + 'keyboard': ('name', ), + } + +@functools.lru_cache() +def hwdb_grammar(): + ParserElement.setDefaultWhitespaceChars('') + + prefix = Or(category + ':' + Or(conn) + ':' + for category, conn in TYPES.items()) + matchline = Combine(prefix + Word(printables + ' ' + '®')) + EOL + propertyline = (White(' ', exact=1).suppress() + + Combine(UDEV_TAG - '=' - Word(alphanums + '_=:@*.! ') - Optional(pythonStyleComment)) + + EOL) + propertycomment = White(' ', exact=1) + pythonStyleComment + EOL + + group = (OneOrMore(matchline('MATCHES*') ^ COMMENTLINE.suppress()) - + OneOrMore(propertyline('PROPERTIES*') ^ propertycomment.suppress()) - + (EMPTYLINE ^ stringEnd()).suppress() ) + commentgroup = OneOrMore(COMMENTLINE).suppress() - EMPTYLINE.suppress() + + grammar = OneOrMore(group('GROUPS*') ^ commentgroup) + stringEnd() + + return grammar + +@functools.lru_cache() +def property_grammar(): + ParserElement.setDefaultWhitespaceChars(' ') + + setting = Optional('*')('DEFAULT') + INTEGER('DPI') + Suppress('@') + INTEGER('HZ') + props = (('MOUSE_DPI', Group(OneOrMore(setting('SETTINGS*')))), + ('MOUSE_WHEEL_CLICK_ANGLE', INTEGER), + ('ID_INPUT_TRACKBALL', Literal('1')), + ('POINTINGSTICK_SENSITIVITY', INTEGER), + ('POINTINGSTICK_CONST_ACCEL', REAL), + ('ID_INPUT_TOUCHPAD_INTEGRATION', Or(('internal', 'external'))), + ) + fixed_props = [Literal(name)('NAME') - Suppress('=') - val('VALUE') + for name, val in props] + kbd_props = [Regex(r'KEYBOARD_KEY_[0-9a-f]+')('NAME') + - Suppress('=') - + ('!' ^ (Optional('!') - Word(alphanums + '_')))('VALUE') + ] + abs_props = [Regex(r'EVDEV_ABS_[0-9a-f]{2}')('NAME') + - Suppress('=') - + Word(nums + ':')('VALUE') + ] + + grammar = Or(fixed_props + kbd_props + abs_props) + + return grammar + +ERROR = False +def error(fmt, *args, **kwargs): + global ERROR + ERROR = True + print(fmt.format(*args, **kwargs)) + +def convert_properties(group): + matches = [m[0] for m in group.MATCHES] + props = [p[0] for p in group.PROPERTIES] + return matches, props + +def parse(fname): + grammar = hwdb_grammar() + try: + parsed = grammar.parseFile(fname) + except ParseBaseException as e: + error('Cannot parse {}: {}', fname, e) + return [] + return [convert_properties(g) for g in parsed.GROUPS] + +def check_match_uniqueness(groups): + matches = sum((group[0] for group in groups), []) + matches.sort() + prev = None + for match in matches: + if match == prev: + error('Match {!r} is duplicated', match) + prev = match + +def check_one_default(prop, settings): + defaults = [s for s in settings if s.DEFAULT] + if len(defaults) > 1: + error('More than one star entry: {!r}', prop) + +def check_one_keycode(prop, value): + if value != '!' and ecodes is not None: + key = 'KEY_' + value.upper() + if key not in ecodes: + error('Keycode {} unknown', key) + +def check_properties(groups): + grammar = property_grammar() + for matches, props in groups: + prop_names = set() + for prop in props: + # print('--', prop) + prop = prop.partition('#')[0].rstrip() + try: + parsed = grammar.parseString(prop) + except ParseBaseException as e: + error('Failed to parse: {!r}', prop) + continue + # print('{!r}'.format(parsed)) + if parsed.NAME in prop_names: + error('Property {} is duplicated', parsed.NAME) + prop_names.add(parsed.NAME) + if parsed.NAME == 'MOUSE_DPI': + check_one_default(prop, parsed.VALUE.SETTINGS) + elif parsed.NAME.startswith('KEYBOARD_KEY_'): + check_one_keycode(prop, parsed.VALUE) + +def print_summary(fname, groups): + print('{}: {} match groups, {} matches, {} properties' + .format(fname, + len(groups), + sum(len(matches) for matches, props in groups), + sum(len(props) for matches, props in groups), + )) + +if __name__ == '__main__': + for fname in sys.argv[1:]: + groups = parse(fname) + print_summary(fname, groups) + check_match_uniqueness(groups) + check_properties(groups) + + sys.exit(ERROR) -- cgit v1.2.3 From 704c3aa9e8b98956b3919ac4b2aa18d1cae1f2a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 4 Aug 2016 15:31:05 -0400 Subject: hwdb: remove duplicated matches for old Logitech unifying receiver Quoting https://github.com/systemd/systemd/pull/3906#discussion_r73828368: > According to > http://support.logitech.com/en_us/product/v220-cordless-optical-mouse-for-notebooks > it seems the mouse is using a pre-version of the small unifying receiver we > know now. If there are 2 mice with the same receiver, that means that the > values should both be dropped IMO. --- hwdb/70-mouse.hwdb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/hwdb/70-mouse.hwdb b/hwdb/70-mouse.hwdb index 62bb77a0ef..73df25def1 100644 --- a/hwdb/70-mouse.hwdb +++ b/hwdb/70-mouse.hwdb @@ -342,8 +342,6 @@ mouse:usb:v046dp402d:name:Logitech M560: mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:402d: MOUSE_DPI=1000@125 -# Logitech V220 Cordless Optical Mouse -mouse:usb:v046dpc51b:name:Logitech USB Receiver: # Logitech Performance MX mouse:usb:v046dp101a:name:Logitech Performance MX: # Logitech MX Master @@ -373,11 +371,6 @@ mouse:usb:v046dp4027:name:Logitech T620: mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:4027: MOUSE_DPI=1200@250 -# Logitech LX8 Cordless Laser Mouse -mouse:usb:v046dpc51b:name:Logitech USB Receiver: - MOUSE_DPI=1300@125 - MOUSE_WHEEL_CLICK_ANGLE=15 - # Logitech ZoneTouch Mouse T400 mouse:usb:v046dp4026:name:Logitech T400: mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:4026: -- cgit v1.2.3 From 3ab779475759277e4a0f1289bc2a1d83ca1b5d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 5 Aug 2016 13:31:37 -0400 Subject: hwdb: use lowercase hex for key numbers It seems awkward to have both cases mixes. Note that the real parser accepts both cases, and this only standarizes the usage in the systemd database. --- hwdb/60-keyboard.hwdb | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/hwdb/60-keyboard.hwdb b/hwdb/60-keyboard.hwdb index 16f29b820a..cf340ee702 100644 --- a/hwdb/60-keyboard.hwdb +++ b/hwdb/60-keyboard.hwdb @@ -213,7 +213,7 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pn* KEYBOARD_KEY_8a=suspend # Fn+F1 hibernate KEYBOARD_KEY_8b=switchvideomode # Fn+F8 CRT/LCD (high keycode: "displaytoggle") KEYBOARD_KEY_8c=unknown # Fn+Right Auto Brightness - KEYBOARD_KEY_8F=switchvideomode # Fn+F7 aspect ratio + KEYBOARD_KEY_8f=switchvideomode # Fn+F7 aspect ratio KEYBOARD_KEY_90=previoussong # Front panel previous song KEYBOARD_KEY_91=prog1 # Wi-Fi Catcher (Dell-specific) KEYBOARD_KEY_92=media # MediaDirect button (house icon) @@ -693,7 +693,7 @@ evdev:input:b0003v046DpC308* KEYBOARD_KEY_90003=finance # Finance KEYBOARD_KEY_90004=prog1 # My Sites KEYBOARD_KEY_90005=prog2 # Community - KEYBOARD_KEY_C0183=media # Media + KEYBOARD_KEY_c0183=media # Media # Cordless Desktop S510 evdev:input:b0003v046DpC50C* @@ -762,18 +762,18 @@ evdev:input:b0003v046DpC309* KEYBOARD_KEY_90002=camera # webcam KEYBOARD_KEY_90003=prog1 # iTouch KEYBOARD_KEY_90004=shop # Shopping - KEYBOARD_KEY_C0201=new # New (F1) - KEYBOARD_KEY_C0289=reply # Reply mail (F2) - KEYBOARD_KEY_C028B=forwardmail # Forward mail (F3) - KEYBOARD_KEY_C028C=send # Send (F4) - KEYBOARD_KEY_C021A=undo # Undo (F5). - KEYBOARD_KEY_C0279=redo # Redo (F6). - KEYBOARD_KEY_C0208=print # Print (F7) - KEYBOARD_KEY_C0207=save # Save (F8) - KEYBOARD_KEY_C0194=file # My Computer (F9) - KEYBOARD_KEY_C01A7=documents # My Documents (F10) - KEYBOARD_KEY_C01B6=images # My Pictures (F11) ?? - KEYBOARD_KEY_C01B7=sound # My Music (F12) ?? + KEYBOARD_KEY_c0201=new # New (F1) + KEYBOARD_KEY_c0289=reply # Reply mail (F2) + KEYBOARD_KEY_c028b=forwardmail # Forward mail (F3) + KEYBOARD_KEY_c028c=send # Send (F4) + KEYBOARD_KEY_c021a=undo # Undo (F5). + KEYBOARD_KEY_c0279=redo # Redo (F6). + KEYBOARD_KEY_c0208=print # Print (F7) + KEYBOARD_KEY_c0207=save # Save (F8) + KEYBOARD_KEY_c0194=file # My Computer (F9) + KEYBOARD_KEY_c01a7=documents # My Documents (F10) + KEYBOARD_KEY_c01b6=images # My Pictures (F11) ?? + KEYBOARD_KEY_c01b7=sound # My Music (F12) ?? ########################################################### @@ -916,7 +916,7 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnOLPC:pnXO:* KEYBOARD_KEY_c9=pageup KEYBOARD_KEY_d1=pagedown KEYBOARD_KEY_c7=home - KEYBOARD_KEY_cF=end + KEYBOARD_KEY_cf=end KEYBOARD_KEY_73=hp KEYBOARD_KEY_7e=hp KEYBOARD_KEY_db=leftmeta # left grab -- cgit v1.2.3 From 2abd461344f3a4d37584795a2644c102c098edc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 5 Aug 2016 13:36:26 -0400 Subject: hwdb: comment out a duplicated key for the XO It's hard to say which one of the two mappings should stay. But the later one would win (when both very present), and nobody complained, so let's assume that that's the one. --- hwdb/60-keyboard.hwdb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwdb/60-keyboard.hwdb b/hwdb/60-keyboard.hwdb index cf340ee702..1176881650 100644 --- a/hwdb/60-keyboard.hwdb +++ b/hwdb/60-keyboard.hwdb @@ -896,7 +896,7 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnOLPC:pnXO:* KEYBOARD_KEY_c2=f8 KEYBOARD_KEY_c3=f9 KEYBOARD_KEY_c4=f10 - KEYBOARD_KEY_c7=f11 + # KEYBOARD_KEY_c7=f11 # FIXME! KEYBOARD_KEY_d8=f12 KEYBOARD_KEY_f7=f13 KEYBOARD_KEY_f6=f14 -- cgit v1.2.3 From c3f6a561e2fcda5b41569e7bf7785fad825d3a72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 5 Aug 2016 15:07:41 -0400 Subject: build-sys: hook up the hwdb parser to check --- Makefile.am | 6 ++++-- hwdb/parse_hwdb.py | 14 ++++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) mode change 100644 => 100755 hwdb/parse_hwdb.py diff --git a/Makefile.am b/Makefile.am index bf853c8f90..d4e3e9a013 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3872,7 +3872,8 @@ TESTS += \ if HAVE_PYTHON TESTS += \ - test/rule-syntax-check.py + test/rule-syntax-check.py \ + hwdb/parse_hwdb.py if HAVE_SYSV_COMPAT TESTS += \ @@ -3924,7 +3925,8 @@ EXTRA_DIST += \ test/udev-test.pl \ test/rule-syntax-check.py \ test/sysv-generator-test.py \ - test/mocks/fsck + test/mocks/fsck \ + hwdb/parse_hwdb.py # ------------------------------------------------------------------------------ ata_id_SOURCES = \ diff --git a/hwdb/parse_hwdb.py b/hwdb/parse_hwdb.py old mode 100644 new mode 100755 index 773513eaf0..99d034b4e0 --- a/hwdb/parse_hwdb.py +++ b/hwdb/parse_hwdb.py @@ -1,3 +1,4 @@ +#!/usr/bin/python3 # -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */ # # This file is part of systemd. @@ -17,9 +18,11 @@ # You should have received a copy of the GNU Lesser General Public License # along with systemd; If not, see . -import sys -import string import functools +import glob +import string +import sys +import os try: from pyparsing import (Word, White, Literal, ParserElement, Regex, @@ -29,7 +32,8 @@ try: stringEnd, pythonStyleComment, ParseBaseException) except ImportError: - sys.exit('pyparsing is not available') + print('pyparsing is not available') + sys.exit(77) try: from evdev.ecodes import ecodes @@ -168,7 +172,9 @@ def print_summary(fname, groups): )) if __name__ == '__main__': - for fname in sys.argv[1:]: + args = sys.argv[1:] or glob.glob(os.path.dirname(sys.argv[0]) + '/[67]0-*.hwdb') + + for fname in args: groups = parse(fname) print_summary(fname, groups) check_match_uniqueness(groups) -- cgit v1.2.3 From ac741b47dc259c190f6897a8cf581f2c3b73e18e Mon Sep 17 00:00:00 2001 From: romanstingler Date: Tue, 9 Aug 2016 06:51:38 +0200 Subject: added Zelotes gaming mouse (#3925) --- hwdb/70-mouse.hwdb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hwdb/70-mouse.hwdb b/hwdb/70-mouse.hwdb index b68c01066e..c569d66d47 100644 --- a/hwdb/70-mouse.hwdb +++ b/hwdb/70-mouse.hwdb @@ -500,3 +500,11 @@ mouse:usb:v1038p1369:name:SteelSeries Sensei Raw Gaming Mouse: # Trust illuminated mouse gxt 152 mouse:usb:v145fp01ac:name:HID-compliant Mouse Trust Gaming Mouse: MOUSE_DPI=*800@528 1200@537 1600@536 2400@521 + + ########################################## + # Zelotes + ########################################## + +# Zelotes 5500 DPI 7 Button USB Wired Gaming Mouse +mouse:usb:v1d57pad17:* + MOUSE_DPI=1000@500 1600@500 2400@500 3200@500 5500@500 *1000@1000 1600@1000 2400@1000 3200@1000 5500@1000 -- cgit v1.2.3 From e73529f9dd878cc449f5271f749929ebd9e93d88 Mon Sep 17 00:00:00 2001 From: Evgeny Vereshchagin Date: Tue, 9 Aug 2016 15:49:32 +0300 Subject: sd-journal: watch logs below container's /{var,run}/log/journal (instead of the /) (#3934) Fixes #3927. --- src/journal/sd-journal.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 75a0ffb49b..2a3824d0e8 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -2290,6 +2290,8 @@ _public_ int sd_journal_get_fd(sd_journal *j) { * inotify */ if (j->no_new_files) r = add_current_paths(j); + else if (j->flags & SD_JOURNAL_OS_ROOT) + r = add_search_paths(j); else if (j->toplevel_fd >= 0) r = add_root_directory(j, NULL, false); else if (j->path) -- cgit v1.2.3 From 1cd1dab98c574f92e1afca184a4c44327c764955 Mon Sep 17 00:00:00 2001 From: Rhys Date: Tue, 9 Aug 2016 23:33:46 +1000 Subject: install: follow config_path symlink (#3362) Under NixOS, the config_path /etc/systemd/system is a symlink to /etc/static/systemd/system. Commands such as `systemctl list-unit-files` and `systemctl is-enabled` did not work as the symlink was not followed. This does not affect how symlinks are treated within the config_path directory. --- src/shared/install.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/install.c b/src/shared/install.c index 7b49e1ece9..e740ef3910 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -777,7 +777,7 @@ static int find_symlinks( assert(config_path); assert(same_name_link); - fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); + fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC); if (fd < 0) { if (IN_SET(errno, ENOENT, ENOTDIR, EACCES)) return 0; -- cgit v1.2.3 From 6998b54093831fac92e2b1e251659e64d56e1df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 11 Aug 2016 04:51:00 -0400 Subject: coredump: treat RLIMIT_CORE below page size as disabling coredumps (#3932) The kernel treats values below a certain threshold (minfmt->min_coredump which is initialized do ELF_EXEC_PAGESIZE, which varies between architectures, but is usually the same as PAGE_SIZE) as disabling coredumps [1]. Any core image below ELF_EXEC_PAGESIZE will yield an invalid backtrace anyway [2], so follow the kernel and not try to parse or store such images. [1] https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/coredump.c#n660 [2] systemd-coredump[16260]: Process 16258 (sleep) of user 1002 dumped core. Stack trace of thread 16258: #0 0x00007f1d8b3d3810 n/a (n/a) https://bugzilla.redhat.com/show_bug.cgi?id=1309172#c19 --- src/coredump/coredump.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index e3d17c864d..be724aed4e 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -327,9 +327,11 @@ static int save_external_coredump( r = safe_atou64(context[CONTEXT_RLIMIT], &rlimit); if (r < 0) return log_error_errno(r, "Failed to parse resource limit: %s", context[CONTEXT_RLIMIT]); - if (rlimit <= 0) { - /* Is coredumping disabled? Then don't bother saving/processing the coredump */ - log_info("Core Dumping has been disabled for process %s (%s).", context[CONTEXT_PID], context[CONTEXT_COMM]); + if (rlimit < page_size()) { + /* Is coredumping disabled? Then don't bother saving/processing the coredump. + * Anything below PAGE_SIZE cannot give a readable coredump (the kernel uses + * ELF_EXEC_PAGESIZE which is not easily accessible, but is usually the same as PAGE_SIZE. */ + log_info("Core dumping has been disabled for process %s (%s).", context[CONTEXT_PID], context[CONTEXT_COMM]); return -EBADSLT; } -- cgit v1.2.3 From 29df65f913ec7d360637ef1703ec284bb1fa6821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 11 Aug 2016 04:51:49 -0400 Subject: man: add "timeout" to status table (#3919) --- man/systemd.exec.xml | 16 ++++++++++++++++ man/systemd.service.xml | 19 +++++++------------ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index f8b2aff81b..bf82326096 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1668,6 +1668,18 @@ + + timeout + killed + TERMKILL + + + + exited + 0123255 + + exit-code exited @@ -1707,6 +1719,10 @@ any of the above any of the above + + + Note: the process may be also terminated by a signal not sent by systemd. In particular the process may send an arbitrary signal to itself in a handler for any of the non-maskable signals. Nevertheless, in the timeout and watchdog rows above only the signals that systemd sends have been included. + diff --git a/man/systemd.service.xml b/man/systemd.service.xml index e82edbe93e..b58e887662 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -276,17 +276,12 @@ below (see section "Command Lines" below). - When Type= is not - , only one command may and must be - given. When Type=oneshot is used, zero or - more commands may be specified. This can be specified by - providing multiple command lines in the same directive, or - alternatively, this directive may be specified more than once - with the same effect. If the empty string is assigned to this - option, the list of commands to start is reset, prior - assignments of this option will have no effect. If no - ExecStart= is specified, then the service - must have RemainAfterExit=yes set. + Unless Type= is , exactly one command must be given. When + Type=oneshot is used, zero or more commands may be specified. Commands may be specified by + providing multiple command lines in the same directive, or alternatively, this directive may be specified more + than once with the same effect. If the empty string is assigned to this option, the list of commands to start + is reset, prior assignments of this option will have no effect. If no ExecStart= is + specified, then the service must have RemainAfterExit=yes set. For each of the specified commands, the first argument must be an absolute path to an executable. Optionally, if this file name is prefixed with @, the second token will be @@ -294,7 +289,7 @@ the absolute filename is prefixed with -, an exit code of the command normally considered a failure (i.e. non-zero exit status or abnormal exit due to signal) is ignored and considered success. If the absolute path is prefixed with + then it is executed with full - privileges. -, @, and + may be used together and they + privileges. @, -, and + may be used together and they can appear in any order. If more than one command is specified, the commands are -- cgit v1.2.3 From 56ef545c24a4b83f6ad9f542a1159b2130a30f3e Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Thu, 11 Aug 2016 18:46:31 +0200 Subject: zsh: _filter_units_by_property: respect --user Use `$_sys_service_mgr` to handle `--user`, so that `systemctl --user stop` will correctly filter the active (user) units. Before this patch, only user units that also exist as system units and are stoppable there would be listed. --- shell-completion/zsh/_systemctl.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell-completion/zsh/_systemctl.in b/shell-completion/zsh/_systemctl.in index 44c31b7833..69f643303d 100644 --- a/shell-completion/zsh/_systemctl.in +++ b/shell-completion/zsh/_systemctl.in @@ -126,7 +126,7 @@ _filter_units_by_property() { local property=$1 value=$2 ; shift ; shift local -a units ; units=($*) local props - for props in ${(ps:\n\n:)"$(_call_program units "$service show --no-pager --property="Id,$property" -- ${units} 2>/dev/null")"}; do + for props in ${(ps:\n\n:)"$(_call_program units "$service $_sys_service_mgr show --no-pager --property="Id,$property" -- ${units} 2>/dev/null")"}; do props=(${(f)props}) if [[ "${props[2]}" = "$property=$value" ]]; then echo -E - " ${props[1]#Id=}" -- cgit v1.2.3 From 21d84318c3a2dc54732513a80c983f560948c4d8 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Thu, 11 Aug 2016 18:52:13 +0200 Subject: zsh: _journalctl: handle --user in _journal_none This uses the same mechanism from _systemctl to inject `--user` into the `journalctrl -F _EXE` call to list executables. Before this patch the "commands" section would list executables from system units always. --- shell-completion/zsh/_journalctl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shell-completion/zsh/_journalctl b/shell-completion/zsh/_journalctl index 2bee23b6d3..2271f7fa9c 100644 --- a/shell-completion/zsh/_journalctl +++ b/shell-completion/zsh/_journalctl @@ -23,7 +23,7 @@ _list_fields() { _journal_none() { local -a _commands _files _jrnl_none # Setting use-cache will slow this down considerably - _commands=( ${"$(_call_program commands "$service" -F _EXE 2>/dev/null)"} ) + _commands=( ${"$(_call_program commands "$service $_sys_service_mgr -F _EXE" 2>/dev/null)"} ) _jrnl_none='yes' _alternative : \ 'files:/dev files:_files -W /dev -P /dev/' \ @@ -51,6 +51,8 @@ _journal_boots() { "bootid:boot ids:compadd -a _bootid" } +local -a _modes; _modes=("--user" "--system") +local _sys_service_mgr=${${words:*_modes}[(R)(${(j.|.)_modes})]:---system} _arguments -s \ {-h,--help}'[Show this help]' \ '--version[Show package version]' \ -- cgit v1.2.3 From 32d9493e593fed7fe5b4dd1e92fe4fd419042fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 11 Aug 2016 21:53:32 -0400 Subject: systemctl: fix preset-all with missing /etc/systemd/system If the directory is missing, we can assume that those pesky symlinks are gone too. --- src/shared/install.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/install.c b/src/shared/install.c index e740ef3910..ccb1a70096 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -620,7 +620,7 @@ static int remove_marked_symlinks( fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); if (fd < 0) - return -errno; + return errno == ENOENT ? 0 : -errno; do { int q, cfd; -- cgit v1.2.3 From ff56349d5a83f2202ed331f232f5d73467db482c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 12 Aug 2016 23:50:58 -0400 Subject: shared/install: remove unused paramater and add more comments --- src/shared/install.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/shared/install.c b/src/shared/install.c index ccb1a70096..0cf6a29403 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -903,6 +903,10 @@ static int install_info_may_process( return 0; } +/** + * Adds a new UnitFileInstallInfo entry under name in the InstallContext.will_process + * hashmap, or retrieves the existing one if already present. + */ static int install_info_add( InstallContext *c, const char *name, @@ -1334,9 +1338,8 @@ static int install_info_follow( } /** - * Search for the unit file. If the unit name is a symlink, - * follow the symlink to the target, maybe more than once. - * Propagate the instance name if present. + * Search for the unit file. If the unit name is a symlink, follow the symlink to the + * target, maybe more than once. Propagate the instance name if present. */ static int install_info_traverse( UnitFileScope scope, @@ -1421,6 +1424,10 @@ static int install_info_traverse( return 0; } +/** + * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/") + * or the name (otherwise). root_dir is prepended to the path. + */ static int install_info_add_auto( InstallContext *c, const LookupPaths *paths, @@ -2685,7 +2692,6 @@ static int preset_prepare_one( InstallContext *plus, InstallContext *minus, LookupPaths *paths, - UnitFilePresetMode mode, const char *name, Presets presets, UnitFileChange **changes, @@ -2748,7 +2754,7 @@ int unit_file_preset( return r; STRV_FOREACH(i, files) { - r = preset_prepare_one(scope, &plus, &minus, &paths, mode, *i, presets, changes, n_changes); + r = preset_prepare_one(scope, &plus, &minus, &paths, *i, presets, changes, n_changes); if (r < 0) return r; } @@ -2809,7 +2815,7 @@ int unit_file_preset_all( continue; /* we don't pass changes[] in, because we want to handle errors on our own */ - r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name, presets, NULL, 0); + r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, presets, NULL, 0); if (r == -ERFKILL) r = unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, de->d_name, NULL); -- cgit v1.2.3 From 11e11fd57a837ea1cb142009c3048882392f3ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 13 Aug 2016 01:20:29 -0400 Subject: shared/install: ignore unit symlinks when doing preset-all Before, when interating over unit files during preset-all, behaviour was the following: - if we hit the real unit name first, presets were queried for that name, and that unit was enabled or disabled accordingly, - if we hit an alias first (one of the symlinks chaining to the real unit), we checked the presets using the symlink name, and then proceeded to enable or disable the real unit. E.g. for systemd-networkd.service we have the alias dbus-org.freedesktop.network1.service (/usr/lib/systemd/system/dbus-org.freedesktop.network1.service), but the preset is only for the systemd-networkd.service name. The service would be enabled or disabled pseudorandomly depending on the order of iteration. For "preset", behaviour was analogous: preset on the alias name disabled the service (following the default disable policy), preset on the "real" name applied the presets. With the patch, for "preset" and "preset-all" we silently skip symlinks. This gives mostly the right behaviour, with the limitation that presets on aliases are ignored. I think that presets on aliases are not that common (at least my preset files on Fedora don't exhibit any such usage), and should not be necessary, since whoever installs the preset can just refer to the real unit file. It would be possible to overcome this limitation by gathering a list of names of a unit first, and then checking whether *any* of the names matches the presets list. That would require a significant redesign of the code, and be a lot slower (since we would have to fully read all unit directories to preset one unit) to so I'm not doing that for now. With this patch, two properties are satisfied: - preset-all and preset are idempotent, and the second and subsequent invocations do not produce any changes, - preset-all and preset for a specific name produce the same state for that unit. Fixes #3616. --- src/shared/install.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/shared/install.c b/src/shared/install.c index 0cf6a29403..8e40c171da 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -1974,7 +1974,6 @@ int unit_file_revert( unsigned *n_changes) { _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - /* _cleanup_(install_context_done) InstallContext c = {}; */ _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_strv_free_ char **todo = NULL; size_t n_todo = 0, n_allocated = 0; @@ -2697,13 +2696,21 @@ static int preset_prepare_one( UnitFileChange **changes, unsigned *n_changes) { + _cleanup_(install_context_done) InstallContext tmp = {}; UnitFileInstallInfo *i; int r; - if (install_info_find(plus, name) || - install_info_find(minus, name)) + if (install_info_find(plus, name) || install_info_find(minus, name)) return 0; + r = install_info_discover(scope, &tmp, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; + if (!streq(name, i->name)) { + log_debug("Skipping %s because is an alias for %s", name, i->name); + return 0; + } + r = query_presets(name, presets); if (r < 0) return r; -- cgit v1.2.3 From d923e42eed9a29137821760dafecb13798264c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 13 Aug 2016 09:38:12 -0400 Subject: man: describe what symlinks to unit do, and specify that presets must use real names The man pages didn't ever mention that symlinks to units can be created, and what exactly this means. Fix that omission, and disallow presets on alias names. --- man/systemctl.xml | 3 ++- man/systemd.preset.xml | 4 ++++ man/systemd.unit.xml | 36 +++++++++++++++++++++++------------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/man/systemctl.xml b/man/systemctl.xml index 0ad0ad6d7e..78607c9ba3 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1092,7 +1092,8 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service enabled and disabled, or only enabled, or only disabled. If the unit carries no install information, it will be silently ignored - by this command. + by this command. NAME must be the real unit name, + any alias names are ignored silently. For more information on the preset policy format, see systemd.preset5. diff --git a/man/systemd.preset.xml b/man/systemd.preset.xml index b7164014f0..d09167baaf 100644 --- a/man/systemd.preset.xml +++ b/man/systemd.preset.xml @@ -98,6 +98,10 @@ Empty lines and lines whose first non-whitespace character is # or ; are ignored. + Presets must refer to the "real" unit file, and not to any aliases. See + systemd.unit5 + for a description of unit aliasing. + Two different directives are understood: enable may be used to enable units by default, disable to disable units by default. diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 85a7b12d76..f818e772a9 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -144,21 +144,31 @@ and are equivalent. - Time span values encoded in unit files can be written in - various formats. A stand-alone number specifies a time in seconds. - If suffixed with a time unit, the unit is honored. A concatenation - of multiple values with units is supported, in which case the - values are added up. Example: "50" refers to 50 seconds; "2min - 200ms" refers to 2 minutes plus 200 milliseconds, i.e. 120200ms. - The following time units are understood: s, min, h, d, w, ms, us. - For details see + Time span values encoded in unit files can be written in various formats. A stand-alone number specifies a + time in seconds. If suffixed with a time unit, the unit is honored. A concatenation of multiple values with units + is supported, in which case the values are added up. Example: 50 refers to 50 seconds; + 2min 200ms refers to 2 minutes and 200 milliseconds, i.e. 120200 ms. The following time units + are understood: s, min, h, d, + w, ms, us. For details see systemd.time7. - Empty lines and lines starting with # or ; are - ignored. This may be used for commenting. Lines ending - in a backslash are concatenated with the following - line while reading and the backslash is replaced by a - space character. This may be used to wrap long lines. + Empty lines and lines starting with # or ; are ignored. This may be + used for commenting. Lines ending in a backslash are concatenated with the following line while reading and the + backslash is replaced by a space character. This may be used to wrap long lines. + + Units can be aliased (have an alternative name), by creating a symlink from the new name to the existing name + in one of the unit search paths. For example, systemd-networkd.service has the alias + dbus-org.freedesktop.network1.service, created during installation as the symlink + /usr/lib/systemd/system/dbus-org.freedesktop.network1.service. In addition, unit files may + specify aliases through the Alias= directive in the [Install] section; those aliases are only + effective when the unit is enabled. When the unit is enabled, symlinks will be created for those names, and removed + when the unit is disabled. For example, reboot.target specifies + Alias=ctrl-alt-del.target, so when enabled it will be invoked whenever CTRL+ALT+DEL is + pressed. Alias names may be used in commands like enable, disable, + start, stop, status, …, and in unit dependency directives + Wants=, Requires=, Before=, After=, …, + with the limitation that aliases specified through Alias= are only effective when the unit is + enabled. Aliases cannot be used with the preset command. Along with a unit file foo.service, the directory foo.service.wants/ may exist. All -- cgit v1.2.3 From 60bec8e4031367869520280350fa1523625d682b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 13 Aug 2016 01:21:57 -0400 Subject: shared/install: move root skipping into create_symlink() No functional change intended. --- src/shared/install.c | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/shared/install.c b/src/shared/install.c index 8e40c171da..ad2e619237 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -394,6 +394,7 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang } static int create_symlink( + const LookupPaths *paths, const char *old_path, const char *new_path, bool force, @@ -401,11 +402,16 @@ static int create_symlink( unsigned *n_changes) { _cleanup_free_ char *dest = NULL; + const char *rp; int r; assert(old_path); assert(new_path); + rp = skip_root(paths, old_path); + if (rp) + old_path = rp; + /* Actually create a symlink, and remember that we did. Is * smart enough to check if there's already a valid symlink in * place. @@ -1486,7 +1492,6 @@ static int install_info_symlink_alias( STRV_FOREACH(s, i->aliases) { _cleanup_free_ char *alias_path = NULL, *dst = NULL; - const char *rp; q = install_full_printf(i, *s, &dst); if (q < 0) @@ -1496,9 +1501,7 @@ static int install_info_symlink_alias( if (!alias_path) return -ENOMEM; - rp = skip_root(paths, i->path); - - q = create_symlink(rp ?: i->path, alias_path, force, changes, n_changes); + q = create_symlink(paths, i->path, alias_path, force, changes, n_changes); if (r == 0) r = q; } @@ -1542,7 +1545,6 @@ static int install_info_symlink_wants( STRV_FOREACH(s, list) { _cleanup_free_ char *path = NULL, *dst = NULL; - const char *rp; q = install_full_printf(i, *s, &dst); if (q < 0) @@ -1557,9 +1559,7 @@ static int install_info_symlink_wants( if (!path) return -ENOMEM; - rp = skip_root(paths, i->path); - - q = create_symlink(rp ?: i->path, path, true, changes, n_changes); + q = create_symlink(paths, i->path, path, true, changes, n_changes); if (r == 0) r = q; } @@ -1576,7 +1576,6 @@ static int install_info_symlink_link( unsigned *n_changes) { _cleanup_free_ char *path = NULL; - const char *rp; int r; assert(i); @@ -1594,9 +1593,7 @@ static int install_info_symlink_link( if (!path) return -ENOMEM; - rp = skip_root(paths, i->path); - - return create_symlink(rp ?: i->path, path, force, changes, n_changes); + return create_symlink(paths, i->path, path, force, changes, n_changes); } static int install_info_apply( @@ -1772,7 +1769,7 @@ int unit_file_mask( if (!path) return -ENOMEM; - q = create_symlink("/dev/null", path, force, changes, n_changes); + q = create_symlink(&paths, "/dev/null", path, force, changes, n_changes); if (q < 0 && r >= 0) r = q; } @@ -1932,14 +1929,12 @@ int unit_file_link( r = 0; STRV_FOREACH(i, todo) { _cleanup_free_ char *new_path = NULL; - const char *old_path; - old_path = skip_root(&paths, *i); new_path = path_make_absolute(basename(*i), config_path); if (!new_path) return -ENOMEM; - q = create_symlink(old_path ?: *i, new_path, force, changes, n_changes); + q = create_symlink(&paths, *i, new_path, force, changes, n_changes); if (q < 0 && r >= 0) r = q; } @@ -2318,7 +2313,7 @@ int unit_file_set_default( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; UnitFileInstallInfo *i; - const char *new_path, *old_path; + const char *new_path; int r; assert(scope >= 0); @@ -2341,10 +2336,8 @@ int unit_file_set_default( if (r < 0) return r; - old_path = skip_root(&paths, i->path); new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET); - - return create_symlink(old_path ?: i->path, new_path, force, changes, n_changes); + return create_symlink(&paths, i->path, new_path, force, changes, n_changes); } int unit_file_get_default( -- cgit v1.2.3 From 25ea92778d5f4339e07c152a99d16223f43ad681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 13 Aug 2016 01:27:21 -0400 Subject: shared/install: when creating symlinks, keep existing relative symlinks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Running preset-all on a system installed from rpms or even created using make install would remove and recreate a lot of symlinks, changing relative to absolute symlinks. In general relative symlinks are nicer, so there is no reason to change them, and those spurious changes were obscuring more interesting stuff. $ make install DESTDIR=/var/tmp/inst1 $ systemctl preset-all --root=/var/tmp/inst1 (before) Removed /var/tmp/inst1/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service. Created symlink /var/tmp/inst1/etc/systemd/system/ctrl-alt-del.target → /usr/lib/systemd/system/exit.target. Removed /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/remote-fs.target. Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/remote-fs.target → /usr/lib/systemd/system/remote-fs.target. Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/machines.target → /usr/lib/systemd/system/machines.target. Created symlink /var/tmp/inst1/etc/systemd/system/sockets.target.wants/systemd-journal-remote.socket → /usr/lib/systemd/system/systemd-journal-remote.socket. Removed /var/tmp/inst1/etc/systemd/system/sockets.target.wants/systemd-networkd.socket. Created symlink /var/tmp/inst1/etc/systemd/system/sockets.target.wants/systemd-networkd.socket → /usr/lib/systemd/system/systemd-networkd.socket. Removed /var/tmp/inst1/etc/systemd/system/getty.target.wants/getty@tty1.service. Created symlink /var/tmp/inst1/etc/systemd/system/getty.target.wants/getty@tty1.service → /usr/lib/systemd/system/getty@.service. Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-journal-upload.service → /usr/lib/systemd/system/systemd-journal-upload.service. Removed /var/tmp/inst1/etc/systemd/system/sysinit.target.wants/systemd-timesyncd.service. Created symlink /var/tmp/inst1/etc/systemd/system/sysinit.target.wants/systemd-timesyncd.service → /usr/lib/systemd/system/systemd-timesyncd.service. Removed /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-resolved.service. Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-resolved.service → /usr/lib/systemd/system/systemd-resolved.service. Removed /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-networkd.service. Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-networkd.service → /usr/lib/systemd/system/systemd-networkd.service. (after) Removed /var/tmp/inst1/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service. Created symlink /var/tmp/inst1/etc/systemd/system/ctrl-alt-del.target → /usr/lib/systemd/system/exit.target. Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/machines.target → /usr/lib/systemd/system/machines.target. Created symlink /var/tmp/inst1/etc/systemd/system/sockets.target.wants/systemd-journal-remote.socket → /usr/lib/systemd/system/systemd-journal-remote.socket. Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-journal-upload.service → /usr/lib/systemd/system/systemd-journal-upload.service. --- src/shared/install.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/shared/install.c b/src/shared/install.c index ad2e619237..6c7eb9b2ef 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -393,6 +393,21 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang log_error_errno(r, "Failed to %s: %m.", verb); } +/** + * Checks if two paths or symlinks from wd are the same, when root is the root of the filesystem. + * wc should be the full path in the host file system. + */ +static bool chroot_symlinks_same(const char *root, const char *wd, const char *a, const char *b) { + assert(path_is_absolute(wd)); + + /* This will give incorrect results if the paths are relative and go outside + * of the chroot. False negatives are possible. */ + + a = strjoina(path_is_absolute(a) ? root : wd, "/", a); + b = strjoina(path_is_absolute(b) ? root : wd, "/", b); + return path_equal_or_files_same(a, b); +} + static int create_symlink( const LookupPaths *paths, const char *old_path, @@ -401,7 +416,7 @@ static int create_symlink( UnitFileChange **changes, unsigned *n_changes) { - _cleanup_free_ char *dest = NULL; + _cleanup_free_ char *dest = NULL, *dirname = NULL; const char *rp; int r; @@ -442,7 +457,11 @@ static int create_symlink( return r; } - if (path_equal(dest, old_path)) + dirname = dirname_malloc(new_path); + if (!dirname) + return -ENOMEM; + + if (chroot_symlinks_same(paths->root_dir, dirname, dest, old_path)) return 1; if (!force) { -- cgit v1.2.3 From 58a6dd15582c038a25bd7059435833943e2e4617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 13 Aug 2016 01:52:12 -0400 Subject: units: enable systemd-networkd-wait-online.service, disable all journal-remote stuff The preset for systemd-networkd-wait-online.service should match whatever we do for systemd-networkd.service. s-n-wait-online.service is only pulled in when some other unit pulls in network-online.target, otherwise it's not used. But if something pulls in network-online.target, they should expect s-n-wait-online.service to be active iff systemd-networkd.service is active. OTOH, the journal-remote and journal-upload services should be disabled by default, since they don't do anything without additional configuration. --- system-preset/90-systemd.preset | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/system-preset/90-systemd.preset b/system-preset/90-systemd.preset index ee1b864bcf..09f920a5f7 100644 --- a/system-preset/90-systemd.preset +++ b/system-preset/90-systemd.preset @@ -15,6 +15,7 @@ enable getty@.service enable systemd-timesyncd.service enable systemd-networkd.service enable systemd-resolved.service +enable systemd-networkd-wait-online.service disable console-getty.service disable console-shell.service @@ -29,4 +30,5 @@ disable rescue.target disable syslog.socket disable systemd-journal-gatewayd.* -disable systemd-networkd-wait-online.service +disable systemd-journal-remote.* +disable systemd-journal-upload.* -- cgit v1.2.3