diff options
Diffstat (limited to 'src')
51 files changed, 2523 insertions, 409 deletions
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 1ef1de0604..9b44c5a7a5 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -2378,9 +2378,9 @@ bool cg_is_unified_systemd_controller_wanted(void) { return wanted; r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller", NULL); - if (r > 0) { + if (r > 0) wanted = false; - } else { + else { _cleanup_free_ char *value = NULL; r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller=", &value); diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index 5d9bee50f5..1a61c7ad22 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -112,6 +112,10 @@ static inline bool CGROUP_BLKIO_WEIGHT_IS_OK(uint64_t x) { (x >= CGROUP_BLKIO_WEIGHT_MIN && x <= CGROUP_BLKIO_WEIGHT_MAX); } +/* Default resource limits */ +#define DEFAULT_TASKS_MAX_PERCENTAGE 15U /* 15% of PIDs, 4915 on default settings */ +#define DEFAULT_USER_TASKS_MAX_PERCENTAGE 33U /* 33% of PIDs, 10813 on default settings */ + typedef enum CGroupUnified { CGROUP_UNIFIED_UNKNOWN = -1, CGROUP_UNIFIED_NONE = 0, /* Both systemd and controllers on legacy */ diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c index 13c3bb6446..e44a357287 100644 --- a/src/basic/hostname-util.c +++ b/src/basic/hostname-util.c @@ -163,7 +163,6 @@ char* hostname_cleanup(char *s) { *(d++) = *p; dot = false; } - } if (dot && d > s) diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c index 28dc778969..bfa04394fe 100644 --- a/src/basic/mount-util.c +++ b/src/basic/mount-util.c @@ -75,7 +75,6 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id return safe_atoi(p, mnt_id); } - int fd_is_mount_point(int fd, const char *filename, int flags) { union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT; int mount_id = -1, mount_id_parent = -1; diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index f0a46c48cf..eafdea9eb3 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -39,6 +39,7 @@ #include <unistd.h> #include "alloc-util.h" +#include "env-util.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" @@ -345,12 +346,7 @@ int open_terminal(const char *name, int mode) { } r = isatty(fd); - if (r < 0) { - safe_close(fd); - return -errno; - } - - if (!r) { + if (r == 0) { safe_close(fd); return -ENOTTY; } @@ -1191,12 +1187,9 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) { return receive_one_fd(pair[0], 0); } -bool terminal_is_dumb(void) { +static bool getenv_terminal_is_dumb(void) { const char *e; - if (!on_tty()) - return true; - e = getenv("TERM"); if (!e) return true; @@ -1204,15 +1197,25 @@ bool terminal_is_dumb(void) { return streq(e, "dumb"); } +bool terminal_is_dumb(void) { + if (!on_tty()) + return true; + + return getenv_terminal_is_dumb(); +} + bool colors_enabled(void) { static int enabled = -1; if (_unlikely_(enabled < 0)) { - const char *colors; - - colors = getenv("SYSTEMD_COLORS"); - if (colors) - enabled = parse_boolean(colors) != 0; + int val; + + val = getenv_bool("SYSTEMD_COLORS"); + if (val >= 0) + enabled = val; + else if (getpid() == 1) + /* PID1 outputs to the console without holding it open all the time */ + enabled = !getenv_terminal_is_dumb(); else enabled = !terminal_is_dumb(); } diff --git a/src/basic/user-util.h b/src/basic/user-util.h index 36f71fb004..f569363811 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -20,6 +20,7 @@ ***/ #include <stdbool.h> +#include <stdint.h> #include <sys/types.h> #include <unistd.h> @@ -57,8 +58,19 @@ int take_etc_passwd_lock(const char *root); #define UID_INVALID ((uid_t) -1) #define GID_INVALID ((gid_t) -1) -/* The following macros add 1 when converting things, since UID 0 is a - * valid UID, while the pointer NULL is special */ +/* 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 DYNAMIC_UID_MIN ((uid_t) UINT32_C(0x0000EF00)) +#define DYNAMIC_UID_MAX ((uid_t) UINT32_C(0x0000FFEF)) + +static inline bool uid_is_dynamic(uid_t uid) { + return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX; +} + +/* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer + * NULL is special */ #define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1)) #define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1)) diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 4b73a9ac13..086cd7a789 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -952,7 +952,7 @@ static void cgroup_context_apply(Unit *u, CGroupMask mask, ManagerState state) { if ((mask & CGROUP_MASK_PIDS) && !is_root) { - if (c->tasks_max != (uint64_t) -1) { + if (c->tasks_max != CGROUP_LIMIT_MAX) { char buf[DECIMAL_STR_MAX(uint64_t) + 2]; sprintf(buf, "%" PRIu64 "\n", c->tasks_max); diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index e35d3ccd2e..7e33a2d201 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -695,6 +695,7 @@ const sd_bus_vtable bus_exec_vtable[] = { 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("RemoveIPC", "b", bus_property_get_bool, offsetof(ExecContext, remove_ipc), 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), @@ -1071,7 +1072,7 @@ int bus_exec_context_set_transient_property( "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute", - "RestrictRealtime", "DynamicUser")) { + "RestrictRealtime", "DynamicUser", "RemoveIPC")) { int b; r = sd_bus_message_read(message, "b", &b); @@ -1103,6 +1104,8 @@ int bus_exec_context_set_transient_property( c->restrict_realtime = b; else if (streq(name, "DynamicUser")) c->dynamic_user = b; + else if (streq(name, "RemoveIPC")) + c->remove_ipc = b; unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, yes_no(b)); } diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c index b4bbee0648..3c6bda4073 100644 --- a/src/core/dbus-mount.c +++ b/src/core/dbus-mount.c @@ -117,6 +117,8 @@ const sd_bus_vtable bus_mount_vtable[] = { SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Mount, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SloppyOptions", "b", bus_property_get_bool, offsetof(Mount, sloppy_options), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Mount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_EXEC_COMMAND_VTABLE("ExecMount", offsetof(Mount, exec_command[MOUNT_EXEC_MOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_VTABLE("ExecUnmount", offsetof(Mount, exec_command[MOUNT_EXEC_UNMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_VTABLE("ExecRemount", offsetof(Mount, exec_command[MOUNT_EXEC_REMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index fab3677a01..3c55e0f7fe 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -50,11 +50,6 @@ const sd_bus_vtable bus_service_vtable[] = { SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST), BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0), - /* The following four are obsolete, and thus marked hidden here. They moved into the Unit interface */ - SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("FailureAction", "s", property_get_failure_action, offsetof(Service, failure_action), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PermissionsStartOnly", "b", bus_property_get_bool, offsetof(Service, permissions_start_only), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST), @@ -70,6 +65,9 @@ const sd_bus_vtable bus_service_vtable[] = { SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("USBFunctionDescriptors", "s", NULL, offsetof(Service, usb_function_descriptors), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("USBFunctionStrings", "s", NULL, offsetof(Service, usb_function_strings), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), @@ -77,6 +75,12 @@ const sd_bus_vtable bus_service_vtable[] = { BUS_EXEC_COMMAND_LIST_VTABLE("ExecReload", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStop", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + + /* The following four are obsolete, and thus marked hidden here. They moved into the Unit interface */ + SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_VTABLE_END }; diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index 9a071a1355..21adb64e15 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -152,6 +152,8 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("TriggerLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, trigger_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c index 292f8738c6..85a2c26b98 100644 --- a/src/core/dbus-swap.c +++ b/src/core/dbus-swap.c @@ -84,6 +84,8 @@ const sd_bus_vtable bus_swap_vtable[] = { SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Swap, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Swap, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Swap, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_EXEC_COMMAND_VTABLE("ExecActivate", offsetof(Swap, exec_command[SWAP_EXEC_ACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_VTABLE("ExecDeactivate", offsetof(Swap, exec_command[SWAP_EXEC_DEACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), SD_BUS_VTABLE_END diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index b55d2cf735..89e56a2e51 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -660,10 +660,6 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("PropagatesReloadTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ReloadPropagatedFrom", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_JOINS_NAMESPACE_OF]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("RequiresMountsFor", "as", NULL, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -705,7 +701,6 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* obsolete alias name */ SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST), @@ -721,6 +716,12 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED), + /* Obsolete properties or obsolete alias names */ + SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_VTABLE_END }; diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c index 8035bee231..310aaa94e1 100644 --- a/src/core/dynamic-user.c +++ b/src/core/dynamic-user.c @@ -31,14 +31,8 @@ #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) +#define UID_CLAMP_INTO_RANGE(rnd) (((uid_t) (rnd) % (DYNAMIC_UID_MAX - DYNAMIC_UID_MIN + 1)) + DYNAMIC_UID_MIN) static DynamicUser* dynamic_user_free(DynamicUser *d) { if (!d) @@ -150,6 +144,45 @@ int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) { return 1; } +static int make_uid_symlinks(uid_t uid, const char *name, bool b) { + + char path1[strlen("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1]; + const char *path2; + int r = 0, k; + + /* Add direct additional symlinks for direct lookups of dynamic UIDs and their names by userspace code. The + * only reason we have this is because dbus-daemon cannot use D-Bus for resolving users and groups (since it + * would be its own client then). We hence keep these world-readable symlinks in place, so that the + * unprivileged dbus user can read the mappings when it needs them via these symlinks instead of having to go + * via the bus. Ideally, we'd use the lock files we keep for this anyway, but we can't since we use BSD locks + * on them and as those may be taken by any user with read access we can't make them world-readable. */ + + xsprintf(path1, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid); + if (unlink(path1) < 0 && errno != ENOENT) + r = -errno; + + if (b && symlink(name, path1) < 0) { + k = log_warning_errno(errno, "Failed to symlink \"%s\": %m", path1); + if (r == 0) + r = k; + } + + path2 = strjoina("/run/systemd/dynamic-uid/direct:", name); + if (unlink(path2) < 0 && errno != ENOENT) { + k = -errno; + if (r == 0) + r = k; + } + + if (b && symlink(path1 + strlen("/run/systemd/dynamic-uid/direct:"), path2) < 0) { + k = log_warning_errno(errno, "Failed to symlink \"%s\": %m", path2); + if (r == 0) + r = k; + } + + return r; +} + static int pick_uid(const char *name, uid_t *ret_uid) { static const uint8_t hash_key[] = { @@ -175,7 +208,7 @@ static int pick_uid(const char *name, uid_t *ret_uid) { if (--n_tries <= 0) /* Give up retrying eventually */ return -EBUSY; - if (candidate < UID_PICK_MIN || candidate > UID_PICK_MAX) + if (!uid_is_dynamic(candidate)) goto next; xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, candidate); @@ -223,6 +256,7 @@ static int pick_uid(const char *name, uid_t *ret_uid) { } (void) ftruncate(lock_fd, l); + (void) make_uid_symlinks(candidate, name, true); /* also add direct lookup symlinks */ *ret_uid = candidate; r = lock_fd; @@ -324,14 +358,16 @@ static int dynamic_user_push(DynamicUser *d, uid_t uid, int lock_fd) { return 0; } -static void unlink_uid_lock(int lock_fd, uid_t uid) { +static void unlink_uid_lock(int lock_fd, uid_t uid, const char *name) { 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); + (void) unlink(lock_path); + + (void) make_uid_symlinks(uid, name, false); /* remove direct lookup symlinks */ } int dynamic_user_realize(DynamicUser *d, uid_t *ret) { @@ -399,7 +435,7 @@ int dynamic_user_realize(DynamicUser *d, uid_t *ret) { /* 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); + unlink_uid_lock(uid_lock_fd, uid, d->name); return -errno; } @@ -407,7 +443,7 @@ int dynamic_user_realize(DynamicUser *d, uid_t *ret) { 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); + unlink_uid_lock(uid_lock_fd, uid, d->name); goto finish; } @@ -416,7 +452,7 @@ int dynamic_user_realize(DynamicUser *d, uid_t *ret) { /* 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); + unlink_uid_lock(uid_lock_fd, uid, d->name); safe_close(uid_lock_fd); uid = new_uid; @@ -513,7 +549,7 @@ static int dynamic_user_close(DynamicUser *d) { 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); + unlink_uid_lock(lock_fd, uid, d->name); r = 1; finish: @@ -634,11 +670,8 @@ int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret) { 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) + /* A friendly way to translate a dynamic user's UID into a name. */ + if (!uid_is_dynamic(uid)) return -ESRCH; xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid); diff --git a/src/core/execute.c b/src/core/execute.c index 6019df7ea6..0af8eb5a02 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -91,6 +91,7 @@ #include "selinux-util.h" #include "signal-util.h" #include "smack-util.h" +#include "special.h" #include "string-table.h" #include "string-util.h" #include "strv.h" @@ -1384,6 +1385,7 @@ static void do_idle_pipe_dance(int idle_pipe[4]) { } static int build_environment( + Unit *u, const ExecContext *c, const ExecParameters *p, unsigned n_fds, @@ -1401,7 +1403,7 @@ static int build_environment( assert(c); assert(ret); - our_env = new0(char*, 12); + our_env = new0(char*, 13); if (!our_env) return -ENOMEM; @@ -1436,6 +1438,16 @@ static int build_environment( our_env[n_env++] = x; } + /* If this is D-Bus, tell the nss-systemd module, since it relies on being able to use D-Bus look up dynamic + * users via PID 1, possibly dead-locking the dbus daemon. This way it will not use D-Bus to resolve names, but + * check the database directly. */ + if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) { + x = strdup("SYSTEMD_NSS_BYPASS_BUS=1"); + if (!x) + return -ENOMEM; + our_env[n_env++] = x; + } + if (home) { x = strappend("HOME=", home); if (!x) @@ -1723,11 +1735,12 @@ static int close_remaining_fds( const ExecParameters *params, ExecRuntime *runtime, DynamicCreds *dcreds, + int user_lookup_fd, int socket_fd, int *fds, unsigned n_fds) { unsigned n_dont_close = 0; - int dont_close[n_fds + 11]; + int dont_close[n_fds + 12]; assert(params); @@ -1755,9 +1768,40 @@ static int close_remaining_fds( append_socket_pair(dont_close, &n_dont_close, dcreds->group->storage_socket); } + if (user_lookup_fd >= 0) + dont_close[n_dont_close++] = user_lookup_fd; + return close_all_fds(dont_close, n_dont_close); } +static int send_user_lookup( + Unit *unit, + int user_lookup_fd, + uid_t uid, + gid_t gid) { + + assert(unit); + + /* Send the resolved UID/GID to PID 1 after we learnt it. We send a single datagram, containing the UID/GID + * data as well as the unit name. Note that we suppress sending this if no user/group to resolve was + * specified. */ + + if (user_lookup_fd < 0) + return 0; + + if (!uid_is_valid(uid) && !gid_is_valid(gid)) + return 0; + + if (writev(user_lookup_fd, + (struct iovec[]) { + { .iov_base = &uid, .iov_len = sizeof(uid) }, + { .iov_base = &gid, .iov_len = sizeof(gid) }, + { .iov_base = unit->id, .iov_len = strlen(unit->id) }}, 3) < 0) + return -errno; + + return 0; +} + static int exec_child( Unit *unit, ExecCommand *command, @@ -1769,6 +1813,7 @@ static int exec_child( int socket_fd, int *fds, unsigned n_fds, char **files_env, + int user_lookup_fd, int *exit_status) { _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL; @@ -1815,7 +1860,7 @@ static int exec_child( log_forget_fds(); - r = close_remaining_fds(params, runtime, dcreds, socket_fd, fds, n_fds); + r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, fds, n_fds); if (r < 0) { *exit_status = EXIT_FDS; return r; @@ -1862,7 +1907,7 @@ static int exec_child( return r; } - if (uid == UID_INVALID || gid == GID_INVALID) { + if (!uid_is_valid(uid) || !gid_is_valid(gid)) { *exit_status = EXIT_USER; return -ESRCH; } @@ -1902,6 +1947,14 @@ static int exec_child( } } + r = send_user_lookup(unit, user_lookup_fd, uid, gid); + if (r < 0) { + *exit_status = EXIT_USER; + return r; + } + + user_lookup_fd = safe_close(user_lookup_fd); + /* If a socket is connected to STDIN/STDOUT/STDERR, we * must sure to drop O_NONBLOCK */ if (socket_fd >= 0) @@ -2059,6 +2112,7 @@ static int exec_child( } r = build_environment( + unit, context, params, n_fds, @@ -2501,6 +2555,7 @@ int exec_spawn(Unit *unit, socket_fd, fds, n_fds, files_env, + unit->manager->user_lookup_fds[1], &exit_status); if (r < 0) { log_open(); diff --git a/src/core/execute.h b/src/core/execute.h index 106154f81a..6082c42aba 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -178,6 +178,7 @@ struct ExecContext { bool no_new_privileges; bool dynamic_user; + bool remove_ipc; /* This is not exposed to the user but available * internally. We need it to make sure that whenever we spawn diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 4ad6c4d79b..d5185cf6a0 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -2999,30 +2999,36 @@ int config_parse_tasks_max( void *data, void *userdata) { - uint64_t *tasks_max = data, u; + uint64_t *tasks_max = data, v; + Unit *u = userdata; int r; - if (isempty(rvalue) || streq(rvalue, "infinity")) { - *tasks_max = (uint64_t) -1; + if (isempty(rvalue)) { + *tasks_max = u->manager->default_tasks_max; + return 0; + } + + if (streq(rvalue, "infinity")) { + *tasks_max = CGROUP_LIMIT_MAX; return 0; } r = parse_percent(rvalue); if (r < 0) { - r = safe_atou64(rvalue, &u); + r = safe_atou64(rvalue, &v); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue); return 0; } } else - u = system_tasks_max_scale(r, 100U); + v = system_tasks_max_scale(r, 100U); - if (u <= 0 || u >= UINT64_MAX) { + if (v <= 0 || v >= UINT64_MAX) { log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue); return 0; } - *tasks_max = u; + *tasks_max = v; return 0; } diff --git a/src/core/main.c b/src/core/main.c index 02324d325e..125cfb28f0 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1505,7 +1505,8 @@ int main(int argc, char *argv[]) { if (getpid() == 1) { /* Don't limit the core dump size, so that coredump handlers such as systemd-coredump (which honour the limit) * will process core dumps for system services by default. */ - (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)); + if (setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)) < 0) + log_warning_errno(errno, "Failed to set RLIMIT_CORE: %m"); /* But at the same time, turn off the core_pattern logic by default, so that no coredumps are stored * until the systemd-coredump tool is enabled via sysctl. */ @@ -1559,7 +1560,7 @@ int main(int argc, char *argv[]) { (void) reset_all_signal_handlers(); (void) ignore_signals(SIGNALS_IGNORE, -1); - arg_default_tasks_max = system_tasks_max_scale(15U, 100U); /* 15% the system PIDs equals 4915 by default. */ + arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U); if (parse_config_file() < 0) { error_message = "Failed to parse config file"; @@ -2013,9 +2014,6 @@ finish: log_error_errno(r, "Failed to switch root, trying to continue: %m"); } - /* Reopen the console */ - (void) make_console_stdio(); - args_size = MAX(6, argc+1); args = newa(const char*, args_size); @@ -2063,6 +2061,9 @@ finish: arg_serialization = safe_fclose(arg_serialization); fds = fdset_free(fds); + /* Reopen the console */ + (void) make_console_stdio(); + for (j = 1, i = 1; j < (unsigned) argc; j++) args[i++] = argv[j]; args[i++] = NULL; diff --git a/src/core/manager.c b/src/core/manager.c index 5c58e64a1b..7576d038a2 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -45,6 +45,7 @@ #include "bus-error.h" #include "bus-kernel.h" #include "bus-util.h" +#include "clean-ipc.h" #include "dbus-job.h" #include "dbus-manager.h" #include "dbus-unit.h" @@ -81,6 +82,7 @@ #include "transaction.h" #include "umask-util.h" #include "unit-name.h" +#include "user-util.h" #include "util.h" #include "virt.h" #include "watchdog.h" @@ -98,6 +100,7 @@ static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, ui static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); +static int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata); static int manager_dispatch_run_queue(sd_event_source *source, void *userdata); static int manager_run_generators(Manager *m); @@ -590,6 +593,8 @@ int manager_new(UnitFileScope scope, bool test_run, Manager **_m) { m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd = m->ask_password_inotify_fd = -1; + m->user_lookup_fds[0] = m->user_lookup_fds[1] = -1; + m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ m->have_ask_password = -EINVAL; /* we don't know */ @@ -812,6 +817,59 @@ static int manager_setup_cgroups_agent(Manager *m) { return 0; } +static int manager_setup_user_lookup_fd(Manager *m) { + int r; + + assert(m); + + /* Set up the socket pair used for passing UID/GID resolution results from forked off processes to PID + * 1. Background: we can't do name lookups (NSS) from PID 1, since it might involve IPC and thus activation, + * and we might hence deadlock on ourselves. Hence we do all user/group lookups asynchronously from the forked + * off processes right before executing the binaries to start. In order to be able to clean up any IPC objects + * created by a unit (see RemoveIPC=) we need to know in PID 1 the used UID/GID of the executed processes, + * hence we establish this communication channel so that forked off processes can pass their UID/GID + * information back to PID 1. The forked off processes send their resolved UID/GID to PID 1 in a simple + * datagram, along with their unit name, so that we can share one communication socket pair among all units for + * this purpose. + * + * You might wonder why we need a communication channel for this that is independent of the usual notification + * socket scheme (i.e. $NOTIFY_SOCKET). The primary difference is about trust: data sent via the $NOTIFY_SOCKET + * channel is only accepted if it originates from the right unit and if reception was enabled for it. The user + * lookup socket OTOH is only accessible by PID 1 and its children until they exec(), and always available. + * + * Note that this function is called under two circumstances: when we first initialize (in which case we + * allocate both the socket pair and the event source to listen on it), and when we deserialize after a reload + * (in which case the socket pair already exists but we still need to allocate the event source for it). */ + + if (m->user_lookup_fds[0] < 0) { + + /* Free all secondary fields */ + safe_close_pair(m->user_lookup_fds); + m->user_lookup_event_source = sd_event_source_unref(m->user_lookup_event_source); + + if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, m->user_lookup_fds) < 0) + return log_error_errno(errno, "Failed to allocate user lookup socket: %m"); + + (void) fd_inc_rcvbuf(m->user_lookup_fds[0], NOTIFY_RCVBUF_SIZE); + } + + if (!m->user_lookup_event_source) { + r = sd_event_add_io(m->event, &m->user_lookup_event_source, m->user_lookup_fds[0], EPOLLIN, manager_dispatch_user_lookup_fd, m); + if (r < 0) + return log_error_errno(errno, "Failed to allocate user lookup event source: %m"); + + /* Process even earlier than the notify event source, so that we always know first about valid UID/GID + * resolutions */ + r = sd_event_source_set_priority(m->user_lookup_event_source, SD_EVENT_PRIORITY_NORMAL-8); + if (r < 0) + return log_error_errno(errno, "Failed to set priority ot user lookup event source: %m"); + + (void) sd_event_source_set_description(m->user_lookup_event_source, "user-lookup"); + } + + return 0; +} + static int manager_connect_bus(Manager *m, bool reexecuting) { bool try_bus_connect; @@ -853,8 +911,7 @@ enum { _GC_OFFSET_MAX }; -static void unit_gc_mark_good(Unit *u, unsigned gc_marker) -{ +static void unit_gc_mark_good(Unit *u, unsigned gc_marker) { Iterator i; Unit *other; @@ -1021,12 +1078,14 @@ Manager* manager_free(Manager *m) { sd_event_source_unref(m->time_change_event_source); sd_event_source_unref(m->jobs_in_progress_event_source); sd_event_source_unref(m->run_queue_event_source); + sd_event_source_unref(m->user_lookup_event_source); safe_close(m->signal_fd); safe_close(m->notify_fd); safe_close(m->cgroups_agent_fd); safe_close(m->time_change_fd); safe_close(m->kdbus_fd); + safe_close_pair(m->user_lookup_fds); manager_close_ask_password(m); @@ -1052,6 +1111,9 @@ Manager* manager_free(Manager *m) { assert(hashmap_isempty(m->units_requiring_mounts_for)); hashmap_free(m->units_requiring_mounts_for); + hashmap_free(m->uid_refs); + hashmap_free(m->gid_refs); + free(m); return NULL; } @@ -1221,6 +1283,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { if (q < 0 && r == 0) r = q; + q = manager_setup_user_lookup_fd(m); + if (q < 0 && r == 0) + r = q; + /* We might have deserialized the kdbus control fd, but if we * didn't, then let's create the bus now. */ manager_connect_bus(m, !!serialization); @@ -1232,6 +1298,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { /* Release any dynamic users no longer referenced */ dynamic_user_vacuum(m, true); + /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */ + manager_vacuum_uid_refs(m); + manager_vacuum_gid_refs(m); + if (serialization) { assert(m->n_reloading > 0); m->n_reloading--; @@ -2396,6 +2466,20 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { fprintf(f, "cgroups-agent-fd=%i\n", copy); } + if (m->user_lookup_fds[0] >= 0) { + int copy0, copy1; + + copy0 = fdset_put_dup(fds, m->user_lookup_fds[0]); + if (copy0 < 0) + return copy0; + + copy1 = fdset_put_dup(fds, m->user_lookup_fds[1]); + if (copy1 < 0) + return copy1; + + fprintf(f, "user-lookup=%i %i\n", copy0, copy1); + } + if (m->kdbus_fd >= 0) { int copy; @@ -2412,6 +2496,9 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { if (r < 0) return r; + manager_serialize_uid_refs(m, f); + manager_serialize_gid_refs(m, f); + fputc('\n', f); HASHMAP_FOREACH_KEY(u, t, m->units, i) { @@ -2578,6 +2665,18 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { m->cgroups_agent_fd = fdset_remove(fds, fd); } + } else if (startswith(l, "user-lookup=")) { + int fd0, fd1; + + if (sscanf(l + 12, "%i %i", &fd0, &fd1) != 2 || fd0 < 0 || fd1 < 0 || fd0 == fd1 || !fdset_contains(fds, fd0) || !fdset_contains(fds, fd1)) + log_debug("Failed to parse user lookup fd: %s", l + 12); + else { + m->user_lookup_event_source = sd_event_source_unref(m->user_lookup_event_source); + safe_close_pair(m->user_lookup_fds); + m->user_lookup_fds[0] = fdset_remove(fds, fd0); + m->user_lookup_fds[1] = fdset_remove(fds, fd1); + } + } else if (startswith(l, "kdbus-fd=")) { int fd; @@ -2590,6 +2689,10 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { } else if (startswith(l, "dynamic-user=")) dynamic_user_deserialize_one(m, l + 13, fds); + else if (startswith(l, "destroy-ipc-uid=")) + manager_deserialize_uid_refs_one(m, l + 16); + else if (startswith(l, "destroy-ipc-gid=")) + manager_deserialize_gid_refs_one(m, l + 16); else { int k; @@ -2672,6 +2775,8 @@ int manager_reload(Manager *m) { lookup_paths_flush_generator(&m->lookup_paths); lookup_paths_free(&m->lookup_paths); dynamic_user_vacuum(m, false); + m->uid_refs = hashmap_free(m->uid_refs); + m->gid_refs = hashmap_free(m->gid_refs); q = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL); if (q < 0 && r >= 0) @@ -2705,12 +2810,20 @@ int manager_reload(Manager *m) { if (q < 0 && r >= 0) r = q; + q = manager_setup_user_lookup_fd(m); + if (q < 0 && r >= 0) + r = q; + /* Third, fire things up! */ manager_coldplug(m); /* Release any dynamic users no longer referenced */ dynamic_user_vacuum(m, true); + /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */ + manager_vacuum_uid_refs(m); + manager_vacuum_gid_refs(m); + /* Sync current state of bus names with our set of listening units */ if (m->api_bus) manager_sync_bus_names(m, m->api_bus); @@ -3144,6 +3257,300 @@ ManagerState manager_state(Manager *m) { return MANAGER_RUNNING; } +#define DESTROY_IPC_FLAG (UINT32_C(1) << 31) + +static void manager_unref_uid_internal( + Manager *m, + Hashmap **uid_refs, + uid_t uid, + bool destroy_now, + int (*_clean_ipc)(uid_t uid)) { + + uint32_t c, n; + + assert(m); + assert(uid_refs); + assert(uid_is_valid(uid)); + assert(_clean_ipc); + + /* A generic implementation, covering both manager_unref_uid() and manager_unref_gid(), under the assumption + * that uid_t and gid_t are actually defined the same way, with the same validity rules. + * + * We store a hashmap where the UID/GID is they key and the value is a 32bit reference counter, whose highest + * bit is used as flag for marking UIDs/GIDs whose IPC objects to remove when the last reference to the UID/GID + * is dropped. The flag is set to on, once at least one reference from a unit where RemoveIPC= is set is added + * on a UID/GID. It is reset when the UID's/GID's reference counter drops to 0 again. */ + + assert_cc(sizeof(uid_t) == sizeof(gid_t)); + assert_cc(UID_INVALID == (uid_t) GID_INVALID); + + if (uid == 0) /* We don't keep track of root, and will never destroy it */ + return; + + c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid))); + + n = c & ~DESTROY_IPC_FLAG; + assert(n > 0); + n--; + + if (destroy_now && n == 0) { + hashmap_remove(*uid_refs, UID_TO_PTR(uid)); + + if (c & DESTROY_IPC_FLAG) { + log_debug("%s " UID_FMT " is no longer referenced, cleaning up its IPC.", + _clean_ipc == clean_ipc_by_uid ? "UID" : "GID", + uid); + (void) _clean_ipc(uid); + } + } else { + c = n | (c & DESTROY_IPC_FLAG); + assert_se(hashmap_update(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c)) >= 0); + } +} + +void manager_unref_uid(Manager *m, uid_t uid, bool destroy_now) { + manager_unref_uid_internal(m, &m->uid_refs, uid, destroy_now, clean_ipc_by_uid); +} + +void manager_unref_gid(Manager *m, gid_t gid, bool destroy_now) { + manager_unref_uid_internal(m, &m->gid_refs, (uid_t) gid, destroy_now, clean_ipc_by_gid); +} + +static int manager_ref_uid_internal( + Manager *m, + Hashmap **uid_refs, + uid_t uid, + bool clean_ipc) { + + uint32_t c, n; + int r; + + assert(m); + assert(uid_refs); + assert(uid_is_valid(uid)); + + /* A generic implementation, covering both manager_ref_uid() and manager_ref_gid(), under the assumption + * that uid_t and gid_t are actually defined the same way, with the same validity rules. */ + + assert_cc(sizeof(uid_t) == sizeof(gid_t)); + assert_cc(UID_INVALID == (uid_t) GID_INVALID); + + if (uid == 0) /* We don't keep track of root, and will never destroy it */ + return 0; + + r = hashmap_ensure_allocated(uid_refs, &trivial_hash_ops); + if (r < 0) + return r; + + c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid))); + + n = c & ~DESTROY_IPC_FLAG; + n++; + + if (n & DESTROY_IPC_FLAG) /* check for overflow */ + return -EOVERFLOW; + + c = n | (c & DESTROY_IPC_FLAG) | (clean_ipc ? DESTROY_IPC_FLAG : 0); + + return hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c)); +} + +int manager_ref_uid(Manager *m, uid_t uid, bool clean_ipc) { + return manager_ref_uid_internal(m, &m->uid_refs, uid, clean_ipc); +} + +int manager_ref_gid(Manager *m, gid_t gid, bool clean_ipc) { + return manager_ref_uid_internal(m, &m->gid_refs, (uid_t) gid, clean_ipc); +} + +static void manager_vacuum_uid_refs_internal( + Manager *m, + Hashmap **uid_refs, + int (*_clean_ipc)(uid_t uid)) { + + Iterator i; + void *p, *k; + + assert(m); + assert(uid_refs); + assert(_clean_ipc); + + HASHMAP_FOREACH_KEY(p, k, *uid_refs, i) { + uint32_t c, n; + uid_t uid; + + uid = PTR_TO_UID(k); + c = PTR_TO_UINT32(p); + + n = c & ~DESTROY_IPC_FLAG; + if (n > 0) + continue; + + if (c & DESTROY_IPC_FLAG) { + log_debug("Found unreferenced %s " UID_FMT " after reload/reexec. Cleaning up.", + _clean_ipc == clean_ipc_by_uid ? "UID" : "GID", + uid); + (void) _clean_ipc(uid); + } + + assert_se(hashmap_remove(*uid_refs, k) == p); + } +} + +void manager_vacuum_uid_refs(Manager *m) { + manager_vacuum_uid_refs_internal(m, &m->uid_refs, clean_ipc_by_uid); +} + +void manager_vacuum_gid_refs(Manager *m) { + manager_vacuum_uid_refs_internal(m, &m->gid_refs, clean_ipc_by_gid); +} + +static void manager_serialize_uid_refs_internal( + Manager *m, + FILE *f, + Hashmap **uid_refs, + const char *field_name) { + + Iterator i; + void *p, *k; + + assert(m); + assert(f); + assert(uid_refs); + assert(field_name); + + /* Serialize the UID reference table. Or actually, just the IPC destruction flag of it, as the actual counter + * of it is better rebuild after a reload/reexec. */ + + HASHMAP_FOREACH_KEY(p, k, *uid_refs, i) { + uint32_t c; + uid_t uid; + + uid = PTR_TO_UID(k); + c = PTR_TO_UINT32(p); + + if (!(c & DESTROY_IPC_FLAG)) + continue; + + fprintf(f, "%s=" UID_FMT "\n", field_name, uid); + } +} + +void manager_serialize_uid_refs(Manager *m, FILE *f) { + manager_serialize_uid_refs_internal(m, f, &m->uid_refs, "destroy-ipc-uid"); +} + +void manager_serialize_gid_refs(Manager *m, FILE *f) { + manager_serialize_uid_refs_internal(m, f, &m->gid_refs, "destroy-ipc-gid"); +} + +static void manager_deserialize_uid_refs_one_internal( + Manager *m, + Hashmap** uid_refs, + const char *value) { + + uid_t uid; + uint32_t c; + int r; + + assert(m); + assert(uid_refs); + assert(value); + + r = parse_uid(value, &uid); + if (r < 0 || uid == 0) { + log_debug("Unable to parse UID reference serialization"); + return; + } + + r = hashmap_ensure_allocated(uid_refs, &trivial_hash_ops); + if (r < 0) { + log_oom(); + return; + } + + c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid))); + if (c & DESTROY_IPC_FLAG) + return; + + c |= DESTROY_IPC_FLAG; + + r = hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c)); + if (r < 0) { + log_debug("Failed to add UID reference entry"); + return; + } +} + +void manager_deserialize_uid_refs_one(Manager *m, const char *value) { + manager_deserialize_uid_refs_one_internal(m, &m->uid_refs, value); +} + +void manager_deserialize_gid_refs_one(Manager *m, const char *value) { + manager_deserialize_uid_refs_one_internal(m, &m->gid_refs, value); +} + +int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + struct buffer { + uid_t uid; + gid_t gid; + char unit_name[UNIT_NAME_MAX+1]; + } _packed_ buffer; + + Manager *m = userdata; + ssize_t l; + size_t n; + Unit *u; + + assert_se(source); + assert_se(m); + + /* Invoked whenever a child process succeeded resolving its user/group to use and sent us the resulting UID/GID + * in a datagram. We parse the datagram here and pass it off to the unit, so that it can add a reference to the + * UID/GID so that it can destroy the UID/GID's IPC objects when the reference counter drops to 0. */ + + l = recv(fd, &buffer, sizeof(buffer), MSG_DONTWAIT); + if (l < 0) { + if (errno == EINTR || errno == EAGAIN) + return 0; + + return log_error_errno(errno, "Failed to read from user lookup fd: %m"); + } + + if ((size_t) l <= offsetof(struct buffer, unit_name)) { + log_warning("Received too short user lookup message, ignoring."); + return 0; + } + + if ((size_t) l > offsetof(struct buffer, unit_name) + UNIT_NAME_MAX) { + log_warning("Received too long user lookup message, ignoring."); + return 0; + } + + if (!uid_is_valid(buffer.uid) && !gid_is_valid(buffer.gid)) { + log_warning("Got user lookup message with invalid UID/GID pair, ignoring."); + return 0; + } + + n = (size_t) l - offsetof(struct buffer, unit_name); + if (memchr(buffer.unit_name, 0, n)) { + log_warning("Received lookup message with embedded NUL character, ignoring."); + return 0; + } + + buffer.unit_name[n] = 0; + u = manager_get_unit(m, buffer.unit_name); + if (!u) { + log_debug("Got user lookup message but unit doesn't exist, ignoring."); + return 0; + } + + log_unit_debug(u, "User lookup succeeded: uid=" UID_FMT " gid=" GID_FMT, buffer.uid, buffer.gid); + + unit_notify_user_lookup(u, buffer.uid, buffer.gid); + return 0; +} + static const char *const manager_state_table[_MANAGER_STATE_MAX] = { [MANAGER_INITIALIZING] = "initializing", [MANAGER_STARTING] = "starting", diff --git a/src/core/manager.h b/src/core/manager.h index c681d5dc46..b9f2e4b5a1 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -143,6 +143,9 @@ struct Manager { sd_event_source *jobs_in_progress_event_source; + int user_lookup_fds[2]; + sd_event_source *user_lookup_event_source; + UnitFileScope unit_file_scope; LookupPaths lookup_paths; Set *unit_path_cache; @@ -234,7 +237,6 @@ struct Manager { bool dispatching_dbus_queue:1; bool taint_usr:1; - bool test_run:1; /* If non-zero, exit with the following value when the systemd @@ -301,6 +303,10 @@ struct Manager { /* Dynamic users/groups, indexed by their name */ Hashmap *dynamic_users; + /* Keep track of all UIDs and GIDs any of our services currently use. This is useful for the RemoveIPC= logic. */ + Hashmap *uid_refs; + Hashmap *gid_refs; + /* When the user hits C-A-D more than 7 times per 2s, reboot immediately... */ RateLimit ctrl_alt_del_ratelimit; @@ -378,5 +384,20 @@ ManagerState manager_state(Manager *m); int manager_update_failed_units(Manager *m, Unit *u, bool failed); +void manager_unref_uid(Manager *m, uid_t uid, bool destroy_now); +int manager_ref_uid(Manager *m, uid_t uid, bool clean_ipc); + +void manager_unref_gid(Manager *m, gid_t gid, bool destroy_now); +int manager_ref_gid(Manager *m, gid_t gid, bool destroy_now); + +void manager_vacuum_uid_refs(Manager *m); +void manager_vacuum_gid_refs(Manager *m); + +void manager_serialize_uid_refs(Manager *m, FILE *f); +void manager_deserialize_uid_refs_one(Manager *m, const char *value); + +void manager_serialize_gid_refs(Manager *m, FILE *f); +void manager_deserialize_gid_refs_one(Manager *m, const char *value); + const char *manager_state_to_string(ManagerState m) _const_; ManagerState manager_state_from_string(const char *s) _pure_; diff --git a/src/core/mount.c b/src/core/mount.c index f3ccf6d48a..f2ac8d171f 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -769,6 +769,8 @@ static void mount_enter_dead(Mount *m, MountResult f) { exec_context_destroy_runtime_directory(&m->exec_context, manager_get_runtime_prefix(UNIT(m)->manager)); + unit_unref_uid_gid(UNIT(m), true); + dynamic_creds_destroy(&m->dynamic_creds); } diff --git a/src/core/service.c b/src/core/service.c index ef5f506fa1..969c62bd83 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1471,6 +1471,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) /* Also, remove the runtime directory */ exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager)); + /* Get rid of the IPC bits of the user */ + unit_unref_uid_gid(UNIT(s), true); + /* Release the user, and destroy it if we are the only remaining owner */ dynamic_creds_destroy(&s->dynamic_creds); diff --git a/src/core/socket.c b/src/core/socket.c index 50872e8366..70d55dd9ed 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1905,6 +1905,8 @@ static void socket_enter_dead(Socket *s, SocketResult f) { exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager)); + unit_unref_uid_gid(UNIT(s), true); + dynamic_creds_destroy(&s->dynamic_creds); } diff --git a/src/core/swap.c b/src/core/swap.c index 2c802da3b5..fb222b6858 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -683,6 +683,8 @@ static void swap_enter_dead(Swap *s, SwapResult f) { exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager)); + unit_unref_uid_gid(UNIT(s), true); + dynamic_creds_destroy(&s->dynamic_creds); } diff --git a/src/core/unit.c b/src/core/unit.c index d690cf89de..c58c501bc3 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -100,7 +100,8 @@ Unit *unit_new(Manager *m, size_t size) { u->on_failure_job_mode = JOB_REPLACE; u->cgroup_inotify_wd = -1; u->job_timeout = USEC_INFINITY; - u->sigchldgen = 0; + u->ref_uid = UID_INVALID; + u->ref_gid = GID_INVALID; RATELIMIT_INIT(u->start_limit, m->default_start_limit_interval, m->default_start_limit_burst); RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16); @@ -550,6 +551,8 @@ void unit_free(Unit *u) { unit_release_cgroup(u); + unit_unref_uid_gid(u, false); + (void) manager_update_failed_units(u->manager, u, false); set_remove(u->manager->startup_units, u); @@ -2614,6 +2617,11 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { unit_serialize_item(u, f, "cgroup", u->cgroup_path); unit_serialize_item(u, f, "cgroup-realized", yes_no(u->cgroup_realized)); + if (uid_is_valid(u->ref_uid)) + unit_serialize_item_format(u, f, "ref-uid", UID_FMT, u->ref_uid); + if (gid_is_valid(u->ref_gid)) + unit_serialize_item_format(u, f, "ref-gid", GID_FMT, u->ref_gid); + if (serialize_jobs) { if (u->job) { fprintf(f, "job\n"); @@ -2851,6 +2859,28 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { u->cgroup_realized = b; continue; + + } else if (streq(l, "ref-uid")) { + uid_t uid; + + r = parse_uid(v, &uid); + if (r < 0) + log_unit_debug(u, "Failed to parse referenced UID %s, ignoring.", v); + else + unit_ref_uid_gid(u, uid, GID_INVALID); + + continue; + + } else if (streq(l, "ref-gid")) { + gid_t gid; + + r = parse_gid(v, &gid); + if (r < 0) + log_unit_debug(u, "Failed to parse referenced GID %s, ignoring.", v); + else + unit_ref_uid_gid(u, UID_INVALID, gid); + + continue; } if (unit_can_serialize(u)) { @@ -3310,6 +3340,7 @@ int unit_patch_contexts(Unit *u) { } ec->private_tmp = true; + ec->remove_ipc = true; } } @@ -3932,3 +3963,144 @@ pid_t unit_main_pid(Unit *u) { return 0; } + +static void unit_unref_uid_internal( + Unit *u, + uid_t *ref_uid, + bool destroy_now, + void (*_manager_unref_uid)(Manager *m, uid_t uid, bool destroy_now)) { + + assert(u); + assert(ref_uid); + assert(_manager_unref_uid); + + /* Generic implementation of both unit_unref_uid() and unit_unref_gid(), under the assumption that uid_t and + * gid_t are actually the same time, with the same validity rules. + * + * Drops a reference to UID/GID from a unit. */ + + assert_cc(sizeof(uid_t) == sizeof(gid_t)); + assert_cc(UID_INVALID == (uid_t) GID_INVALID); + + if (!uid_is_valid(*ref_uid)) + return; + + _manager_unref_uid(u->manager, *ref_uid, destroy_now); + *ref_uid = UID_INVALID; +} + +void unit_unref_uid(Unit *u, bool destroy_now) { + unit_unref_uid_internal(u, &u->ref_uid, destroy_now, manager_unref_uid); +} + +void unit_unref_gid(Unit *u, bool destroy_now) { + unit_unref_uid_internal(u, (uid_t*) &u->ref_gid, destroy_now, manager_unref_gid); +} + +static int unit_ref_uid_internal( + Unit *u, + uid_t *ref_uid, + uid_t uid, + bool clean_ipc, + int (*_manager_ref_uid)(Manager *m, uid_t uid, bool clean_ipc)) { + + int r; + + assert(u); + assert(ref_uid); + assert(uid_is_valid(uid)); + assert(_manager_ref_uid); + + /* Generic implementation of both unit_ref_uid() and unit_ref_guid(), under the assumption that uid_t and gid_t + * are actually the same type, and have the same validity rules. + * + * Adds a reference on a specific UID/GID to this unit. Each unit referencing the same UID/GID maintains a + * reference so that we can destroy the UID/GID's IPC resources as soon as this is requested and the counter + * drops to zero. */ + + assert_cc(sizeof(uid_t) == sizeof(gid_t)); + assert_cc(UID_INVALID == (uid_t) GID_INVALID); + + if (*ref_uid == uid) + return 0; + + if (uid_is_valid(*ref_uid)) /* Already set? */ + return -EBUSY; + + r = _manager_ref_uid(u->manager, uid, clean_ipc); + if (r < 0) + return r; + + *ref_uid = uid; + return 1; +} + +int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc) { + return unit_ref_uid_internal(u, &u->ref_uid, uid, clean_ipc, manager_ref_uid); +} + +int unit_ref_gid(Unit *u, gid_t gid, bool clean_ipc) { + return unit_ref_uid_internal(u, (uid_t*) &u->ref_gid, (uid_t) gid, clean_ipc, manager_ref_gid); +} + +static int unit_ref_uid_gid_internal(Unit *u, uid_t uid, gid_t gid, bool clean_ipc) { + int r = 0, q = 0; + + assert(u); + + /* Reference both a UID and a GID in one go. Either references both, or neither. */ + + if (uid_is_valid(uid)) { + r = unit_ref_uid(u, uid, clean_ipc); + if (r < 0) + return r; + } + + if (gid_is_valid(gid)) { + q = unit_ref_gid(u, gid, clean_ipc); + if (q < 0) { + if (r > 0) + unit_unref_uid(u, false); + + return q; + } + } + + return r > 0 || q > 0; +} + +int unit_ref_uid_gid(Unit *u, uid_t uid, gid_t gid) { + ExecContext *c; + int r; + + assert(u); + + c = unit_get_exec_context(u); + + r = unit_ref_uid_gid_internal(u, uid, gid, c ? c->remove_ipc : false); + if (r < 0) + return log_unit_warning_errno(u, r, "Couldn't add UID/GID reference to unit, proceeding without: %m"); + + return r; +} + +void unit_unref_uid_gid(Unit *u, bool destroy_now) { + assert(u); + + unit_unref_uid(u, destroy_now); + unit_unref_gid(u, destroy_now); +} + +void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid) { + int r; + + assert(u); + + /* This is invoked whenever one of the forked off processes let's us know the UID/GID its user name/group names + * resolved to. We keep track of which UID/GID is currently assigned in order to be able to destroy its IPC + * objects when no service references the UID/GID anymore. */ + + r = unit_ref_uid_gid(u, uid, gid); + if (r > 0) + bus_unit_send_change_signal(u); +} diff --git a/src/core/unit.h b/src/core/unit.h index a6c69938dd..53875653d7 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -180,6 +180,10 @@ struct Unit { /* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */ RateLimit auto_stop_ratelimit; + /* Reference to a specific UID/GID */ + uid_t ref_uid; + gid_t ref_gid; + /* Cached unit file state and preset */ UnitFileState unit_file_state; int unit_file_preset; @@ -195,8 +199,6 @@ struct Unit { CGroupMask cgroup_members_mask; int cgroup_inotify_wd; - uint32_t cgroup_netclass_id; - /* How to start OnFailure units */ JobMode on_failure_job_mode; @@ -373,8 +375,7 @@ struct UnitVTable { /* Called whenever a process of this unit sends us a message */ void (*notify_message)(Unit *u, pid_t pid, char **tags, FDSet *fds); - /* Called whenever a name this Unit registered for comes or - * goes away. */ + /* Called whenever a name this Unit registered for comes or goes away. */ void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner); /* Called for each property that is being set */ @@ -623,6 +624,17 @@ int unit_fail_if_symlink(Unit *u, const char* where); int unit_start_limit_test(Unit *u); +void unit_unref_uid(Unit *u, bool destroy_now); +int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc); + +void unit_unref_gid(Unit *u, bool destroy_now); +int unit_ref_gid(Unit *u, gid_t gid, bool clean_ipc); + +int unit_ref_uid_gid(Unit *u, uid_t uid, gid_t gid); +void unit_unref_uid_gid(Unit *u, bool destroy_now); + +void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid); + /* Macros which append UNIT= or USER_UNIT= to the message */ #define log_unit_full(unit, level, error, ...) \ diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c index c16a324232..4795324667 100644 --- a/src/hostname/hostnamectl.c +++ b/src/hostname/hostnamectl.c @@ -251,7 +251,7 @@ static int set_simple_string(sd_bus *bus, const char *method, const char *value) static int set_hostname(sd_bus *bus, char **args, unsigned n) { _cleanup_free_ char *h = NULL; - char *hostname = args[1]; + const char *hostname = args[1]; int r; assert(args); @@ -263,27 +263,29 @@ static int set_hostname(sd_bus *bus, char **args, unsigned n) { if (arg_pretty) { const char *p; - /* If the passed hostname is already valid, then - * assume the user doesn't know anything about pretty - * hostnames, so let's unset the pretty hostname, and - * just set the passed hostname as static/dynamic + /* If the passed hostname is already valid, then assume the user doesn't know anything about pretty + * hostnames, so let's unset the pretty hostname, and just set the passed hostname as static/dynamic * hostname. */ - - if (arg_static && hostname_is_valid(hostname, true)) { - p = ""; - /* maybe get rid of trailing dot */ - hostname = hostname_cleanup(hostname); - } else { - p = h = strdup(hostname); - if (!p) - return log_oom(); - - hostname_cleanup(hostname); - } + if (arg_static && hostname_is_valid(hostname, true)) + p = ""; /* No pretty hostname (as it is redundant), just a static one */ + else + p = hostname; /* Use the passed name as pretty hostname */ r = set_simple_string(bus, "SetPrettyHostname", p); if (r < 0) return r; + + /* Now that we set the pretty hostname, let's clean up the parameter and use that as static + * hostname. If the hostname was already valid as static hostname, this will only chop off the trailing + * dot if there is one. If it was not valid, then it will be made fully valid by truncating, dropping + * multiple dots, and and dropping weird chars. Note that we clean the name up only if we also are + * supposed to set the pretty name. If the pretty name is not being set we assume the user knows what + * he does and pass the name as-is. */ + h = strdup(hostname); + if (!h) + return log_oom(); + + hostname = hostname_cleanup(h); /* Use the cleaned up name as static hostname */ } if (arg_static) { diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 6f841efb69..381e219390 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -310,7 +310,7 @@ static void help(void) { " -m --merge Show entries from all available journals\n" " -D --directory=PATH Show journal files from directory\n" " --file=PATH Show journal file\n" - " --root=ROOT Operate on catalog files below a root directory\n" + " --root=ROOT Operate on files below a root directory\n" #ifdef HAVE_GCRYPT " --interval=TIME Time interval for changing the FSS sealing key\n" " --verify-key=KEY Specify FSS verification key\n" @@ -848,8 +848,8 @@ static int parse_argv(int argc, char *argv[]) { if (arg_follow && !arg_no_tail && !arg_since && arg_lines == ARG_LINES_DEFAULT) arg_lines = 10; - if (!!arg_directory + !!arg_file + !!arg_machine > 1) { - log_error("Please specify either -D/--directory= or --file= or -M/--machine=, not more than one."); + if (!!arg_directory + !!arg_file + !!arg_machine + !!arg_root > 1) { + log_error("Please specify at most one of -D/--directory=, --file=, -M/--machine=, --root."); return -EINVAL; } @@ -1267,7 +1267,7 @@ static int add_boot(sd_journal *j) { * We can do this only when we logs are coming from the current machine, * so take the slow path if log location is specified. */ if (arg_boot_offset == 0 && sd_id128_is_null(arg_boot_id) && - !arg_directory && !arg_file) + !arg_directory && !arg_file && !arg_root) return add_match_this_boot(j, arg_machine); @@ -2161,6 +2161,8 @@ int main(int argc, char *argv[]) { if (arg_directory) r = sd_journal_open_directory(&j, arg_directory, arg_journal_type); + else if (arg_root) + r = sd_journal_open_directory(&j, arg_root, arg_journal_type | SD_JOURNAL_OS_ROOT); else if (arg_file_stdin) { int ifd = STDIN_FILENO; r = sd_journal_open_files_fd(&j, &ifd, 1, 0); diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 587c343b31..2a043a95b1 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -267,6 +267,82 @@ static int open_journal( return r; } +static int system_journal_open(Server *s, bool flush_requested) { + const char *fn; + int r = 0; + + if (!s->system_journal && + (s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) && + (flush_requested + || access("/run/systemd/journal/flushed", F_OK) >= 0)) { + + /* If in auto mode: first try to create the machine + * path, but not the prefix. + * + * If in persistent mode: create /var/log/journal and + * the machine path */ + + if (s->storage == STORAGE_PERSISTENT) + (void) mkdir_p("/var/log/journal/", 0755); + + fn = strjoina("/var/log/journal/", SERVER_MACHINE_ID(s)); + (void) mkdir(fn, 0755); + + fn = strjoina(fn, "/system.journal"); + r = open_journal(s, true, fn, O_RDWR|O_CREAT, s->seal, &s->system_metrics, &s->system_journal); + if (r >= 0) { + server_add_acls(s->system_journal, 0); + (void) determine_space_for(s, &s->system_metrics, "/var/log/journal/", "System journal", true, true, NULL, NULL); + } else if (r < 0) { + if (r != -ENOENT && r != -EROFS) + log_warning_errno(r, "Failed to open system journal: %m"); + + r = 0; + } + } + + if (!s->runtime_journal && + (s->storage != STORAGE_NONE)) { + + fn = strjoina("/run/log/journal/", SERVER_MACHINE_ID(s), "/system.journal"); + + if (s->system_journal) { + + /* Try to open the runtime journal, but only + * if it already exists, so that we can flush + * it into the system journal */ + + r = open_journal(s, false, fn, O_RDWR, false, &s->runtime_metrics, &s->runtime_journal); + if (r < 0) { + if (r != -ENOENT) + log_warning_errno(r, "Failed to open runtime journal: %m"); + + r = 0; + } + + } else { + + /* OK, we really need the runtime journal, so create + * it if necessary. */ + + (void) mkdir("/run/log", 0755); + (void) mkdir("/run/log/journal", 0755); + (void) mkdir_parents(fn, 0750); + + r = open_journal(s, true, fn, O_RDWR|O_CREAT, false, &s->runtime_metrics, &s->runtime_journal); + if (r < 0) + return log_error_errno(r, "Failed to open runtime journal: %m"); + } + + if (s->runtime_journal) { + server_add_acls(s->runtime_journal, 0); + (void) determine_space_for(s, &s->runtime_metrics, "/run/log/journal/", "Runtime journal", true, true, NULL, NULL); + } + } + + return r; +} + static JournalFile* find_journal(Server *s, uid_t uid) { _cleanup_free_ char *p = NULL; int r; @@ -275,6 +351,17 @@ static JournalFile* find_journal(Server *s, uid_t uid) { assert(s); + /* A rotate that fails to create the new journal (ENOSPC) leaves the + * rotated journal as NULL. Unless we revisit opening, even after + * space is made available we'll continue to return NULL indefinitely. + * + * system_journal_open() is a noop if the journals are already open, so + * we can just call it here to recover from failed rotates (or anything + * else that's left the journals as NULL). + * + * Fixes https://github.com/systemd/systemd/issues/3968 */ + (void) system_journal_open(s, false); + /* We split up user logs only on /var, not on /run. If the * runtime file is open, we write to it exclusively, in order * to guarantee proper order as soon as we flush /run to @@ -283,7 +370,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) { if (s->runtime_journal) return s->runtime_journal; - if (uid <= SYSTEM_UID_MAX) + if (uid <= SYSTEM_UID_MAX || uid_is_dynamic(uid)) return s->system_journal; r = sd_id128_get_machine(&machine); @@ -979,83 +1066,6 @@ finish: dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id, priority, object_pid); } - -static int system_journal_open(Server *s, bool flush_requested) { - const char *fn; - int r = 0; - - if (!s->system_journal && - (s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) && - (flush_requested - || access("/run/systemd/journal/flushed", F_OK) >= 0)) { - - /* If in auto mode: first try to create the machine - * path, but not the prefix. - * - * If in persistent mode: create /var/log/journal and - * the machine path */ - - if (s->storage == STORAGE_PERSISTENT) - (void) mkdir_p("/var/log/journal/", 0755); - - fn = strjoina("/var/log/journal/", SERVER_MACHINE_ID(s)); - (void) mkdir(fn, 0755); - - fn = strjoina(fn, "/system.journal"); - r = open_journal(s, true, fn, O_RDWR|O_CREAT, s->seal, &s->system_metrics, &s->system_journal); - if (r >= 0) { - server_add_acls(s->system_journal, 0); - (void) determine_space_for(s, &s->system_metrics, "/var/log/journal/", "System journal", true, true, NULL, NULL); - } else if (r < 0) { - if (r != -ENOENT && r != -EROFS) - log_warning_errno(r, "Failed to open system journal: %m"); - - r = 0; - } - } - - if (!s->runtime_journal && - (s->storage != STORAGE_NONE)) { - - fn = strjoina("/run/log/journal/", SERVER_MACHINE_ID(s), "/system.journal"); - - if (s->system_journal) { - - /* Try to open the runtime journal, but only - * if it already exists, so that we can flush - * it into the system journal */ - - r = open_journal(s, false, fn, O_RDWR, false, &s->runtime_metrics, &s->runtime_journal); - if (r < 0) { - if (r != -ENOENT) - log_warning_errno(r, "Failed to open runtime journal: %m"); - - r = 0; - } - - } else { - - /* OK, we really need the runtime journal, so create - * it if necessary. */ - - (void) mkdir("/run/log", 0755); - (void) mkdir("/run/log/journal", 0755); - (void) mkdir_parents(fn, 0750); - - r = open_journal(s, true, fn, O_RDWR|O_CREAT, false, &s->runtime_metrics, &s->runtime_journal); - if (r < 0) - return log_error_errno(r, "Failed to open runtime journal: %m"); - } - - if (s->runtime_journal) { - server_add_acls(s->runtime_journal, 0); - (void) determine_space_for(s, &s->runtime_metrics, "/run/log/journal/", "Runtime journal", true, true, NULL, NULL); - } - } - - return r; -} - int server_flush_to_var(Server *s) { sd_id128_t machine; sd_journal *j = NULL; diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 2a3824d0e8..98c8a47afe 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -1719,9 +1719,16 @@ static sd_journal *journal_new(int flags, const char *path) { j->data_threshold = DEFAULT_DATA_THRESHOLD; if (path) { - j->path = strdup(path); - if (!j->path) + char *t; + + t = strdup(path); + if (!t) goto fail; + + if (flags & SD_JOURNAL_OS_ROOT) + j->prefix = t; + else + j->path = t; } j->files = ordered_hashmap_new(&string_hash_ops); @@ -1737,12 +1744,17 @@ fail: return NULL; } +#define OPEN_ALLOWED_FLAGS \ + (SD_JOURNAL_LOCAL_ONLY | \ + SD_JOURNAL_RUNTIME_ONLY | \ + SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER) + _public_ int sd_journal_open(sd_journal **ret, int flags) { sd_journal *j; int r; assert_return(ret, -EINVAL); - assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL); + assert_return((flags & ~OPEN_ALLOWED_FLAGS) == 0, -EINVAL); j = journal_new(flags, NULL); if (!j) @@ -1761,6 +1773,9 @@ fail: return r; } +#define OPEN_CONTAINER_ALLOWED_FLAGS \ + (SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM) + _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) { _cleanup_free_ char *root = NULL, *class = NULL; sd_journal *j; @@ -1772,7 +1787,7 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in assert_return(machine, -EINVAL); assert_return(ret, -EINVAL); - assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL); + assert_return((flags & ~OPEN_CONTAINER_ALLOWED_FLAGS) == 0, -EINVAL); assert_return(machine_name_is_valid(machine), -EINVAL); p = strjoina("/run/systemd/machines/", machine); @@ -1787,13 +1802,10 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in if (!streq_ptr(class, "container")) return -EIO; - j = journal_new(flags, NULL); + j = journal_new(flags, root); if (!j) return -ENOMEM; - j->prefix = root; - root = NULL; - r = add_search_paths(j); if (r < 0) goto fail; @@ -1806,13 +1818,17 @@ fail: return r; } +#define OPEN_DIRECTORY_ALLOWED_FLAGS \ + (SD_JOURNAL_OS_ROOT | \ + SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER ) + _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) { sd_journal *j; int r; assert_return(ret, -EINVAL); assert_return(path, -EINVAL); - assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL); + assert_return((flags & ~OPEN_DIRECTORY_ALLOWED_FLAGS) == 0, -EINVAL); j = journal_new(flags, path); if (!j) @@ -1861,6 +1877,10 @@ fail: return r; } +#define OPEN_DIRECTORY_FD_ALLOWED_FLAGS \ + (SD_JOURNAL_OS_ROOT | \ + SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER ) + _public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) { sd_journal *j; struct stat st; @@ -1868,7 +1888,7 @@ _public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) { assert_return(ret, -EINVAL); assert_return(fd >= 0, -EBADF); - assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL); + assert_return((flags & ~OPEN_DIRECTORY_FD_ALLOWED_FLAGS) == 0, -EINVAL); if (fstat(fd, &st) < 0) return -errno; diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c index 995bf56586..c5f36725dc 100644 --- a/src/libudev/libudev-device.c +++ b/src/libudev/libudev-device.c @@ -495,7 +495,7 @@ _public_ struct udev_device *udev_device_get_parent_with_subsystem_devtype(struc return NULL; } - /* then walk the chain of udev_device parents until the correspanding + /* then walk the chain of udev_device parents until the corresponding one is found */ while ((udev_device = udev_device_get_parent(udev_device))) { if (udev_device->device == parent) diff --git a/src/login/logind-action.c b/src/login/logind-action.c index 8ef48dbaa1..a950409254 100644 --- a/src/login/logind-action.c +++ b/src/login/logind-action.c @@ -85,7 +85,7 @@ int manager_handle_action( } /* If the key handling is inhibited, don't do anything */ - if (!ignore_inhibited && inhibit_key > 0) { + if (inhibit_key > 0) { if (manager_is_inhibited(m, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0, NULL)) { log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_key)); return 0; diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 63363035e7..e0e73b034d 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -26,6 +26,7 @@ #include "bus-common-errors.h" #include "bus-error.h" #include "bus-util.h" +#include "cgroup-util.h" #include "clean-ipc.h" #include "conf-parser.h" #include "escape.h" @@ -612,9 +613,14 @@ int user_finalize(User *u) { if (k < 0) r = k; - /* Clean SysV + POSIX IPC objects */ - if (u->manager->remove_ipc) { - k = clean_ipc(u->uid); + /* Clean SysV + POSIX IPC objects, but only if this is not a system user. Background: in many setups cronjobs + * are run in full PAM and thus logind sessions, even if the code run doesn't belong to actual users but to + * system components. Since enable RemoveIPC= globally for all users, we need to be a bit careful with such + * cases, as we shouldn't accidentally remove a system service's IPC objects while it is running, just because + * a cronjob running as the same user just finished. Hence: exclude system users generally from IPC clean-up, + * and do it only for normal users. */ + if (u->manager->remove_ipc && u->uid > SYSTEM_UID_MAX) { + k = clean_ipc_by_uid(u->uid); if (k < 0) r = k; } @@ -891,7 +897,17 @@ int config_parse_user_tasks_max( assert(rvalue); assert(data); - /* First, try to parse as percentage */ + if (isempty(rvalue)) { + *m = system_tasks_max_scale(DEFAULT_USER_TASKS_MAX_PERCENTAGE, 100U); + return 0; + } + + if (streq(rvalue, "infinity")) { + *m = CGROUP_LIMIT_MAX; + return 0; + } + + /* Try to parse as percentage */ r = parse_percent(rvalue); if (r >= 0) k = system_tasks_max_scale(r, 100U); diff --git a/src/login/logind.c b/src/login/logind.c index 5ce36d28c7..bbbf4aef57 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -38,6 +38,7 @@ #include "signal-util.h" #include "strv.h" #include "udev-util.h" +#include "cgroup-util.h" static void manager_free(Manager *m); @@ -62,7 +63,7 @@ static void manager_reset_config(Manager *m) { m->idle_action = HANDLE_IGNORE; m->runtime_dir_size = physical_memory_scale(10U, 100U); /* 10% */ - m->user_tasks_max = system_tasks_max_scale(33U, 100U); /* 33% */ + m->user_tasks_max = system_tasks_max_scale(DEFAULT_USER_TASKS_MAX_PERCENTAGE, 100U); /* 33% */ m->sessions_max = 8192; m->inhibitors_max = 8192; diff --git a/src/mount/Makefile b/src/mount/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/mount/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c new file mode 100644 index 0000000000..9076ba3b0b --- /dev/null +++ b/src/mount/mount-tool.c @@ -0,0 +1,1112 @@ +/*** + 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 <http://www.gnu.org/licenses/>. +***/ + +#include <getopt.h> + +#include "libudev.h" +#include "sd-bus.h" + +#include "bus-error.h" +#include "bus-unit-util.h" +#include "bus-util.h" +#include "escape.h" +#include "fstab-util.h" +#include "pager.h" +#include "parse-util.h" +#include "path-util.h" +#include "spawn-polkit-agent.h" +#include "strv.h" +#include "udev-util.h" +#include "unit-name.h" +#include "terminal-util.h" + +enum { + ACTION_DEFAULT, + ACTION_MOUNT, + ACTION_AUTOMOUNT, + ACTION_LIST, +} arg_action = ACTION_DEFAULT; + +static bool arg_no_block = false; +static bool arg_no_pager = false; +static bool arg_ask_password = true; +static bool arg_quiet = false; +static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; +static bool arg_user = false; +static const char *arg_host = NULL; +static bool arg_discover = false; +static char *arg_mount_what = NULL; +static char *arg_mount_where = NULL; +static char *arg_mount_type = NULL; +static char *arg_mount_options = NULL; +static char *arg_description = NULL; +static char **arg_property = NULL; +static usec_t arg_timeout_idle = USEC_INFINITY; +static bool arg_timeout_idle_set = false; +static char **arg_automount_property = NULL; +static int arg_bind_device = -1; +static bool arg_fsck = true; + +static void polkit_agent_open_if_enabled(void) { + + /* Open the polkit agent as a child process if necessary */ + if (!arg_ask_password) + return; + + if (arg_transport != BUS_TRANSPORT_LOCAL) + return; + + polkit_agent_open(); +} + +static void help(void) { + printf("%s [OPTIONS...] WHAT [WHERE]\n\n" + "Establish a mount or auto-mount point transiently.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-block Do not wait until operation finished\n" + " --no-pager Do not pipe output into a pager\n" + " --no-ask-password Do not prompt for password\n" + " -q --quiet Suppress information messages during runtime\n" + " --user Run as user unit\n" + " -H --host=[USER@]HOST Operate on remote host\n" + " -M --machine=CONTAINER Operate on local container\n" + " --discover Discover mount device metadata\n" + " -t --type=TYPE File system type\n" + " -o --options=OPTIONS Mount options\n" + " --fsck=no Don't run file system check before mount\n" + " --description=TEXT Description for unit\n" + " -p --property=NAME=VALUE Set mount unit property\n" + " -A --automount=BOOL Create an auto-mount point\n" + " --timeout-idle-sec=SEC Specify automount idle timeout\n" + " --automount-property=NAME=VALUE\n" + " Set automount unit property\n" + " --bind-device Bind automount unit to device\n" + " --list List mountable block devices\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_BLOCK, + ARG_NO_PAGER, + ARG_NO_ASK_PASSWORD, + ARG_USER, + ARG_SYSTEM, + ARG_DISCOVER, + ARG_MOUNT_TYPE, + ARG_MOUNT_OPTIONS, + ARG_FSCK, + ARG_DESCRIPTION, + ARG_TIMEOUT_IDLE, + ARG_AUTOMOUNT, + ARG_AUTOMOUNT_PROPERTY, + ARG_BIND_DEVICE, + ARG_LIST, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "no-block", no_argument, NULL, ARG_NO_BLOCK }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { "quiet", no_argument, NULL, 'q' }, + { "user", no_argument, NULL, ARG_USER }, + { "system", no_argument, NULL, ARG_SYSTEM }, + { "host", required_argument, NULL, 'H' }, + { "machine", required_argument, NULL, 'M' }, + { "discover", no_argument, NULL, ARG_DISCOVER }, + { "type", required_argument, NULL, 't' }, + { "options", required_argument, NULL, 'o' }, + { "description", required_argument, NULL, ARG_DESCRIPTION }, + { "property", required_argument, NULL, 'p' }, + { "automount", required_argument, NULL, ARG_AUTOMOUNT }, + { "timeout-idle-sec", required_argument, NULL, ARG_TIMEOUT_IDLE }, + { "automount-property", required_argument, NULL, ARG_AUTOMOUNT_PROPERTY }, + { "bind-device", no_argument, NULL, ARG_BIND_DEVICE }, + { "list", no_argument, NULL, ARG_LIST }, + {}, + }; + + int r, c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hqH:M:t:o:p:A", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_NO_BLOCK: + arg_no_block = true; + break; + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case ARG_NO_ASK_PASSWORD: + arg_ask_password = false; + break; + + case 'q': + arg_quiet = true; + break; + + case ARG_USER: + arg_user = true; + break; + + case ARG_SYSTEM: + arg_user = false; + break; + + case 'H': + arg_transport = BUS_TRANSPORT_REMOTE; + arg_host = optarg; + break; + + case 'M': + arg_transport = BUS_TRANSPORT_MACHINE; + arg_host = optarg; + break; + + case ARG_DISCOVER: + arg_discover = true; + break; + + case 't': + if (free_and_strdup(&arg_mount_type, optarg) < 0) + return log_oom(); + break; + + case 'o': + if (free_and_strdup(&arg_mount_options, optarg) < 0) + return log_oom(); + break; + + case ARG_FSCK: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --fsck= argument: %s", optarg); + + arg_fsck = r; + break; + + case ARG_DESCRIPTION: + if (free_and_strdup(&arg_description, optarg) < 0) + return log_oom(); + break; + + case 'p': + if (strv_extend(&arg_property, optarg) < 0) + return log_oom(); + + break; + + case 'A': + arg_action = ACTION_AUTOMOUNT; + break; + + case ARG_AUTOMOUNT: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "--automount= expects a valid boolean parameter: %s", optarg); + + arg_action = r ? ACTION_AUTOMOUNT : ACTION_MOUNT; + break; + + case ARG_TIMEOUT_IDLE: + r = parse_sec(optarg, &arg_timeout_idle); + if (r < 0) + return log_error_errno(r, "Failed to parse timeout: %s", optarg); + + break; + + case ARG_AUTOMOUNT_PROPERTY: + if (strv_extend(&arg_automount_property, optarg) < 0) + return log_oom(); + + break; + + case ARG_BIND_DEVICE: + arg_bind_device = true; + break; + + case ARG_LIST: + arg_action = ACTION_LIST; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) { + log_error("Execution in user context is not supported on non-local systems."); + return -EINVAL; + } + + if (arg_action == ACTION_LIST) { + if (optind < argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + if (arg_transport != BUS_TRANSPORT_LOCAL) { + log_error("Listing devices only supported locally."); + return -EOPNOTSUPP; + } + } else { + if (optind >= argc) { + log_error("At least one argument required."); + return -EINVAL; + } + + if (argc > optind+2) { + log_error("At most two arguments required."); + return -EINVAL; + } + + arg_mount_what = fstab_node_to_udev_node(argv[optind]); + if (!arg_mount_what) + return log_oom(); + + if (argc > optind+1) { + r = path_make_absolute_cwd(argv[optind+1], &arg_mount_where); + if (r < 0) + return log_error_errno(r, "Failed to make path absolute: %m"); + } else + arg_discover = true; + + if (arg_discover && arg_transport != BUS_TRANSPORT_LOCAL) { + log_error("Automatic mount location discovery is only supported locally."); + return -EOPNOTSUPP; + } + } + + return 1; +} + +static int transient_unit_set_properties(sd_bus_message *m, char **properties) { + int r; + + r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description); + if (r < 0) + return r; + + if (arg_bind_device && is_device_path(arg_mount_what)) { + _cleanup_free_ char *device_unit = NULL; + + r = unit_name_from_path(arg_mount_what, ".device", &device_unit); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "(sv)(sv)", + "After", "as", 1, device_unit, + "BindsTo", "as", 1, device_unit); + if (r < 0) + return r; + } + + r = bus_append_unit_property_assignment_many(m, properties); + if (r < 0) + return r; + + return 0; +} + +static int transient_mount_set_properties(sd_bus_message *m) { + int r; + + assert(m); + + r = transient_unit_set_properties(m, arg_property); + if (r < 0) + return r; + + if (arg_mount_what) { + r = sd_bus_message_append(m, "(sv)", "What", "s", arg_mount_what); + if (r < 0) + return r; + } + + if (arg_mount_type) { + r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_mount_type); + if (r < 0) + return r; + } + + if (arg_mount_options) { + r = sd_bus_message_append(m, "(sv)", "Options", "s", arg_mount_options); + if (r < 0) + return r; + } + + if (arg_fsck) { + _cleanup_free_ char *fsck = NULL; + + r = unit_name_from_path_instance("systemd-fsck", arg_mount_what, ".service", &fsck); + if (r < 0) + return r; + + r = sd_bus_message_append(m, + "(sv)(sv)", + "Requires", "as", 1, fsck, + "After", "as", 1, fsck); + if (r < 0) + return r; + } + + return 0; +} + +static int transient_automount_set_properties(sd_bus_message *m) { + int r; + + assert(m); + + r = transient_unit_set_properties(m, arg_automount_property); + if (r < 0) + return r; + + if (arg_timeout_idle != USEC_INFINITY) { + r = sd_bus_message_append(m, "(sv)", "TimeoutIdleUSec", "t", arg_timeout_idle); + if (r < 0) + return r; + } + + return 0; +} + +static int start_transient_mount( + sd_bus *bus, + char **argv) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; + _cleanup_free_ char *mount_unit = NULL; + int r; + + if (!arg_no_block) { + r = bus_wait_for_jobs_new(bus, &w); + if (r < 0) + return log_error_errno(r, "Could not watch jobs: %m"); + } + + r = unit_name_from_path(arg_mount_where, ".mount", &mount_unit); + if (r < 0) + return log_error_errno(r, "Failed to make mount unit name: %m"); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); + if (r < 0) + return bus_log_create_error(r); + + /* Name and mode */ + r = sd_bus_message_append(m, "ss", mount_unit, "fail"); + if (r < 0) + return bus_log_create_error(r); + + /* Properties */ + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return bus_log_create_error(r); + + r = transient_mount_set_properties(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + /* Auxiliary units */ + r = sd_bus_message_append(m, "a(sa(sv))", 0); + if (r < 0) + return bus_log_create_error(r); + + polkit_agent_open_if_enabled(); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) + return log_error_errno(r, "Failed to start transient mount unit: %s", bus_error_message(&error, r)); + + if (w) { + const char *object; + + r = sd_bus_message_read(reply, "o", &object); + if (r < 0) + return bus_log_parse_error(r); + + r = bus_wait_for_jobs_one(w, object, arg_quiet); + if (r < 0) + return r; + } + + if (!arg_quiet) + log_info("Started unit %s%s%s for mount point: %s%s%s", + ansi_highlight(), mount_unit, ansi_normal(), + ansi_highlight(), arg_mount_where, ansi_normal()); + + return 0; +} + +static int start_transient_automount( + sd_bus *bus, + char **argv) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; + _cleanup_free_ char *automount_unit = NULL, *mount_unit = NULL; + int r; + + if (!arg_no_block) { + r = bus_wait_for_jobs_new(bus, &w); + if (r < 0) + return log_error_errno(r, "Could not watch jobs: %m"); + } + + r = unit_name_from_path(arg_mount_where, ".automount", &automount_unit); + if (r < 0) + return log_error_errno(r, "Failed to make automount unit name: %m"); + + r = unit_name_from_path(arg_mount_where, ".mount", &mount_unit); + if (r < 0) + return log_error_errno(r, "Failed to make mount unit name: %m"); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); + if (r < 0) + return bus_log_create_error(r); + + /* Name and mode */ + r = sd_bus_message_append(m, "ss", automount_unit, "fail"); + if (r < 0) + return bus_log_create_error(r); + + /* Properties */ + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return bus_log_create_error(r); + + r = transient_automount_set_properties(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + /* Auxiliary units */ + r = sd_bus_message_open_container(m, 'a', "(sa(sv))"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'r', "sa(sv)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "s", mount_unit); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return bus_log_create_error(r); + + r = transient_mount_set_properties(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + polkit_agent_open_if_enabled(); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) + return log_error_errno(r, "Failed to start transient automount unit: %s", bus_error_message(&error, r)); + + if (w) { + const char *object; + + r = sd_bus_message_read(reply, "o", &object); + if (r < 0) + return bus_log_parse_error(r); + + r = bus_wait_for_jobs_one(w, object, arg_quiet); + if (r < 0) + return r; + } + + if (!arg_quiet) + log_info("Started unit %s%s%s for mount point: %s%s%s", + ansi_highlight(), automount_unit, ansi_normal(), + ansi_highlight(), arg_mount_where, ansi_normal()); + + return 0; +} + +static int acquire_mount_type(struct udev_device *d) { + const char *v; + + assert(d); + + if (arg_mount_type) + return 0; + + v = udev_device_get_property_value(d, "ID_FS_TYPE"); + if (isempty(v)) + return 0; + + arg_mount_type = strdup(v); + if (!arg_mount_type) + return log_oom(); + + log_debug("Discovered type=%s", arg_mount_type); + return 1; +} + +static int acquire_mount_options(struct udev_device *d) { + const char *v; + + if (arg_mount_options) + return 0; + + v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_OPTIONS"); + if (isempty(v)) + return 0; + + arg_mount_options = strdup(v); + if (!arg_mount_options) + return log_oom(); + + log_debug("Discovered options=%s", arg_mount_options); + return 1; +} + +static const char *get_model(struct udev_device *d) { + const char *model; + + assert(d); + + model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE"); + if (model) + return model; + + return udev_device_get_property_value(d, "ID_MODEL"); +} + +static const char* get_label(struct udev_device *d) { + const char *label; + + assert(d); + + label = udev_device_get_property_value(d, "ID_FS_LABEL"); + if (label) + return label; + + return udev_device_get_property_value(d, "ID_PART_ENTRY_NAME"); +} + +static int acquire_mount_where(struct udev_device *d) { + const char *v; + + if (arg_mount_where) + return 0; + + v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE"); + if (isempty(v)) { + _cleanup_free_ char *escaped = NULL; + const char *name; + + name = get_label(d); + if (!name) + name = get_model(d); + if (!name) { + const char *dn; + + dn = udev_device_get_devnode(d); + if (!dn) + return 0; + + name = basename(dn); + } + + escaped = xescape(name, "\\"); + if (!filename_is_valid(escaped)) + return 0; + + arg_mount_where = strjoin("/run/media/system/", escaped, NULL); + } else + arg_mount_where = strdup(v); + + if (!arg_mount_where) + return log_oom(); + + log_debug("Discovered where=%s", arg_mount_where); + return 1; +} + +static int acquire_description(struct udev_device *d) { + const char *model, *label; + + if (arg_description) + return 0; + + model = get_model(d); + + label = get_label(d); + if (!label) + label = udev_device_get_property_value(d, "ID_PART_ENTRY_NUMBER"); + + if (model && label) + arg_description = strjoin(model, " ", label, NULL); + else if (label) + arg_description = strdup(label); + else if (model) + arg_description = strdup(model); + else + return NULL; + + if (!arg_description) + return log_oom(); + + log_debug("Discovered description=%s", arg_description); + return 1; +} + +static int acquire_removable(struct udev_device *d) { + const char *v; + + /* Shortcut this if there's no reason to check it */ + if (arg_action != ACTION_DEFAULT && arg_timeout_idle_set && arg_bind_device >= 0) + return 0; + + for (;;) { + v = udev_device_get_sysattr_value(d, "removable"); + if (v) + break; + + d = udev_device_get_parent(d); + if (!d) + return 0; + + if (!streq_ptr(udev_device_get_subsystem(d), "block")) + return 0; + } + + if (parse_boolean(v) <= 0) + return 0; + + log_debug("Discovered removable device."); + + if (arg_action == ACTION_DEFAULT) { + log_debug("Automatically turning on automount."); + arg_action = ACTION_AUTOMOUNT; + } + + if (!arg_timeout_idle_set) { + log_debug("Setting idle timeout to 1s."); + arg_timeout_idle = USEC_PER_SEC; + } + + if (arg_bind_device < 0) { + log_debug("Binding automount unit to device."); + arg_bind_device = true; + } + + return 1; +} + +static int discover_device(void) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + _cleanup_udev_unref_ struct udev *udev = NULL; + struct stat st; + const char *v; + int r; + + if (!arg_discover) + return 0; + + if (!is_device_path(arg_mount_what)) { + log_error("Discovery only supported for block devices, don't know what to do."); + return -EINVAL; + } + + if (stat(arg_mount_what, &st) < 0) + return log_error_errno(errno, "Can't stat %s: %m", arg_mount_what); + + if (!S_ISBLK(st.st_mode)) { + log_error("Path %s is not a block device, don't know what to do.", arg_mount_what); + return -ENOTBLK; + } + + udev = udev_new(); + if (!udev) + return log_oom(); + + d = udev_device_new_from_devnum(udev, 'b', st.st_rdev); + if (!d) + return log_oom(); + + v = udev_device_get_property_value(d, "ID_FS_USAGE"); + if (!streq_ptr(v, "filesystem")) { + log_error("%s does not contain a file system.", arg_mount_what); + return -EINVAL; + } + + r = acquire_mount_type(d); + if (r < 0) + return r; + + r = acquire_mount_options(d); + if (r < 0) + return r; + + r = acquire_mount_where(d); + if (r < 0) + return r; + + r = acquire_description(d); + if (r < 0) + return r; + + r = acquire_removable(d); + if (r < 0) + return r; + + return 0; +} + +enum { + COLUMN_NODE, + COLUMN_PATH, + COLUMN_MODEL, + COLUMN_WWN, + COLUMN_FSTYPE, + COLUMN_LABEL, + COLUMN_UUID, + _COLUMN_MAX, +}; + +struct item { + char* columns[_COLUMN_MAX]; +}; + +static int compare_item(const void *a, const void *b) { + const struct item *x = a, *y = b; + + if (x->columns[COLUMN_NODE] == y->columns[COLUMN_NODE]) + return 0; + if (!x->columns[COLUMN_NODE]) + return 1; + if (!y->columns[COLUMN_NODE]) + return -1; + + return path_compare(x->columns[COLUMN_NODE], y->columns[COLUMN_NODE]); +} + +static int list_devices(void) { + + static const char * const titles[_COLUMN_MAX] = { + [COLUMN_NODE] = "NODE", + [COLUMN_PATH] = "PATH", + [COLUMN_MODEL] = "MODEL", + [COLUMN_WWN] = "WWN", + [COLUMN_FSTYPE] = "TYPE", + [COLUMN_LABEL] = "LABEL", + [COLUMN_UUID] = "UUID" + }; + + _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + _cleanup_udev_unref_ struct udev *udev = NULL; + struct udev_list_entry *item = NULL, *first = NULL; + size_t n_allocated = 0, n = 0, i; + size_t column_width[_COLUMN_MAX]; + struct item *items = NULL; + unsigned c; + int r; + + for (c = 0; c < _COLUMN_MAX; c++) + column_width[c] = strlen(titles[c]); + + udev = udev_new(); + if (!udev) + return log_oom(); + + e = udev_enumerate_new(udev); + if (!e) + return log_oom(); + + r = udev_enumerate_add_match_subsystem(e, "block"); + if (r < 0) + return log_error_errno(r, "Failed to add block match: %m"); + + r = udev_enumerate_add_match_property(e, "ID_FS_USAGE", "filesystem"); + if (r < 0) + return log_error_errno(r, "Failed to add property match: %m"); + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return log_error_errno(r, "Failed to scan devices: %m"); + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + _cleanup_udev_device_unref_ struct udev_device *d; + struct item *j; + + d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); + if (!d) { + r = log_oom(); + goto finish; + } + + if (!GREEDY_REALLOC0(items, n_allocated, n+1)) { + r = log_oom(); + goto finish; + } + + j = items + n++; + + for (c = 0; c < _COLUMN_MAX; c++) { + const char *x; + size_t k; + + switch (c) { + + case COLUMN_NODE: + x = udev_device_get_devnode(d); + break; + + case COLUMN_PATH: + x = udev_device_get_property_value(d, "ID_PATH"); + break; + + case COLUMN_MODEL: + x = get_model(d); + break; + + case COLUMN_WWN: + x = udev_device_get_property_value(d, "ID_WWN"); + break; + + case COLUMN_FSTYPE: + x = udev_device_get_property_value(d, "ID_FS_TYPE"); + break; + + case COLUMN_LABEL: + x = get_label(d); + break; + + case COLUMN_UUID: + x = udev_device_get_property_value(d, "ID_FS_UUID"); + break; + } + + if (isempty(x)) + continue; + + j->columns[c] = strdup(x); + if (!j->columns[c]) { + r = log_oom(); + goto finish; + } + + k = strlen(x); + if (k > column_width[c]) + column_width[c] = k; + } + } + + if (n == 0) { + log_info("No devices found."); + goto finish; + } + + qsort_safe(items, n, sizeof(struct item), compare_item); + + pager_open(arg_no_pager, false); + + fputs(ansi_underline(), stdout); + for (c = 0; c < _COLUMN_MAX; c++) { + if (c > 0) + fputc(' ', stdout); + + printf("%-*s", (int) column_width[c], titles[c]); + } + fputs(ansi_normal(), stdout); + fputc('\n', stdout); + + for (i = 0; i < n; i++) { + for (c = 0; c < _COLUMN_MAX; c++) { + if (c > 0) + fputc(' ', stdout); + + printf("%-*s", (int) column_width[c], strna(items[i].columns[c])); + } + fputc('\n', stdout); + } + + r = 0; + +finish: + for (i = 0; i < n; i++) + for (c = 0; c < _COLUMN_MAX; c++) + free(items[i].columns[c]); + + free(items); + return r; +} + +int main(int argc, char* argv[]) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int r; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + if (arg_action == ACTION_LIST) { + r = list_devices(); + goto finish; + } + + r = discover_device(); + if (r < 0) + goto finish; + if (!arg_mount_where) { + log_error("Can't figure out where to mount %s.", arg_mount_what); + r = -EINVAL; + goto finish; + } + + path_kill_slashes(arg_mount_where); + + if (path_equal(arg_mount_where, "/")) { + log_error("Refusing to operate on root directory."); + r = -EINVAL; + goto finish; + } + + if (!path_is_safe(arg_mount_where)) { + log_error("Path is contains unsafe components."); + r = -EINVAL; + goto finish; + } + + if (streq_ptr(arg_mount_type, "auto")) + arg_mount_type = mfree(arg_mount_type); + if (streq_ptr(arg_mount_options, "defaults")) + arg_mount_options = mfree(arg_mount_options); + + if (!is_device_path(arg_mount_what)) + arg_fsck = false; + + if (arg_fsck && arg_mount_type && arg_transport == BUS_TRANSPORT_LOCAL) { + r = fsck_exists(arg_mount_type); + if (r < 0) + log_warning_errno(r, "Couldn't determine whether fsck for %s exists, proceeding anyway.", arg_mount_type); + else if (r == 0) { + log_debug("Disabling file system check as fsck for %s doesn't exist.", arg_mount_type); + arg_fsck = false; /* fsck doesn't exist, let's not attempt it */ + } + } + + r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus); + if (r < 0) { + log_error_errno(r, "Failed to create bus connection: %m"); + goto finish; + } + + switch (arg_action) { + + case ACTION_MOUNT: + case ACTION_DEFAULT: + r = start_transient_mount(bus, argv + optind); + break; + + case ACTION_AUTOMOUNT: + r = start_transient_automount(bus, argv + optind); + break; + + default: + assert_not_reached("Unexpected action."); + } + +finish: + bus = sd_bus_flush_close_unref(bus); + + pager_close(); + + free(arg_mount_what); + free(arg_mount_where); + free(arg_mount_type); + free(arg_mount_options); + free(arg_description); + strv_free(arg_property); + strv_free(arg_automount_property); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index cedaf47cf8..b197a5da6b 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -41,7 +41,7 @@ int route_new(Route **ret) { route->family = AF_UNSPEC; route->scope = RT_SCOPE_UNIVERSE; route->protocol = RTPROT_UNSPEC; - route->table = RT_TABLE_DEFAULT; + route->table = RT_TABLE_MAIN; route->lifetime = USEC_INFINITY; *ret = route; @@ -322,7 +322,8 @@ int route_add( } else return r; - *ret = route; + if (ret) + *ret = route; return 0; } @@ -440,20 +441,14 @@ static int route_expire_callback(sd_netlink *rtnl, sd_netlink_message *m, void * assert(m); assert(link); assert(link->ifname); - assert(link->link_messages > 0); if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; - link->link_messages--; - r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) log_link_warning_errno(link, r, "could not remove route: %m"); - if (link->link_messages == 0) - log_link_debug(link, "route removed"); - return 1; } @@ -466,11 +461,8 @@ int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) { r = route_remove(route, route->link, route_expire_callback); if (r < 0) log_warning_errno(r, "Could not remove route: %m"); - else { - /* route may not be exist in kernel. If we fail still remove it */ - route->link->link_messages++; + else route_free(route); - } return 1; } @@ -557,14 +549,12 @@ int route_configure( if (r < 0) return log_error_errno(r, "Could not set flags: %m"); - if (route->table != RT_TABLE_DEFAULT) { - + if (route->table != RT_TABLE_MAIN) { if (route->table < 256) { r = sd_rtnl_message_route_set_table(req, route->table); if (r < 0) return log_error_errno(r, "Could not set route table: %m"); } else { - r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC); if (r < 0) return log_error_errno(r, "Could not set route table: %m"); diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c index e5b76a0c5d..06c56d9ec8 100644 --- a/src/nspawn/nspawn-register.c +++ b/src/nspawn/nspawn-register.c @@ -68,7 +68,6 @@ int register_machine( local_ifindex > 0 ? 1 : 0, local_ifindex); } else { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - char **i; unsigned j; r = sd_bus_message_new_method_call( @@ -157,11 +156,9 @@ int register_machine( return bus_log_create_error(r); } - STRV_FOREACH(i, properties) { - r = bus_append_unit_property_assignment(m, *i); - if (r < 0) - return r; - } + r = bus_append_unit_property_assignment_many(m, properties); + if (r < 0) + return r; r = sd_bus_message_close_container(m); if (r < 0) diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c index 8d57b26cbc..895f61c462 100644 --- a/src/nss-mymachines/nss-mymachines.c +++ b/src/nss-mymachines/nss-mymachines.c @@ -25,6 +25,7 @@ #include "alloc-util.h" #include "bus-common-errors.h" +#include "env-util.h" #include "hostname-util.h" #include "in-addr-util.h" #include "macro.h" @@ -434,6 +435,12 @@ enum nss_status _nss_mymachines_getpwnam_r( if (!machine_name_is_valid(machine)) goto not_found; + if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) + /* Make sure we can't deadlock if we are invoked by dbus-daemon. This way, it won't be able to resolve + * these UIDs, but that should be unproblematic as containers should never be able to connect to a bus + * running on the host. */ + goto not_found; + r = sd_bus_open_system(&bus); if (r < 0) goto fail; @@ -514,6 +521,9 @@ enum nss_status _nss_mymachines_getpwuid_r( if (uid < HOST_UID_LIMIT) goto not_found; + if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) + goto not_found; + r = sd_bus_open_system(&bus); if (r < 0) goto fail; @@ -605,6 +615,9 @@ enum nss_status _nss_mymachines_getgrnam_r( if (!machine_name_is_valid(machine)) goto not_found; + if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) + goto not_found; + r = sd_bus_open_system(&bus); if (r < 0) goto fail; @@ -682,6 +695,9 @@ enum nss_status _nss_mymachines_getgrgid_r( if (gid < HOST_GID_LIMIT) goto not_found; + if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) + goto not_found; + r = sd_bus_open_system(&bus); if (r < 0) goto fail; diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index 7078c0c50c..17d04e958d 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -21,11 +21,14 @@ #include "sd-bus.h" +#include "alloc-util.h" #include "bus-common-errors.h" #include "env-util.h" +#include "fs-util.h" #include "macro.h" #include "nss-util.h" #include "signal-util.h" +#include "stdio-util.h" #include "string-util.h" #include "user-util.h" #include "util.h" @@ -75,15 +78,50 @@ static const struct group nobody_group = { NSS_GETPW_PROTOTYPES(systemd); NSS_GETGR_PROTOTYPES(systemd); +static int direct_lookup_name(const char *name, uid_t *ret) { + _cleanup_free_ char *s = NULL; + const char *path; + int r; + + assert(name); + + /* Normally, we go via the bus to resolve names. That has the benefit that it is available from any mount + * namespace and subject to proper authentication. However, there's one problem: if our module is called from + * dbus-daemon itself we really can't use D-Bus to communicate. In this case, resort to a client-side hack, + * and look for the dynamic names directly. This is pretty ugly, but breaks the cyclic dependency. */ + + path = strjoina("/run/systemd/dynamic-uid/direct:", name); + r = readlink_malloc(path, &s); + if (r < 0) + return r; + + return parse_uid(s, ret); +} + +static int direct_lookup_uid(uid_t uid, char **ret) { + char path[strlen("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s; + int r; + + xsprintf(path, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid); + + r = readlink_malloc(path, &s); + if (r < 0) + return r; + if (!valid_user_group_name(s)) { /* extra safety check */ + free(s); + return -EINVAL; + } + + *ret = s; + return 0; +} + 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; @@ -114,30 +152,45 @@ enum nss_status _nss_systemd_getpwnam_r( if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) goto not_found; - r = sd_bus_open_system(&bus); - if (r < 0) - goto fail; + if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) { - 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)) + /* Access the dynamic UID allocation directly if we are called from dbus-daemon, see above. */ + r = direct_lookup_name(name, (uid_t*) &translated); + if (r == -ENOENT) goto not_found; - - goto fail; + if (r < 0) + goto fail; + + } else { + _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; + + 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; } - r = sd_bus_message_read(reply, "u", &translated); - if (r < 0) - goto fail; - l = strlen(name); if (buflen < l+1) { *errnop = ENOMEM; @@ -175,6 +228,7 @@ enum nss_status _nss_systemd_getpwuid_r( _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; + _cleanup_free_ char *direct = NULL; const char *translated; size_t l; int r; @@ -204,30 +258,42 @@ enum nss_status _nss_systemd_getpwuid_r( if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) goto not_found; - r = sd_bus_open_system(&bus); - if (r < 0) - goto fail; + if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) { - 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)) + r = direct_lookup_uid(uid, &direct); + if (r == -ENOENT) goto not_found; - - goto fail; + if (r < 0) + goto fail; + + translated = direct; + + } else { + 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; } - r = sd_bus_message_read(reply, "s", &translated); - if (r < 0) - goto fail; - l = strlen(translated) + 1; if (buflen < l) { *errnop = ENOMEM; @@ -262,9 +328,6 @@ enum nss_status _nss_systemd_getgrnam_r( 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; @@ -294,30 +357,45 @@ enum nss_status _nss_systemd_getgrnam_r( if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) goto not_found; - r = sd_bus_open_system(&bus); - if (r < 0) - goto fail; + if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) { - 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)) + /* Access the dynamic GID allocation directly if we are called from dbus-daemon, see above. */ + r = direct_lookup_name(name, (uid_t*) &translated); + if (r == -ENOENT) goto not_found; - - goto fail; + if (r < 0) + goto fail; + } else { + + _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; + + 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; } - r = sd_bus_message_read(reply, "u", &translated); - if (r < 0) - goto fail; - l = sizeof(char*) + strlen(name) + 1; if (buflen < l) { *errnop = ENOMEM; @@ -353,6 +431,7 @@ enum nss_status _nss_systemd_getgrgid_r( _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; + _cleanup_free_ char *direct = NULL; const char *translated; size_t l; int r; @@ -382,30 +461,41 @@ enum nss_status _nss_systemd_getgrgid_r( if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) goto not_found; - r = sd_bus_open_system(&bus); - if (r < 0) - goto fail; + if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) { - 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)) + r = direct_lookup_uid(gid, &direct); + if (r == -ENOENT) goto not_found; - - goto fail; + if (r < 0) + goto fail; + + translated = direct; + } else { + 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; } - r = sd_bus_message_read(reply, "s", &translated); - if (r < 0) - goto fail; - l = sizeof(char*) + strlen(translated) + 1; if (buflen < l) { *errnop = ENOMEM; diff --git a/src/run/run.c b/src/run/run.c index 1917ffd857..0797547684 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -83,9 +83,7 @@ static void polkit_agent_open_if_enabled(void) { static void help(void) { printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n" - "Run the specified command in a transient scope or service or timer\n" - "unit. If a timer option is specified and the unit specified with\n" - "the --unit option exists, the command can be omitted.\n\n" + "Run the specified command in a transient scope or service.\n\n" " -h --help Show this help\n" " --version Show package version\n" " --no-ask-password Do not prompt for password\n" @@ -94,7 +92,7 @@ static void help(void) { " -M --machine=CONTAINER Operate on local container\n" " --scope Run this as scope rather than service\n" " --unit=UNIT Run under the specified unit name\n" - " -p --property=NAME=VALUE Set unit property\n" + " -p --property=NAME=VALUE Set service or scope unit property\n" " --description=TEXT Description for unit\n" " --slice=SLICE Run in the specified slice\n" " --no-block Do not wait until operation finished\n" @@ -107,15 +105,15 @@ static void help(void) { " -E --setenv=NAME=VALUE Set environment\n" " -t --pty Run service on pseudo tty\n" " -q --quiet Suppress information messages during runtime\n\n" - "Timer options:\n\n" + "Timer options:\n" " --on-active=SECONDS Run after SECONDS delay\n" " --on-boot=SECONDS Run SECONDS after machine was booted up\n" " --on-startup=SECONDS Run SECONDS after systemd activation\n" " --on-unit-active=SECONDS Run SECONDS after the last activation\n" " --on-unit-inactive=SECONDS Run SECONDS after the last deactivation\n" " --on-calendar=SPEC Realtime timer\n" - " --timer-property=NAME=VALUE Set timer unit property\n", - program_invocation_short_name); + " --timer-property=NAME=VALUE Set timer unit property\n" + , program_invocation_short_name); } static bool with_timer(void) { @@ -178,7 +176,7 @@ static int parse_argv(int argc, char *argv[]) { { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR }, { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY }, { "no-block", no_argument, NULL, ARG_NO_BLOCK }, - { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, {}, }; @@ -195,13 +193,13 @@ static int parse_argv(int argc, char *argv[]) { help(); return 0; + case ARG_VERSION: + return version(); + case ARG_NO_ASK_PASSWORD: arg_ask_password = false; break; - case ARG_VERSION: - return version(); - case ARG_USER: arg_user = true; break; @@ -410,18 +408,15 @@ static int parse_argv(int argc, char *argv[]) { } static int transient_unit_set_properties(sd_bus_message *m, char **properties) { - char **i; int r; r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description); if (r < 0) return r; - STRV_FOREACH(i, properties) { - r = bus_append_unit_property_assignment(m, *i); - if (r < 0) - return r; - } + r = bus_append_unit_property_assignment_many(m, properties); + if (r < 0) + return r; return 0; } @@ -1144,7 +1139,7 @@ static int start_transient_timer( if (r < 0) return bus_log_create_error(r); - if (argv[0]) { + if (!strv_isempty(argv)) { r = sd_bus_message_open_container(m, 'r', "sa(sv)"); if (r < 0) return bus_log_create_error(r); diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index f9e12e0578..feb4a06737 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -204,7 +204,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges", "SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute", - "RestrictRealtime", "DynamicUser")) { + "RestrictRealtime", "DynamicUser", "RemoveIPC")) { r = parse_boolean(eq); if (r < 0) @@ -568,6 +568,21 @@ finish: return 0; } +int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l) { + char **i; + int r; + + assert(m); + + STRV_FOREACH(i, l) { + r = bus_append_unit_property_assignment(m, *i); + if (r < 0) + return r; + } + + return 0; +} + typedef struct BusWaitForJobs { sd_bus *bus; Set *jobs; diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h index c0c172f336..d102ea180e 100644 --- a/src/shared/bus-unit-util.h +++ b/src/shared/bus-unit-util.h @@ -41,6 +41,7 @@ typedef struct UnitInfo { int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u); int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment); +int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l); typedef struct BusWaitForJobs BusWaitForJobs; diff --git a/src/shared/clean-ipc.c b/src/shared/clean-ipc.c index a3ac7aeb82..d5db604f03 100644 --- a/src/shared/clean-ipc.c +++ b/src/shared/clean-ipc.c @@ -41,8 +41,20 @@ #include "macro.h" #include "string-util.h" #include "strv.h" +#include "user-util.h" -static int clean_sysvipc_shm(uid_t delete_uid) { +static bool match_uid_gid(uid_t subject_uid, gid_t subject_gid, uid_t delete_uid, gid_t delete_gid) { + + if (uid_is_valid(delete_uid) && subject_uid == delete_uid) + return true; + + if (gid_is_valid(delete_gid) && subject_gid == delete_gid) + return true; + + return false; +} + +static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid) { _cleanup_fclose_ FILE *f = NULL; char line[LINE_MAX]; bool first = true; @@ -77,7 +89,7 @@ static int clean_sysvipc_shm(uid_t delete_uid) { if (n_attached > 0) continue; - if (uid != delete_uid) + if (!match_uid_gid(uid, gid, delete_uid, delete_gid)) continue; if (shmctl(shmid, IPC_RMID, NULL) < 0) { @@ -89,7 +101,8 @@ static int clean_sysvipc_shm(uid_t delete_uid) { ret = log_warning_errno(errno, "Failed to remove SysV shared memory segment %i: %m", shmid); - } + } else + log_debug("Removed SysV shared memory segment %i.", shmid); } return ret; @@ -98,7 +111,7 @@ fail: return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m"); } -static int clean_sysvipc_sem(uid_t delete_uid) { +static int clean_sysvipc_sem(uid_t delete_uid, gid_t delete_gid) { _cleanup_fclose_ FILE *f = NULL; char line[LINE_MAX]; bool first = true; @@ -128,7 +141,7 @@ static int clean_sysvipc_sem(uid_t delete_uid) { &semid, &uid, &gid, &cuid, &cgid) != 5) continue; - if (uid != delete_uid) + if (!match_uid_gid(uid, gid, delete_uid, delete_gid)) continue; if (semctl(semid, 0, IPC_RMID) < 0) { @@ -140,7 +153,8 @@ static int clean_sysvipc_sem(uid_t delete_uid) { ret = log_warning_errno(errno, "Failed to remove SysV semaphores object %i: %m", semid); - } + } else + log_debug("Removed SysV semaphore %i.", semid); } return ret; @@ -149,7 +163,7 @@ fail: return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m"); } -static int clean_sysvipc_msg(uid_t delete_uid) { +static int clean_sysvipc_msg(uid_t delete_uid, gid_t delete_gid) { _cleanup_fclose_ FILE *f = NULL; char line[LINE_MAX]; bool first = true; @@ -180,7 +194,7 @@ static int clean_sysvipc_msg(uid_t delete_uid) { &msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7) continue; - if (uid != delete_uid) + if (!match_uid_gid(uid, gid, delete_uid, delete_gid)) continue; if (msgctl(msgid, IPC_RMID, NULL) < 0) { @@ -192,7 +206,8 @@ static int clean_sysvipc_msg(uid_t delete_uid) { ret = log_warning_errno(errno, "Failed to remove SysV message queue %i: %m", msgid); - } + } else + log_debug("Removed SysV message queue %i.", msgid); } return ret; @@ -201,13 +216,13 @@ fail: return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m"); } -static int clean_posix_shm_internal(DIR *dir, uid_t uid) { +static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid) { struct dirent *de; int ret = 0, r; assert(dir); - FOREACH_DIRENT(de, dir, goto fail) { + FOREACH_DIRENT_ALL(de, dir, goto fail) { struct stat st; if (STR_IN_SET(de->d_name, "..", ".")) @@ -217,12 +232,11 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) { if (errno == ENOENT) continue; - log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name); - ret = -errno; + ret = log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name); continue; } - if (st.st_uid != uid) + if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid)) continue; if (S_ISDIR(st.st_mode)) { @@ -230,12 +244,10 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) { kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME); if (!kid) { - if (errno != ENOENT) { - log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name); - ret = -errno; - } + if (errno != ENOENT) + ret = log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name); } else { - r = clean_posix_shm_internal(kid, uid); + r = clean_posix_shm_internal(kid, uid, gid); if (r < 0) ret = r; } @@ -245,9 +257,9 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) { if (errno == ENOENT) continue; - log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name); - ret = -errno; - } + ret = log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name); + } else + log_debug("Removed POSIX shared memory directory %s", de->d_name); } else { if (unlinkat(dirfd(dir), de->d_name, 0) < 0) { @@ -255,20 +267,19 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) { if (errno == ENOENT) continue; - log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name); - ret = -errno; - } + ret = log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name); + } else + log_debug("Removed POSIX shared memory segment %s", de->d_name); } } return ret; fail: - log_warning_errno(errno, "Failed to read /dev/shm: %m"); - return -errno; + return log_warning_errno(errno, "Failed to read /dev/shm: %m"); } -static int clean_posix_shm(uid_t uid) { +static int clean_posix_shm(uid_t uid, gid_t gid) { _cleanup_closedir_ DIR *dir = NULL; dir = opendir("/dev/shm"); @@ -279,10 +290,10 @@ static int clean_posix_shm(uid_t uid) { return log_warning_errno(errno, "Failed to open /dev/shm: %m"); } - return clean_posix_shm_internal(dir, uid); + return clean_posix_shm_internal(dir, uid, gid); } -static int clean_posix_mq(uid_t uid) { +static int clean_posix_mq(uid_t uid, gid_t gid) { _cleanup_closedir_ DIR *dir = NULL; struct dirent *de; int ret = 0; @@ -295,7 +306,7 @@ static int clean_posix_mq(uid_t uid) { return log_warning_errno(errno, "Failed to open /dev/mqueue: %m"); } - FOREACH_DIRENT(de, dir, goto fail) { + FOREACH_DIRENT_ALL(de, dir, goto fail) { struct stat st; char fn[1+strlen(de->d_name)+1]; @@ -312,7 +323,7 @@ static int clean_posix_mq(uid_t uid) { continue; } - if (st.st_uid != uid) + if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid)) continue; fn[0] = '/'; @@ -325,7 +336,8 @@ static int clean_posix_mq(uid_t uid) { ret = log_warning_errno(errno, "Failed to unlink POSIX message queue %s: %m", fn); - } + } else + log_debug("Removed POSIX message queue %s", fn); } return ret; @@ -334,32 +346,44 @@ fail: return log_warning_errno(errno, "Failed to read /dev/mqueue: %m"); } -int clean_ipc(uid_t uid) { +int clean_ipc(uid_t uid, gid_t gid) { int ret = 0, r; - /* Refuse to clean IPC of the root and system users */ - if (uid <= SYSTEM_UID_MAX) + /* Anything to do? */ + if (!uid_is_valid(uid) && !gid_is_valid(gid)) + return 0; + + /* Refuse to clean IPC of the root user */ + if (uid == 0 && gid == 0) return 0; - r = clean_sysvipc_shm(uid); + r = clean_sysvipc_shm(uid, gid); if (r < 0) ret = r; - r = clean_sysvipc_sem(uid); + r = clean_sysvipc_sem(uid, gid); if (r < 0) ret = r; - r = clean_sysvipc_msg(uid); + r = clean_sysvipc_msg(uid, gid); if (r < 0) ret = r; - r = clean_posix_shm(uid); + r = clean_posix_shm(uid, gid); if (r < 0) ret = r; - r = clean_posix_mq(uid); + r = clean_posix_mq(uid, gid); if (r < 0) ret = r; return ret; } + +int clean_ipc_by_uid(uid_t uid) { + return clean_ipc(uid, GID_INVALID); +} + +int clean_ipc_by_gid(gid_t gid) { + return clean_ipc(UID_INVALID, gid); +} diff --git a/src/shared/clean-ipc.h b/src/shared/clean-ipc.h index 44a83afcf7..6ca57f44fd 100644 --- a/src/shared/clean-ipc.h +++ b/src/shared/clean-ipc.h @@ -21,4 +21,6 @@ #include <sys/types.h> -int clean_ipc(uid_t uid); +int clean_ipc(uid_t uid, gid_t gid); +int clean_ipc_by_uid(uid_t uid); +int clean_ipc_by_gid(gid_t gid); diff --git a/src/shared/install.c b/src/shared/install.c index e740ef3910..6a16f8985b 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -393,19 +393,40 @@ 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, const char *new_path, bool force, UnitFileChange **changes, unsigned *n_changes) { - _cleanup_free_ char *dest = NULL; + _cleanup_free_ char *dest = NULL, *dirname = 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. @@ -436,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) { @@ -620,7 +645,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; @@ -903,6 +928,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 +1363,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 +1449,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, @@ -1479,7 +1511,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) @@ -1489,9 +1520,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; } @@ -1535,7 +1564,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) @@ -1550,9 +1578,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; } @@ -1569,7 +1595,6 @@ static int install_info_symlink_link( unsigned *n_changes) { _cleanup_free_ char *path = NULL; - const char *rp; int r; assert(i); @@ -1587,9 +1612,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( @@ -1663,6 +1686,17 @@ static int install_context_apply( if (r < 0) return r; + /* We can attempt to process a masked unit when a different unit + * that we were processing specifies it in DefaultInstance= or Also=. */ + if (i->type == UNIT_FILE_TYPE_MASKED) { + unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path, NULL); + if (r >= 0) + /* Assume that some *could* have been enabled here, avoid + * "empty [Install] section" warning. */ + r += 1; + continue; + } + if (i->type != UNIT_FILE_TYPE_REGULAR) continue; @@ -1765,7 +1799,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; } @@ -1925,14 +1959,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; } @@ -1967,7 +1999,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; @@ -2312,7 +2343,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); @@ -2335,10 +2366,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( @@ -2685,18 +2714,25 @@ static int preset_prepare_one( InstallContext *plus, InstallContext *minus, LookupPaths *paths, - UnitFilePresetMode mode, const char *name, Presets presets, 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) @@ -2748,7 +2784,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 +2845,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); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index b4ce6fba5a..4444e3064d 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -5093,7 +5093,6 @@ static int set_property(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *n = NULL; sd_bus *bus; - char **i; int r; r = acquire_bus(BUS_MANAGER, &bus); @@ -5124,11 +5123,9 @@ static int set_property(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_create_error(r); - STRV_FOREACH(i, strv_skip(argv, 2)) { - r = bus_append_unit_property_assignment(m, *i); - if (r < 0) - return r; - } + r = bus_append_unit_property_assignment_many(m, strv_skip(argv, 2)); + if (r < 0) + return r; r = sd_bus_message_close_container(m); if (r < 0) diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index 3ed8f23ff9..39821687b9 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -247,7 +247,7 @@ static char *sysv_translate_name(const char *name) { return res; } -static int sysv_translate_facility(const char *name, const char *filename, char **ret) { +static int sysv_translate_facility(SysvStub *s, unsigned line, const char *name, char **ret) { /* We silently ignore the $ prefix here. According to the LSB * spec it simply indicates whether something is a @@ -266,15 +266,18 @@ static int sysv_translate_facility(const char *name, const char *filename, char "time", SPECIAL_TIME_SYNC_TARGET, }; + const char *filename; char *filename_no_sh, *e, *m; const char *n; unsigned i; int r; assert(name); - assert(filename); + assert(s); assert(ret); + filename = basename(s->path); + n = *name == '$' ? name + 1 : name; for (i = 0; i < ELEMENTSOF(table); i += 2) { @@ -299,7 +302,7 @@ static int sysv_translate_facility(const char *name, const char *filename, char if (*name == '$') { r = unit_name_build(n, NULL, ".target", ret); if (r < 0) - return log_error_errno(r, "Failed to build name: %m"); + return log_error_errno(r, "[%s:%u] Could not build name for facility %s: %m", s->path, line, name); return r; } @@ -337,11 +340,11 @@ static int handle_provides(SysvStub *s, unsigned line, const char *full_text, co r = extract_first_word(&text, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); if (r < 0) - return log_error_errno(r, "Failed to parse word from provides string: %m"); + return log_error_errno(r, "[%s:%u] Failed to parse word from provides string: %m", s->path, line); if (r == 0) break; - r = sysv_translate_facility(word, basename(s->path), &m); + r = sysv_translate_facility(s, line, word, &m); if (r <= 0) /* continue on error */ continue; @@ -403,11 +406,11 @@ static int handle_dependencies(SysvStub *s, unsigned line, const char *full_text r = extract_first_word(&text, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); if (r < 0) - return log_error_errno(r, "Failed to parse word from provides string: %m"); + return log_error_errno(r, "[%s:%u] Failed to parse word from provides string: %m", s->path, line); if (r == 0) break; - r = sysv_translate_facility(word, basename(s->path), &m); + r = sysv_translate_facility(s, line, word, &m); if (r <= 0) /* continue on error */ continue; diff --git a/src/test/test-condition.c b/src/test/test-condition.c index 66003aa6bd..6f7d71ef9a 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -37,66 +37,82 @@ static void test_condition_test_path(void) { Condition *condition; condition = condition_new(CONDITION_PATH_EXISTS, "/bin/sh", false, false); + assert_se(condition); assert_se(condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_PATH_EXISTS, "/bin/s?", false, false); + assert_se(condition); assert_se(!condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, false); + assert_se(condition); assert_se(condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, true); + assert_se(condition); assert_se(!condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, false); + assert_se(condition); assert_se(!condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, true); + assert_se(condition); assert_se(condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_PATH_IS_DIRECTORY, "/bin", false, false); + assert_se(condition); assert_se(condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_DIRECTORY_NOT_EMPTY, "/bin", false, false); + assert_se(condition); assert_se(condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_FILE_NOT_EMPTY, "/bin/sh", false, false); + assert_se(condition); assert_se(condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/bin/sh", false, false); + assert_se(condition); assert_se(condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/etc/passwd", false, false); + assert_se(condition); assert_se(!condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/proc", false, false); + assert_se(condition); assert_se(condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/", false, false); + assert_se(condition); assert_se(condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/bin", false, false); + assert_se(condition); assert_se(!condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_PATH_IS_READ_WRITE, "/tmp", false, false); + assert_se(condition); assert_se(condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_PATH_IS_SYMBOLIC_LINK, "/dev/stdout", false, false); + assert_se(condition); assert_se(condition_test(condition)); condition_free(condition); } @@ -105,38 +121,44 @@ static void test_condition_test_ac_power(void) { Condition *condition; condition = condition_new(CONDITION_AC_POWER, "true", false, false); + assert_se(condition); assert_se(condition_test(condition) == on_ac_power()); condition_free(condition); condition = condition_new(CONDITION_AC_POWER, "false", false, false); + assert_se(condition); assert_se(condition_test(condition) != on_ac_power()); condition_free(condition); condition = condition_new(CONDITION_AC_POWER, "false", false, true); + assert_se(condition); assert_se(condition_test(condition) == on_ac_power()); condition_free(condition); } static void test_condition_test_host(void) { + _cleanup_free_ char *hostname = NULL; + char sid[SD_ID128_STRING_MAX]; Condition *condition; sd_id128_t id; int r; - char sid[SD_ID128_STRING_MAX]; - _cleanup_free_ char *hostname = NULL; r = sd_id128_get_machine(&id); assert_se(r >= 0); assert_se(sd_id128_to_string(id, sid)); condition = condition_new(CONDITION_HOST, sid, false, false); + assert_se(condition); assert_se(condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_HOST, "garbage value jjjjjjjjjjjjjj", false, false); + assert_se(condition); assert_se(!condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_HOST, sid, false, true); + assert_se(condition); assert_se(!condition_test(condition)); condition_free(condition); @@ -148,6 +170,7 @@ static void test_condition_test_host(void) { log_notice("hostname is an id128, skipping test"); else { condition = condition_new(CONDITION_HOST, hostname, false, false); + assert_se(condition); assert_se(condition_test(condition)); condition_free(condition); } @@ -165,14 +188,17 @@ static void test_condition_test_architecture(void) { assert_se(sa); condition = condition_new(CONDITION_ARCHITECTURE, sa, false, false); + assert_se(condition); assert_se(condition_test(condition) > 0); condition_free(condition); condition = condition_new(CONDITION_ARCHITECTURE, "garbage value", false, false); + assert_se(condition); assert_se(condition_test(condition) == 0); condition_free(condition); condition = condition_new(CONDITION_ARCHITECTURE, sa, false, true); + assert_se(condition); assert_se(condition_test(condition) == 0); condition_free(condition); } @@ -181,10 +207,12 @@ static void test_condition_test_kernel_command_line(void) { Condition *condition; condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "thisreallyshouldntbeonthekernelcommandline", false, false); + assert_se(condition); assert_se(!condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "andthis=neither", false, false); + assert_se(condition); assert_se(!condition_test(condition)); condition_free(condition); } @@ -193,10 +221,12 @@ static void test_condition_test_null(void) { Condition *condition; condition = condition_new(CONDITION_NULL, NULL, false, false); + assert_se(condition); assert_se(condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_NULL, NULL, false, true); + assert_se(condition); assert_se(!condition_test(condition)); condition_free(condition); } @@ -205,31 +235,36 @@ static void test_condition_test_security(void) { Condition *condition; condition = condition_new(CONDITION_SECURITY, "garbage oifdsjfoidsjoj", false, false); + assert_se(condition); assert_se(!condition_test(condition)); condition_free(condition); condition = condition_new(CONDITION_SECURITY, "selinux", false, true); + assert_se(condition); assert_se(condition_test(condition) != mac_selinux_have()); condition_free(condition); condition = condition_new(CONDITION_SECURITY, "ima", false, false); + assert_se(condition); assert_se(condition_test(condition) == use_ima()); condition_free(condition); condition = condition_new(CONDITION_SECURITY, "apparmor", false, false); + assert_se(condition); assert_se(condition_test(condition) == mac_apparmor_use()); condition_free(condition); condition = condition_new(CONDITION_SECURITY, "smack", false, false); + assert_se(condition); assert_se(condition_test(condition) == mac_smack_use()); condition_free(condition); condition = condition_new(CONDITION_SECURITY, "audit", false, false); + assert_se(condition); assert_se(condition_test(condition) == use_audit()); condition_free(condition); } - int main(int argc, char *argv[]) { log_parse_environment(); log_open(); diff --git a/src/test/test-hostname-util.c b/src/test/test-hostname-util.c index 17fde9f27e..1c3d13ed1d 100644 --- a/src/test/test-hostname-util.c +++ b/src/test/test-hostname-util.c @@ -42,6 +42,7 @@ static void test_hostname_is_valid(void) { assert_se(!hostname_is_valid("foo..bar", false)); assert_se(!hostname_is_valid("foo.bar..", false)); assert_se(!hostname_is_valid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", false)); + assert_se(!hostname_is_valid("au-xph5-rvgrdsb5hcxc-47et3a5vvkrc-server-wyoz4elpdpe3.openstack.local", false)); assert_se(hostname_is_valid("foobar", true)); assert_se(hostname_is_valid("foobar.com", true)); diff --git a/src/test/test-ipcrm.c b/src/test/test-ipcrm.c index c5bcaf47bb..551eba7215 100644 --- a/src/test/test-ipcrm.c +++ b/src/test/test-ipcrm.c @@ -32,5 +32,5 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - return clean_ipc(uid) < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + return clean_ipc_by_uid(uid) < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } |