diff options
Diffstat (limited to 'src')
107 files changed, 2044 insertions, 904 deletions
diff --git a/src/analyze/analyze-srk.c b/src/analyze/analyze-srk.c index 3138246225..0e24b416bb 100644 --- a/src/analyze/analyze-srk.c +++ b/src/analyze/analyze-srk.c @@ -2,6 +2,7 @@ #include "analyze.h" #include "analyze-srk.h" +#include "fileio.h" #include "tpm2-util.h" int verb_srk(int argc, char *argv[], void *userdata) { @@ -33,12 +34,15 @@ int verb_srk(int argc, char *argv[], void *userdata) { return log_error_errno(r, "Failed to marshal SRK: %m"); if (isatty(STDOUT_FILENO)) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Refusing to write binary data to TTY, please redirect output to file."); + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Refusing to write binary data to TTY, please redirect output to file."); if (fwrite(marshalled, 1, marshalled_size, stdout) != marshalled_size) - return log_error_errno(errno, "Failed to write SRK to stdout: %m"); + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write SRK to stdout: %m"); - fflush(stdout); + r = fflush_and_check(stdout); + if (r < 0) + return log_error_errno(r, "Failed to write SRK to stdout: %m"); return EXIT_SUCCESS; #else diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index 0690bcd830..9904e3e484 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -900,10 +900,7 @@ int fd_get_diskseq(int fd, uint64_t *ret) { } int path_is_root_at(int dir_fd, const char *path) { - STRUCT_NEW_STATX_DEFINE(st); - STRUCT_NEW_STATX_DEFINE(pst); - _cleanup_close_ int fd = -EBADF; - int r; + _cleanup_close_ int fd = -EBADF, pfd = -EBADF; assert(dir_fd >= 0 || dir_fd == AT_FDCWD); @@ -915,60 +912,74 @@ int path_is_root_at(int dir_fd, const char *path) { dir_fd = fd; } - r = statx_fallback(dir_fd, ".", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st.sx); - if (r == -ENOTDIR) - return false; + pfd = openat(dir_fd, "..", O_PATH|O_DIRECTORY|O_CLOEXEC); + if (pfd < 0) + return errno == ENOTDIR ? false : -errno; + + /* Even if the parent directory has the same inode, the fd may not point to the root directory "/", + * and we also need to check that the mount ids are the same. Otherwise, a construct like the + * following could be used to trick us: + * + * $ mkdir /tmp/x /tmp/x/y + * $ mount --bind /tmp/x /tmp/x/y + */ + + return fds_are_same_mount(dir_fd, pfd); +} + +int fds_are_same_mount(int fd1, int fd2) { + STRUCT_NEW_STATX_DEFINE(st1); + STRUCT_NEW_STATX_DEFINE(st2); + int r; + + assert(fd1 >= 0); + assert(fd2 >= 0); + + r = statx_fallback(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st1.sx); if (r < 0) return r; - r = statx_fallback(dir_fd, "..", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &pst.sx); + r = statx_fallback(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st2.sx); if (r < 0) return r; /* First, compare inode. If these are different, the fd does not point to the root directory "/". */ - if (!statx_inode_same(&st.sx, &pst.sx)) + if (!statx_inode_same(&st1.sx, &st2.sx)) return false; - /* Even if the parent directory has the same inode, the fd may not point to the root directory "/", - * and we also need to check that the mount ids are the same. Otherwise, a construct like the - * following could be used to trick us: - * - * $ mkdir /tmp/x /tmp/x/y - * $ mount --bind /tmp/x /tmp/x/y - * - * Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old + /* Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old * kernel is used. In that case, let's assume that we do not have such spurious mount points in an * early boot stage, and silently skip the following check. */ - if (!FLAGS_SET(st.nsx.stx_mask, STATX_MNT_ID)) { + if (!FLAGS_SET(st1.nsx.stx_mask, STATX_MNT_ID)) { int mntid; - r = path_get_mnt_id_at_fallback(dir_fd, "", &mntid); + r = path_get_mnt_id_at_fallback(fd1, "", &mntid); if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) return true; /* skip the mount ID check */ if (r < 0) return r; assert(mntid >= 0); - st.nsx.stx_mnt_id = mntid; - st.nsx.stx_mask |= STATX_MNT_ID; + st1.nsx.stx_mnt_id = mntid; + st1.nsx.stx_mask |= STATX_MNT_ID; } - if (!FLAGS_SET(pst.nsx.stx_mask, STATX_MNT_ID)) { + if (!FLAGS_SET(st2.nsx.stx_mask, STATX_MNT_ID)) { int mntid; - r = path_get_mnt_id_at_fallback(dir_fd, "..", &mntid); + r = path_get_mnt_id_at_fallback(fd2, "", &mntid); if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) return true; /* skip the mount ID check */ if (r < 0) return r; assert(mntid >= 0); - pst.nsx.stx_mnt_id = mntid; - pst.nsx.stx_mask |= STATX_MNT_ID; + st2.nsx.stx_mnt_id = mntid; + st2.nsx.stx_mask |= STATX_MNT_ID; } - return statx_mount_same(&st.nsx, &pst.nsx); + return statx_mount_same(&st1.nsx, &st2.nsx); } const char *accmode_to_string(int flags) { diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index 5061e32196..64918a4861 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -117,6 +117,8 @@ static inline int dir_fd_is_root_or_cwd(int dir_fd) { return dir_fd == AT_FDCWD ? true : path_is_root_at(dir_fd, NULL); } +int fds_are_same_mount(int fd1, int fd2); + /* The maximum length a buffer for a /proc/self/fd/<fd> path needs */ #define PROC_FD_PATH_MAX \ (STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)) diff --git a/src/basic/log.c b/src/basic/log.c index 0d78ecfd24..1470611a75 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -53,6 +53,7 @@ static int log_facility = LOG_DAEMON; static bool ratelimit_kmsg = true; static int console_fd = STDERR_FILENO; +static int console_fd_is_tty = -1; /* tri-state: -1 means don't know */ static int syslog_fd = -EBADF; static int kmsg_fd = -EBADF; static int journal_fd = -EBADF; @@ -108,12 +109,14 @@ bool _log_message_dummy = false; /* Always false */ static void log_close_console(void) { /* See comment in log_close_journal() */ (void) safe_close_above_stdio(TAKE_FD(console_fd)); + console_fd_is_tty = -1; } static int log_open_console(void) { if (!always_reopen_console) { console_fd = STDERR_FILENO; + console_fd_is_tty = -1; return 0; } @@ -125,6 +128,7 @@ static int log_open_console(void) { return fd; console_fd = fd_move_above_stdio(fd); + console_fd_is_tty = true; } return 0; @@ -381,6 +385,7 @@ void log_forget_fds(void) { /* Do not call from library code. */ console_fd = kmsg_fd = syslog_fd = journal_fd = -EBADF; + console_fd_is_tty = -1; } void log_set_max_level(int level) { @@ -404,6 +409,16 @@ void log_set_facility(int facility) { log_facility = facility; } +static bool check_console_fd_is_tty(void) { + if (console_fd < 0) + return false; + + if (console_fd_is_tty < 0) + console_fd_is_tty = isatty(console_fd) > 0; + + return console_fd_is_tty; +} + static int write_to_console( int level, int error, @@ -462,7 +477,12 @@ static int write_to_console( iovec[n++] = IOVEC_MAKE_STRING(buffer); if (off) iovec[n++] = IOVEC_MAKE_STRING(off); - iovec[n++] = IOVEC_MAKE_STRING("\n"); + + /* When writing to a TTY we output an extra '\r' (i.e. CR) first, to generate CRNL rather than just + * NL. This is a robustness thing in case the TTY is currently in raw mode (specifically: has the + * ONLCR flag off). We want that subsequent output definitely starts at the beginning of the line + * again, after all. If the TTY is not in raw mode the extra CR should not hurt. */ + iovec[n++] = IOVEC_MAKE_STRING(check_console_fd_is_tty() ? "\r\n" : "\n"); if (writev(console_fd, iovec, n) < 0) { diff --git a/src/basic/missing_stat.h b/src/basic/missing_stat.h index 372fdf90bd..eba1a3876f 100644 --- a/src/basic/missing_stat.h +++ b/src/basic/missing_stat.h @@ -8,7 +8,7 @@ #include <linux/stat.h> #endif -/* Thew newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */ +/* The newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */ #define STATX_DEFINITION { \ __u32 stx_mask; \ __u32 stx_blksize; \ diff --git a/src/basic/recurse-dir.c b/src/basic/recurse-dir.c index 5e98b7a5d8..1f505d5750 100644 --- a/src/basic/recurse-dir.c +++ b/src/basic/recurse-dir.c @@ -4,6 +4,7 @@ #include "dirent-util.h" #include "fd-util.h" #include "fileio.h" +#include "fs-util.h" #include "missing_syscall.h" #include "mountpoint-util.h" #include "recurse-dir.h" @@ -132,6 +133,18 @@ int readdir_all(int dir_fd, return 0; } +int readdir_all_at(int fd, const char *path, RecurseDirFlags flags, DirectoryEntries **ret) { + _cleanup_close_ int dir_fd = -EBADF; + + assert(fd >= 0 || fd == AT_FDCWD); + + dir_fd = xopenat(fd, path, O_DIRECTORY|O_CLOEXEC, /* xopen_flags= */ 0, /* mode= */ 0); + if (dir_fd < 0) + return dir_fd; + + return readdir_all(dir_fd, flags, ret); +} + int recurse_dir( int dir_fd, const char *path, diff --git a/src/basic/recurse-dir.h b/src/basic/recurse-dir.h index 9f6a7adb95..aaeae950fe 100644 --- a/src/basic/recurse-dir.h +++ b/src/basic/recurse-dir.h @@ -76,6 +76,7 @@ typedef struct DirectoryEntries { } DirectoryEntries; int readdir_all(int dir_fd, RecurseDirFlags flags, DirectoryEntries **ret); +int readdir_all_at(int fd, const char *path, RecurseDirFlags flags, DirectoryEntries **ret); int recurse_dir(int dir_fd, const char *path, unsigned statx_mask, unsigned n_depth_max, RecurseDirFlags flags, recurse_dir_func_t func, void *userdata); int recurse_dir_at(int atfd, const char *path, unsigned statx_mask, unsigned n_depth_max, RecurseDirFlags flags, recurse_dir_func_t func, void *userdata); diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index c54374b2c9..1783c6eb74 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -187,14 +187,12 @@ int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int fl struct stat a, b; assert(fda >= 0 || fda == AT_FDCWD); - assert(filea); assert(fdb >= 0 || fdb == AT_FDCWD); - assert(fileb); - if (fstatat(fda, filea, &a, flags) < 0) + if (fstatat(fda, strempty(filea), &a, flags) < 0) return log_debug_errno(errno, "Cannot stat %s: %m", filea); - if (fstatat(fdb, fileb, &b, flags) < 0) + if (fstatat(fdb, strempty(fileb), &b, flags) < 0) return log_debug_errno(errno, "Cannot stat %s: %m", fileb); return stat_inode_same(&a, &b); diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index 2a7d48b95d..cae42887c4 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -59,6 +59,8 @@ /* Other ANSI codes */ #define ANSI_UNDERLINE "\x1B[0;4m" +#define ANSI_ADD_UNDERLINE "\x1B[4m" +#define ANSI_ADD_UNDERLINE_GREY ANSI_ADD_UNDERLINE "\x1B[58;5;245m" #define ANSI_HIGHLIGHT "\x1B[0;1;39m" #define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m" @@ -189,6 +191,15 @@ static inline const char *ansi_underline(void) { return underline_enabled() ? ANSI_UNDERLINE : ANSI_NORMAL; } +static inline const char *ansi_add_underline(void) { + return underline_enabled() ? ANSI_ADD_UNDERLINE : ""; +} + +static inline const char *ansi_add_underline_grey(void) { + return underline_enabled() ? + (colors_enabled() ? ANSI_ADD_UNDERLINE_GREY : ANSI_ADD_UNDERLINE) : ""; +} + #define DEFINE_ANSI_FUNC_UNDERLINE(name, NAME) \ static inline const char *ansi_##name(void) { \ return underline_enabled() ? ANSI_##NAME##_UNDERLINE : \ diff --git a/src/basic/user-util.c b/src/basic/user-util.c index 9ae8577238..8aaffe8d04 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -661,17 +661,26 @@ int get_shell(char **ret) { return path_simplify_alloc(e, ret); } -int reset_uid_gid(void) { +int fully_set_uid_gid(uid_t uid, gid_t gid, const gid_t supplementary_gids[], size_t n_supplementary_gids) { int r; - r = maybe_setgroups(0, NULL); + assert(supplementary_gids || n_supplementary_gids == 0); + + /* Sets all UIDs and all GIDs to the specified ones. Drops all auxiliary GIDs */ + + r = maybe_setgroups(n_supplementary_gids, supplementary_gids); if (r < 0) return r; - if (setresgid(0, 0, 0) < 0) - return -errno; + if (gid_is_valid(gid)) + if (setresgid(gid, gid, gid) < 0) + return -errno; + + if (uid_is_valid(uid)) + if (setresuid(uid, uid, uid) < 0) + return -errno; - return RET_NERRNO(setresuid(0, 0, 0)); + return 0; } int take_etc_passwd_lock(const char *root) { diff --git a/src/basic/user-util.h b/src/basic/user-util.h index f394f6251d..b3e254662e 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -57,7 +57,10 @@ int getgroups_alloc(gid_t** gids); int get_home_dir(char **ret); int get_shell(char **ret); -int reset_uid_gid(void); +int fully_set_uid_gid(uid_t uid, gid_t gid, const gid_t supplementary_gids[], size_t n_supplementary_gids); +static inline int reset_uid_gid(void) { + return fully_set_uid_gid(0, 0, NULL, 0); +} int take_etc_passwd_lock(const char *root); diff --git a/src/core/automount.c b/src/core/automount.c index 14bf7e6998..4cbb3727e5 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -126,7 +126,7 @@ static int automount_add_mount_dependencies(Automount *a) { if (r < 0) return r; - return unit_require_mounts_for(UNIT(a), parent, UNIT_DEPENDENCY_IMPLICIT); + return unit_add_mounts_for(UNIT(a), parent, UNIT_DEPENDENCY_IMPLICIT, UNIT_MOUNT_REQUIRES); } static int automount_add_default_dependencies(Automount *a) { diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 1a037b7035..48b7e10ea5 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -177,7 +177,7 @@ static int property_get_dependencies( return sd_bus_message_close_container(reply); } -static int property_get_requires_mounts_for( +static int property_get_mounts_for( sd_bus *bus, const char *path, const char *interface, @@ -879,7 +879,8 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("StopPropagatedFrom", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SliceOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RequiresMountsFor", "as", property_get_requires_mounts_for, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RequiresMountsFor", "as", property_get_mounts_for, offsetof(Unit, mounts_for[UNIT_MOUNT_REQUIRES]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("WantsMountsFor", "as", property_get_mounts_for, offsetof(Unit, mounts_for[UNIT_MOUNT_WANTS]), 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), SD_BUS_PROPERTY("AccessSELinuxContext", "s", NULL, offsetof(Unit, access_selinux_context), SD_BUS_VTABLE_PROPERTY_CONST), @@ -2308,7 +2309,7 @@ static int bus_unit_set_transient_property( return 1; - } else if (streq(name, "RequiresMountsFor")) { + } else if (STR_IN_SET(name, "RequiresMountsFor", "WantsMountsFor")) { _cleanup_strv_free_ char **l = NULL; r = sd_bus_message_read_strv(message, &l); @@ -2328,9 +2329,9 @@ static int bus_unit_set_transient_property( return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path specified in %s is not normalized: %s", name, *p); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - r = unit_require_mounts_for(u, *p, UNIT_DEPENDENCY_FILE); + r = unit_add_mounts_for(u, *p, UNIT_DEPENDENCY_FILE, unit_mount_dependency_type_from_string(name)); if (r < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to add required mount \"%s\": %m", *p); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to add requested mount \"%s\": %m", *p); unit_write_settingf(u, flags, name, "%s=%s", name, *p); } diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index a3e8b8297c..4560067186 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -105,7 +105,7 @@ static int shift_fds(int fds[], size_t n_fds) { return 0; } -static int flags_fds( +static int flag_fds( const int fds[], size_t n_socket_fds, size_t n_fds, @@ -113,10 +113,7 @@ static int flags_fds( int r; - if (n_fds <= 0) - return 0; - - assert(fds); + assert(fds || n_fds == 0); /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags. * O_NONBLOCK only applies to socket activation though. */ @@ -1220,13 +1217,9 @@ static int setup_pam( * PR_SET_PDEATHSIG work in most cases. If this fails, ignore the error - but expect sd-pam * threads to fail to exit normally */ - r = maybe_setgroups(0, NULL); + r = fully_set_uid_gid(uid, gid, /* supplementary_gids= */ NULL, /* n_supplementary_gids= */ 0); if (r < 0) - log_warning_errno(r, "Failed to setgroups() in sd-pam: %m"); - if (setresgid(gid, gid, gid) < 0) - log_warning_errno(errno, "Failed to setresgid() in sd-pam: %m"); - if (setresuid(uid, uid, uid) < 0) - log_warning_errno(errno, "Failed to setresuid() in sd-pam: %m"); + log_warning_errno(r, "Failed to drop privileges in sd-pam: %m"); (void) ignore_signals(SIGPIPE); @@ -3604,32 +3597,29 @@ static int exec_context_cpu_affinity_from_numa(const ExecContext *c, CPUSet *ret return cpu_set_add_all(ret, &s); } -static int add_shifted_fd(int *fds, size_t fds_size, size_t *n_fds, int fd, int *ret_fd) { +static int add_shifted_fd(int *fds, size_t fds_size, size_t *n_fds, int *fd) { int r; assert(fds); assert(n_fds); assert(*n_fds < fds_size); - assert(ret_fd); + assert(fd); - if (fd < 0) { - *ret_fd = -EBADF; - return 0; - } + if (*fd < 0) + return 0; - if (fd < 3 + (int) *n_fds) { + if (*fd < 3 + (int) *n_fds) { /* Let's move the fd up, so that it's outside of the fd range we will use to store * the fds we pass to the process (or which are closed only during execve). */ - r = fcntl(fd, F_DUPFD_CLOEXEC, 3 + (int) *n_fds); + r = fcntl(*fd, F_DUPFD_CLOEXEC, 3 + (int) *n_fds); if (r < 0) return -errno; - close_and_replace(fd, r); + close_and_replace(*fd, r); } - *ret_fd = fds[*n_fds] = fd; - (*n_fds) ++; + fds[(*n_fds)++] = *fd; return 1; } @@ -3929,7 +3919,7 @@ int exec_invoke( int *exit_status) { _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **joined_exec_search_path = NULL, **accum_env = NULL, **replaced_argv = NULL; - int r, ngids = 0, exec_fd; + int r, ngids = 0; _cleanup_free_ gid_t *supplementary_gids = NULL; const char *username = NULL, *groupname = NULL; _cleanup_free_ char *home_buffer = NULL, *memory_pressure_path = NULL; @@ -3976,6 +3966,9 @@ int exec_invoke( assert(params); assert(exit_status); + if (context->log_level_max >= 0) + log_set_max_level(context->log_level_max); + /* Explicitly test for CVE-2021-4034 inspired invocations */ if (!command->path || strv_isempty(command->argv)) { *exit_status = EXIT_EXEC; @@ -4038,8 +4031,6 @@ int exec_invoke( log_forget_fds(); log_set_open_when_needed(true); log_settle_target(); - if (context->log_level_max >= 0) - log_set_max_level(context->log_level_max); /* In case anything used libc syslog(), close this here, too */ closelog(); @@ -4066,19 +4057,17 @@ int exec_invoke( memcpy_safe(keep_fds, fds, n_fds * sizeof(int)); n_keep_fds = n_fds; - r = add_shifted_fd(keep_fds, ELEMENTSOF(keep_fds), &n_keep_fds, params->exec_fd, &exec_fd); + r = add_shifted_fd(keep_fds, ELEMENTSOF(keep_fds), &n_keep_fds, ¶ms->exec_fd); if (r < 0) { *exit_status = EXIT_FDS; - return log_exec_error_errno(context, params, r, "Failed to shift fd and set FD_CLOEXEC: %m"); + return log_exec_error_errno(context, params, r, "Failed to collect shifted fd: %m"); } #if HAVE_LIBBPF - if (params->bpf_outer_map_fd >= 0) { - r = add_shifted_fd(keep_fds, ELEMENTSOF(keep_fds), &n_keep_fds, params->bpf_outer_map_fd, (int *)¶ms->bpf_outer_map_fd); - if (r < 0) { - *exit_status = EXIT_FDS; - return log_exec_error_errno(context, params, r, "Failed to shift fd and set FD_CLOEXEC: %m"); - } + r = add_shifted_fd(keep_fds, ELEMENTSOF(keep_fds), &n_keep_fds, ¶ms->bpf_outer_map_fd); + if (r < 0) { + *exit_status = EXIT_FDS; + return log_exec_error_errno(context, params, r, "Failed to collect shifted fd: %m"); } #endif @@ -4759,10 +4748,10 @@ int exec_invoke( "EXECUTABLE=%s", command->path); } - r = add_shifted_fd(keep_fds, ELEMENTSOF(keep_fds), &n_keep_fds, executable_fd, &executable_fd); + r = add_shifted_fd(keep_fds, ELEMENTSOF(keep_fds), &n_keep_fds, &executable_fd); if (r < 0) { *exit_status = EXIT_FDS; - return log_exec_error_errno(context, params, r, "Failed to shift fd and set FD_CLOEXEC: %m"); + return log_exec_error_errno(context, params, r, "Failed to collect shifted fd: %m"); } #if HAVE_SELINUX @@ -4807,7 +4796,7 @@ int exec_invoke( if (r >= 0) r = shift_fds(fds, n_fds); if (r >= 0) - r = flags_fds(fds, n_socket_fds, n_fds, context->non_blocking); + r = flag_fds(fds, n_socket_fds, n_fds, context->non_blocking); if (r < 0) { *exit_status = EXIT_FDS; return log_exec_error_errno(context, params, r, "Failed to adjust passed file descriptors: %m"); @@ -5212,13 +5201,13 @@ int exec_invoke( log_command_line(context, params, "Executing", executable, final_argv); - if (exec_fd >= 0) { + if (params->exec_fd >= 0) { uint8_t hot = 1; /* We have finished with all our initializations. Let's now let the manager know that. From this point * on, if the manager sees POLLHUP on the exec_fd, then execve() was successful. */ - if (write(exec_fd, &hot, sizeof(hot)) < 0) { + if (write(params->exec_fd, &hot, sizeof(hot)) < 0) { *exit_status = EXIT_EXEC; return log_exec_error_errno(context, params, errno, "Failed to enable exec_fd: %m"); } @@ -5226,13 +5215,13 @@ int exec_invoke( r = fexecve_or_execve(executable_fd, executable, final_argv, accum_env); - if (exec_fd >= 0) { + if (params->exec_fd >= 0) { uint8_t hot = 0; /* The execve() failed. This means the exec_fd is still open. Which means we need to tell the manager * that POLLHUP on it no longer means execve() succeeded. */ - if (write(exec_fd, &hot, sizeof(hot)) < 0) { + if (write(params->exec_fd, &hot, sizeof(hot)) < 0) { *exit_status = EXIT_EXEC; return log_exec_error_errno(context, params, errno, "Failed to disable exec_fd: %m"); } diff --git a/src/core/execute-serialize.c b/src/core/execute-serialize.c index 6c19cd42a2..b67a4f9141 100644 --- a/src/core/execute-serialize.c +++ b/src/core/execute-serialize.c @@ -373,8 +373,7 @@ static int exec_cgroup_context_serialize(const CGroupContext *c, FILE *f) { if (il->limits[type] == cgroup_io_limit_defaults[type]) continue; - key = strjoin("exec-cgroup-context-io-device-limit-", - cgroup_io_limit_type_to_string(type)); + key = strjoin("exec-cgroup-context-io-device-limit-", cgroup_io_limit_type_to_string(type)); if (!key) return -ENOMEM; @@ -1479,8 +1478,8 @@ static int exec_parameters_deserialize(ExecParameters *p, FILE *f, FDSet *fds) { return log_oom_debug(); /* Ensure we don't leave any FD uninitialized on error, it makes the fuzzer sad */ - for (size_t i = 0; i < p->n_socket_fds + p->n_storage_fds; ++i) - p->fds[i] = -EBADF; + FOREACH_ARRAY(i, p->fds, p->n_socket_fds + p->n_storage_fds) + *i = -EBADF; r = deserialize_fd_many(fds, val, p->n_socket_fds + p->n_storage_fds, p->fds); if (r < 0) @@ -1611,12 +1610,6 @@ static int exec_parameters_deserialize(ExecParameters *p, FILE *f, FDSet *fds) { if (fd < 0) continue; - /* This is special and relies on close-on-exec semantics, make sure it's - * there */ - r = fd_cloexec(fd, true); - if (r < 0) - return r; - p->exec_fd = fd; } else if ((val = startswith(l, "exec-parameters-bpf-outer-map-fd="))) { int fd; @@ -1625,12 +1618,6 @@ static int exec_parameters_deserialize(ExecParameters *p, FILE *f, FDSet *fds) { if (fd < 0) continue; - /* This is special and relies on close-on-exec semantics, make sure it's - * there */ - r = fd_cloexec(fd, true); - if (r < 0) - return r; - p->bpf_outer_map_fd = fd; } else if ((val = startswith(l, "exec-parameters-notify-socket="))) { r = free_and_strdup(&p->notify_socket, val); diff --git a/src/core/executor.c b/src/core/executor.c index f55bacdbd8..993cd4a4d2 100644 --- a/src/core/executor.c +++ b/src/core/executor.c @@ -204,7 +204,9 @@ int main(int argc, char *argv[]) { log_set_prohibit_ipc(false); log_open(); - /* The serialization fd is set to CLOEXEC in parse_argv, so it's also filtered. */ + /* This call would collect all passed fds and enable CLOEXEC. We'll unset it in exec_invoke (flag_fds) + * for fds that shall be passed to the child. + * The serialization fd is set to CLOEXEC in parse_argv, so it's also filtered. */ r = fdset_new_fill(/* filter_cloexec= */ 0, &fdset); if (r < 0) return log_error_errno(r, "Failed to create fd set: %m"); diff --git a/src/core/import-creds.c b/src/core/import-creds.c index 48f3160923..e53deb639e 100644 --- a/src/core/import-creds.c +++ b/src/core/import-creds.c @@ -815,7 +815,6 @@ static int setenv_notify_socket(void) { static int report_credentials_per_func(const char *title, int (*get_directory_func)(const char **ret)) { _cleanup_free_ DirectoryEntries *de = NULL; - _cleanup_close_ int dir_fd = -EBADF; _cleanup_free_ char *ll = NULL; const char *d = NULL; int r, c = 0; @@ -831,11 +830,7 @@ static int report_credentials_per_func(const char *title, int (*get_directory_fu return log_warning_errno(r, "Failed to determine %s directory: %m", title); } - dir_fd = open(d, O_RDONLY|O_DIRECTORY|O_CLOEXEC); - if (dir_fd < 0) - return log_warning_errno(errno, "Failed to open credentials directory %s: %m", d); - - r = readdir_all(dir_fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de); + r = readdir_all_at(AT_FDCWD, d, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de); if (r < 0) return log_warning_errno(r, "Failed to enumerate credentials directory %s: %m", d); diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in index 45f9ab03c4..ed19c84697 100644 --- a/src/core/load-fragment-gperf.gperf.in +++ b/src/core/load-fragment-gperf.gperf.in @@ -309,7 +309,8 @@ Unit.PartOf, config_parse_unit_deps, Unit.JoinsNamespaceOf, config_parse_unit_deps, UNIT_JOINS_NAMESPACE_OF, 0 Unit.RequiresOverridable, config_parse_obsolete_unit_deps, UNIT_REQUIRES, 0 Unit.RequisiteOverridable, config_parse_obsolete_unit_deps, UNIT_REQUISITE, 0 -Unit.RequiresMountsFor, config_parse_unit_requires_mounts_for, 0, 0 +Unit.RequiresMountsFor, config_parse_unit_mounts_for, 0, 0 +Unit.WantsMountsFor, config_parse_unit_mounts_for, 0, 0 Unit.StopWhenUnneeded, config_parse_bool, 0, offsetof(Unit, stop_when_unneeded) Unit.RefuseManualStart, config_parse_bool, 0, offsetof(Unit, refuse_manual_start) Unit.RefuseManualStop, config_parse_bool, 0, offsetof(Unit, refuse_manual_stop) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 6e3a22bc16..05843662c7 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -3152,7 +3152,7 @@ int config_parse_unit_condition_string( return 0; } -int config_parse_unit_requires_mounts_for( +int config_parse_unit_mounts_for( const char *unit, const char *filename, unsigned line, @@ -3171,6 +3171,7 @@ int config_parse_unit_requires_mounts_for( assert(lvalue); assert(rvalue); assert(data); + assert(STR_IN_SET(lvalue, "RequiresMountsFor", "WantsMountsFor")); for (const char *p = rvalue;;) { _cleanup_free_ char *word = NULL, *resolved = NULL; @@ -3196,9 +3197,9 @@ int config_parse_unit_requires_mounts_for( if (r < 0) continue; - r = unit_require_mounts_for(u, resolved, UNIT_DEPENDENCY_FILE); + r = unit_add_mounts_for(u, resolved, UNIT_DEPENDENCY_FILE, unit_mount_dependency_type_from_string(lvalue)); if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add required mount '%s', ignoring: %m", resolved); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add requested mount '%s', ignoring: %m", resolved); continue; } } @@ -3800,8 +3801,23 @@ int config_parse_allowed_cpuset( void *userdata) { CPUSet *c = data; + const Unit *u = userdata; + _cleanup_free_ char *k = NULL; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = unit_full_printf(u, rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to resolve unit specifiers in '%s', ignoring: %m", + rvalue); + return 0; + } - (void) parse_cpu_set_extend(rvalue, c, true, unit, filename, line, lvalue); + (void) parse_cpu_set_extend(k, c, true, unit, filename, line, lvalue); return 0; } @@ -6301,8 +6317,7 @@ void unit_dump_config_items(FILE *f) { { config_parse_nsec, "NANOSECONDS" }, { config_parse_namespace_path_strv, "PATH [...]" }, { config_parse_bind_paths, "PATH[:PATH[:OPTIONS]] [...]" }, - { config_parse_unit_requires_mounts_for, - "PATH [...]" }, + { config_parse_unit_mounts_for, "PATH [...]" }, { config_parse_exec_mount_propagation_flag, "MOUNTFLAG" }, { config_parse_unit_string_printf, "STRING" }, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 69198050ea..c001397ff2 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -71,7 +71,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_unit_condition_string); CONFIG_PARSER_PROTOTYPE(config_parse_kill_mode); CONFIG_PARSER_PROTOTYPE(config_parse_notify_access); CONFIG_PARSER_PROTOTYPE(config_parse_emergency_action); -CONFIG_PARSER_PROTOTYPE(config_parse_unit_requires_mounts_for); +CONFIG_PARSER_PROTOTYPE(config_parse_unit_mounts_for); CONFIG_PARSER_PROTOTYPE(config_parse_syscall_filter); CONFIG_PARSER_PROTOTYPE(config_parse_syscall_archs); CONFIG_PARSER_PROTOTYPE(config_parse_syscall_errno); diff --git a/src/core/main.c b/src/core/main.c index 2ac59dabf5..f15d2ff25c 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -68,6 +68,7 @@ #include "manager-serialize.h" #include "mkdir-label.h" #include "mount-setup.h" +#include "mount-util.h" #include "os-util.h" #include "pager.h" #include "parse-argument.h" @@ -140,6 +141,7 @@ static char **arg_default_environment; static char **arg_manager_environment; static uint64_t arg_capability_bounding_set; static bool arg_no_new_privs; +static int arg_protect_system; static nsec_t arg_timer_slack_nsec; static Set* arg_syscall_archs; static FILE* arg_serialization; @@ -610,6 +612,43 @@ static int config_parse_oom_score_adjust( return 0; } +static int config_parse_protect_system_pid1( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + int *v = ASSERT_PTR(data), r; + + /* This is modelled after the per-service ProtectSystem= setting, but a bit more restricted on one + * hand, and more automatic in another. i.e. we currently only support yes/no (not "strict" or + * "full"). And we will enable this automatically for the initrd unless configured otherwise. + * + * We might extend this later to match more closely what the per-service ProtectSystem= can do, but + * this is not trivial, due to ordering constraints: besides /usr/ we don't really have much mounted + * at the moment we enable this logic. */ + + if (isempty(rvalue) || streq(rvalue, "auto")) { + *v = -1; + return 0; + } + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse ProtectSystem= argument '%s', ignoring: %m", rvalue); + return 0; + } + + *v = r; + return 0; +} + static int parse_config_file(void) { const ConfigTableItem items[] = { { "Manager", "LogLevel", config_parse_level2, 0, NULL }, @@ -637,6 +676,7 @@ static int parse_config_file(void) { { "Manager", "RuntimeWatchdogPreGovernor", config_parse_string, CONFIG_PARSE_STRING_SAFE, &arg_watchdog_pretimeout_governor }, { "Manager", "CapabilityBoundingSet", config_parse_capability_set, 0, &arg_capability_bounding_set }, { "Manager", "NoNewPrivileges", config_parse_bool, 0, &arg_no_new_privs }, + { "Manager", "ProtectSystem", config_parse_protect_system_pid1, 0, &arg_protect_system }, #if HAVE_SECCOMP { "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs }, #else @@ -1684,6 +1724,35 @@ static void initialize_core_pattern(bool skip_setup) { arg_early_core_pattern); } +static void apply_protect_system(bool skip_setup) { + int r; + + if (skip_setup || getpid_cached() != 1 || arg_protect_system == 0) + return; + + if (arg_protect_system < 0 && !in_initrd()) { + log_debug("ProtectSystem=auto selected, but not running in an initrd, skipping."); + return; + } + + r = make_mount_point("/usr"); + if (r < 0) { + log_warning_errno(r, "Failed to make /usr/ a mount point, ignoring: %m"); + return; + } + + if (mount_nofollow_verbose( + LOG_WARNING, + /* what= */ NULL, + "/usr", + /* fstype= */ NULL, + MS_BIND|MS_REMOUNT|MS_RDONLY, + /* options= */ NULL) < 0) + return; + + log_info("Successfully made /usr/ read-only."); +} + static void update_cpu_affinity(bool skip_setup) { _cleanup_free_ char *mask = NULL; @@ -2531,6 +2600,7 @@ static void reset_arguments(void) { arg_capability_bounding_set = CAP_MASK_UNSET; arg_no_new_privs = false; + arg_protect_system = -1; arg_timer_slack_nsec = NSEC_INFINITY; arg_syscall_archs = set_free(arg_syscall_archs); @@ -2739,8 +2809,6 @@ static int collect_fds(FDSet **ret_fds, const char **ret_error_message) { "MESSAGE_ID=" SD_MESSAGE_CORE_FD_SET_FAILED_STR); } - (void) fdset_cloexec(*ret_fds, true); - /* The serialization fd should have O_CLOEXEC turned on already, let's verify that we didn't pick it up here */ assert_se(!arg_serialization || !fdset_contains(*ret_fds, fileno(arg_serialization))); @@ -3040,9 +3108,12 @@ int main(int argc, char *argv[]) { cmdline_take_random_seed(); } - /* A core pattern might have been specified via the cmdline. */ + /* A core pattern might have been specified via the cmdline. */ initialize_core_pattern(skip_setup); + /* Make /usr/ read-only */ + apply_protect_system(skip_setup); + /* Close logging fds, in order not to confuse collecting passed fds and terminal logic below */ log_close(); diff --git a/src/core/manager.c b/src/core/manager.c index 37e4f70950..6ca643e693 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -1691,8 +1691,10 @@ Manager* manager_free(Manager *m) { unit_defaults_done(&m->defaults); - assert(hashmap_isempty(m->units_requiring_mounts_for)); - hashmap_free(m->units_requiring_mounts_for); + FOREACH_ARRAY(map, m->units_needing_mounts_for, _UNIT_MOUNT_DEPENDENCY_TYPE_MAX) { + assert(hashmap_isempty(*map)); + hashmap_free(*map); + } hashmap_free(m->uid_refs); hashmap_free(m->gid_refs); @@ -4478,14 +4480,15 @@ void manager_status_printf(Manager *m, StatusType type, const char *status, cons va_end(ap); } -Set* manager_get_units_requiring_mounts_for(Manager *m, const char *path) { +Set* manager_get_units_needing_mounts_for(Manager *m, const char *path, UnitMountDependencyType t) { assert(m); assert(path); + assert(t >= 0 && t < _UNIT_MOUNT_DEPENDENCY_TYPE_MAX); if (path_equal(path, "/")) path = ""; - return hashmap_get(m->units_requiring_mounts_for, path); + return hashmap_get(m->units_needing_mounts_for[t], path); } int manager_update_failed_units(Manager *m, Unit *u, bool failed) { diff --git a/src/core/manager.h b/src/core/manager.h index d96eb7b995..d7bc6e761d 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -137,6 +137,7 @@ typedef enum WatchdogType { #include "path-lookup.h" #include "show-status.h" #include "unit-name.h" +#include "unit.h" typedef enum ManagerTestRunFlags { MANAGER_TEST_NORMAL = 0, /* run normally */ @@ -438,10 +439,9 @@ struct Manager { /* This is true before and after switching root. */ bool switching_root; - /* This maps all possible path prefixes to the units needing - * them. It's a hashmap with a path string as key and a Set as - * value where Unit objects are contained. */ - Hashmap *units_requiring_mounts_for; + /* These map all possible path prefixes to the units needing them. They are hashmaps with a path + * string as key, and a Set as value where Unit objects are contained. */ + Hashmap *units_needing_mounts_for[_UNIT_MOUNT_DEPENDENCY_TYPE_MAX]; /* Used for processing polkit authorization responses */ Hashmap *polkit_registry; @@ -596,7 +596,7 @@ double manager_get_progress(Manager *m); void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) _printf_(4,5); -Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path); +Set* manager_get_units_needing_mounts_for(Manager *m, const char *path, UnitMountDependencyType t); ManagerState manager_state(Manager *m); diff --git a/src/core/mount.c b/src/core/mount.c index ded322d332..f364b2ab0b 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -281,8 +281,6 @@ static int update_parameters_proc_self_mountinfo( static int mount_add_mount_dependencies(Mount *m) { MountParameters *pm; - Unit *other; - Set *s; int r; assert(m); @@ -296,7 +294,7 @@ static int mount_add_mount_dependencies(Mount *m) { if (r < 0) return r; - r = unit_require_mounts_for(UNIT(m), parent, UNIT_DEPENDENCY_IMPLICIT); + r = unit_add_mounts_for(UNIT(m), parent, UNIT_DEPENDENCY_IMPLICIT, UNIT_MOUNT_REQUIRES); if (r < 0) return r; } @@ -308,30 +306,43 @@ static int mount_add_mount_dependencies(Mount *m) { path_is_absolute(pm->what) && (mount_is_bind(pm) || mount_is_loop(pm) || !mount_is_network(pm))) { - r = unit_require_mounts_for(UNIT(m), pm->what, UNIT_DEPENDENCY_FILE); + r = unit_add_mounts_for(UNIT(m), pm->what, UNIT_DEPENDENCY_FILE, UNIT_MOUNT_REQUIRES); if (r < 0) return r; } /* Adds in dependencies to other units that use this path or paths further down in the hierarchy */ - s = manager_get_units_requiring_mounts_for(UNIT(m)->manager, m->where); - SET_FOREACH(other, s) { - - if (other->load_state != UNIT_LOADED) - continue; - - if (other == UNIT(m)) - continue; - - r = unit_add_dependency(other, UNIT_AFTER, UNIT(m), true, UNIT_DEPENDENCY_PATH); - if (r < 0) - return r; - - if (UNIT(m)->fragment_path) { - /* If we have fragment configuration, then make this dependency required */ - r = unit_add_dependency(other, UNIT_REQUIRES, UNIT(m), true, UNIT_DEPENDENCY_PATH); + for (UnitMountDependencyType t = 0; t < _UNIT_MOUNT_DEPENDENCY_TYPE_MAX; ++t) { + Unit *other; + Set *s = manager_get_units_needing_mounts_for(UNIT(m)->manager, m->where, t); + + SET_FOREACH(other, s) { + if (other->load_state != UNIT_LOADED) + continue; + + if (other == UNIT(m)) + continue; + + r = unit_add_dependency( + other, + UNIT_AFTER, + UNIT(m), + /* add_reference= */ true, + UNIT_DEPENDENCY_PATH); if (r < 0) return r; + + if (UNIT(m)->fragment_path) { + /* If we have fragment configuration, then make this dependency required/wanted */ + r = unit_add_dependency( + other, + unit_mount_dependency_type_to_dependency_type(t), + UNIT(m), + /* add_reference= */ true, + UNIT_DEPENDENCY_PATH); + if (r < 0) + return r; + } } } diff --git a/src/core/path.c b/src/core/path.c index 44481a95d5..471d159d81 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -309,7 +309,7 @@ static int path_add_mount_dependencies(Path *p) { assert(p); LIST_FOREACH(spec, s, p->specs) { - r = unit_require_mounts_for(UNIT(p), s->path, UNIT_DEPENDENCY_FILE); + r = unit_add_mounts_for(UNIT(p), s->path, UNIT_DEPENDENCY_FILE, UNIT_MOUNT_REQUIRES); if (r < 0) return r; } diff --git a/src/core/show-status.c b/src/core/show-status.c index 1d47c0af4d..606237ee0e 100644 --- a/src/core/show-status.c +++ b/src/core/show-status.c @@ -94,7 +94,7 @@ int status_vprintf(const char *status, ShowStatusFlags flags, const char *format } iovec[n++] = IOVEC_MAKE_STRING(s); - iovec[n++] = IOVEC_MAKE_STRING("\n"); + iovec[n++] = IOVEC_MAKE_STRING("\r\n"); /* use CRNL instead of just NL, to be robust towards TTYs in raw mode */ if (prev_ephemeral && !FLAGS_SET(flags, SHOW_STATUS_EPHEMERAL)) iovec[n++] = IOVEC_MAKE_STRING(ANSI_ERASE_TO_END_OF_LINE); diff --git a/src/core/socket.c b/src/core/socket.c index 388be62318..c42a94d046 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -221,7 +221,7 @@ static int socket_add_mount_dependencies(Socket *s) { if (!path) continue; - r = unit_require_mounts_for(UNIT(s), path, UNIT_DEPENDENCY_FILE); + r = unit_add_mounts_for(UNIT(s), path, UNIT_DEPENDENCY_FILE, UNIT_MOUNT_REQUIRES); if (r < 0) return r; } diff --git a/src/core/swap.c b/src/core/swap.c index 488b1719c5..ce35a5c441 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -321,7 +321,7 @@ static int swap_add_extras(Swap *s) { return r; } - r = unit_require_mounts_for(UNIT(s), s->what, UNIT_DEPENDENCY_IMPLICIT); + r = unit_add_mounts_for(UNIT(s), s->what, UNIT_DEPENDENCY_IMPLICIT, UNIT_MOUNT_REQUIRES); if (r < 0) return r; diff --git a/src/core/system.conf.in b/src/core/system.conf.in index 05eb681270..9b89a6aa77 100644 --- a/src/core/system.conf.in +++ b/src/core/system.conf.in @@ -39,6 +39,7 @@ #WatchdogDevice= #CapabilityBoundingSet= #NoNewPrivileges=no +#ProtectSystem=auto #SystemCallArchitectures= #TimerSlackNSec= #StatusUnitFormat={{STATUS_UNIT_FORMAT_DEFAULT_STR}} diff --git a/src/core/timer.c b/src/core/timer.c index 3c41a250b0..19358c7665 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -141,7 +141,7 @@ static int timer_setup_persistent(Timer *t) { if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) { - r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers", UNIT_DEPENDENCY_FILE); + r = unit_add_mounts_for(UNIT(t), "/var/lib/systemd/timers", UNIT_DEPENDENCY_FILE, UNIT_MOUNT_REQUIRES); if (r < 0) return r; diff --git a/src/core/unit-serialize.c b/src/core/unit-serialize.c index fe4221ca46..40cdb615be 100644 --- a/src/core/unit-serialize.c +++ b/src/core/unit-serialize.c @@ -831,21 +831,26 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { } } - if (!hashmap_isempty(u->requires_mounts_for)) { - UnitDependencyInfo di; - const char *path; + for (UnitMountDependencyType type = 0; type < _UNIT_MOUNT_DEPENDENCY_TYPE_MAX; type++) + if (!hashmap_isempty(u->mounts_for[type])) { + UnitDependencyInfo di; + const char *path; - HASHMAP_FOREACH_KEY(di.data, path, u->requires_mounts_for) { - bool space = false; + HASHMAP_FOREACH_KEY(di.data, path, u->mounts_for[type]) { + bool space = false; - fprintf(f, "%s\tRequiresMountsFor: %s (", prefix, path); + fprintf(f, + "%s\t%s: %s (", + prefix, + unit_mount_dependency_type_to_string(type), + path); - print_unit_dependency_mask(f, "origin", di.origin_mask, &space); - print_unit_dependency_mask(f, "destination", di.destination_mask, &space); + print_unit_dependency_mask(f, "origin", di.origin_mask, &space); + print_unit_dependency_mask(f, "destination", di.destination_mask, &space); - fputs(")\n", f); + fputs(")\n", f); + } } - } if (u->load_state == UNIT_LOADED) { diff --git a/src/core/unit.c b/src/core/unit.c index 41f3bdb226..d29dc77bdb 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -689,38 +689,39 @@ static void unit_remove_transient(Unit *u) { } } -static void unit_free_requires_mounts_for(Unit *u) { +static void unit_free_mounts_for(Unit *u) { assert(u); - for (;;) { - _cleanup_free_ char *path = NULL; + for (UnitMountDependencyType t = 0; t < _UNIT_MOUNT_DEPENDENCY_TYPE_MAX; ++t) { + for (;;) { + _cleanup_free_ char *path = NULL; + + path = hashmap_steal_first_key(u->mounts_for[t]); + if (!path) + break; - path = hashmap_steal_first_key(u->requires_mounts_for); - if (!path) - break; - else { char s[strlen(path) + 1]; PATH_FOREACH_PREFIX_MORE(s, path) { char *y; Set *x; - x = hashmap_get2(u->manager->units_requiring_mounts_for, s, (void**) &y); + x = hashmap_get2(u->manager->units_needing_mounts_for[t], s, (void**) &y); if (!x) continue; (void) set_remove(x, u); if (set_isempty(x)) { - (void) hashmap_remove(u->manager->units_requiring_mounts_for, y); + assert_se(hashmap_remove(u->manager->units_needing_mounts_for[t], y)); free(y); set_free(x); } } } - } - u->requires_mounts_for = hashmap_free(u->requires_mounts_for); + u->mounts_for[t] = hashmap_free(u->mounts_for[t]); + } } static void unit_done(Unit *u) { @@ -769,7 +770,7 @@ Unit* unit_free(Unit *u) { u->deserialized_refs = strv_free(u->deserialized_refs); u->pending_freezer_invocation = sd_bus_message_unref(u->pending_freezer_invocation); - unit_free_requires_mounts_for(u); + unit_free_mounts_for(u); SET_FOREACH(t, u->aliases) hashmap_remove_value(u->manager->units, t, u); @@ -1277,20 +1278,24 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { /* Unlike unit_add_dependency() or friends, this always returns 0 on success. */ - if (c->working_directory && !c->working_directory_missing_ok) { - r = unit_require_mounts_for(u, c->working_directory, UNIT_DEPENDENCY_FILE); + if (c->working_directory) { + r = unit_add_mounts_for( + u, + c->working_directory, + UNIT_DEPENDENCY_FILE, + c->working_directory_missing_ok ? UNIT_MOUNT_WANTS : UNIT_MOUNT_REQUIRES); if (r < 0) return r; } if (c->root_directory) { - r = unit_require_mounts_for(u, c->root_directory, UNIT_DEPENDENCY_FILE); + r = unit_add_mounts_for(u, c->root_directory, UNIT_DEPENDENCY_FILE, UNIT_MOUNT_REQUIRES); if (r < 0) return r; } if (c->root_image) { - r = unit_require_mounts_for(u, c->root_image, UNIT_DEPENDENCY_FILE); + r = unit_add_mounts_for(u, c->root_image, UNIT_DEPENDENCY_FILE, UNIT_MOUNT_REQUIRES); if (r < 0) return r; } @@ -1306,7 +1311,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { if (!p) return -ENOMEM; - r = unit_require_mounts_for(u, p, UNIT_DEPENDENCY_FILE); + r = unit_add_mounts_for(u, p, UNIT_DEPENDENCY_FILE, UNIT_MOUNT_REQUIRES); if (r < 0) return r; } @@ -1326,16 +1331,11 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { } if (c->private_tmp) { - - /* FIXME: for now we make a special case for /tmp and add a weak dependency on - * tmp.mount so /tmp being masked is supported. However there's no reason to treat - * /tmp specifically and masking other mount units should be handled more - * gracefully too, see PR#16894. */ - r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, "tmp.mount", true, UNIT_DEPENDENCY_FILE); + r = unit_add_mounts_for(u, "/tmp", UNIT_DEPENDENCY_FILE, UNIT_MOUNT_WANTS); if (r < 0) return r; - r = unit_require_mounts_for(u, "/var/tmp", UNIT_DEPENDENCY_FILE); + r = unit_add_mounts_for(u, "/var/tmp", UNIT_DEPENDENCY_FILE, UNIT_MOUNT_WANTS); if (r < 0) return r; @@ -1536,51 +1536,72 @@ static int unit_add_slice_dependencies(Unit *u) { } static int unit_add_mount_dependencies(Unit *u) { - UnitDependencyInfo di; - const char *path; bool changed = false; int r; assert(u); - HASHMAP_FOREACH_KEY(di.data, path, u->requires_mounts_for) { - char prefix[strlen(path) + 1]; + for (UnitMountDependencyType t = 0; t < _UNIT_MOUNT_DEPENDENCY_TYPE_MAX; ++t) { + UnitDependencyInfo di; + const char *path; - PATH_FOREACH_PREFIX_MORE(prefix, path) { - _cleanup_free_ char *p = NULL; - Unit *m; + HASHMAP_FOREACH_KEY(di.data, path, u->mounts_for[t]) { - r = unit_name_from_path(prefix, ".mount", &p); - if (r == -EINVAL) - continue; /* If the path cannot be converted to a mount unit name, then it's - * not manageable as a unit by systemd, and hence we don't need a - * dependency on it. Let's thus silently ignore the issue. */ - if (r < 0) - return r; + char prefix[strlen(ASSERT_PTR(path)) + 1]; - m = manager_get_unit(u->manager, p); - if (!m) { - /* Make sure to load the mount unit if it exists. If so the dependencies on - * this unit will be added later during the loading of the mount unit. */ - (void) manager_load_unit_prepare(u->manager, p, NULL, NULL, &m); - continue; - } - if (m == u) - continue; + PATH_FOREACH_PREFIX_MORE(prefix, path) { + _cleanup_free_ char *p = NULL; + Unit *m; - if (m->load_state != UNIT_LOADED) - continue; + r = unit_name_from_path(prefix, ".mount", &p); + if (r == -EINVAL) + continue; /* If the path cannot be converted to a mount unit name, + * then it's not manageable as a unit by systemd, and + * hence we don't need a dependency on it. Let's thus + * silently ignore the issue. */ + if (r < 0) + return r; - r = unit_add_dependency(u, UNIT_AFTER, m, true, di.origin_mask); - if (r < 0) - return r; - changed = changed || r > 0; + m = manager_get_unit(u->manager, p); + if (!m) { + /* Make sure to load the mount unit if it exists. If so the + * dependencies on this unit will be added later during the loading + * of the mount unit. */ + (void) manager_load_unit_prepare( + u->manager, + p, + /* path= */NULL, + /* e= */NULL, + &m); + continue; + } + if (m == u) + continue; - if (m->fragment_path) { - r = unit_add_dependency(u, UNIT_REQUIRES, m, true, di.origin_mask); + if (m->load_state != UNIT_LOADED) + continue; + + r = unit_add_dependency( + u, + UNIT_AFTER, + m, + /* add_reference= */ true, + di.origin_mask); if (r < 0) return r; changed = changed || r > 0; + + if (m->fragment_path) { + r = unit_add_dependency( + u, + unit_mount_dependency_type_to_dependency_type(t), + m, + /* add_reference= */ true, + di.origin_mask); + if (r < 0) + return r; + changed = changed || r > 0; + } } } } @@ -4942,11 +4963,16 @@ int unit_kill_context( return wait_for_exit; } -int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask) { +int unit_add_mounts_for(Unit *u, const char *path, UnitDependencyMask mask, UnitMountDependencyType type) { + Hashmap **unit_map, **manager_map; int r; assert(u); assert(path); + assert(type >= 0 && type < _UNIT_MOUNT_DEPENDENCY_TYPE_MAX); + + unit_map = &u->mounts_for[type]; + manager_map = &u->manager->units_needing_mounts_for[type]; /* Registers a unit for requiring a certain path and all its prefixes. We keep a hashtable of these * paths in the unit (from the path to the UnitDependencyInfo structure indicating how to the @@ -4956,7 +4982,7 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask) if (!path_is_absolute(path)) return -EINVAL; - if (hashmap_contains(u->requires_mounts_for, path)) /* Exit quickly if the path is already covered. */ + if (hashmap_contains(*unit_map, path)) /* Exit quickly if the path is already covered. */ return 0; /* Use the canonical form of the path as the stored key. We call path_is_normalized() @@ -4975,7 +5001,7 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask) .origin_mask = mask }; - r = hashmap_ensure_put(&u->requires_mounts_for, &path_hash_ops, p, di.data); + r = hashmap_ensure_put(unit_map, &path_hash_ops, p, di.data); if (r < 0) return r; assert(r > 0); @@ -4985,11 +5011,11 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask) PATH_FOREACH_PREFIX_MORE(prefix, path) { Set *x; - x = hashmap_get(u->manager->units_requiring_mounts_for, prefix); + x = hashmap_get(*manager_map, prefix); if (!x) { _cleanup_free_ char *q = NULL; - r = hashmap_ensure_allocated(&u->manager->units_requiring_mounts_for, &path_hash_ops); + r = hashmap_ensure_allocated(manager_map, &path_hash_ops); if (r < 0) return r; @@ -5001,7 +5027,7 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask) if (!x) return -ENOMEM; - r = hashmap_put(u->manager->units_requiring_mounts_for, q, x); + r = hashmap_put(*manager_map, q, x); if (r < 0) { set_free(x); return r; @@ -6615,3 +6641,24 @@ int activation_details_append_pair(ActivationDetails *details, char ***strv) { } DEFINE_TRIVIAL_REF_UNREF_FUNC(ActivationDetails, activation_details, activation_details_free); + +static const char* const unit_mount_dependency_type_table[_UNIT_MOUNT_DEPENDENCY_TYPE_MAX] = { + [UNIT_MOUNT_WANTS] = "WantsMountsFor", + [UNIT_MOUNT_REQUIRES] = "RequiresMountsFor", +}; + +DEFINE_STRING_TABLE_LOOKUP(unit_mount_dependency_type, UnitMountDependencyType); + +UnitDependency unit_mount_dependency_type_to_dependency_type(UnitMountDependencyType t) { + switch (t) { + + case UNIT_MOUNT_WANTS: + return UNIT_WANTS; + + case UNIT_MOUNT_REQUIRES: + return UNIT_REQUIRES; + + default: + assert_not_reached(); + } +} diff --git a/src/core/unit.h b/src/core/unit.h index 60bc2e3d35..fdaa3b8a8e 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include <errno.h> #include <stdbool.h> #include <stdlib.h> #include <sys/socket.h> @@ -8,6 +9,14 @@ #include "sd-id128.h" +/* Circular dependency with manager.h, needs to be defined before local includes */ +typedef enum UnitMountDependencyType { + UNIT_MOUNT_WANTS, + UNIT_MOUNT_REQUIRES, + _UNIT_MOUNT_DEPENDENCY_TYPE_MAX, + _UNIT_MOUNT_DEPENDENCY_TYPE_INVALID = -EINVAL, +} UnitMountDependencyType; + #include "bpf-program.h" #include "cgroup.h" #include "condition.h" @@ -216,9 +225,9 @@ typedef struct Unit { * Hashmap(UnitDependency → Hashmap(Unit* → UnitDependencyInfo)) */ Hashmap *dependencies; - /* Similar, for RequiresMountsFor= path dependencies. The key is the path, the value the - * UnitDependencyInfo type */ - Hashmap *requires_mounts_for; + /* Similar, for RequiresMountsFor= and WantsMountsFor= path dependencies. The key is the path, the + * value the UnitDependencyInfo type */ + Hashmap *mounts_for[_UNIT_MOUNT_DEPENDENCY_TYPE_MAX]; char *description; char **documentation; @@ -1001,7 +1010,7 @@ int unit_kill_context(Unit *u, KillContext *c, KillOperation k, PidRef *main_pid int unit_make_transient(Unit *u); -int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask); +int unit_add_mounts_for(Unit *u, const char *path, UnitDependencyMask mask, UnitMountDependencyType type); bool unit_type_supported(UnitType t); @@ -1101,6 +1110,10 @@ int unit_arm_timer(Unit *u, sd_event_source **source, bool relative, usec_t usec int unit_compare_priority(Unit *a, Unit *b); +UnitMountDependencyType unit_mount_dependency_type_from_string(const char *s) _const_; +const char* unit_mount_dependency_type_to_string(UnitMountDependencyType t) _const_; +UnitDependency unit_mount_dependency_type_to_dependency_type(UnitMountDependencyType t) _pure_; + /* Macros which append UNIT= or USER_UNIT= to the message */ #define log_unit_full_errno_zerook(unit, level, error, ...) \ diff --git a/src/creds/creds.c b/src/creds/creds.c index 101e5abf9b..7a98a5dcd3 100644 --- a/src/creds/creds.c +++ b/src/creds/creds.c @@ -350,14 +350,15 @@ static int write_blob(FILE *f, const void *data, size_t size) { } if (fwrite(data, 1, size, f) != size) - return log_error_errno(errno, "Failed to write credential data: %m"); + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write credential data: %m"); r = print_newline(f, data, size); if (r < 0) return r; - if (fflush(f) != 0) - return log_error_errno(errno, "Failed to flush output: %m"); + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to flush output: %m"); return 0; } diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c index 904e4cd3fa..93bbd833fe 100644 --- a/src/cryptsetup/cryptsetup-generator.c +++ b/src/cryptsetup/cryptsetup-generator.c @@ -229,7 +229,8 @@ static int generate_device_umount(const char *name, static int print_dependencies(FILE *f, const char* device_path, const char* timeout_value, bool canfail) { int r; - assert(!canfail || timeout_value); + assert(f); + assert(device_path); if (STR_IN_SET(device_path, "-", "none")) /* None, nothing to do */ @@ -263,11 +264,13 @@ static int print_dependencies(FILE *f, const char* device_path, const char* time fprintf(f, "After=%1$s\n", unit); if (canfail) { fprintf(f, "Wants=%1$s\n", unit); - r = write_drop_in_format(arg_dest, unit, 90, "device-timeout", - "# Automatically generated by systemd-cryptsetup-generator \n\n" - "[Unit]\nJobRunningTimeoutSec=%s", timeout_value); - if (r < 0) - return log_error_errno(r, "Failed to write device drop-in: %m"); + if (timeout_value) { + r = write_drop_in_format(arg_dest, unit, 90, "device-timeout", + "# Automatically generated by systemd-cryptsetup-generator \n\n" + "[Unit]\nJobRunningTimeoutSec=%s", timeout_value); + if (r < 0) + return log_error_errno(r, "Failed to write device drop-in: %m"); + } } else fprintf(f, "Requires=%1$s\n", unit); } else { @@ -276,7 +279,7 @@ static int print_dependencies(FILE *f, const char* device_path, const char* time if (!escaped_path) return log_oom(); - fprintf(f, "RequiresMountsFor=%s\n", escaped_path); + fprintf(f, "%s=%s\n", canfail ? "WantsMountsFor" : "RequiresMountsFor", escaped_path); } return 0; @@ -486,7 +489,7 @@ static int create_disk( if (key_file && !keydev) { r = print_dependencies(f, key_file, keyfile_timeout_value, - /* canfail= */ keyfile_can_timeout > 0); + /* canfail= */ keyfile_can_timeout > 0 || nofail); if (r < 0) return r; } @@ -494,8 +497,8 @@ static int create_disk( /* Check if a header option was specified */ if (detached_header > 0 && !headerdev) { r = print_dependencies(f, header_path, - NULL, - /* canfail= */ false); /* header is always necessary */ + /* timeout_value= */ NULL, + /* canfail= */ nofail); if (r < 0) return r; } diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 016f3baa7f..fd1a78aed2 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -416,15 +416,17 @@ static int write_before(FILE *f, const char *opts) { "x-systemd.before\0", "Before=%1$s\n"); } -static int write_requires_mounts_for(FILE *f, const char *opts) { +static int write_mounts_for(const char *x_opt, const char *unit_setting, FILE *f, const char *opts) { _cleanup_strv_free_ char **paths = NULL, **paths_escaped = NULL; _cleanup_free_ char *res = NULL; int r; + assert(x_opt); + assert(unit_setting); assert(f); assert(opts); - r = fstab_filter_options(opts, "x-systemd.requires-mounts-for\0", NULL, NULL, &paths, NULL); + r = fstab_filter_options(opts, x_opt, NULL, NULL, &paths, NULL); if (r < 0) return log_warning_errno(r, "Failed to parse options: %m"); if (r == 0) @@ -438,7 +440,7 @@ static int write_requires_mounts_for(FILE *f, const char *opts) { if (!res) return log_oom(); - fprintf(f, "RequiresMountsFor=%s\n", res); + fprintf(f, "%s=%s\n", unit_setting, res); return 0; } @@ -458,7 +460,10 @@ static int write_extra_dependencies(FILE *f, const char *opts) { r = write_before(f, opts); if (r < 0) return r; - r = write_requires_mounts_for(f, opts); + r = write_mounts_for("x-systemd.requires-mounts-for\0", "RequiresMountsFor", f, opts); + if (r < 0) + return r; + r = write_mounts_for("x-systemd.wants-mounts-for\0", "WantsMountsFor", f, opts); if (r < 0) return r; } diff --git a/src/home/homed-home.c b/src/home/homed-home.c index 37b3270841..7673e50435 100644 --- a/src/home/homed-home.c +++ b/src/home/homed-home.c @@ -2085,23 +2085,11 @@ int home_killall(Home *h) { if (r < 0) return r; if (r == 0) { - gid_t gid; - /* Child */ - gid = user_record_gid(h->record); - if (setresgid(gid, gid, gid) < 0) { - log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid); - _exit(EXIT_FAILURE); - } - - if (setgroups(0, NULL) < 0) { - log_error_errno(errno, "Failed to reset auxiliary groups list: %m"); - _exit(EXIT_FAILURE); - } - - if (setresuid(h->uid, h->uid, h->uid) < 0) { - log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", h->uid); + r = fully_set_uid_gid(h->uid, user_record_gid(h->record), /* supplementary_gids= */ NULL, /* n_supplementary_gids= */ 0); + if (r < 0) { + log_error_errno(r, "Failed to change UID/GID to " UID_FMT "/" GID_FMT ": %m", h->uid, user_record_gid(h->record)); _exit(EXIT_FAILURE); } diff --git a/src/home/homework-fscrypt.c b/src/home/homework-fscrypt.c index 6aae1d2626..ad0b69b021 100644 --- a/src/home/homework-fscrypt.c +++ b/src/home/homework-fscrypt.c @@ -319,23 +319,11 @@ int home_setup_fscrypt( if (r < 0) return log_error_errno(r, "Failed install encryption key in user's keyring: %m"); if (r == 0) { - gid_t gid; - /* Child */ - gid = user_record_gid(h); - if (setresgid(gid, gid, gid) < 0) { - log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid); - _exit(EXIT_FAILURE); - } - - if (setgroups(0, NULL) < 0) { - log_error_errno(errno, "Failed to reset auxiliary groups list: %m"); - _exit(EXIT_FAILURE); - } - - if (setresuid(h->uid, h->uid, h->uid) < 0) { - log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", h->uid); + r = fully_set_uid_gid(h->uid, user_record_gid(h), /* supplementary_gids= */ NULL, /* n_supplementary_gids= */ 0); + if (r < 0) { + log_error_errno(r, "Failed to change UID/GID to " UID_FMT "/" GID_FMT ": %m", h->uid, user_record_gid(h)); _exit(EXIT_FAILURE); } diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c index b1cf19205b..14fc160909 100644 --- a/src/hostname/hostnamectl.c +++ b/src/hostname/hostnamectl.c @@ -56,6 +56,8 @@ typedef struct StatusInfo { const char *hardware_model; const char *firmware_version; usec_t firmware_date; + sd_id128_t machine_id; + sd_id128_t boot_id; } StatusInfo; static const char* chassis_string_to_glyph(const char *chassis) { @@ -97,7 +99,6 @@ static const char *os_support_end_color(usec_t n, usec_t eol) { static int print_status_info(StatusInfo *i) { _cleanup_(table_unrefp) Table *table = NULL; - sd_id128_t mid = {}, bid = {}; TableCell *cell; int r; @@ -174,20 +175,18 @@ static int print_status_info(StatusInfo *i) { return table_log_add_error(r); } - r = sd_id128_get_machine(&mid); - if (r >= 0) { + if (!sd_id128_is_null(i->machine_id)) { r = table_add_many(table, TABLE_FIELD, "Machine ID", - TABLE_ID128, mid); + TABLE_ID128, i->machine_id); if (r < 0) return table_log_add_error(r); } - r = sd_id128_get_boot(&bid); - if (r >= 0) { + if (!sd_id128_is_null(i->boot_id)) { r = table_add_many(table, TABLE_FIELD, "Boot ID", - TABLE_ID128, bid); + TABLE_ID128, i->boot_id); if (r < 0) return table_log_add_error(r); } @@ -336,29 +335,29 @@ static int show_all_names(sd_bus *bus) { StatusInfo info = {}; static const struct bus_properties_map hostname_map[] = { - { "Hostname", "s", NULL, offsetof(StatusInfo, hostname) }, - { "StaticHostname", "s", NULL, offsetof(StatusInfo, static_hostname) }, - { "PrettyHostname", "s", NULL, offsetof(StatusInfo, pretty_hostname) }, - { "IconName", "s", NULL, offsetof(StatusInfo, icon_name) }, - { "Chassis", "s", NULL, offsetof(StatusInfo, chassis) }, - { "Deployment", "s", NULL, offsetof(StatusInfo, deployment) }, - { "Location", "s", NULL, offsetof(StatusInfo, location) }, - { "KernelName", "s", NULL, offsetof(StatusInfo, kernel_name) }, - { "KernelRelease", "s", NULL, offsetof(StatusInfo, kernel_release) }, - { "OperatingSystemPrettyName", "s", NULL, offsetof(StatusInfo, os_pretty_name) }, - { "OperatingSystemCPEName", "s", NULL, offsetof(StatusInfo, os_cpe_name) }, - { "OperatingSystemSupportEnd", "t", NULL, offsetof(StatusInfo, os_support_end) }, - { "HomeURL", "s", NULL, offsetof(StatusInfo, home_url) }, - { "HardwareVendor", "s", NULL, offsetof(StatusInfo, hardware_vendor) }, - { "HardwareModel", "s", NULL, offsetof(StatusInfo, hardware_model) }, - { "FirmwareVersion", "s", NULL, offsetof(StatusInfo, firmware_version) }, - { "FirmwareDate", "t", NULL, offsetof(StatusInfo, firmware_date) }, + { "Hostname", "s", NULL, offsetof(StatusInfo, hostname) }, + { "StaticHostname", "s", NULL, offsetof(StatusInfo, static_hostname) }, + { "PrettyHostname", "s", NULL, offsetof(StatusInfo, pretty_hostname) }, + { "IconName", "s", NULL, offsetof(StatusInfo, icon_name) }, + { "Chassis", "s", NULL, offsetof(StatusInfo, chassis) }, + { "Deployment", "s", NULL, offsetof(StatusInfo, deployment) }, + { "Location", "s", NULL, offsetof(StatusInfo, location) }, + { "KernelName", "s", NULL, offsetof(StatusInfo, kernel_name) }, + { "KernelRelease", "s", NULL, offsetof(StatusInfo, kernel_release) }, + { "OperatingSystemPrettyName", "s", NULL, offsetof(StatusInfo, os_pretty_name) }, + { "OperatingSystemCPEName", "s", NULL, offsetof(StatusInfo, os_cpe_name) }, + { "OperatingSystemSupportEnd", "t", NULL, offsetof(StatusInfo, os_support_end) }, + { "HomeURL", "s", NULL, offsetof(StatusInfo, home_url) }, + { "HardwareVendor", "s", NULL, offsetof(StatusInfo, hardware_vendor) }, + { "HardwareModel", "s", NULL, offsetof(StatusInfo, hardware_model) }, + { "FirmwareVersion", "s", NULL, offsetof(StatusInfo, firmware_version) }, + { "FirmwareDate", "t", NULL, offsetof(StatusInfo, firmware_date) }, + { "MachineID", "ay", bus_map_id128, offsetof(StatusInfo, machine_id) }, + { "BootID", "ay", bus_map_id128, offsetof(StatusInfo, boot_id) }, {} - }; - - static const struct bus_properties_map manager_map[] = { - { "Virtualization", "s", NULL, offsetof(StatusInfo, virtualization) }, - { "Architecture", "s", NULL, offsetof(StatusInfo, architecture) }, + }, manager_map[] = { + { "Virtualization", "s", NULL, offsetof(StatusInfo, virtualization) }, + { "Architecture", "s", NULL, offsetof(StatusInfo, architecture) }, {} }; @@ -388,6 +387,14 @@ static int show_all_names(sd_bus *bus) { if (r < 0) return log_error_errno(r, "Failed to query system properties: %s", bus_error_message(&error, r)); + /* For older version of hostnamed. */ + if (!arg_host) { + if (sd_id128_is_null(info.machine_id)) + (void) sd_id128_get_machine(&info.machine_id); + if (sd_id128_is_null(info.boot_id)) + (void) sd_id128_get_boot(&info.boot_id); + } + return print_status_info(&info); } diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index e1d53f2395..fc7a97fb99 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -995,6 +995,44 @@ static int property_get_uname_field( return sd_bus_message_append(reply, "s", (char*) &u + PTR_TO_SIZE(userdata)); } +static int property_get_machine_id( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + sd_id128_t id; + int r; + + r = sd_id128_get_machine(&id); + if (r < 0) + return r; + + return bus_property_get_id128(bus, path, interface, property, reply, &id, error); +} + +static int property_get_boot_id( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + sd_id128_t id; + int r; + + r = sd_id128_get_boot(&id); + if (r < 0) + return r; + + return bus_property_get_id128(bus, path, interface, property, reply, &id, error); +} + static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) { Context *c = ASSERT_PTR(userdata); const char *name; @@ -1302,7 +1340,7 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro usec_t firmware_date = USEC_INFINITY, eol = USEC_INFINITY; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; - sd_id128_t product_uuid = SD_ID128_NULL; + sd_id128_t machine_id, boot_id, product_uuid = SD_ID128_NULL; Context *c = ASSERT_PTR(userdata); bool privileged; struct utsname u; @@ -1369,6 +1407,14 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro if (c->data[PROP_OS_SUPPORT_END]) (void) os_release_support_ended(c->data[PROP_OS_SUPPORT_END], /* quiet= */ false, &eol); + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return log_error_errno(r, "Failed to get machine ID: %m"); + + r = sd_id128_get_boot(&boot_id); + if (r < 0) + return log_error_errno(r, "Failed to get boot ID: %m"); + r = json_build(&v, JSON_BUILD_OBJECT( JSON_BUILD_PAIR("Hostname", JSON_BUILD_STRING(hn)), JSON_BUILD_PAIR("StaticHostname", JSON_BUILD_STRING(c->data[PROP_STATIC_HOSTNAME])), @@ -1392,6 +1438,8 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro JSON_BUILD_PAIR("FirmwareVersion", JSON_BUILD_STRING(firmware_version)), JSON_BUILD_PAIR("FirmwareVendor", JSON_BUILD_STRING(firmware_vendor)), JSON_BUILD_PAIR_FINITE_USEC("FirmwareDate", firmware_date), + JSON_BUILD_PAIR_ID128("MachineID", machine_id), + JSON_BUILD_PAIR_ID128("BootID", boot_id), JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_ID128(product_uuid)), JSON_BUILD_PAIR_CONDITION(sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_NULL))); @@ -1436,6 +1484,8 @@ static const sd_bus_vtable hostname_vtable[] = { SD_BUS_PROPERTY("FirmwareVersion", "s", property_get_firmware_version, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("FirmwareVendor", "s", property_get_firmware_vendor, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("FirmwareDate", "t", property_get_firmware_date, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MachineID", "ay", property_get_machine_id, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("BootID", "ay", property_get_boot_id, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_METHOD_WITH_ARGS("SetHostname", SD_BUS_ARGS("s", hostname, "b", interactive), diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 09194710cb..9f0d0ec34a 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -32,6 +32,7 @@ #include "pretty-print.h" #include "sigbus.h" #include "signal-util.h" +#include "time-util.h" #include "tmpfile-util.h" #define JOURNAL_WAIT_TIMEOUT (10*USEC_PER_SEC) @@ -54,9 +55,10 @@ typedef struct RequestMeta { OutputMode mode; char *cursor; + usec_t since, until; int64_t n_skip; uint64_t n_entries; - bool n_entries_set; + bool n_entries_set, since_set, until_set; FILE *tmp; uint64_t delta, size; @@ -211,6 +213,17 @@ static ssize_t request_reader_entries( return MHD_CONTENT_READER_END_OF_STREAM; } + if (m->until_set) { + usec_t usec; + + r = sd_journal_get_realtime_usec(m->journal, &usec); + if (r < 0) { + log_error_errno(r, "Failed to determine timestamp: %m"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } + if (usec > m->until) + return MHD_CONTENT_READER_END_OF_STREAM; + } pos -= m->size; m->delta += m->size; @@ -292,60 +305,56 @@ static int request_parse_accept( return 0; } -static int request_parse_range( +static int request_parse_range_skip_and_n_entries( RequestMeta *m, - struct MHD_Connection *connection) { + const char *colon) { - const char *range, *colon, *colon2; + const char *p, *colon2; int r; - assert(m); - assert(connection); + colon2 = strchr(colon + 1, ':'); + if (colon2) { + _cleanup_free_ char *t = NULL; - range = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Range"); - if (!range) - return 0; + t = strndup(colon + 1, colon2 - colon - 1); + if (!t) + return -ENOMEM; - if (!startswith(range, "entries=")) - return 0; - - range += 8; - range += strspn(range, WHITESPACE); + r = safe_atoi64(t, &m->n_skip); + if (r < 0) + return r; + } - colon = strchr(range, ':'); - if (!colon) - m->cursor = strdup(range); - else { - const char *p; + p = (colon2 ?: colon) + 1; + r = safe_atou64(p, &m->n_entries); + if (r < 0) + return r; - colon2 = strchr(colon + 1, ':'); - if (colon2) { - _cleanup_free_ char *t = NULL; + if (m->n_entries <= 0) + return -EINVAL; - t = strndup(colon + 1, colon2 - colon - 1); - if (!t) - return -ENOMEM; + m->n_entries_set = true; - r = safe_atoi64(t, &m->n_skip); - if (r < 0) - return r; - } + return 0; +} - p = (colon2 ?: colon) + 1; - if (*p) { - r = safe_atou64(p, &m->n_entries); - if (r < 0) - return r; +static int request_parse_range_entries( + RequestMeta *m, + const char *entries_request) { - if (m->n_entries <= 0) - return -EINVAL; + const char *colon; + int r; - m->n_entries_set = true; - } + colon = strchr(entries_request, ':'); + if (!colon) + m->cursor = strdup(entries_request); + else { + r = request_parse_range_skip_and_n_entries(m, colon); + if (r < 0) + return r; - m->cursor = strndup(range, colon - range); + m->cursor = strndup(entries_request, colon - entries_request); } - if (!m->cursor) return -ENOMEM; @@ -356,6 +365,88 @@ static int request_parse_range( return 0; } +static int request_parse_range_time( + RequestMeta *m, + const char *time_request) { + + _cleanup_free_ char *until = NULL; + const char *colon; + int r; + + colon = strchr(time_request, ':'); + if (!colon) + return -EINVAL; + + if (colon - time_request > 0) { + _cleanup_free_ char *t = NULL; + + t = strndup(time_request, colon - time_request); + if (!t) + return -ENOMEM; + + r = parse_sec(t, &m->since); + if (r < 0) + return r; + + m->since_set = true; + } + + time_request = colon + 1; + colon = strchr(time_request, ':'); + if (!colon) + until = strdup(time_request); + else { + r = request_parse_range_skip_and_n_entries(m, colon); + if (r < 0) + return r; + + until = strndup(time_request, colon - time_request); + } + if (!until) + return -ENOMEM; + + if (!isempty(until)) { + r = parse_sec(until, &m->until); + if (r < 0) + return r; + + m->until_set = true; + if (m->until < m->since) + return -EINVAL; + } + + return 0; +} + +static int request_parse_range( + RequestMeta *m, + struct MHD_Connection *connection) { + + const char *range, *range_after_eq; + + assert(m); + assert(connection); + + range = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Range"); + if (!range) + return 0; + + m->n_skip = 0; + range_after_eq = startswith(range, "entries="); + if (range_after_eq) { + range_after_eq += strspn(range_after_eq, WHITESPACE); + return request_parse_range_entries(m, range_after_eq); + } + + range_after_eq = startswith(range, "realtime="); + if (startswith(range, "realtime=")) { + range_after_eq += strspn(range_after_eq, WHITESPACE); + return request_parse_range_time(m, range_after_eq); + } + + return 0; +} + static mhd_result request_parse_arguments_iterator( void *cls, enum MHD_ValueKind kind, @@ -496,10 +587,15 @@ static int request_handler_entries( if (m->cursor) r = sd_journal_seek_cursor(m->journal, m->cursor); + else if (m->since_set) + r = sd_journal_seek_realtime_usec(m->journal, m->since); else if (m->n_skip >= 0) r = sd_journal_seek_head(m->journal); + else if (m->until_set && m->n_skip < 0) + r = sd_journal_seek_realtime_usec(m->journal, m->until); else if (m->n_skip < 0) r = sd_journal_seek_tail(m->journal); + if (r < 0) return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to seek in journal."); diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index c52a8d13be..e79acc65bc 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -365,7 +365,7 @@ static int help(void) { " -M --machine=CONTAINER Operate on local container\n" " -m --merge Show entries from all available journals\n" " -D --directory=PATH Show journal files from directory\n" - " --file=PATH Show journal file\n" + " -i --file=PATH Show journal file\n" " --root=PATH Operate on an alternate filesystem root\n" " --image=PATH Operate on disk image as filesystem root\n" " --image-policy=POLICY Specify disk image dissection policy\n" @@ -461,7 +461,6 @@ static int parse_argv(int argc, char *argv[]) { ARG_HEADER, ARG_FACILITY, ARG_SETUP_KEYS, - ARG_FILE, ARG_INTERVAL, ARG_VERIFY, ARG_VERIFY_KEY, @@ -514,7 +513,7 @@ static int parse_argv(int argc, char *argv[]) { { "system", no_argument, NULL, ARG_SYSTEM }, { "user", no_argument, NULL, ARG_USER }, { "directory", required_argument, NULL, 'D' }, - { "file", required_argument, NULL, ARG_FILE }, + { "file", required_argument, NULL, 'i' }, { "root", required_argument, NULL, ARG_ROOT }, { "image", required_argument, NULL, ARG_IMAGE }, { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY }, @@ -565,7 +564,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:g:c:S:U:t:u:NF:xrM:", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:g:c:S:U:t:u:NF:xrM:i:", options, NULL)) >= 0) switch (c) { @@ -727,7 +726,7 @@ static int parse_argv(int argc, char *argv[]) { arg_directory = optarg; break; - case ARG_FILE: + case 'i': if (streq(optarg, "-")) /* An undocumented feature: we can read journal files from STDIN. We don't document * this though, since after all we only support this for mmap-able, seekable files, and diff --git a/src/libsystemd/sd-journal/catalog.c b/src/libsystemd/sd-journal/catalog.c index ae91534198..4c5562ba48 100644 --- a/src/libsystemd/sd-journal/catalog.c +++ b/src/libsystemd/sd-journal/catalog.c @@ -16,6 +16,7 @@ #include "conf-files.h" #include "fd-util.h" #include "fileio.h" +#include "fs-util.h" #include "hashmap.h" #include "log.h" #include "memory-util.h" @@ -382,10 +383,8 @@ static int64_t write_catalog( CatalogItem *items, size_t n) { + _cleanup_(unlink_and_freep) char *p = NULL; _cleanup_fclose_ FILE *w = NULL; - _cleanup_free_ char *p = NULL; - CatalogHeader header; - size_t k; int r; r = mkdir_parents(database, 0755); @@ -394,54 +393,35 @@ static int64_t write_catalog( r = fopen_temporary(database, &w, &p); if (r < 0) - return log_error_errno(r, "Failed to open database for writing: %s: %m", - database); + return log_error_errno(r, "Failed to open database for writing: %s: %m", database); - header = (CatalogHeader) { + CatalogHeader header = { .signature = CATALOG_SIGNATURE, .header_size = htole64(CONST_ALIGN_TO(sizeof(CatalogHeader), 8)), .catalog_item_size = htole64(sizeof(CatalogItem)), .n_items = htole64(n), }; - r = -EIO; + if (fwrite(&header, sizeof(header), 1, w) != 1) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "%s: failed to write header.", p); - k = fwrite(&header, 1, sizeof(header), w); - if (k != sizeof(header)) { - log_error("%s: failed to write header.", p); - goto error; - } + if (fwrite(items, sizeof(CatalogItem), n, w) != n) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "%s: failed to write database.", p); - k = fwrite(items, 1, n * sizeof(CatalogItem), w); - if (k != n * sizeof(CatalogItem)) { - log_error("%s: failed to write database.", p); - goto error; - } - - k = fwrite(sb->buf, 1, sb->len, w); - if (k != sb->len) { - log_error("%s: failed to write strings.", p); - goto error; - } + if (fwrite(sb->buf, sb->len, 1, w) != 1) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "%s: failed to write strings.", p); r = fflush_and_check(w); - if (r < 0) { - log_error_errno(r, "%s: failed to write database: %m", p); - goto error; - } + if (r < 0) + return log_error_errno(r, "%s: failed to write database: %m", p); (void) fchmod(fileno(w), 0644); - if (rename(p, database) < 0) { - r = log_error_errno(errno, "rename (%s -> %s) failed: %m", p, database); - goto error; - } + if (rename(p, database) < 0) + return log_error_errno(errno, "rename (%s -> %s) failed: %m", p, database); + p = mfree(p); /* free without unlinking */ return ftello(w); - -error: - (void) unlink(p); - return r; } int catalog_update(const char* database, const char* root, const char* const* dirs) { diff --git a/src/libsystemd/sd-journal/journal-verify.c b/src/libsystemd/sd-journal/journal-verify.c index 8fc53beb42..bdaa01d66f 100644 --- a/src/libsystemd/sd-journal/journal-verify.c +++ b/src/libsystemd/sd-journal/journal-verify.c @@ -384,7 +384,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o static int write_uint64(FILE *fp, uint64_t p) { if (fwrite(&p, sizeof(p), 1, fp) != 1) - return -errno; + return -EIO; return 0; } diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 3f8f1aa9be..7fc6efc9da 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -992,11 +992,17 @@ static int show_session(int argc, char *argv[], void *userdata) { pager_open(arg_pager_flags); if (argc <= 1) { + _cleanup_free_ char *path = NULL; + /* If no argument is specified inspect the manager itself */ if (properties) return show_properties(bus, "/org/freedesktop/login1"); - return print_session_status_info(bus, "/org/freedesktop/login1/session/auto"); + r = get_bus_path_by_id(bus, "session", "GetSession", "auto", &path); + if (r < 0) + return r; + + return print_session_status_info(bus, path); } for (int i = 1, first = true; i < argc; i++, first = false) { @@ -1083,11 +1089,17 @@ static int show_seat(int argc, char *argv[], void *userdata) { pager_open(arg_pager_flags); if (argc <= 1) { + _cleanup_free_ char *path = NULL; + /* If no argument is specified inspect the manager itself */ if (properties) return show_properties(bus, "/org/freedesktop/login1"); - return print_seat_status_info(bus, "/org/freedesktop/login1/seat/auto"); + r = get_bus_path_by_id(bus, "seat", "GetSeat", "auto", &path); + if (r < 0) + return r; + + return print_seat_status_info(bus, path); } for (int i = 1, first = true; i < argc; i++, first = false) { diff --git a/src/login/logind-action.c b/src/login/logind-action.c index e678edd66f..b9cb9b818d 100644 --- a/src/login/logind-action.c +++ b/src/login/logind-action.c @@ -133,6 +133,49 @@ const HandleActionData* handle_action_lookup(HandleAction action) { return &handle_action_data_table[action]; } +/* The order in which we try each sleep operation. We should typically prefer operations without a delay, + * i.e. s2h and suspend, and use hibernation at last since it requires minimum hardware support. + * hybrid-sleep is disabled by default, and thus should be ordered before suspend if manually chosen by user, + * since it implies suspend and will probably never be selected by us otherwise. */ +static const HandleAction sleep_actions[] = { + HANDLE_SUSPEND_THEN_HIBERNATE, + HANDLE_HYBRID_SLEEP, + HANDLE_SUSPEND, + HANDLE_HIBERNATE, +}; + +int handle_action_get_enabled_sleep_actions(HandleActionSleepMask mask, char ***ret) { + _cleanup_strv_free_ char **actions = NULL; + int r; + + assert(ret); + + FOREACH_ARRAY(i, sleep_actions, ELEMENTSOF(sleep_actions)) + if (FLAGS_SET(mask, 1U << *i)) { + r = strv_extend(&actions, handle_action_to_string(*i)); + if (r < 0) + return r; + } + + *ret = TAKE_PTR(actions); + return 0; +} + +HandleAction handle_action_sleep_select(HandleActionSleepMask mask) { + + FOREACH_ARRAY(i, sleep_actions, ELEMENTSOF(sleep_actions)) { + HandleActionSleepMask a = 1U << *i; + + if (!FLAGS_SET(mask, a)) + continue; + + if (sleep_supported(handle_action_lookup(*i)->sleep_operation) > 0) + return *i; + } + + return _HANDLE_ACTION_INVALID; +} + static int handle_action_execute( Manager *m, HandleAction handle, @@ -158,6 +201,7 @@ static int handle_action_execute( int r; assert(m); + assert(!IN_SET(handle, HANDLE_IGNORE, HANDLE_LOCK, HANDLE_SLEEP)); if (handle == HANDLE_KEXEC && access(KEXEC, X_OK) < 0) return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), @@ -208,11 +252,22 @@ static int handle_action_sleep_execute( bool ignore_inhibited, bool is_edge) { - bool supported; - assert(m); assert(HANDLE_ACTION_IS_SLEEP(handle)); + if (handle == HANDLE_SLEEP) { + HandleAction a; + + a = handle_action_sleep_select(m->handle_action_sleep_mask); + if (a < 0) + return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "None of the configured sleep operations are supported, ignoring."); + + return handle_action_sleep_execute(m, a, ignore_inhibited, is_edge); + } + + bool supported; + if (handle == HANDLE_SUSPEND) supported = sleep_supported(SLEEP_SUSPEND) > 0; else if (handle == HANDLE_HIBERNATE) @@ -302,8 +357,9 @@ static const char* const handle_action_verb_table[_HANDLE_ACTION_MAX] = { [HANDLE_SOFT_REBOOT] = "soft-reboot", [HANDLE_SUSPEND] = "suspend", [HANDLE_HIBERNATE] = "hibernate", - [HANDLE_HYBRID_SLEEP] = "enter hybrid sleep", + [HANDLE_HYBRID_SLEEP] = "hybrid sleep", [HANDLE_SUSPEND_THEN_HIBERNATE] = "suspend and later hibernate", + [HANDLE_SLEEP] = "sleep", [HANDLE_FACTORY_RESET] = "perform a factory reset", [HANDLE_LOCK] = "be locked", }; @@ -323,9 +379,66 @@ static const char* const handle_action_table[_HANDLE_ACTION_MAX] = { [HANDLE_HIBERNATE] = "hibernate", [HANDLE_HYBRID_SLEEP] = "hybrid-sleep", [HANDLE_SUSPEND_THEN_HIBERNATE] = "suspend-then-hibernate", + [HANDLE_SLEEP] = "sleep", [HANDLE_FACTORY_RESET] = "factory-reset", [HANDLE_LOCK] = "lock", }; DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction); DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_action, handle_action, HandleAction, "Failed to parse handle action setting"); + +int config_parse_handle_action_sleep( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + HandleActionSleepMask *mask = ASSERT_PTR(data); + _cleanup_strv_free_ char **actions = NULL; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) + goto empty; + + if (strv_split_full(&actions, rvalue, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE) < 0) + return log_oom(); + + *mask = 0; + + STRV_FOREACH(action, actions) { + HandleAction a; + + a = handle_action_from_string(*action); + if (a < 0) { + log_syntax(unit, LOG_WARNING, filename, line, a, + "Failed to parse SleepOperation '%s', ignoring: %m", *action); + continue; + } + + if (!HANDLE_ACTION_IS_SLEEP(a) || a == HANDLE_SLEEP) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "HandleAction '%s' is not a sleep operation, ignoring: %m", *action); + continue; + } + + *mask |= 1U << a; + } + + if (*mask == 0) + goto empty; + + return 0; + +empty: + *mask = HANDLE_ACTION_SLEEP_MASK_DEFAULT; + return 0; +} diff --git a/src/login/logind-action.h b/src/login/logind-action.h index dbca963f2d..5ee86486ec 100644 --- a/src/login/logind-action.h +++ b/src/login/logind-action.h @@ -5,6 +5,7 @@ typedef enum HandleAction { HANDLE_IGNORE, + HANDLE_POWEROFF, _HANDLE_ACTION_SHUTDOWN_FIRST = HANDLE_POWEROFF, HANDLE_REBOOT, @@ -12,20 +13,33 @@ typedef enum HandleAction { HANDLE_KEXEC, HANDLE_SOFT_REBOOT, _HANDLE_ACTION_SHUTDOWN_LAST = HANDLE_SOFT_REBOOT, + HANDLE_SUSPEND, _HANDLE_ACTION_SLEEP_FIRST = HANDLE_SUSPEND, HANDLE_HIBERNATE, HANDLE_HYBRID_SLEEP, HANDLE_SUSPEND_THEN_HIBERNATE, - _HANDLE_ACTION_SLEEP_LAST = HANDLE_SUSPEND_THEN_HIBERNATE, + HANDLE_SLEEP, /* A "high-level" action that automatically choose an appropriate low-level sleep action */ + _HANDLE_ACTION_SLEEP_LAST = HANDLE_SLEEP, + HANDLE_LOCK, HANDLE_FACTORY_RESET, + _HANDLE_ACTION_MAX, _HANDLE_ACTION_INVALID = -EINVAL, } HandleAction; typedef struct HandleActionData HandleActionData; +typedef enum HandleActionSleepMask { + HANDLE_SLEEP_SUSPEND_MASK = 1U << HANDLE_SUSPEND, + HANDLE_SLEEP_HIBERNATE_MASK = 1U << HANDLE_HIBERNATE, + HANDLE_SLEEP_HYBRID_SLEEP_MASK = 1U << HANDLE_HYBRID_SLEEP, + HANDLE_SLEEP_SUSPEND_THEN_HIBERNATE_MASK = 1U << HANDLE_SUSPEND_THEN_HIBERNATE, +} HandleActionSleepMask; + +#define HANDLE_ACTION_SLEEP_MASK_DEFAULT (HANDLE_SLEEP_SUSPEND_THEN_HIBERNATE_MASK|HANDLE_SLEEP_SUSPEND_MASK|HANDLE_SLEEP_HIBERNATE_MASK) + #include "logind-inhibit.h" #include "logind.h" #include "sleep-config.h" @@ -55,6 +69,9 @@ struct HandleActionData { const char* log_verb; }; +int handle_action_get_enabled_sleep_actions(HandleActionSleepMask mask, char ***ret); +HandleAction handle_action_sleep_select(HandleActionSleepMask mask); + int manager_handle_action( Manager *m, InhibitWhat inhibit_key, @@ -70,3 +87,5 @@ HandleAction handle_action_from_string(const char *s) _pure_; const HandleActionData* handle_action_lookup(HandleAction handle); CONFIG_PARSER_PROTOTYPE(config_parse_handle_action); + +CONFIG_PARSER_PROTOTYPE(config_parse_handle_action_sleep); diff --git a/src/login/logind-core.c b/src/login/logind-core.c index f15008e0df..becc21b0de 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -40,6 +40,8 @@ void manager_reset_config(Manager *m) { m->inhibit_delay_max = 5 * USEC_PER_SEC; m->user_stop_delay = 10 * USEC_PER_SEC; + m->handle_action_sleep_mask = HANDLE_ACTION_SLEEP_MASK_DEFAULT; + m->handle_power_key = HANDLE_POWEROFF; m->handle_power_key_long_press = HANDLE_IGNORE; m->handle_reboot_key = HANDLE_REBOOT; diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index ae9b2cbf36..34992b5681 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -342,6 +342,29 @@ static int property_get_preparing( return sd_bus_message_append(reply, "b", b); } +static int property_get_sleep_operations( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = ASSERT_PTR(userdata); + _cleanup_strv_free_ char **actions = NULL; + int r; + + assert(bus); + assert(reply); + + r = handle_action_get_enabled_sleep_actions(m->handle_action_sleep_mask, &actions); + if (r < 0) + return r; + + return sd_bus_message_append_strv(reply, actions); +} + static int property_get_scheduled_shutdown( sd_bus *bus, const char *path, @@ -361,9 +384,10 @@ static int property_get_scheduled_shutdown( if (r < 0) return r; - r = sd_bus_message_append(reply, "st", - m->scheduled_shutdown_action ? handle_action_to_string(m->scheduled_shutdown_action->handle) : NULL, - m->scheduled_shutdown_timeout); + r = sd_bus_message_append( + reply, "st", + handle_action_to_string(m->scheduled_shutdown_action), + m->scheduled_shutdown_timeout); if (r < 0) return r; @@ -1993,7 +2017,7 @@ static int setup_wall_message_timer(Manager *m, sd_bus_message* message) { static int method_do_shutdown_or_sleep( Manager *m, sd_bus_message *message, - const HandleActionData *a, + HandleAction action, bool with_flags, sd_bus_error *error) { @@ -2002,7 +2026,7 @@ static int method_do_shutdown_or_sleep( assert(m); assert(message); - assert(a); + assert(HANDLE_ACTION_IS_SHUTDOWN(action) || HANDLE_ACTION_IS_SLEEP(action)); if (with_flags) { /* New style method: with flags parameter (and interactive bool in the bus message header) */ @@ -2017,7 +2041,7 @@ static int method_do_shutdown_or_sleep( return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Both reboot via kexec and soft reboot selected, which is not supported"); - if (a->handle != HANDLE_REBOOT) { + if (action != HANDLE_REBOOT) { if (flags & SD_LOGIND_REBOOT_VIA_KEXEC) return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Reboot via kexec option is only applicable with reboot operations"); @@ -2038,20 +2062,32 @@ static int method_do_shutdown_or_sleep( flags = interactive ? SD_LOGIND_INTERACTIVE : 0; } + const HandleActionData *a = NULL; + if ((flags & SD_LOGIND_SOFT_REBOOT) || ((flags & SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP) && path_is_os_tree("/run/nextroot") > 0)) a = handle_action_lookup(HANDLE_SOFT_REBOOT); else if ((flags & SD_LOGIND_REBOOT_VIA_KEXEC) && kexec_loaded()) a = handle_action_lookup(HANDLE_KEXEC); - /* Don't allow multiple jobs being executed at the same time */ - if (m->delayed_action) - return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, - "There's already a shutdown or sleep operation in progress"); + if (action == HANDLE_SLEEP) { + HandleAction selected; - if (a->sleep_operation >= 0) { + selected = handle_action_sleep_select(m->handle_action_sleep_mask); + if (selected < 0) + return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, + "None of the configured sleep operations are supported"); + + assert_se(a = handle_action_lookup(selected)); + + } else if (HANDLE_ACTION_IS_SLEEP(action)) { SleepSupport support; + assert_se(a = handle_action_lookup(action)); + + assert(a->sleep_operation >= 0); + assert(a->sleep_operation < _SLEEP_OPERATION_MAX); + r = sleep_supported_full(a->sleep_operation, &support); if (r < 0) return r; @@ -2082,7 +2118,8 @@ static int method_do_shutdown_or_sleep( assert_not_reached(); } - } + } else if (!a) + assert_se(a = handle_action_lookup(action)); r = verify_shutdown_creds(m, message, a, flags, error); if (r != 0) @@ -2093,7 +2130,7 @@ static int method_do_shutdown_or_sleep( reset_scheduled_shutdown(m); m->scheduled_shutdown_timeout = 0; - m->scheduled_shutdown_action = a; + m->scheduled_shutdown_action = action; (void) setup_wall_message_timer(m, message); @@ -2109,7 +2146,7 @@ static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error return method_do_shutdown_or_sleep( m, message, - handle_action_lookup(HANDLE_POWEROFF), + HANDLE_POWEROFF, sd_bus_message_is_method_call(message, NULL, "PowerOffWithFlags"), error); } @@ -2119,7 +2156,7 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error * return method_do_shutdown_or_sleep( m, message, - handle_action_lookup(HANDLE_REBOOT), + HANDLE_REBOOT, sd_bus_message_is_method_call(message, NULL, "RebootWithFlags"), error); } @@ -2129,7 +2166,7 @@ static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *er return method_do_shutdown_or_sleep( m, message, - handle_action_lookup(HANDLE_HALT), + HANDLE_HALT, sd_bus_message_is_method_call(message, NULL, "HaltWithFlags"), error); } @@ -2139,7 +2176,7 @@ static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error return method_do_shutdown_or_sleep( m, message, - handle_action_lookup(HANDLE_SUSPEND), + HANDLE_SUSPEND, sd_bus_message_is_method_call(message, NULL, "SuspendWithFlags"), error); } @@ -2149,7 +2186,7 @@ static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_erro return method_do_shutdown_or_sleep( m, message, - handle_action_lookup(HANDLE_HIBERNATE), + HANDLE_HIBERNATE, sd_bus_message_is_method_call(message, NULL, "HibernateWithFlags"), error); } @@ -2159,7 +2196,7 @@ static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_e return method_do_shutdown_or_sleep( m, message, - handle_action_lookup(HANDLE_HYBRID_SLEEP), + HANDLE_HYBRID_SLEEP, sd_bus_message_is_method_call(message, NULL, "HybridSleepWithFlags"), error); } @@ -2169,11 +2206,21 @@ static int method_suspend_then_hibernate(sd_bus_message *message, void *userdata return method_do_shutdown_or_sleep( m, message, - handle_action_lookup(HANDLE_SUSPEND_THEN_HIBERNATE), + HANDLE_SUSPEND_THEN_HIBERNATE, sd_bus_message_is_method_call(message, NULL, "SuspendThenHibernateWithFlags"), error); } +static int method_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_do_shutdown_or_sleep( + m, message, + HANDLE_SLEEP, + /* with_flags = */ true, + error); +} + static int nologin_timeout_handler( sd_event_source *s, uint64_t usec, @@ -2232,7 +2279,7 @@ void manager_load_scheduled_shutdown(Manager *m) { return; /* assign parsed type only after we know usec is also valid */ - m->scheduled_shutdown_action = handle_action_lookup(handle); + m->scheduled_shutdown_action = handle; if (warn_wall) { r = parse_boolean(warn_wall); @@ -2275,7 +2322,7 @@ static int update_schedule_file(Manager *m) { int r; assert(m); - assert(m->scheduled_shutdown_action); + assert(handle_action_valid(m->scheduled_shutdown_action)); r = mkdir_parents_label(SHUTDOWN_SCHEDULE_FILE, 0755); if (r < 0) @@ -2289,7 +2336,7 @@ static int update_schedule_file(Manager *m) { serialize_usec(f, "USEC", m->scheduled_shutdown_timeout); serialize_item_format(f, "WARN_WALL", "%s", one_zero(m->enable_wall_messages)); - serialize_item_format(f, "MODE", "%s", handle_action_to_string(m->scheduled_shutdown_action->handle)); + serialize_item_format(f, "MODE", "%s", handle_action_to_string(m->scheduled_shutdown_action)); serialize_item_format(f, "UID", UID_FMT, m->scheduled_shutdown_uid); if (m->scheduled_shutdown_tty) @@ -2326,7 +2373,7 @@ static void reset_scheduled_shutdown(Manager *m) { m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source); m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source); - m->scheduled_shutdown_action = NULL; + m->scheduled_shutdown_action = _HANDLE_ACTION_INVALID; m->scheduled_shutdown_timeout = USEC_INFINITY; m->scheduled_shutdown_uid = UID_INVALID; m->scheduled_shutdown_tty = mfree(m->scheduled_shutdown_tty); @@ -2345,13 +2392,12 @@ static int manager_scheduled_shutdown_handler( uint64_t usec, void *userdata) { - const HandleActionData *a = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; Manager *m = ASSERT_PTR(userdata); + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + const HandleActionData *a; int r; - a = m->scheduled_shutdown_action; - assert(a); + assert_se(a = handle_action_lookup(m->scheduled_shutdown_action)); /* Don't allow multiple jobs being executed at the same time */ if (m->delayed_action) { @@ -2373,7 +2419,7 @@ static int manager_scheduled_shutdown_handler( return 0; } - r = bus_manager_shutdown_or_sleep_now_or_later(m, m->scheduled_shutdown_action, &error); + r = bus_manager_shutdown_or_sleep_now_or_later(m, a, &error); if (r < 0) { log_error_errno(r, "Scheduled shutdown to %s failed: %m", a->target); goto error; @@ -2410,15 +2456,14 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_ if (!HANDLE_ACTION_IS_SHUTDOWN(handle)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type: %s", type); - a = handle_action_lookup(handle); - assert(a); + assert_se(a = handle_action_lookup(handle)); assert(a->polkit_action); r = verify_shutdown_creds(m, message, a, 0, error); if (r != 0) return r; - m->scheduled_shutdown_action = a; + m->scheduled_shutdown_action = handle; m->shutdown_dry_run = dry_run; m->scheduled_shutdown_timeout = elapse; @@ -2474,12 +2519,11 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd assert(message); - cancelled = m->scheduled_shutdown_action - && !IN_SET(m->scheduled_shutdown_action->handle, HANDLE_IGNORE, _HANDLE_ACTION_INVALID); + cancelled = handle_action_valid(m->scheduled_shutdown_action) && m->scheduled_shutdown_action != HANDLE_IGNORE; if (!cancelled) return sd_bus_reply_method_return(message, "b", false); - a = m->scheduled_shutdown_action; + assert_se(a = handle_action_lookup(m->scheduled_shutdown_action)); if (!a->polkit_action) return sd_bus_error_set(error, SD_BUS_ERROR_AUTH_FAILED, "Unsupported shutdown type"); @@ -2528,28 +2572,44 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd static int method_can_shutdown_or_sleep( Manager *m, sd_bus_message *message, - const HandleActionData *a, + HandleAction action, sd_bus_error *error) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; bool multiple_sessions, challenge, blocked; + const HandleActionData *a; const char *result = NULL; uid_t uid; int r; assert(m); assert(message); - assert(a); + assert(HANDLE_ACTION_IS_SHUTDOWN(action) || HANDLE_ACTION_IS_SLEEP(action)); + + if (action == HANDLE_SLEEP) { + HandleAction selected; + + selected = handle_action_sleep_select(m->handle_action_sleep_mask); + if (selected < 0) + return sd_bus_reply_method_return(message, "s", "na"); - if (a->sleep_operation >= 0) { + assert_se(a = handle_action_lookup(selected)); + + } else if (HANDLE_ACTION_IS_SLEEP(action)) { SleepSupport support; + assert_se(a = handle_action_lookup(action)); + + assert(a->sleep_operation >= 0); + assert(a->sleep_operation < _SLEEP_OPERATION_MAX); + r = sleep_supported_full(a->sleep_operation, &support); if (r < 0) return r; if (r == 0) return sd_bus_reply_method_return(message, "s", support == SLEEP_DISABLED ? "no" : "na"); - } + } else + assert_se(a = handle_action_lookup(action)); r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); if (r < 0) @@ -2566,22 +2626,16 @@ static int method_can_shutdown_or_sleep( multiple_sessions = r > 0; blocked = manager_is_inhibited(m, a->inhibit_what, INHIBIT_BLOCK, NULL, false, true, uid, NULL); - HandleAction handle = handle_action_from_string(sleep_operation_to_string(a->sleep_operation)); - if (handle >= 0) { - const char *target; + if (a->target) { + _cleanup_free_ char *load_state = NULL; - target = handle_action_lookup(handle)->target; - if (target) { - _cleanup_free_ char *load_state = NULL; - - r = unit_load_state(m->bus, target, &load_state); - if (r < 0) - return r; + r = unit_load_state(m->bus, a->target, &load_state); + if (r < 0) + return r; - if (!streq(load_state, "loaded")) { - result = "no"; - goto finish; - } + if (!streq(load_state, "loaded")) { + result = "no"; + goto finish; } } @@ -2636,57 +2690,49 @@ static int method_can_shutdown_or_sleep( static int method_can_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; - return method_can_shutdown_or_sleep( - m, message, handle_action_lookup(HANDLE_POWEROFF), - error); + return method_can_shutdown_or_sleep(m, message, HANDLE_POWEROFF, error); } static int method_can_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; - return method_can_shutdown_or_sleep( - m, message, handle_action_lookup(HANDLE_REBOOT), - error); + return method_can_shutdown_or_sleep(m, message, HANDLE_REBOOT, error); } static int method_can_halt(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; - return method_can_shutdown_or_sleep( - m, message, handle_action_lookup(HANDLE_HALT), - error); + return method_can_shutdown_or_sleep(m, message, HANDLE_HALT, error); } static int method_can_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; - return method_can_shutdown_or_sleep( - m, message, handle_action_lookup(HANDLE_SUSPEND), - error); + return method_can_shutdown_or_sleep(m, message, HANDLE_SUSPEND, error); } static int method_can_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; - return method_can_shutdown_or_sleep( - m, message, handle_action_lookup(HANDLE_HIBERNATE), - error); + return method_can_shutdown_or_sleep(m, message, HANDLE_HIBERNATE, error); } static int method_can_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; - return method_can_shutdown_or_sleep( - m, message, handle_action_lookup(HANDLE_HYBRID_SLEEP), - error); + return method_can_shutdown_or_sleep(m, message, HANDLE_HYBRID_SLEEP, error); } static int method_can_suspend_then_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; - return method_can_shutdown_or_sleep( - m, message, handle_action_lookup(HANDLE_SUSPEND_THEN_HIBERNATE), - error); + return method_can_shutdown_or_sleep(m, message, HANDLE_SUSPEND_THEN_HIBERNATE, error); +} + +static int method_can_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_can_shutdown_or_sleep(m, message, HANDLE_SLEEP, error); } static int property_get_reboot_parameter( @@ -3521,6 +3567,7 @@ static const sd_bus_vtable manager_vtable[] = { SD_BUS_PROPERTY("DelayInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("InhibitDelayMaxUSec", "t", NULL, offsetof(Manager, inhibit_delay_max), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("UserStopDelayUSec", "t", NULL, offsetof(Manager, user_stop_delay), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SleepOperation", "as", property_get_sleep_operations, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("HandlePowerKey", "s", property_get_handle_action, offsetof(Manager, handle_power_key), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("HandlePowerKeyLongPress", "s", property_get_handle_action, offsetof(Manager, handle_power_key_long_press), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("HandleRebootKey", "s", property_get_handle_action, offsetof(Manager, handle_reboot_key), SD_BUS_VTABLE_PROPERTY_CONST), @@ -3792,6 +3839,11 @@ static const sd_bus_vtable manager_vtable[] = { SD_BUS_NO_RESULT, method_suspend_then_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("Sleep", + SD_BUS_ARGS("t", flags), + SD_BUS_NO_RESULT, + method_sleep, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD_WITH_ARGS("CanPowerOff", SD_BUS_NO_ARGS, SD_BUS_RESULT("s", result), @@ -3827,6 +3879,11 @@ static const sd_bus_vtable manager_vtable[] = { SD_BUS_RESULT("s", result), method_can_suspend_then_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("CanSleep", + SD_BUS_NO_ARGS, + SD_BUS_RESULT("s", result), + method_can_sleep, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD_WITH_ARGS("ScheduleShutdown", SD_BUS_ARGS("s", type, "t", usec), SD_BUS_NO_RESULT, diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf index c95a3b2dc3..7d1520b469 100644 --- a/src/login/logind-gperf.gperf +++ b/src/login/logind-gperf.gperf @@ -25,6 +25,7 @@ Login.KillOnlyUsers, config_parse_strv, 0, offse Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users) Login.InhibitDelayMaxSec, config_parse_sec, 0, offsetof(Manager, inhibit_delay_max) Login.UserStopDelaySec, config_parse_sec, 0, offsetof(Manager, user_stop_delay) +Login.SleepOperation, config_parse_handle_action_sleep, 0, offsetof(Manager, handle_action_sleep_mask) Login.HandlePowerKey, config_parse_handle_action, 0, offsetof(Manager, handle_power_key) Login.HandlePowerKeyLongPress, config_parse_handle_action, 0, offsetof(Manager, handle_power_key_long_press) Login.HandleRebootKey, config_parse_handle_action, 0, offsetof(Manager, handle_reboot_key) diff --git a/src/login/logind-wall.c b/src/login/logind-wall.c index 97b74e9e04..ff70a595f1 100644 --- a/src/login/logind-wall.c +++ b/src/login/logind-wall.c @@ -42,7 +42,7 @@ static usec_t when_wall(usec_t n, usec_t elapse) { bool logind_wall_tty_filter(const char *tty, bool is_local, void *userdata) { Manager *m = ASSERT_PTR(userdata); - assert(m->scheduled_shutdown_action); + assert(handle_action_valid(m->scheduled_shutdown_action)); const char *p = path_startswith(tty, "/dev/"); if (!p) @@ -52,7 +52,7 @@ bool logind_wall_tty_filter(const char *tty, bool is_local, void *userdata) { * can assume that if the system enters sleep or hibernation, this will be visible in an obvious way * for any local user. And once the systems exits sleep or hibernation, the notification would be * just noise, in particular for auto-suspend. */ - if (is_local && HANDLE_ACTION_IS_SLEEP(m->scheduled_shutdown_action->handle)) + if (is_local && HANDLE_ACTION_IS_SLEEP(m->scheduled_shutdown_action)) return false; return !streq_ptr(p, m->scheduled_shutdown_tty); @@ -61,7 +61,7 @@ bool logind_wall_tty_filter(const char *tty, bool is_local, void *userdata) { static int warn_wall(Manager *m, usec_t n) { assert(m); - if (!m->scheduled_shutdown_action) + if (!handle_action_valid(m->scheduled_shutdown_action)) return 0; bool left = m->scheduled_shutdown_timeout > n; @@ -70,7 +70,7 @@ static int warn_wall(Manager *m, usec_t n) { if (asprintf(&l, "%s%sThe system will %s %s%s!", strempty(m->wall_message), isempty(m->wall_message) ? "" : "\n", - handle_action_verb_to_string(m->scheduled_shutdown_action->handle), + handle_action_verb_to_string(m->scheduled_shutdown_action), left ? "at " : "now", left ? FORMAT_TIMESTAMP(m->scheduled_shutdown_timeout) : "") < 0) { @@ -84,7 +84,7 @@ static int warn_wall(Manager *m, usec_t n) { log_struct(level, LOG_MESSAGE("%s", l), - "ACTION=%s", handle_action_to_string(m->scheduled_shutdown_action->handle), + "ACTION=%s", handle_action_to_string(m->scheduled_shutdown_action), "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_SCHEDULED_STR, username ? "OPERATOR=%s" : NULL, username); @@ -134,7 +134,7 @@ int manager_setup_wall_message_timer(Manager *m) { /* wall message handling */ - if (!m->scheduled_shutdown_action) + if (!handle_action_valid(m->scheduled_shutdown_action)) return 0; if (elapse > 0 && elapse < n) diff --git a/src/login/logind.c b/src/login/logind.c index 88e05bb769..965e2a4aef 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -65,17 +65,18 @@ static int manager_new(Manager **ret) { .reserve_vt_fd = -EBADF, .enable_wall_messages = true, .idle_action_not_before_usec = now(CLOCK_MONOTONIC), - }; + .scheduled_shutdown_action = _HANDLE_ACTION_INVALID, - m->devices = hashmap_new(&device_hash_ops); - m->seats = hashmap_new(&seat_hash_ops); - m->sessions = hashmap_new(&session_hash_ops); - m->users = hashmap_new(&user_hash_ops); - m->inhibitors = hashmap_new(&inhibitor_hash_ops); - m->buttons = hashmap_new(&button_hash_ops); + .devices = hashmap_new(&device_hash_ops), + .seats = hashmap_new(&seat_hash_ops), + .sessions = hashmap_new(&session_hash_ops), + .users = hashmap_new(&user_hash_ops), + .inhibitors = hashmap_new(&inhibitor_hash_ops), + .buttons = hashmap_new(&button_hash_ops), - m->user_units = hashmap_new(&string_hash_ops); - m->session_units = hashmap_new(&string_hash_ops); + .user_units = hashmap_new(&string_hash_ops), + .session_units = hashmap_new(&string_hash_ops), + }; if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->user_units || !m->session_units) return -ENOMEM; diff --git a/src/login/logind.conf.in b/src/login/logind.conf.in index e5fe924681..b62458ec3c 100644 --- a/src/login/logind.conf.in +++ b/src/login/logind.conf.in @@ -24,6 +24,7 @@ #KillExcludeUsers=root #InhibitDelayMaxSec=5 #UserStopDelaySec=10 +#SleepOperation=suspend-then-hibernate suspend #HandlePowerKey=poweroff #HandlePowerKeyLongPress=ignore #HandleRebootKey=reboot diff --git a/src/login/logind.h b/src/login/logind.h index 7532d379c0..cac6a30357 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -76,7 +76,7 @@ struct Manager { char *action_job; sd_event_source *inhibit_timeout_source; - const HandleActionData *scheduled_shutdown_action; + HandleAction scheduled_shutdown_action; usec_t scheduled_shutdown_timeout; sd_event_source *scheduled_shutdown_timeout_source; uid_t scheduled_shutdown_uid; @@ -98,6 +98,8 @@ struct Manager { usec_t stop_idle_session_usec; + HandleActionSleepMask handle_action_sleep_mask; + HandleAction handle_power_key; HandleAction handle_power_key_long_press; HandleAction handle_reboot_key; diff --git a/src/login/test-login-tables.c b/src/login/test-login-tables.c index 3c5ec04271..e9ff3085ec 100644 --- a/src/login/test-login-tables.c +++ b/src/login/test-login-tables.c @@ -2,9 +2,27 @@ #include "logind-action.h" #include "logind-session.h" +#include "sleep-config.h" #include "test-tables.h" #include "tests.h" +static void test_sleep_handle_action(void) { + for (HandleAction action = _HANDLE_ACTION_SLEEP_FIRST; action < _HANDLE_ACTION_SLEEP_LAST; action++) { + const HandleActionData *data; + const char *sleep_operation_str, *handle_action_str; + + if (action == HANDLE_SLEEP) + continue; + + assert_se(data = handle_action_lookup(action)); + + assert_se(handle_action_str = handle_action_to_string(action)); + assert_se(sleep_operation_str = sleep_operation_to_string(data->sleep_operation)); + + assert_se(streq(handle_action_str, sleep_operation_str)); + } +} + int main(int argc, char **argv) { test_setup_logging(LOG_DEBUG); @@ -16,5 +34,7 @@ int main(int argc, char **argv) { test_table(session_type, SESSION_TYPE); test_table(user_state, USER_STATE); + test_sleep_handle_action(); + return EXIT_SUCCESS; } diff --git a/src/network/networkctl.c b/src/network/networkctl.c index ec31e8eb44..939b590a6d 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -87,6 +87,7 @@ static bool arg_no_reload = false; static bool arg_all = false; static bool arg_stats = false; static bool arg_full = false; +static bool arg_runtime = false; static unsigned arg_lines = 10; static char *arg_drop_in = NULL; static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; @@ -2741,23 +2742,25 @@ static int link_renew_one(sd_bus *bus, int index, const char *name) { static int link_renew(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - int index, k = 0, r; + int r; r = acquire_bus(&bus); if (r < 0) return r; + r = 0; + for (int i = 1; i < argc; i++) { + int index; + index = rtnl_resolve_interface_or_warn(&rtnl, argv[i]); if (index < 0) return index; - r = link_renew_one(bus, index, argv[i]); - if (r < 0 && k >= 0) - k = r; + RET_GATHER(r, link_renew_one(bus, index, argv[i])); } - return k; + return r; } static int link_force_renew_one(sd_bus *bus, int index, const char *name) { @@ -2907,7 +2910,6 @@ static int get_dropin_by_name( char **ret) { assert(name); - assert(dropins); assert(ret); STRV_FOREACH(i, dropins) @@ -3074,16 +3076,21 @@ static int add_config_to_edit( assert(context); assert(path); - assert(!arg_drop_in || dropins); - if (path_startswith(path, "/usr")) { + /* If we're supposed to edit main config file in /run/, but a config with the same name is present + * under /etc/, we bail out since the one in /etc/ always overrides that in /run/. */ + if (arg_runtime && !arg_drop_in && path_startswith(path, "/etc")) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), + "Cannot edit runtime config file: overriden by %s", path); + + if (path_startswith(path, "/usr") || arg_runtime != !!path_startswith(path, "/run")) { _cleanup_free_ char *name = NULL; r = path_extract_filename(path, &name); if (r < 0) return log_error_errno(r, "Failed to extract filename from '%s': %m", path); - new_path = path_join(NETWORK_DIRS[0], name); + new_path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], name); if (!new_path) return log_oom(); } @@ -3091,16 +3098,27 @@ static int add_config_to_edit( if (!arg_drop_in) return edit_files_add(context, new_path ?: path, path, NULL); + bool need_new_dropin; + r = get_dropin_by_name(arg_drop_in, dropins, &old_dropin); if (r < 0) return log_error_errno(r, "Failed to acquire drop-in '%s': %m", arg_drop_in); + if (r > 0) { + /* See the explanation above */ + if (arg_runtime && path_startswith(old_dropin, "/etc")) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), + "Cannot edit runtime config file: overriden by %s", old_dropin); - if (r > 0 && !path_startswith(old_dropin, "/usr")) - /* An existing drop-in is found and not in /usr/. Let's edit it directly. */ + need_new_dropin = path_startswith(old_dropin, "/usr") || arg_runtime != !!path_startswith(old_dropin, "/run"); + } else + need_new_dropin = true; + + if (!need_new_dropin) + /* An existing drop-in is found in the correct scope. Let's edit it directly. */ dropin_path = TAKE_PTR(old_dropin); else { - /* No drop-in was found or an existing drop-in resides in /usr/. Let's create - * a new drop-in file. */ + /* No drop-in was found or an existing drop-in is in a different scope. Let's create a new + * drop-in file. */ dropin_path = strjoin(new_path ?: path, ".d/", arg_drop_in); if (!dropin_path) return log_oom(); @@ -3209,7 +3227,7 @@ static int verb_edit(int argc, char *argv[], void *userdata) { log_debug("No existing network config '%s' found, creating a new file.", *name); - path = path_join(NETWORK_DIRS[0], *name); + path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], *name); if (!path) return log_oom(); @@ -3281,23 +3299,22 @@ static int verb_cat(int argc, char *argv[], void *userdata) { if (link_config) { r = get_config_files_by_link_config(link_config, &rtnl, &path, &dropins, /* ret_reload = */ NULL); if (r < 0) - return ret < 0 ? ret : r; + return RET_GATHER(ret, r); } else { r = get_config_files_by_name(*name, &path, &dropins); if (r == -ENOENT) { - log_error_errno(r, "Cannot find network config file '%s'.", *name); - ret = ret < 0 ? ret : r; + RET_GATHER(ret, log_error_errno(r, "Cannot find network config file '%s'.", *name)); continue; } if (r < 0) { log_error_errno(r, "Failed to get the path of network config '%s': %m", *name); - return ret < 0 ? ret : r; + return RET_GATHER(ret, r); } } r = cat_files(path, dropins, /* flags = */ CAT_FORMAT_HAS_SECTIONS); if (r < 0) - return ret < 0 ? ret : r; + return RET_GATHER(ret, r); } return ret; @@ -3341,6 +3358,7 @@ static int help(void) { " --no-reload Do not reload systemd-networkd or systemd-udevd\n" " after editing network config\n" " --drop-in=NAME Edit specified drop-in instead of main config file\n" + " --runtime Edit runtime config files\n" "\nSee the %s for details.\n", program_invocation_short_name, ansi_highlight(), @@ -3358,6 +3376,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_JSON, ARG_NO_RELOAD, ARG_DROP_IN, + ARG_RUNTIME, }; static const struct option options[] = { @@ -3372,6 +3391,7 @@ static int parse_argv(int argc, char *argv[]) { { "json", required_argument, NULL, ARG_JSON }, { "no-reload", no_argument, NULL, ARG_NO_RELOAD }, { "drop-in", required_argument, NULL, ARG_DROP_IN }, + { "runtime", no_argument, NULL, ARG_RUNTIME }, {} }; @@ -3402,6 +3422,10 @@ static int parse_argv(int argc, char *argv[]) { arg_no_reload = true; break; + case ARG_RUNTIME: + arg_runtime = true; + break; + case ARG_DROP_IN: if (isempty(optarg)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty drop-in file name."); diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index c1a8cd884a..7071137676 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -645,7 +645,7 @@ static void address_modify_nft_set_context(Address *address, bool add, NFTSetCon assert(nft_set_context); if (!address->link->manager->fw_ctx) { - r = fw_ctx_new(&address->link->manager->fw_ctx); + r = fw_ctx_new_full(&address->link->manager->fw_ctx, /* init_tables= */ false); if (r < 0) return; } diff --git a/src/network/networkd-bridge-vlan.c b/src/network/networkd-bridge-vlan.c index 36e3610a8f..d84b6b34f6 100644 --- a/src/network/networkd-bridge-vlan.c +++ b/src/network/networkd-bridge-vlan.c @@ -17,166 +17,311 @@ #include "parse-util.h" #include "vlan-util.h" -static bool is_bit_set(unsigned bit, uint32_t scope) { - assert(bit < sizeof(scope)*8); - return scope & (UINT32_C(1) << bit); +static bool is_bit_set(unsigned nr, const uint32_t *addr) { + assert(nr < BRIDGE_VLAN_BITMAP_MAX); + return addr[nr / 32] & (UINT32_C(1) << (nr % 32)); } static void set_bit(unsigned nr, uint32_t *addr) { - if (nr < BRIDGE_VLAN_BITMAP_MAX) - addr[nr / 32] |= (UINT32_C(1) << (nr % 32)); + assert(nr < BRIDGE_VLAN_BITMAP_MAX); + addr[nr / 32] |= (UINT32_C(1) << (nr % 32)); } -static int find_next_bit(int i, uint32_t x) { - int j; +static int add_single(sd_netlink_message *m, uint16_t id, bool untagged, bool is_pvid) { + assert(m); + assert(id < BRIDGE_VLAN_BITMAP_MAX); + return sd_netlink_message_append_data(m, IFLA_BRIDGE_VLAN_INFO, + &(struct bridge_vlan_info) { + .vid = id, + .flags = (untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0) | + (is_pvid ? BRIDGE_VLAN_INFO_PVID : 0), + }, + sizeof(struct bridge_vlan_info)); +} + +static int add_range(sd_netlink_message *m, uint16_t begin, uint16_t end, bool untagged) { + int r; + + assert(m); + assert(begin <= end); + assert(end < BRIDGE_VLAN_BITMAP_MAX); - if (i >= 32) - return -1; + if (begin == end) + return add_single(m, begin, untagged, /* is_pvid = */ false); - /* find first bit */ - if (i < 0) - return BUILTIN_FFS_U32(x); + r = sd_netlink_message_append_data(m, IFLA_BRIDGE_VLAN_INFO, + &(struct bridge_vlan_info) { + .vid = begin, + .flags = (untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0) | + BRIDGE_VLAN_INFO_RANGE_BEGIN, + }, + sizeof(struct bridge_vlan_info)); + if (r < 0) + return r; + + r = sd_netlink_message_append_data(m, IFLA_BRIDGE_VLAN_INFO, + &(struct bridge_vlan_info) { + .vid = end, + .flags = (untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0) | + BRIDGE_VLAN_INFO_RANGE_END, + }, + sizeof(struct bridge_vlan_info)); + if (r < 0) + return r; - /* mask off prior finds to get next */ - j = __builtin_ffs(x >> i); - return j ? j + i : 0; + return 0; } -int bridge_vlan_append_info( - const Link *link, - sd_netlink_message *req, - uint16_t pvid, - const uint32_t *br_vid_bitmap, - const uint32_t *br_untagged_bitmap) { +static uint16_t link_get_pvid(Link *link, bool *ret_untagged) { + assert(link); + assert(link->network); + + if (vlanid_is_valid(link->network->bridge_vlan_pvid)) { + if (ret_untagged) + *ret_untagged = is_bit_set(link->network->bridge_vlan_pvid, + link->network->bridge_vlan_untagged_bitmap); + return link->network->bridge_vlan_pvid; + } + + if (link->network->bridge_vlan_pvid == BRIDGE_VLAN_KEEP_PVID) { + if (ret_untagged) + *ret_untagged = link->bridge_vlan_pvid_is_untagged; + return link->bridge_vlan_pvid; + } - struct bridge_vlan_info br_vlan; - bool done, untagged = false; - uint16_t begin, end; - int r, cnt; + if (ret_untagged) + *ret_untagged = false; + return UINT16_MAX; +} + +static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) { + uint16_t pvid, begin = UINT16_MAX; + bool untagged, pvid_is_untagged; + int r; assert(link); - assert(req); - assert(br_vid_bitmap); - assert(br_untagged_bitmap); - - cnt = 0; - - begin = end = UINT16_MAX; - for (int k = 0; k < BRIDGE_VLAN_BITMAP_LEN; k++) { - uint32_t untagged_map = br_untagged_bitmap[k]; - uint32_t vid_map = br_vid_bitmap[k]; - unsigned base_bit = k * 32; - int i = -1; - - done = false; - do { - int j = find_next_bit(i, vid_map); - if (j > 0) { - /* first hit of any bit */ - if (begin == UINT16_MAX && end == UINT16_MAX) { - begin = end = j - 1 + base_bit; - untagged = is_bit_set(j - 1, untagged_map); - goto next; - } - - /* this bit is a continuation of prior bits */ - if (j - 2 + base_bit == end && untagged == is_bit_set(j - 1, untagged_map) && (uint16_t)j - 1 + base_bit != pvid && (uint16_t)begin != pvid) { - end++; - goto next; - } - } else - done = true; + assert(link->network); + assert(m); + + pvid = link_get_pvid(link, &pvid_is_untagged); + + for (uint16_t k = 0; k < BRIDGE_VLAN_BITMAP_MAX; k++) { + if (k == pvid) { + /* PVID needs to be sent alone. Finish previous bits. */ if (begin != UINT16_MAX) { - cnt++; - if (done && k < BRIDGE_VLAN_BITMAP_LEN - 1) - break; - - br_vlan.flags = 0; - if (untagged) - br_vlan.flags |= BRIDGE_VLAN_INFO_UNTAGGED; - - if (begin == end) { - br_vlan.vid = begin; - - if (begin == pvid) - br_vlan.flags |= BRIDGE_VLAN_INFO_PVID; - - r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan)); - if (r < 0) - return r; - } else { - br_vlan.vid = begin; - br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN; - - r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan)); - if (r < 0) - return r; - - br_vlan.vid = end; - br_vlan.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN; - br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_END; - - r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan)); - if (r < 0) - return r; - } - - if (done) - break; + assert(begin < k); + + r = add_range(m, begin, k - 1, untagged); + if (r < 0) + return r; + + begin = UINT16_MAX; } - if (j > 0) { - begin = end = j - 1 + base_bit; - untagged = is_bit_set(j - 1, untagged_map); + + r = add_single(m, pvid, pvid_is_untagged, /* is_pvid = */ true); + if (r < 0) + return r; + + continue; + } + + if (!is_bit_set(k, link->network->bridge_vlan_bitmap)) { + /* This bit is not set. Finish previous bits. */ + if (begin != UINT16_MAX) { + assert(begin < k); + + r = add_range(m, begin, k - 1, untagged); + if (r < 0) + return r; + + begin = UINT16_MAX; } - next: - i = j; - } while (!done); + continue; + } + + if (begin != UINT16_MAX) { + bool u; + + assert(begin < k); + + u = is_bit_set(k, link->network->bridge_vlan_untagged_bitmap); + if (untagged == u) + continue; + + /* Tagging flag is changed from the previous bits. Finish them. */ + r = add_range(m, begin, k - 1, untagged); + if (r < 0) + return r; + + begin = k; + untagged = u; + continue; + } + + /* This is the starting point of a new bit sequence. Save the position and the tagging flag. */ + begin = k; + untagged = is_bit_set(k, link->network->bridge_vlan_untagged_bitmap); } - assert(cnt > 0); - return cnt; + /* No pending bit sequence. + * Why? There is a trick. The conf parsers below only accepts vlan ID in the range 0…4094, but in + * the above loop, we run 0…4095. */ + assert_cc(BRIDGE_VLAN_BITMAP_MAX > VLANID_MAX); + assert(begin == UINT16_MAX); + return 0; } -void network_adjust_bridge_vlan(Network *network) { - assert(network); +static int bridge_vlan_append_del_info(Link *link, sd_netlink_message *m) { + uint16_t pvid, begin = UINT16_MAX; + int r; - if (!network->use_br_vlan) - return; + assert(link); + assert(link->network); + assert(m); - /* pvid might not be in br_vid_bitmap yet */ - if (network->pvid) - set_bit(network->pvid, network->br_vid_bitmap); -} + pvid = link_get_pvid(link, NULL); -int config_parse_brvlan_pvid( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { + for (uint16_t k = 0; k < BRIDGE_VLAN_BITMAP_MAX; k++) { - Network *network = userdata; - uint16_t pvid; + if (k == pvid || + !is_bit_set(k, link->bridge_vlan_bitmap) || + is_bit_set(k, link->network->bridge_vlan_bitmap)) { + /* This bit is not necessary to be removed. Finish previous bits. */ + if (begin != UINT16_MAX) { + assert(begin < k); + + r = add_range(m, begin, k - 1, /* untagged = */ false); + if (r < 0) + return r; + + begin = UINT16_MAX; + } + + continue; + } + + if (begin != UINT16_MAX) + continue; + + /* This is the starting point of a new bit sequence. Save the position. */ + begin = k; + } + + /* No pending bit sequence. */ + assert(begin == UINT16_MAX); + return 0; +} + +int bridge_vlan_set_message(Link *link, sd_netlink_message *m, bool is_set) { int r; - r = parse_vlanid(rvalue, &pvid); + assert(link); + assert(m); + + r = sd_rtnl_message_link_set_family(m, AF_BRIDGE); + if (r < 0) + return r; + + r = sd_netlink_message_open_container(m, IFLA_AF_SPEC); + if (r < 0) + return r; + + if (link->master_ifindex <= 0) { + /* master needs BRIDGE_FLAGS_SELF flag */ + r = sd_netlink_message_append_u16(m, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF); + if (r < 0) + return r; + } + + if (is_set) + r = bridge_vlan_append_set_info(link, m); + else + r = bridge_vlan_append_del_info(link, m); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(m); + if (r < 0) + return r; + + return 0; +} + +#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK) + +int link_update_bridge_vlan(Link *link, sd_netlink_message *m) { + _cleanup_free_ void *data = NULL; + size_t len; + uint16_t begin = UINT16_MAX; + int r, family; + + assert(link); + assert(m); + + r = sd_rtnl_message_get_family(m, &family); + if (r < 0) + return r; + + if (family != AF_BRIDGE) + return 0; + + r = sd_netlink_message_read_data(m, IFLA_AF_SPEC, &len, &data); + if (r == -ENODATA) + return 0; if (r < 0) return r; - network->pvid = pvid; - network->use_br_vlan = true; + memzero(link->bridge_vlan_bitmap, sizeof(link->bridge_vlan_bitmap)); + + for (struct rtattr *rta = data; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { + struct bridge_vlan_info *p; + + if (RTA_TYPE(rta) != IFLA_BRIDGE_VLAN_INFO) + continue; + if (RTA_PAYLOAD(rta) != sizeof(struct bridge_vlan_info)) + continue; + + p = RTA_DATA(rta); + + if (FLAGS_SET(p->flags, BRIDGE_VLAN_INFO_RANGE_BEGIN)) { + begin = p->vid; + continue; + } + + if (FLAGS_SET(p->flags, BRIDGE_VLAN_INFO_RANGE_END)) { + for (uint16_t k = begin; k <= p->vid; k++) + set_bit(k, link->bridge_vlan_bitmap); + + begin = UINT16_MAX; + continue; + } + + if (FLAGS_SET(p->flags, BRIDGE_VLAN_INFO_PVID)) { + link->bridge_vlan_pvid = p->vid; + link->bridge_vlan_pvid_is_untagged = FLAGS_SET(p->flags, BRIDGE_VLAN_INFO_UNTAGGED); + } + + set_bit(p->vid, link->bridge_vlan_bitmap); + begin = UINT16_MAX; + } return 0; } -int config_parse_brvlan_vlan( +void network_adjust_bridge_vlan(Network *network) { + assert(network); + + for (uint16_t k = 0; k < BRIDGE_VLAN_BITMAP_MAX; k++) + if (is_bit_set(k, network->bridge_vlan_untagged_bitmap)) + set_bit(k, network->bridge_vlan_bitmap); + + if (vlanid_is_valid(network->bridge_vlan_pvid)) + set_bit(network->bridge_vlan_pvid, network->bridge_vlan_bitmap); +} + +int config_parse_bridge_vlan_id( const char *unit, const char *filename, unsigned line, @@ -188,30 +333,37 @@ int config_parse_brvlan_vlan( void *data, void *userdata) { - Network *network = userdata; - uint16_t vid, vid_end; + uint16_t v, *id = ASSERT_PTR(data); int r; assert(filename); assert(section); assert(lvalue); assert(rvalue); - assert(data); - r = parse_vid_range(rvalue, &vid, &vid_end); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse VLAN, ignoring: %s", rvalue); + if (isempty(rvalue)) { + *id = BRIDGE_VLAN_KEEP_PVID; return 0; } - for (; vid <= vid_end; vid++) - set_bit(vid, network->br_vid_bitmap); + if (parse_boolean(rvalue) == 0) { + *id = BRIDGE_VLAN_REMOVE_PVID; + return 0; + } - network->use_br_vlan = true; + r = parse_vlanid(rvalue, &v); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse %s=, ignoring: %s", + lvalue, rvalue); + return 0; + } + + *id = v; return 0; } -int config_parse_brvlan_untagged( +int config_parse_bridge_vlan_id_range( const char *unit, const char *filename, unsigned line, @@ -223,7 +375,7 @@ int config_parse_brvlan_untagged( void *data, void *userdata) { - Network *network = userdata; + uint32_t *bitmap = ASSERT_PTR(data); uint16_t vid, vid_end; int r; @@ -231,19 +383,22 @@ int config_parse_brvlan_untagged( assert(section); assert(lvalue); assert(rvalue); - assert(data); + + if (isempty(rvalue)) { + memzero(bitmap, BRIDGE_VLAN_BITMAP_LEN * sizeof(uint32_t)); + return 0; + } r = parse_vid_range(rvalue, &vid, &vid_end); if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, "Could not parse VLAN: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse %s=, ignoring: %s", + lvalue, rvalue); return 0; } - for (; vid <= vid_end; vid++) { - set_bit(vid, network->br_vid_bitmap); - set_bit(vid, network->br_untagged_bitmap); - } + for (; vid <= vid_end; vid++) + set_bit(vid, bitmap); - network->use_br_vlan = true; return 0; } diff --git a/src/network/networkd-bridge-vlan.h b/src/network/networkd-bridge-vlan.h index f44b8101a5..0366cc6fee 100644 --- a/src/network/networkd-bridge-vlan.h +++ b/src/network/networkd-bridge-vlan.h @@ -6,26 +6,28 @@ ***/ #include <inttypes.h> +#include <stdbool.h> #include "sd-netlink.h" #include "conf-parser.h" +#include "vlan-util.h" #define BRIDGE_VLAN_BITMAP_MAX 4096 #define BRIDGE_VLAN_BITMAP_LEN (BRIDGE_VLAN_BITMAP_MAX / 32) +#define BRIDGE_VLAN_KEEP_PVID UINT16_MAX +#define BRIDGE_VLAN_REMOVE_PVID (UINT16_MAX - 1) +assert_cc(BRIDGE_VLAN_REMOVE_PVID > VLANID_MAX); + typedef struct Link Link; typedef struct Network Network; void network_adjust_bridge_vlan(Network *network); -int bridge_vlan_append_info( - const Link * link, - sd_netlink_message *req, - uint16_t pvid, - const uint32_t *br_vid_bitmap, - const uint32_t *br_untagged_bitmap); +int bridge_vlan_set_message(Link *link, sd_netlink_message *m, bool is_set); + +int link_update_bridge_vlan(Link *link, sd_netlink_message *m); -CONFIG_PARSER_PROTOTYPE(config_parse_brvlan_pvid); -CONFIG_PARSER_PROTOTYPE(config_parse_brvlan_vlan); -CONFIG_PARSER_PROTOTYPE(config_parse_brvlan_untagged); +CONFIG_PARSER_PROTOTYPE(config_parse_bridge_vlan_id); +CONFIG_PARSER_PROTOTYPE(config_parse_bridge_vlan_id_range); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 2caf4ff249..aad86eed99 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -35,6 +35,7 @@ #include "networkd-address.h" #include "networkd-bridge-fdb.h" #include "networkd-bridge-mdb.h" +#include "networkd-bridge-vlan.h" #include "networkd-can.h" #include "networkd-dhcp-prefix-delegation.h" #include "networkd-dhcp-server.h" @@ -2435,6 +2436,10 @@ static int link_update(Link *link, sd_netlink_message *message) { if (r < 0) return r; + r = link_update_bridge_vlan(link, message); + if (r < 0) + return r; + return needs_reconfigure; } @@ -2508,6 +2513,8 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { .ifname = TAKE_PTR(ifname), .kind = TAKE_PTR(kind), + .bridge_vlan_pvid = UINT16_MAX, + .ipv6ll_address_gen_mode = _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID, .state_file = TAKE_PTR(state_file), diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 938bbf482e..7458ea93bd 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -21,6 +21,7 @@ #include "log-link.h" #include "netif-util.h" #include "network-util.h" +#include "networkd-bridge-vlan.h" #include "networkd-ipv6ll.h" #include "networkd-util.h" #include "ordered-set.h" @@ -72,6 +73,11 @@ typedef struct Link { sd_device *dev; char *driver; + /* bridge vlan */ + uint16_t bridge_vlan_pvid; + bool bridge_vlan_pvid_is_untagged; + uint32_t bridge_vlan_bitmap[BRIDGE_VLAN_BITMAP_LEN]; + /* to prevent multiple ethtool calls */ bool ethtool_driver_read; bool ethtool_permanent_hw_addr_read; @@ -149,6 +155,7 @@ typedef struct Link { bool activated:1; bool master_set:1; bool stacked_netdevs_created:1; + bool bridge_vlan_set:1; sd_dhcp_server *dhcp_server; diff --git a/src/network/networkd-lldp-rx.c b/src/network/networkd-lldp-rx.c index 3a5988405f..c45d3e32d7 100644 --- a/src/network/networkd-lldp-rx.c +++ b/src/network/networkd-lldp-rx.c @@ -147,8 +147,8 @@ int link_lldp_save(Link *link) { goto finish; u = htole64(sz); - (void) fwrite(&u, 1, sizeof(u), f); - (void) fwrite(p, 1, sz, f); + fwrite(&u, 1, sizeof(u), f); + fwrite(p, 1, sz, f); } r = fflush_and_check(f); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 1ca1d7abe5..bdf6088f2f 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -753,6 +753,20 @@ static int manager_enumerate_links(Manager *m) { if (r < 0) return r; + r = manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_link); + if (r < 0) + return r; + + req = sd_netlink_message_unref(req); + + r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0); + if (r < 0) + return r; + + r = sd_rtnl_message_link_set_family(req, AF_BRIDGE); + if (r < 0) + return r; + return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_link); } diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c index c4e1c463c4..e9986567ac 100644 --- a/src/network/networkd-neighbor.c +++ b/src/network/networkd-neighbor.c @@ -510,10 +510,9 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, if (r < 0) { log_warning_errno(r, "rtnl: received neighbor message with invalid state, ignoring: %m"); return 0; - } else if (!FLAGS_SET(state, NUD_PERMANENT)) { - log_debug("rtnl: received non-static neighbor, ignoring."); + } else if (!FLAGS_SET(state, NUD_PERMANENT)) + /* Currently, we are interested in only static neighbors. */ return 0; - } r = sd_rtnl_message_neigh_get_ifindex(message, &ifindex); if (r < 0) { @@ -525,12 +524,10 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, } r = link_get_by_index(m, ifindex, &link); - if (r < 0) { + if (r < 0) /* when enumerating we might be out of sync, but we will get the neighbor again. Also, * kernel sends messages about neighbors after a link is removed. So, just ignore it. */ - log_debug("rtnl: received neighbor for link '%d' we don't know about, ignoring.", ifindex); return 0; - } tmp = new0(Neighbor, 1); @@ -539,7 +536,10 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, if (r < 0) { log_link_warning(link, "rtnl: received neighbor message without family, ignoring."); return 0; - } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) { + } + if (tmp->family == AF_BRIDGE) /* Currently, we do not support it. */ + return 0; + if (!IN_SET(tmp->family, AF_INET, AF_INET6)) { log_link_debug(link, "rtnl: received neighbor message with invalid family '%i', ignoring.", tmp->family); return 0; } diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index a6593a080a..24b0f24aec 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -368,9 +368,9 @@ BridgeFDB.AssociatedWith, config_parse_fdb_ntf_flags, BridgeFDB.OutgoingInterface, config_parse_fdb_interface, 0, 0 BridgeMDB.MulticastGroupAddress, config_parse_mdb_group_address, 0, 0 BridgeMDB.VLANId, config_parse_mdb_vlan_id, 0, 0 -BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0 -BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0 -BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0 +BridgeVLAN.PVID, config_parse_bridge_vlan_id, 0, offsetof(Network, bridge_vlan_pvid) +BridgeVLAN.VLAN, config_parse_bridge_vlan_id_range, 0, offsetof(Network, bridge_vlan_bitmap) +BridgeVLAN.EgressUntagged, config_parse_bridge_vlan_id_range, 0, offsetof(Network, bridge_vlan_untagged_bitmap) DHCPPrefixDelegation.UplinkInterface, config_parse_uplink, 0, 0 DHCPPrefixDelegation.SubnetId, config_parse_dhcp_pd_subnet_id, 0, offsetof(Network, dhcp_pd_subnet_id) DHCPPrefixDelegation.Announce, config_parse_bool, 0, offsetof(Network, dhcp_pd_announce) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 6cbaf82d6f..f721228b85 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -450,6 +450,8 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .priority = LINK_BRIDGE_PORT_PRIORITY_INVALID, .multicast_router = _MULTICAST_ROUTER_INVALID, + .bridge_vlan_pvid = BRIDGE_VLAN_KEEP_PVID, + .lldp_mode = LLDP_MODE_ROUTERS_ONLY, .lldp_multicast_mode = _SD_LLDP_MULTICAST_MODE_INVALID, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 03131b7061..4dc9e342ba 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -289,10 +289,9 @@ struct Network { MulticastRouter multicast_router; /* Bridge VLAN */ - bool use_br_vlan; - uint16_t pvid; - uint32_t br_vid_bitmap[BRIDGE_VLAN_BITMAP_LEN]; - uint32_t br_untagged_bitmap[BRIDGE_VLAN_BITMAP_LEN]; + uint16_t bridge_vlan_pvid; + uint32_t bridge_vlan_bitmap[BRIDGE_VLAN_BITMAP_LEN]; + uint32_t bridge_vlan_untagged_bitmap[BRIDGE_VLAN_BITMAP_LEN]; /* CAN support */ uint32_t can_bitrate; diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c index 1695aacf76..0d782dc9a7 100644 --- a/src/network/networkd-queue.c +++ b/src/network/networkd-queue.c @@ -312,7 +312,8 @@ static const char *const request_type_table[_REQUEST_TYPE_MAX] = { [REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE] = "IPv6LL address generation mode", [REQUEST_TYPE_SET_LINK_BOND] = "bond configurations", [REQUEST_TYPE_SET_LINK_BRIDGE] = "bridge configurations", - [REQUEST_TYPE_SET_LINK_BRIDGE_VLAN] = "bridge VLAN configurations", + [REQUEST_TYPE_SET_LINK_BRIDGE_VLAN] = "bridge VLAN configurations (step 1)", + [REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN] = "bridge VLAN configurations (step 2)", [REQUEST_TYPE_SET_LINK_CAN] = "CAN interface configurations", [REQUEST_TYPE_SET_LINK_FLAGS] = "link flags", [REQUEST_TYPE_SET_LINK_GROUP] = "interface group", diff --git a/src/network/networkd-queue.h b/src/network/networkd-queue.h index e58d1be66f..d6f5de421e 100644 --- a/src/network/networkd-queue.h +++ b/src/network/networkd-queue.h @@ -37,6 +37,7 @@ typedef enum RequestType { REQUEST_TYPE_SET_LINK_BOND, /* Setting bond configs. */ REQUEST_TYPE_SET_LINK_BRIDGE, /* Setting bridge configs. */ REQUEST_TYPE_SET_LINK_BRIDGE_VLAN, /* Setting bridge VLAN configs. */ + REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN, /* Removing bridge VLAN configs. */ REQUEST_TYPE_SET_LINK_CAN, /* Setting CAN interface configs. */ REQUEST_TYPE_SET_LINK_FLAGS, /* Setting IFF_NOARP or friends. */ REQUEST_TYPE_SET_LINK_GROUP, /* Setting interface group. */ diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c index 541c4b8a72..2b37c86d23 100644 --- a/src/network/networkd-setlink.c +++ b/src/network/networkd-setlink.c @@ -103,6 +103,19 @@ static int link_set_bridge_handler(sd_netlink *rtnl, sd_netlink_message *m, Requ } static int link_set_bridge_vlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { + int r; + + assert(link); + + r = set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, NULL); + if (r <= 0) + return r; + + link->bridge_vlan_set = true; + return 0; +} + +static int link_del_bridge_vlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, NULL); } @@ -326,29 +339,14 @@ static int link_configure_fill_message( return r; break; case REQUEST_TYPE_SET_LINK_BRIDGE_VLAN: - r = sd_rtnl_message_link_set_family(req, AF_BRIDGE); - if (r < 0) - return r; - - r = sd_netlink_message_open_container(req, IFLA_AF_SPEC); - if (r < 0) - return r; - - if (link->master_ifindex <= 0) { - /* master needs BRIDGE_FLAGS_SELF flag */ - r = sd_netlink_message_append_u16(req, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF); - if (r < 0) - return r; - } - - r = bridge_vlan_append_info(link, req, link->network->pvid, link->network->br_vid_bitmap, link->network->br_untagged_bitmap); + r = bridge_vlan_set_message(link, req, /* is_set = */ true); if (r < 0) return r; - - r = sd_netlink_message_close_container(req); + break; + case REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN: + r = bridge_vlan_set_message(link, req, /* is_set = */ false); if (r < 0) return r; - break; case REQUEST_TYPE_SET_LINK_CAN: r = can_set_netlink_message(link, req); @@ -430,6 +428,8 @@ static int link_configure(Link *link, Request *req) { r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->master_ifindex); else if (IN_SET(req->type, REQUEST_TYPE_SET_LINK_CAN, REQUEST_TYPE_SET_LINK_IPOIB)) r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->ifindex); + else if (req->type == REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN) + r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_DELLINK, link->ifindex); else r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_SETLINK, link->ifindex); if (r < 0) @@ -480,9 +480,11 @@ static int link_is_ready_to_set_link(Link *link, Request *req) { if (link->network->keep_master && link->master_ifindex <= 0 && !streq_ptr(link->kind, "bridge")) return false; - break; + case REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN: + return link->bridge_vlan_set; + case REQUEST_TYPE_SET_LINK_CAN: /* Do not check link->set_flgas_messages here, as it is ok even if link->flags * is outdated, and checking the counter causes a deadlock. */ @@ -704,10 +706,14 @@ int link_request_to_set_bridge(Link *link) { } int link_request_to_set_bridge_vlan(Link *link) { + int r; + assert(link); assert(link->network); - if (!link->network->use_br_vlan) + /* If nothing configured, use the default vlan ID. */ + if (memeqzero(link->network->bridge_vlan_bitmap, BRIDGE_VLAN_BITMAP_LEN * sizeof(uint32_t)) && + link->network->bridge_vlan_pvid == BRIDGE_VLAN_KEEP_PVID) return 0; if (!link->network->bridge && !streq_ptr(link->kind, "bridge")) { @@ -723,9 +729,21 @@ int link_request_to_set_bridge_vlan(Link *link) { return 0; } - return link_request_set_link(link, REQUEST_TYPE_SET_LINK_BRIDGE_VLAN, - link_set_bridge_vlan_handler, - NULL); + link->bridge_vlan_set = false; + + r = link_request_set_link(link, REQUEST_TYPE_SET_LINK_BRIDGE_VLAN, + link_set_bridge_vlan_handler, + NULL); + if (r < 0) + return r; + + r = link_request_set_link(link, REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN, + link_del_bridge_vlan_handler, + NULL); + if (r < 0) + return r; + + return 0; } int link_request_to_set_can(Link *link) { diff --git a/src/notify/notify.c b/src/notify/notify.c index 675fbda752..f63ec8b355 100644 --- a/src/notify/notify.c +++ b/src/notify/notify.c @@ -225,10 +225,6 @@ static int parse_argv(int argc, char *argv[]) { r = fdset_new_fill(/* filter_cloexec= */ 0, &passed); if (r < 0) return log_error_errno(r, "Failed to take possession of passed file descriptors: %m"); - - r = fdset_cloexec(passed, true); - if (r < 0) - return log_error_errno(r, "Failed to enable O_CLOEXEC for passed file descriptors: %m"); } if (fdnr < 3) { diff --git a/src/nspawn/nspawn-setuid.c b/src/nspawn/nspawn-setuid.c index 2d67c3d9de..e350b22806 100644 --- a/src/nspawn/nspawn-setuid.c +++ b/src/nspawn/nspawn-setuid.c @@ -56,6 +56,8 @@ int change_uid_gid_raw( size_t n_supplementary_gids, bool chown_stdio) { + int r; + if (!uid_is_valid(uid)) uid = 0; if (!gid_is_valid(gid)) @@ -67,14 +69,9 @@ int change_uid_gid_raw( (void) fchown(STDERR_FILENO, uid, gid); } - if (setgroups(n_supplementary_gids, supplementary_gids) < 0) - return log_error_errno(errno, "Failed to set auxiliary groups: %m"); - - if (setresgid(gid, gid, gid) < 0) - return log_error_errno(errno, "setresgid() failed: %m"); - - if (setresuid(uid, uid, uid) < 0) - return log_error_errno(errno, "setresuid() failed: %m"); + r = fully_set_uid_gid(uid, gid, supplementary_gids, n_supplementary_gids); + if (r < 0) + return log_error_errno(r, "Changing privileges failed: %m"); return 0; } diff --git a/src/partition/definitions/confext.repart.d/10-root.conf b/src/partition/definitions/confext.repart.d/10-root.conf index e41dc0578b..f728ab66d5 100644 --- a/src/partition/definitions/confext.repart.d/10-root.conf +++ b/src/partition/definitions/confext.repart.d/10-root.conf @@ -13,3 +13,4 @@ Format=erofs CopyFiles=/etc/ Verity=data VerityMatchKey=root +Minimize=best diff --git a/src/partition/definitions/confext.repart.d/20-root-verity.conf b/src/partition/definitions/confext.repart.d/20-root-verity.conf index 437d88e068..8179351b6c 100644 --- a/src/partition/definitions/confext.repart.d/20-root-verity.conf +++ b/src/partition/definitions/confext.repart.d/20-root-verity.conf @@ -11,3 +11,4 @@ Type=root-verity Verity=hash VerityMatchKey=root +Minimize=best diff --git a/src/partition/definitions/portable.repart.d/10-root.conf b/src/partition/definitions/portable.repart.d/10-root.conf index 78758002f5..6f500d04d8 100644 --- a/src/partition/definitions/portable.repart.d/10-root.conf +++ b/src/partition/definitions/portable.repart.d/10-root.conf @@ -13,3 +13,4 @@ Format=erofs CopyFiles=/ Verity=data VerityMatchKey=root +Minimize=best diff --git a/src/partition/definitions/portable.repart.d/20-root-verity.conf b/src/partition/definitions/portable.repart.d/20-root-verity.conf index 437d88e068..8179351b6c 100644 --- a/src/partition/definitions/portable.repart.d/20-root-verity.conf +++ b/src/partition/definitions/portable.repart.d/20-root-verity.conf @@ -11,3 +11,4 @@ Type=root-verity Verity=hash VerityMatchKey=root +Minimize=best diff --git a/src/partition/definitions/sysext.repart.d/10-root.conf b/src/partition/definitions/sysext.repart.d/10-root.conf index 41a7ca56d7..b8ef985b92 100644 --- a/src/partition/definitions/sysext.repart.d/10-root.conf +++ b/src/partition/definitions/sysext.repart.d/10-root.conf @@ -10,6 +10,8 @@ [Partition] Type=root Format=erofs -CopyFiles=/usr/ /opt/ +CopyFiles=/usr/ +CopyFiles=/opt/ Verity=data VerityMatchKey=root +Minimize=best diff --git a/src/partition/definitions/sysext.repart.d/20-root-verity.conf b/src/partition/definitions/sysext.repart.d/20-root-verity.conf index 437d88e068..8179351b6c 100644 --- a/src/partition/definitions/sysext.repart.d/20-root-verity.conf +++ b/src/partition/definitions/sysext.repart.d/20-root-verity.conf @@ -11,3 +11,4 @@ Type=root-verity Verity=hash VerityMatchKey=root +Minimize=best diff --git a/src/partition/repart.c b/src/partition/repart.c index 654b931c19..41eb1d771a 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -2205,8 +2205,8 @@ static int context_read_definitions(Context *context) { if (q) { if (q->priority != p->priority) return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL), - "Priority mismatch (%i != %i) for verity sibling partitions with VerityMatchKey=%s", - p->priority, q->priority, p->verity_match_key); + "Priority mismatch (%i != %i) for verity sibling partitions with VerityMatchKey=%s", + p->priority, q->priority, p->verity_match_key); p->siblings[mode] = q; } @@ -2331,7 +2331,7 @@ static int context_load_partition_table(Context *context) { return r; if (fstat(context->backing_fd, &st) < 0) - return log_error_errno(r, "Failed to stat %s: %m", context->node); + return log_error_errno(errno, "Failed to stat %s: %m", context->node); /* Auto-detect sector size if not specified. */ r = probe_sector_size_prefer_ioctl(context->backing_fd, &ssz); @@ -3194,13 +3194,13 @@ static int context_wipe_range(Context *context, uint64_t offset, uint64_t size) errno = 0; r = blkid_do_probe(probe); if (r < 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe for file systems."); + return log_error_errno(errno_or_else(EIO), "Failed to probe for file systems."); if (r > 0) break; errno = 0; if (blkid_do_wipe(probe, false) < 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to wipe file system signature."); + return log_error_errno(errno_or_else(EIO), "Failed to wipe file system signature."); } return 0; @@ -3530,7 +3530,7 @@ static int prepare_temporary_file(PartitionTarget *t, uint64_t size) { if (ftruncate(fd, size) < 0) return log_error_errno(errno, "Failed to truncate temporary file to %s: %m", - FORMAT_BYTES(size)); + FORMAT_BYTES(size)); t->fd = TAKE_FD(fd); t->path = TAKE_PTR(temp); @@ -3711,9 +3711,8 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta /* Weird cryptsetup requirement which requires the header file to be the size of at least one * sector. */ - r = ftruncate(fileno(h), luks_params.sector_size); - if (r < 0) - return log_error_errno(r, "Failed to grow temporary LUKS header file: %m"); + if (ftruncate(fileno(h), luks_params.sector_size) < 0) + return log_error_errno(errno, "Failed to grow temporary LUKS header file: %m"); } else { if (asprintf(&dm_name, "luks-repart-%08" PRIx64, random_u64()) < 0) return log_oom(); @@ -3746,14 +3745,15 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta return log_error_errno(r, "Failed to set data offset: %m"); } - r = sym_crypt_format(cd, - CRYPT_LUKS2, - "aes", - "xts-plain64", - SD_ID128_TO_UUID_STRING(p->luks_uuid), - NULL, - VOLUME_KEY_SIZE, - &luks_params); + r = sym_crypt_format( + cd, + CRYPT_LUKS2, + "aes", + "xts-plain64", + SD_ID128_TO_UUID_STRING(p->luks_uuid), + NULL, + VOLUME_KEY_SIZE, + &luks_params); if (r < 0) return log_error_errno(r, "Failed to LUKS2 format future partition: %m"); @@ -4547,7 +4547,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) { rfd = open(root, O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); if (rfd < 0) - return rfd; + return -errno; sfd = chase_and_open(*source, arg_copy_source, CHASE_PREFIX_ROOT, O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOCTTY, NULL); if (sfd < 0) @@ -6264,10 +6264,10 @@ static int context_minimize(Context *context) { d = loop_device_unref(d); /* Erase the previous filesystem first. */ - if (ftruncate(fd, 0)) + if (ftruncate(fd, 0) < 0) return log_error_errno(errno, "Failed to erase temporary file: %m"); - if (ftruncate(fd, fsz)) + if (ftruncate(fd, fsz) < 0) return log_error_errno(errno, "Failed to truncate temporary file to %s: %m", FORMAT_BYTES(fsz)); if (arg_offline <= 0) { @@ -6359,7 +6359,7 @@ static int context_minimize(Context *context) { return log_error_errno(errno, "Failed to open temporary file %s: %m", temp); if (fstat(fd, &st) < 0) - return log_error_errno(r, "Failed to stat temporary file: %m"); + return log_error_errno(errno, "Failed to stat temporary file: %m"); log_info("Minimal partition size of verity hash partition %s is %s", strna(hint), FORMAT_BYTES(st.st_size)); @@ -7569,7 +7569,7 @@ static int run(int argc, char *argv[]) { r = search_and_access(d, F_OK, arg_root, CONF_PATHS_USR_STRV("systemd/repart/definitions"), &dp); if (r < 0) - return log_error_errno(errno, "DDI type '%s' is not defined: %m", arg_make_ddi); + return log_error_errno(r, "DDI type '%s' is not defined: %m", arg_make_ddi); if (strv_consume(&arg_definitions, TAKE_PTR(dp)) < 0) return log_oom(); diff --git a/src/run/run.c b/src/run/run.c index f97150eaab..88eca0fd6d 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -43,7 +43,7 @@ static bool arg_remain_after_exit = false; static bool arg_no_block = false; static bool arg_wait = false; static const char *arg_unit = NULL; -static const char *arg_description = NULL; +static char *arg_description = NULL; static const char *arg_slice = NULL; static bool arg_slice_inherit = false; static int arg_expand_environment = -1; @@ -74,6 +74,7 @@ static char *arg_working_directory = NULL; static bool arg_shell = false; static char **arg_cmdline = NULL; +STATIC_DESTRUCTOR_REGISTER(arg_description, freep); STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_path_property, strv_freep); @@ -281,7 +282,9 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_DESCRIPTION: - arg_description = optarg; + r = free_and_strdup(&arg_description, optarg); + if (r < 0) + return r; break; case ARG_SLICE: @@ -1376,7 +1379,10 @@ static int start_transient_service(sd_bus *bus) { if (r < 0) return bus_log_parse_error(r); - r = bus_wait_for_jobs_one(w, object, arg_quiet, arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL); + r = bus_wait_for_jobs_one(w, + object, + arg_quiet, + arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL); if (r < 0) return r; } @@ -1908,7 +1914,6 @@ static bool shall_make_executable_absolute(void) { static int run(int argc, char* argv[]) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_free_ char *description = NULL; int r; log_show_color(true); @@ -1933,19 +1938,16 @@ static int run(int argc, char* argv[]) { } if (!arg_description) { - if (strv_isempty(arg_cmdline)) - arg_description = arg_unit; - else { - _cleanup_free_ char *joined = strv_join(arg_cmdline, " "); - if (!joined) - return log_oom(); + char *t; - description = shell_escape(joined, "\""); - if (!description) - return log_oom(); + if (strv_isempty(arg_cmdline)) + t = strdup(arg_unit); + else + t = quote_command_line(arg_cmdline, SHELL_ESCAPE_EMPTY); + if (!t) + return log_oom(); - arg_description = description; - } + free_and_replace(arg_description, t); } /* For backward compatibility reasons env var expansion is disabled by default for scopes, and @@ -1960,9 +1962,11 @@ static int run(int argc, char* argv[]) { " Use --expand-environment=yes/no to explicitly control it as needed."); } - /* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the limited direct - * connection */ - if (arg_wait || arg_stdio != ARG_STDIO_NONE || (arg_runtime_scope == RUNTIME_SCOPE_USER && arg_transport != BUS_TRANSPORT_LOCAL)) + /* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the + * limited direct connection */ + if (arg_wait || + arg_stdio != ARG_STDIO_NONE || + (arg_runtime_scope == RUNTIME_SCOPE_USER && arg_transport != BUS_TRANSPORT_LOCAL)) r = bus_connect_transport(arg_transport, arg_host, arg_runtime_scope, &bus); else r = bus_connect_transport_systemd(arg_transport, arg_host, arg_runtime_scope, &bus); diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 4ee9706847..bba040112d 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -2643,6 +2643,7 @@ static int bus_append_unit_property(sd_bus_message *m, const char *field, const if (unit_dependency_from_string(field) >= 0 || STR_IN_SET(field, "Documentation", "RequiresMountsFor", + "WantsMountsFor", "Markers")) return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE); diff --git a/src/shared/fdset.c b/src/shared/fdset.c index b62f15c649..e5b8e92e80 100644 --- a/src/shared/fdset.c +++ b/src/shared/fdset.c @@ -150,13 +150,15 @@ int fdset_remove(FDSet *s, int fd) { int fdset_new_fill( int filter_cloexec, /* if < 0 takes all fds, otherwise only those with O_CLOEXEC set (1) or unset (0) */ FDSet **ret) { + _cleanup_(fdset_shallow_freep) FDSet *s = NULL; _cleanup_closedir_ DIR *d = NULL; int r; assert(ret); - /* Creates an fdset and fills in all currently open file descriptors. */ + /* Creates an fdset and fills in all currently open file descriptors. Also set all collected fds + * to CLOEXEC. */ d = opendir("/proc/self/fd"); if (!d) { @@ -191,6 +193,7 @@ int fdset_new_fill( /* If user asked for that filter by O_CLOEXEC. This is useful so that fds that have * been passed in can be collected and fds which have been created locally can be * ignored, under the assumption that only the latter have O_CLOEXEC set. */ + fl = fcntl(fd, F_GETFD); if (fl < 0) return -errno; @@ -199,6 +202,13 @@ int fdset_new_fill( continue; } + /* We need to set CLOEXEC manually only if we're collecting non-CLOEXEC fds. */ + if (filter_cloexec <= 0) { + r = fd_cloexec(fd, true); + if (r < 0) + return r; + } + r = fdset_put(s, fd); if (r < 0) return r; diff --git a/src/shared/format-table.c b/src/shared/format-table.c index 9a19177fde..41471dade0 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -77,7 +77,9 @@ typedef struct TableData { unsigned ellipsize_percent; /* 0 … 100, where to place the ellipsis when compression is needed */ unsigned align_percent; /* 0 … 100, where to pad with spaces when expanding is needed. 0: left-aligned, 100: right-aligned */ - bool uppercase; /* Uppercase string on display */ + bool uppercase:1; /* Uppercase string on display */ + bool underline:1; + bool rgap_underline:1; const char *color; /* ANSI color string to use for this cell. When written to terminal should not move cursor. Will automatically be reset after the cell */ const char *rgap_color; /* The ANSI color to use for the gap right of this cell. Usually used to underline entire rows in a gapless fashion */ @@ -401,7 +403,7 @@ static bool table_data_matches( return false; /* If a color/url is set, refuse to merge */ - if (d->color || d->rgap_color) + if (d->color || d->rgap_color || d->underline || d->rgap_underline) return false; if (d->url) return false; @@ -617,6 +619,8 @@ static int table_dedup_cell(Table *t, TableCell *cell) { nd->color = od->color; nd->rgap_color = od->rgap_color; + nd->underline = od->underline; + nd->rgap_underline = od->rgap_underline; nd->url = TAKE_PTR(curl); table_data_unref(od); @@ -759,6 +763,46 @@ int table_set_rgap_color(Table *t, TableCell *cell, const char *color) { return 0; } +int table_set_underline(Table *t, TableCell *cell, bool b) { + TableData *d; + int r; + + assert(t); + assert(cell); + + r = table_dedup_cell(t, cell); + if (r < 0) + return r; + + assert_se(d = table_get_data(t, cell)); + + if (d->underline == b) + return 0; + + d->underline = b; + return 1; +} + +int table_set_rgap_underline(Table *t, TableCell *cell, bool b) { + TableData *d; + int r; + + assert(t); + assert(cell); + + r = table_dedup_cell(t, cell); + if (r < 0) + return r; + + assert_se(d = table_get_data(t, cell)); + + if (d->rgap_underline == b) + return 0; + + d->rgap_underline = b; + return 1; +} + int table_set_url(Table *t, TableCell *cell, const char *url) { _cleanup_free_ char *copy = NULL; int r; @@ -834,6 +878,8 @@ int table_update(Table *t, TableCell *cell, TableDataType type, const void *data nd->color = od->color; nd->rgap_color = od->rgap_color; + nd->underline = od->underline; + nd->rgap_underline = od->rgap_underline; nd->url = TAKE_PTR(curl); table_data_unref(od); @@ -1101,6 +1147,31 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) { goto check; } + case TABLE_SET_UNDERLINE: { + int u = va_arg(ap, int); + r = table_set_underline(t, last_cell, u); + goto check; + } + + case TABLE_SET_RGAP_UNDERLINE: { + int u = va_arg(ap, int); + r = table_set_rgap_underline(t, last_cell, u); + goto check; + } + + case TABLE_SET_BOTH_UNDERLINES: { + int u = va_arg(ap, int); + + r = table_set_underline(t, last_cell, u); + if (r < 0) { + va_end(ap); + return r; + } + + r = table_set_rgap_underline(t, last_cell, u); + goto check; + } + case TABLE_SET_URL: { const char *u = va_arg(ap, const char*); r = table_set_url(t, last_cell, u); @@ -2130,8 +2201,6 @@ static const char* table_data_color(TableData *d) { if (d->type == TABLE_FIELD) return ansi_bright_blue(); - if (d->type == TABLE_HEADER) - return ansi_underline(); return NULL; } @@ -2139,11 +2208,29 @@ static const char* table_data_color(TableData *d) { static const char* table_data_rgap_color(TableData *d) { assert(d); - if (d->rgap_color) - return d->rgap_color; + return d->rgap_color ?: d->rgap_color; +} + +static const char* table_data_underline(TableData *d) { + assert(d); + + if (d->underline) + return /* cescape( */ansi_add_underline_grey()/* ) */; if (d->type == TABLE_HEADER) - return ansi_underline(); + return ansi_add_underline(); + + return NULL; +} + +static const char* table_data_rgap_underline(TableData *d) { + assert(d); + + if (d->rgap_underline) + return ansi_add_underline_grey(); + + if (d->type == TABLE_HEADER) + return ansi_add_underline(); return NULL; } @@ -2418,13 +2505,13 @@ int table_print(Table *t, FILE *f) { row = t->data + i * t->n_columns; do { - const char *gap_color = NULL; + const char *gap_color = NULL, *gap_underline = NULL; more_sublines = false; for (size_t j = 0; j < display_columns; j++) { _cleanup_free_ char *buffer = NULL, *extracted = NULL; bool lines_truncated = false; - const char *field, *color = NULL; + const char *field, *color = NULL, *underline = NULL; TableData *d; size_t l; @@ -2490,6 +2577,7 @@ int table_print(Table *t, FILE *f) { /* Drop trailing white spaces of last column when no cosmetics is set. */ if (j == display_columns - 1 && (!colors_enabled() || !table_data_color(d)) && + (!underline_enabled() || !table_data_underline(d)) && (!urlify_enabled() || !d->url)) delete_trailing_chars(aligned, NULL); @@ -2511,27 +2599,36 @@ int table_print(Table *t, FILE *f) { if (colors_enabled() && gap_color) fputs(gap_color, f); + if (underline_enabled() && gap_underline) + fputs(gap_underline, f); if (j > 0) fputc(' ', f); /* column separator left of cell */ + /* Undo gap color/underline */ + if ((colors_enabled() && gap_color) || + (underline_enabled() && gap_underline)) + fputs(ANSI_NORMAL, f); + if (colors_enabled()) { color = table_data_color(d); - - /* Undo gap color */ - if (gap_color) - fputs(ANSI_NORMAL, f); - if (color) fputs(color, f); } + if (underline_enabled()) { + underline = table_data_underline(d); + if (underline) + fputs(underline, f); + } + fputs(field, f); - if (colors_enabled() && color) + if (color || underline) fputs(ANSI_NORMAL, f); gap_color = table_data_rgap_color(d); + gap_underline = table_data_rgap_underline(d); } fputc('\n', f); diff --git a/src/shared/format-table.h b/src/shared/format-table.h index 37bfbca693..0368447638 100644 --- a/src/shared/format-table.h +++ b/src/shared/format-table.h @@ -68,6 +68,9 @@ typedef enum TableDataType { TABLE_SET_COLOR, TABLE_SET_RGAP_COLOR, TABLE_SET_BOTH_COLORS, + TABLE_SET_UNDERLINE, + TABLE_SET_RGAP_UNDERLINE, + TABLE_SET_BOTH_UNDERLINES, TABLE_SET_URL, TABLE_SET_UPPERCASE, @@ -111,6 +114,8 @@ int table_set_align_percent(Table *t, TableCell *cell, unsigned percent); int table_set_ellipsize_percent(Table *t, TableCell *cell, unsigned percent); int table_set_color(Table *t, TableCell *cell, const char *color); int table_set_rgap_color(Table *t, TableCell *cell, const char *color); +int table_set_underline(Table *t, TableCell *cell, bool b); +int table_set_rgap_underline(Table *t, TableCell *cell, bool b); int table_set_url(Table *t, TableCell *cell, const char *url); int table_set_uppercase(Table *t, TableCell *cell, bool b); diff --git a/src/shared/killall.c b/src/shared/killall.c index 9c2babe7d2..330b4c3272 100644 --- a/src/shared/killall.c +++ b/src/shared/killall.c @@ -101,7 +101,7 @@ static bool ignore_proc(const PidRef *pid, bool warn_rootfs) { return false; if (warn_rootfs && - pid_from_same_root_fs(pid->pid) == 0) { + pid_from_same_root_fs(pid->pid) > 0) { _cleanup_free_ char *comm = NULL; diff --git a/src/shared/mkfs-util.c b/src/shared/mkfs-util.c index 4e58b6e871..19b119b4af 100644 --- a/src/shared/mkfs-util.c +++ b/src/shared/mkfs-util.c @@ -104,7 +104,6 @@ static int mangle_fat_label(const char *s, char **ret) { static int do_mcopy(const char *node, const char *root) { _cleanup_free_ char *mcopy = NULL; _cleanup_strv_free_ char **argv = NULL; - _cleanup_close_ int rfd = -EBADF; _cleanup_free_ DirectoryEntries *de = NULL; int r; @@ -128,11 +127,7 @@ static int do_mcopy(const char *node, const char *root) { /* mcopy copies the top level directory instead of everything in it so we have to pass all * the subdirectories to mcopy instead to end up with the correct directory structure. */ - rfd = open(root, O_RDONLY|O_DIRECTORY|O_CLOEXEC); - if (rfd < 0) - return log_error_errno(errno, "Failed to open directory '%s': %m", root); - - r = readdir_all(rfd, RECURSE_DIR_SORT|RECURSE_DIR_ENSURE_TYPE, &de); + r = readdir_all_at(AT_FDCWD, root, RECURSE_DIR_SORT|RECURSE_DIR_ENSURE_TYPE, &de); if (r < 0) return log_error_errno(r, "Failed to read '%s' contents: %m", root); diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c index 195e603224..4950932a96 100644 --- a/src/shared/ptyfwd.c +++ b/src/shared/ptyfwd.c @@ -22,6 +22,7 @@ #include "log.h" #include "macro.h" #include "ptyfwd.h" +#include "stat-util.h" #include "terminal-util.h" #include "time-util.h" @@ -142,7 +143,7 @@ static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) { if (*p == 0x1D) { usec_t nw = now(CLOCK_MONOTONIC); - if (f->escape_counter == 0 || nw > f->escape_timestamp + ESCAPE_USEC) { + if (f->escape_counter == 0 || nw > f->escape_timestamp + ESCAPE_USEC) { f->escape_timestamp = nw; f->escape_counter = 1; } else { @@ -228,7 +229,7 @@ static int shovel(PTYForward *f) { f->stdin_hangup = true; f->stdin_event_source = sd_event_source_unref(f->stdin_event_source); - } else { + } else { /* Check if ^] has been pressed three times within one second. If we get this we quite * immediately. */ if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k)) @@ -266,12 +267,9 @@ static int shovel(PTYForward *f) { k = read(f->master, f->out_buffer + f->out_buffer_full, LINE_MAX - f->out_buffer_full); if (k < 0) { - /* Note that EIO on the master device - * might be caused by vhangup() or - * temporary closing of everything on - * the other side, we treat it like - * EAGAIN here and try again, unless - * ignore_vhangup is off. */ + /* Note that EIO on the master device might be caused by vhangup() or + * temporary closing of everything on the other side, we treat it like EAGAIN + * here and try again, unless ignore_vhangup is off. */ if (errno == EAGAIN || (errno == EIO && ignore_vhangup(f))) f->master_readable = false; @@ -284,7 +282,7 @@ static int shovel(PTYForward *f) { log_error_errno(errno, "read(): %m"); return pty_forward_done(f, -errno); } - } else { + } else { f->read_from_master = true; f->out_buffer_full += (size_t) k; } @@ -480,8 +478,14 @@ int pty_forward_new( (void) ioctl(master, TIOCSWINSZ, &ws); if (!(flags & PTY_FORWARD_READ_ONLY)) { + int same; + assert(f->input_fd >= 0); + same = inode_same_at(f->input_fd, NULL, f->output_fd, NULL, AT_EMPTY_PATH); + if (same < 0) + return same; + if (tcgetattr(f->input_fd, &f->saved_stdin_attr) >= 0) { struct termios raw_stdin_attr; @@ -489,11 +493,14 @@ int pty_forward_new( raw_stdin_attr = f->saved_stdin_attr; cfmakeraw(&raw_stdin_attr); - raw_stdin_attr.c_oflag = f->saved_stdin_attr.c_oflag; - tcsetattr(f->input_fd, TCSANOW, &raw_stdin_attr); + + if (!same) + raw_stdin_attr.c_oflag = f->saved_stdin_attr.c_oflag; + + (void) tcsetattr(f->input_fd, TCSANOW, &raw_stdin_attr); } - if (tcgetattr(f->output_fd, &f->saved_stdout_attr) >= 0) { + if (!same && tcgetattr(f->output_fd, &f->saved_stdout_attr) >= 0) { struct termios raw_stdout_attr; f->saved_stdout = true; @@ -502,7 +509,7 @@ int pty_forward_new( cfmakeraw(&raw_stdout_attr); raw_stdout_attr.c_iflag = f->saved_stdout_attr.c_iflag; raw_stdout_attr.c_lflag = f->saved_stdout_attr.c_lflag; - tcsetattr(f->output_fd, TCSANOW, &raw_stdout_attr); + (void) tcsetattr(f->output_fd, TCSANOW, &raw_stdout_attr); } r = sd_event_add_io(f->event, &f->stdin_event_source, f->input_fd, EPOLLIN|EPOLLET, on_stdin_event, f); diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index cd30e72ad5..00a8cedcb8 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -1080,8 +1080,24 @@ static int add_syscall_filter_set( return 0; } +static uint32_t override_default_action(uint32_t default_action) { + /* When the requested filter is an allow-list, and the default action is something critical, we + * install ENOSYS as the default action, but it will only apply to syscalls which are not in the + * @known set. */ + + if (default_action == SCMP_ACT_ALLOW) + return default_action; + +#ifdef SCMP_ACT_LOG + if (default_action == SCMP_ACT_LOG) + return default_action; +#endif + + return SCMP_ACT_ERRNO(ENOSYS); +} + int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action, bool log_missing) { - uint32_t arch; + uint32_t arch, default_action_override; int r; assert(set); @@ -1089,19 +1105,47 @@ int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilter /* The one-stop solution: allocate a seccomp object, add the specified filter to it, and apply it. Once for * each local arch. */ + default_action_override = override_default_action(default_action); + SECCOMP_FOREACH_LOCAL_ARCH(arch) { _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; + _cleanup_strv_free_ char **added = NULL; log_trace("Operating on architecture: %s", seccomp_arch_to_string(arch)); - r = seccomp_init_for_arch(&seccomp, arch, default_action); + r = seccomp_init_for_arch(&seccomp, arch, default_action_override); if (r < 0) return r; - r = add_syscall_filter_set(seccomp, set, action, NULL, log_missing, NULL); + r = add_syscall_filter_set(seccomp, set, action, NULL, log_missing, &added); if (r < 0) return log_debug_errno(r, "Failed to add filter set: %m"); + if (default_action != default_action_override) + NULSTR_FOREACH(name, syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].value) { + int id; + + id = seccomp_syscall_resolve_name(name); + if (id < 0) + continue; + + /* Ignore the syscall if it was already handled above */ + if (strv_contains(added, name)) + continue; + + r = seccomp_rule_add_exact(seccomp, default_action, id, 0); + if (r < 0 && r != -EDOM) /* EDOM means that the syscall is not available for arch */ + return log_debug_errno(r, "Failed to add rule for system call %s() / %d: %m", + name, id); + } + +#if (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR >= 5) || SCMP_VER_MAJOR > 2 + /* We have a large filter here, so let's turn on the binary tree mode if possible. */ + r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_OPTIMIZE, 2); + if (r < 0) + log_warning_errno(r, "Failed to set SCMP_FLTATR_CTL_OPTIMIZE, ignoring: %m"); +#endif + r = seccomp_load(seccomp); if (ERRNO_IS_NEG_SECCOMP_FATAL(r)) return r; @@ -1114,7 +1158,7 @@ int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilter } int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* filter, uint32_t action, bool log_missing) { - uint32_t arch; + uint32_t arch, default_action_override; int r; /* Similar to seccomp_load_syscall_filter_set(), but takes a raw Hashmap* of syscalls, instead @@ -1123,15 +1167,15 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* filter if (hashmap_isempty(filter) && default_action == SCMP_ACT_ALLOW) return 0; + default_action_override = override_default_action(default_action); + SECCOMP_FOREACH_LOCAL_ARCH(arch) { _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; void *syscall_id, *val; log_trace("Operating on architecture: %s", seccomp_arch_to_string(arch)); - /* We install ENOSYS as the default action, but it will only apply to syscalls which are not - * in the @known set. */ - r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ERRNO(ENOSYS)); + r = seccomp_init_for_arch(&seccomp, arch, default_action_override); if (r < 0) return r; @@ -1166,22 +1210,23 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* filter } } - NULSTR_FOREACH(name, syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].value) { - int id; + if (default_action != default_action_override) + NULSTR_FOREACH(name, syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].value) { + int id; - id = seccomp_syscall_resolve_name(name); - if (id < 0) - continue; + id = seccomp_syscall_resolve_name(name); + if (id < 0) + continue; - /* Ignore the syscall if it was already handled above */ - if (hashmap_contains(filter, INT_TO_PTR(id + 1))) - continue; + /* Ignore the syscall if it was already handled above */ + if (hashmap_contains(filter, INT_TO_PTR(id + 1))) + continue; - r = seccomp_rule_add_exact(seccomp, default_action, id, 0); - if (r < 0 && r != -EDOM) /* EDOM means that the syscall is not available for arch */ - return log_debug_errno(r, "Failed to add rule for system call %s() / %d: %m", - name, id); - } + r = seccomp_rule_add_exact(seccomp, default_action, id, 0); + if (r < 0 && r != -EDOM) /* EDOM means that the syscall is not available for arch */ + return log_debug_errno(r, "Failed to add rule for system call %s() / %d: %m", + name, id); + } #if (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR >= 5) || SCMP_VER_MAJOR > 2 /* We have a large filter here, so let's turn on the binary tree mode if possible. */ diff --git a/src/shared/selinux-util.c b/src/shared/selinux-util.c index cdad5e1ab0..2fef29c67f 100644 --- a/src/shared/selinux-util.c +++ b/src/shared/selinux-util.c @@ -76,7 +76,7 @@ bool mac_selinux_use(void) { #if HAVE_SELINUX if (_unlikely_(cached_use < 0)) { cached_use = is_selinux_enabled() > 0; - log_debug("SELinux enabled state cached to: %s", cached_use ? "enabled" : "disabled"); + log_trace("SELinux enabled state cached to: %s", enabled_disabled(cached_use)); } return cached_use; diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c index b620156c75..787fb79afb 100644 --- a/src/shared/switch-root.c +++ b/src/shared/switch-root.c @@ -63,11 +63,11 @@ int switch_root(const char *new_root, if (new_root_fd < 0) return log_error_errno(errno, "Failed to open target directory '%s': %m", new_root); - r = inode_same_at(old_root_fd, "", new_root_fd, "", AT_EMPTY_PATH); + r = fds_are_same_mount(old_root_fd, new_root_fd); if (r < 0) - return log_error_errno(r, "Failed to determine if old and new root directory are the same: %m"); + return log_error_errno(r, "Failed to check if old and new root directory/mount are the same: %m"); if (r > 0) { - log_debug("Skipping switch root, as old and new root directory are the same."); + log_debug("Skipping switch root, as old and new root directories/mounts are the same."); return 0; } diff --git a/src/stdio-bridge/stdio-bridge.c b/src/stdio-bridge/stdio-bridge.c index ce2bd5f86f..fe551cfaea 100644 --- a/src/stdio-bridge/stdio-bridge.c +++ b/src/stdio-bridge/stdio-bridge.c @@ -183,12 +183,12 @@ static int run(int argc, char *argv[]) { r = sd_bus_process(a, &m); if (ERRNO_IS_NEG_DISCONNECT(r)) /* Treat 'connection reset by peer' as clean exit condition */ - break; + return 0; if (r < 0) return log_error_errno(r, "Failed to process bus a: %m"); if (m) { if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) - break; + return 0; r = sd_bus_send(b, m, NULL); if (r < 0) @@ -200,12 +200,12 @@ static int run(int argc, char *argv[]) { r = sd_bus_process(b, &m); if (ERRNO_IS_NEG_DISCONNECT(r)) /* Treat 'connection reset by peer' as clean exit condition */ - break; + return 0; if (r < 0) return log_error_errno(r, "Failed to process bus: %m"); if (m) { if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) - break; + return 0; r = sd_bus_send(a, m, NULL); if (r < 0) @@ -247,8 +247,6 @@ static int run(int argc, char *argv[]) { if (r < 0 && !ERRNO_IS_TRANSIENT(r)) /* don't be bothered by signals, i.e. EINTR */ return log_error_errno(r, "ppoll() failed: %m"); } - - return 0; } DEFINE_MAIN_FUNCTION(run); diff --git a/src/systemctl/systemctl-list-units.c b/src/systemctl/systemctl-list-units.c index e48cf45333..a1482d40c2 100644 --- a/src/systemctl/systemctl-list-units.c +++ b/src/systemctl/systemctl-list-units.c @@ -127,49 +127,70 @@ static int output_units_list(const UnitInfo *unit_infos, size_t c) { table_set_ersatz_string(table, TABLE_ERSATZ_DASH); FOREACH_ARRAY(u, unit_infos, c) { + const char *on_loaded = NULL, *on_active = NULL, *on_sub = NULL, *on_circle = NULL; _cleanup_free_ char *id = NULL; - const char *on_underline = "", *on_loaded = "", *on_active = "", *on_circle = ""; - bool circle = false, underline = false; + bool circle = false, underline; - if (u + 1 < unit_infos + c && - !streq(unit_type_suffix(u->id), unit_type_suffix((u + 1)->id))) { - on_underline = ansi_underline(); - underline = true; - } + underline = u + 1 < unit_infos + c && !streq(unit_type_suffix(u->id), unit_type_suffix((u + 1)->id)); - if (STR_IN_SET(u->load_state, "error", "not-found", "bad-setting", "masked") && !arg_plain) { - on_circle = underline ? ansi_highlight_yellow_underline() : ansi_highlight_yellow(); + if (streq(u->load_state, "not-found")) { + on_circle = on_loaded = ansi_highlight_yellow(); + on_circle = ansi_highlight_yellow(); circle = true; - on_loaded = underline ? ansi_highlight_red_underline() : ansi_highlight_red(); - } else if (streq(u->active_state, "failed") && !arg_plain) { - on_circle = underline ? ansi_highlight_red_underline() : ansi_highlight_red(); + } else if (STR_IN_SET(u->load_state, "bad-setting", "error", "masked")) { + on_loaded = ansi_highlight_red(); + on_circle = ansi_highlight_yellow(); circle = true; - on_active = underline ? ansi_highlight_red_underline() : ansi_highlight_red(); - } else { - on_circle = on_underline; - on_active = on_underline; - on_loaded = on_underline; } + if (streq(u->active_state, "failed")) { + on_sub = on_active = ansi_highlight_red(); + + /* Here override any load_state highlighting */ + on_circle = ansi_highlight_red(); + circle = true; + } else if (STR_IN_SET(u->active_state, "reloading", "activating", "maintenance", "deactivating")) { + on_sub = on_active = ansi_highlight(); + + if (!circle) { /* Here we let load_state highlighting win */ + on_circle = ansi_highlight(); + circle = true; + } + } else if (streq(u->active_state, "inactive")) + on_sub = on_active = ansi_grey(); + + /* As a special case, when this is a service which has not process running, let's grey out + * its state, to highlight that a bit */ + if (!on_sub && endswith(u->id, ".service") && streq(u->sub_state, "exited")) + on_sub = ansi_grey(); + + if (arg_plain) + circle = false; + id = format_unit_id(u->id, u->machine); if (!id) return log_oom(); r = table_add_many(table, TABLE_STRING, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ", - TABLE_SET_BOTH_COLORS, on_circle, + TABLE_SET_COLOR, on_circle, + TABLE_SET_BOTH_UNDERLINES, underline, TABLE_STRING, id, - TABLE_SET_BOTH_COLORS, on_active, + TABLE_SET_COLOR, on_active, + TABLE_SET_BOTH_UNDERLINES, underline, TABLE_STRING, u->load_state, - TABLE_SET_BOTH_COLORS, on_loaded, + TABLE_SET_COLOR, on_loaded, + TABLE_SET_BOTH_UNDERLINES, underline, TABLE_STRING, u->active_state, - TABLE_SET_BOTH_COLORS, on_active, + TABLE_SET_COLOR, on_active, + TABLE_SET_BOTH_UNDERLINES, underline, TABLE_STRING, u->sub_state, - TABLE_SET_BOTH_COLORS, on_active, + TABLE_SET_COLOR, on_sub, + TABLE_SET_BOTH_UNDERLINES, underline, TABLE_STRING, u->job_id ? u->job_type: "", - TABLE_SET_BOTH_COLORS, on_underline, + TABLE_SET_BOTH_UNDERLINES, underline, TABLE_STRING, u->description, - TABLE_SET_BOTH_COLORS, on_underline); + TABLE_SET_BOTH_UNDERLINES, underline); if (r < 0) return table_log_add_error(r); @@ -214,12 +235,14 @@ static int output_units_list(const UnitInfo *unit_infos, size_t c) { if (arg_all || strv_contains(arg_states, "inactive")) printf("%s%zu loaded units listed.%s\n" - "To show all installed unit files use 'systemctl list-unit-files'.\n", - on, records, off); + "%sTo show all installed unit files use 'systemctl list-unit-files'.%s\n", + on, records, off, + ansi_grey(), ansi_normal()); else if (!arg_states) - printf("%s%zu loaded units listed.%s Pass --all to see loaded but inactive units, too.\n" - "To show all installed unit files use 'systemctl list-unit-files'.\n", - on, records, off); + printf("%s%zu loaded units listed.%s %sPass --all to see loaded but inactive units, too.%s\n" + "%sTo show all installed unit files use 'systemctl list-unit-files'.%s\n", + on, records, off, + ansi_grey(), ansi_normal(), ansi_grey(), ansi_normal()); else printf("%zu loaded units listed.\n", records); } diff --git a/src/systemctl/systemctl-logind.c b/src/systemctl/systemctl-logind.c index 268e528856..2e35413b5f 100644 --- a/src/systemctl/systemctl-logind.c +++ b/src/systemctl/systemctl-logind.c @@ -51,6 +51,7 @@ int logind_reboot(enum action a) { [ACTION_HIBERNATE] = "Hibernate", [ACTION_HYBRID_SLEEP] = "HybridSleep", [ACTION_SUSPEND_THEN_HIBERNATE] = "SuspendThenHibernate", + [ACTION_SLEEP] = "Sleep", }; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -71,7 +72,7 @@ int logind_reboot(enum action a) { polkit_agent_open_maybe(); (void) logind_set_wall_message(bus); - const char *method_with_flags = strjoina(actions[a], "WithFlags"); + const char *method_with_flags = a == ACTION_SLEEP ? actions[a] : strjoina(actions[a], "WithFlags"); log_debug("%s org.freedesktop.login1.Manager %s dbus call.", arg_dry_run ? "Would execute" : "Executing", method_with_flags); @@ -103,7 +104,7 @@ int logind_reboot(enum action a) { } if (r >= 0) return 0; - if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) + if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD) || a == ACTION_SLEEP) return log_error_errno(r, "Call to %s failed: %s", actions[a], bus_error_message(&error, r)); /* Fall back to original methods in case there is an older version of systemd-logind */ diff --git a/src/systemctl/systemctl-start-special.c b/src/systemctl/systemctl-start-special.c index d93bffb759..2cf746c5a6 100644 --- a/src/systemctl/systemctl-start-special.c +++ b/src/systemctl/systemctl-start-special.c @@ -229,6 +229,9 @@ int verb_start_special(int argc, char *argv[], void *userdata) { arg_no_block = true; break; + case ACTION_SLEEP: + return logind_reboot(a); + case ACTION_EXIT: /* Since exit is so close in behaviour to power-off/reboot, let's also make * it asynchronous, in order to not confuse the user needlessly with unexpected diff --git a/src/systemctl/systemctl-start-unit.c b/src/systemctl/systemctl-start-unit.c index 6927e97747..ae7e25eedb 100644 --- a/src/systemctl/systemctl-start-unit.c +++ b/src/systemctl/systemctl-start-unit.c @@ -236,6 +236,7 @@ const struct action_metadata action_table[_ACTION_MAX] = { [ACTION_HIBERNATE] = { SPECIAL_HIBERNATE_TARGET, "hibernate", "replace-irreversibly" }, [ACTION_HYBRID_SLEEP] = { SPECIAL_HYBRID_SLEEP_TARGET, "hybrid-sleep", "replace-irreversibly" }, [ACTION_SUSPEND_THEN_HIBERNATE] = { SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET, "suspend-then-hibernate", "replace-irreversibly" }, + [ACTION_SLEEP] = { NULL, /* handled only by logind */ "sleep", NULL }, }; enum action verb_to_action(const char *verb) { @@ -294,6 +295,8 @@ int verb_start(int argc, char *argv[], void *userdata) { action = verb_to_action(argv[0]); + assert(action != ACTION_SLEEP); + if (action != _ACTION_INVALID) { /* A command in style "systemctl reboot", "systemctl poweroff", … */ method = "StartUnit"; diff --git a/src/systemctl/systemctl-whoami.c b/src/systemctl/systemctl-whoami.c index 4ee6592525..bac72c8973 100644 --- a/src/systemctl/systemctl-whoami.c +++ b/src/systemctl/systemctl-whoami.c @@ -1,24 +1,39 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "bus-common-errors.h" #include "bus-error.h" #include "bus-locator.h" +#include "format-util.h" +#include "parse-util.h" +#include "pidref.h" +#include "process-util.h" #include "systemctl.h" #include "systemctl-util.h" #include "systemctl-whoami.h" -#include "parse-util.h" -static int lookup_pid(sd_bus *bus, pid_t pid) { +static int get_unit_by_pid(sd_bus *bus, pid_t pid, char **ret) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ char *unit = NULL; const char *path; int r; + assert(bus); + assert(pid >= 0); /* 0 is accepted by GetUnitByPID for querying our own process. */ + assert(ret); + r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPID", &error, &reply, "u", (uint32_t) pid); - if (r < 0) - return log_error_errno(r, "Failed to get unit for ourselves: %s", bus_error_message(&error, r)); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_UNIT_FOR_PID)) + return log_error_errno(r, "%s", bus_error_message(&error, r)); + + return log_error_errno(r, + "Failed to get unit that PID " PID_FMT " belongs to: %s", + pid > 0 ? pid : getpid_cached(), + bus_error_message(&error, r)); + } - r = sd_bus_message_read(reply, "o", &path); + r = sd_bus_message_read_basic(reply, 'o', &path); if (r < 0) return bus_log_parse_error(r); @@ -26,7 +41,103 @@ static int lookup_pid(sd_bus *bus, pid_t pid) { if (r < 0) return log_error_errno(r, "Failed to extract unit name from D-Bus object path '%s': %m", path); - printf("%s\n", unit); + *ret = TAKE_PTR(unit); + return 0; +} + +static int lookup_pidfd(sd_bus *bus, const PidRef *pid, char **ret) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + const char *unit; + int r; + + assert(bus); + assert(pidref_is_set(pid)); + assert(ret); + + if (pid->fd < 0) + return -EOPNOTSUPP; + + r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPIDFD", &error, &reply, "h", pid->fd); + if (r < 0) { + if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) + return -EOPNOTSUPP; + + if (sd_bus_error_has_names(&error, BUS_ERROR_NO_UNIT_FOR_PID, BUS_ERROR_NO_SUCH_PROCESS)) + return log_error_errno(r, "%s", bus_error_message(&error, r)); + + return log_error_errno(r, + "Failed to get unit that PID " PID_FMT " belongs to: %s", + pid->pid, bus_error_message(&error, r)); + } + + r = sd_bus_message_read(reply, "os", NULL, &unit); + if (r < 0) + return bus_log_parse_error(r); + + char *u = strdup(unit); + if (!u) + return log_oom(); + + *ret = TAKE_PTR(u); + + return 0; +} + +static int lookup_pid(sd_bus *bus, const char *pidstr) { + _cleanup_free_ char *unit = NULL; + int r; + + assert(bus); + assert(pidstr); + + if (arg_transport == BUS_TRANSPORT_LOCAL) { + static bool use_pidfd = true; + _cleanup_(pidref_done) PidRef pid = PIDREF_NULL; + + r = pidref_set_pidstr(&pid, pidstr); + if (r < 0) + return log_error_errno(r, + r == -ESRCH ? + "PID %s doesn't exist or is already gone." : + "Failed to create reference to PID %s: %m", + pidstr); + + if (use_pidfd) { + r = lookup_pidfd(bus, &pid, &unit); + if (r == -EOPNOTSUPP) { + use_pidfd = false; + log_debug_errno(r, "Unable to look up process using pidfd, ignoring."); + } else if (r < 0) + return r; + } + + if (!use_pidfd) { + assert(!unit); + + r = get_unit_by_pid(bus, pid.pid, &unit); + if (r < 0) + return r; + + r = pidref_verify(&pid); + if (r < 0) + return log_error_errno(r, + "Failed to verify our reference to PID " PID_FMT ": %m", + pid.pid); + } + } else { + pid_t pid; + + r = parse_pid(pidstr, &pid); + if (r < 0) + return log_error_errno(r, "Failed to parse PID %s: %m", pidstr); + + r = get_unit_by_pid(bus, pid, &unit); + if (r < 0) + return r; + } + + puts(unit); return 0; } @@ -38,33 +149,26 @@ int verb_whoami(int argc, char *argv[], void *userdata) { if (r < 0) return r; - char **pids = strv_skip(argv, 1); - - if (strv_isempty(pids)) { + if (argc <= 1) { + _cleanup_free_ char *unit = NULL; if (arg_transport != BUS_TRANSPORT_LOCAL) - return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "Refusing to look up local PID on remote host."); + return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "Refusing to look up our local PID on remote host."); - return lookup_pid(bus, 0); - } else { - int ret = 0; - - STRV_FOREACH(p, pids) { - pid_t pid; - - r = parse_pid(*p, &pid); - if (r < 0) { - log_error_errno(r, "Failed to parse PID: %s", *p); - if (ret >= 0) - ret = r; - continue; - } - - r = lookup_pid(bus, pid); - if (r < 0 && ret >= 0) - ret = r; - } + /* Our own process can never go away while querying, hence no need to open pidfd. */ + + r = get_unit_by_pid(bus, 0, &unit); + if (r < 0) + return r; - return ret; + puts(unit); + return 0; } + + r = 0; + + STRV_FOREACH(pid, strv_skip(argv, 1)) + RET_GATHER(r, lookup_pid(bus, *pid)); + + return r; } diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 2a1d2d7967..d278d74789 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -250,6 +250,8 @@ static int systemctl_help(void) { " soft-reboot Shut down and reboot userspace\n" " exit [EXIT_CODE] Request user instance or container exit\n" " switch-root [ROOT [INIT]] Change to a different root file system\n" + " sleep Put the system to sleep (through one of\n" + " the operations below)\n" " suspend Suspend the system\n" " hibernate Hibernate the system\n" " hybrid-sleep Hibernate and suspend the system\n" @@ -1188,6 +1190,7 @@ static int systemctl_main(int argc, char *argv[]) { { "reboot", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special }, { "kexec", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special }, { "soft-reboot", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special }, + { "sleep", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special }, { "suspend", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special }, { "hibernate", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special }, { "hybrid-sleep", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special }, @@ -1332,6 +1335,7 @@ static int run(int argc, char *argv[]) { break; case ACTION_EXIT: + case ACTION_SLEEP: case ACTION_SUSPEND: case ACTION_HIBERNATE: case ACTION_HYBRID_SLEEP: diff --git a/src/systemctl/systemctl.h b/src/systemctl/systemctl.h index 9f63443fde..17dcfb4048 100644 --- a/src/systemctl/systemctl.h +++ b/src/systemctl/systemctl.h @@ -18,6 +18,7 @@ enum action { ACTION_KEXEC, ACTION_SOFT_REBOOT, ACTION_EXIT, + ACTION_SLEEP, ACTION_SUSPEND, ACTION_HIBERNATE, ACTION_HYBRID_SLEEP, diff --git a/src/test/test-fd-util.c b/src/test/test-fd-util.c index 021d4b47c2..5dd9e48c80 100644 --- a/src/test/test-fd-util.c +++ b/src/test/test-fd-util.c @@ -646,6 +646,24 @@ TEST(dir_fd_is_root) { assert_se(dir_fd_is_root_or_cwd(fd) == 0); } +TEST(fds_are_same_mount) { + _cleanup_close_ int fd1 = -EBADF, fd2 = -EBADF, fd3 = -EBADF, fd4 = -EBADF; + + fd1 = open("/sys", O_CLOEXEC|O_PATH|O_DIRECTORY|O_NOFOLLOW); + fd2 = open("/proc", O_CLOEXEC|O_PATH|O_DIRECTORY|O_NOFOLLOW); + fd3 = open("/proc", O_CLOEXEC|O_PATH|O_DIRECTORY|O_NOFOLLOW); + fd4 = open("/", O_CLOEXEC|O_PATH|O_DIRECTORY|O_NOFOLLOW); + + if (fd1 < 0 || fd2 < 0 || fd3 < 0 || fd4 < 0) + return (void) log_tests_skipped_errno(errno, "Failed to open /sys or /proc or /"); + + if (fds_are_same_mount(fd1, fd4) > 0 && fds_are_same_mount(fd2, fd4) > 0) + return (void) log_tests_skipped("Cannot test fds_are_same_mount() as /sys and /proc are not mounted"); + + assert_se(fds_are_same_mount(fd1, fd2) == 0); + assert_se(fds_are_same_mount(fd2, fd3) > 0); +} + TEST(fd_get_path) { _cleanup_(rm_rf_physical_and_freep) char *t = NULL; _cleanup_close_ int tfd = -EBADF, fd = -EBADF; diff --git a/src/test/test-fdset.c b/src/test/test-fdset.c index 8947a319b6..8f00e598fd 100644 --- a/src/test/test-fdset.c +++ b/src/test/test-fdset.c @@ -11,8 +11,8 @@ #include "tmpfile-util.h" TEST(fdset_new_fill) { - int fd = -EBADF; _cleanup_fdset_free_ FDSet *fdset = NULL; + int fd = -EBADF, flags; log_close(); log_set_open_when_needed(true); @@ -50,6 +50,9 @@ TEST(fdset_new_fill) { assert_se(fdset_new_fill(/* filter_cloexec= */ 0, &fdset) >= 0); assert_se(fdset_contains(fdset, fd)); + flags = fcntl(fd, F_GETFD); + assert_se(flags >= 0); + assert_se(FLAGS_SET(flags, FD_CLOEXEC)); fdset = fdset_free(fdset); assert_se(fcntl(fd, F_GETFD) < 0); assert_se(errno == EBADF); diff --git a/src/test/test-macro.c b/src/test/test-macro.c index 63fe6ea95e..b91a1f96ac 100644 --- a/src/test/test-macro.c +++ b/src/test/test-macro.c @@ -15,7 +15,7 @@ TEST(saturate_add) { assert_se(saturate_add(60, 60, 50) == 50); } -TEST(align_power2) { +TEST(ALIGN_POWER2) { unsigned long i, p2; assert_se(ALIGN_POWER2(0) == 0); @@ -57,7 +57,7 @@ TEST(align_power2) { } } -TEST(max) { +TEST(MAX) { static const struct { int a; int b[CONST_MAX(10, 100)]; @@ -159,7 +159,7 @@ TEST(container_of) { #pragma GCC diagnostic pop -TEST(div_round_up) { +TEST(DIV_ROUND_UP) { int div; /* basic tests */ @@ -192,7 +192,7 @@ TEST(div_round_up) { assert_se(0xfffffffdU / 10U + !!(0xfffffffdU % 10U) == 429496730U); } -TEST(ptr_to_int) { +TEST(PTR_TO_INT) { /* Primary reason to have this test is to validate that pointers are large enough to hold entire int range */ assert_se(PTR_TO_INT(INT_TO_PTR(0)) == 0); assert_se(PTR_TO_INT(INT_TO_PTR(1)) == 1); @@ -201,7 +201,7 @@ TEST(ptr_to_int) { assert_se(PTR_TO_INT(INT_TO_PTR(INT_MIN)) == INT_MIN); } -TEST(in_set) { +TEST(IN_SET) { assert_se(IN_SET(1, 1, 2)); assert_se(IN_SET(1, 1, 2, 3, 4)); assert_se(IN_SET(2, 1, 2, 3, 4)); @@ -221,7 +221,7 @@ TEST(in_set) { assert_se(!IN_SET(t.x, 2, 3, 4)); } -TEST(foreach_pointer) { +TEST(FOREACH_POINTER) { int a, b, c, *i; size_t k = 0; @@ -300,7 +300,7 @@ TEST(foreach_pointer) { assert_se(k == 11); } -TEST(foreach_va_args) { +TEST(FOREACH_VA_ARGS) { size_t i; i = 0; @@ -484,7 +484,7 @@ TEST(foreach_va_args) { assert_se(false); } -TEST(align_to) { +TEST(ALIGN_TO) { assert_se(ALIGN_TO(0, 1) == 0); assert_se(ALIGN_TO(1, 1) == 1); assert_se(ALIGN_TO(2, 1) == 2); @@ -1005,7 +1005,7 @@ TEST(FOREACH_ARRAY) { assert_se(ROUND_UP(x, y) == max_value); \ }) -TEST(round_up) { +TEST(ROUND_UP) { TEST_ROUND_UP_BY_TYPE(uint8_t, UINT8_MAX); TEST_ROUND_UP_BY_TYPE(uint16_t, UINT16_MAX); TEST_ROUND_UP_BY_TYPE(uint32_t, UINT32_MAX); diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c index e9c776a8c5..4233ca6527 100644 --- a/src/test/test-socket-util.c +++ b/src/test/test-socket-util.c @@ -176,10 +176,7 @@ TEST(getpeercred_getpeergroups) { test_gids = (gid_t*) gids; n_test_gids = ELEMENTSOF(gids); - assert_se(setgroups(n_test_gids, test_gids) >= 0); - assert_se(setresgid(test_gid, test_gid, test_gid) >= 0); - assert_se(setresuid(test_uid, test_uid, test_uid) >= 0); - + assert_se(fully_set_uid_gid(test_uid, test_gid, test_gids, n_test_gids) >= 0); } else { long ngroups_max; diff --git a/src/tpm2-setup/tpm2-setup.c b/src/tpm2-setup/tpm2-setup.c index be34d166d7..0be7ffc6a5 100644 --- a/src/tpm2-setup/tpm2-setup.c +++ b/src/tpm2-setup/tpm2-setup.c @@ -284,7 +284,8 @@ static int run(int argc, char *argv[]) { if (runtime_key.pkey) { if (memcmp_nn(tpm2_key.fingerprint, tpm2_key.fingerprint_size, runtime_key.fingerprint, runtime_key.fingerprint_size) != 0) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Saved runtime SRK differs from TPM SRK, refusing."); + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Saved runtime SRK differs from TPM SRK, refusing."); if (arg_early) { log_info("SRK saved in '%s' matches SRK in TPM2.", runtime_key.path); @@ -351,7 +352,8 @@ static int run(int argc, char *argv[]) { return log_error_errno(r, "Failed to marshal TPM2_PUBLIC key."); if (fwrite(marshalled, 1, marshalled_size, f) != marshalled_size) - return log_error_errno(errno, "Failed to write SRK public key file '%s'.", tpm2b_public_path); + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to write SRK public key file '%s'.", tpm2b_public_path); if (fchmod(fileno(f), 0444) < 0) return log_error_errno(errno, "Failed to adjust access mode of SRK public key file '%s' to 0444: %m", tpm2b_public_path); diff --git a/src/ukify/test/test_ukify.py b/src/ukify/test/test_ukify.py index b12c09d4bf..3a60a21f55 100755 --- a/src/ukify/test/test_ukify.py +++ b/src/ukify/test/test_ukify.py @@ -464,7 +464,7 @@ def test_sections(kernel_initrd, tmpdir): dump = subprocess.check_output(['objdump', '-h', output], text=True) for sect in 'text osrel cmdline linux initrd uname test'.split(): - assert re.search(fr'^\s*\d+\s+.{sect}\s+0', dump, re.MULTILINE) + assert re.search(fr'^\s*\d+\s+\.{sect}\s+[0-9a-f]+', dump, re.MULTILINE) def test_addon(tmpdir): output = f'{tmpdir}/addon.efi' @@ -499,7 +499,7 @@ baz,3 dump = subprocess.check_output(['objdump', '-h', output], text=True) for sect in 'text cmdline test sbat'.split(): - assert re.search(fr'^\s*\d+\s+.{sect}\s+0', dump, re.MULTILINE) + assert re.search(fr'^\s*\d+\s+\.{sect}\s+[0-9a-f]+', dump, re.MULTILINE) pe = pefile.PE(output, fast_load=True) found = False @@ -529,7 +529,8 @@ def test_uname_scraping(kernel_initrd): uname = ukify.Uname.scrape(kernel_initrd[1]) assert re.match(r'\d+\.\d+\.\d+', uname) -def test_efi_signing_sbsign(kernel_initrd, tmpdir): +@pytest.mark.parametrize("days", [365*10, None]) +def test_efi_signing_sbsign(days, kernel_initrd, tmpdir): if kernel_initrd is None: pytest.skip('linux+initrd not found') if not shutil.which('sbsign'): @@ -540,7 +541,7 @@ def test_efi_signing_sbsign(kernel_initrd, tmpdir): key = unbase64(ourdir / 'example.signing.key.base64') output = f'{tmpdir}/signed.efi' - opts = ukify.parse_args([ + args = [ 'build', *kernel_initrd, f'--output={output}', @@ -548,7 +549,11 @@ def test_efi_signing_sbsign(kernel_initrd, tmpdir): '--cmdline=ARG1 ARG2 ARG3', f'--secureboot-certificate={cert.name}', f'--secureboot-private-key={key.name}', - ]) + ] + if days is not None: + args += [f'--secureboot-certificate-validity={days}'] + + opts = ukify.parse_args(args) try: ukify.check_inputs(opts) @@ -694,7 +699,7 @@ def test_pcr_signing(kernel_initrd, tmpdir): dump = subprocess.check_output(['objdump', '-h', output], text=True) for sect in 'text osrel cmdline linux initrd uname pcrsig'.split(): - assert re.search(fr'^\s*\d+\s+.{sect}\s+0', dump, re.MULTILINE) + assert re.search(fr'^\s*\d+\s+\.{sect}\s+[0-9a-f]+', dump, re.MULTILINE) # objcopy fails when called without an output argument (EPERM). # It also fails when called with /dev/null (file truncated). @@ -767,7 +772,7 @@ def test_pcr_signing2(kernel_initrd, tmpdir): dump = subprocess.check_output(['objdump', '-h', output], text=True) for sect in 'text osrel cmdline linux initrd uname pcrsig'.split(): - assert re.search(fr'^\s*\d+\s+.{sect}\s+0', dump, re.MULTILINE) + assert re.search(fr'^\s*\d+\s+\.{sect}\s+[0-9a-f]+', dump, re.MULTILINE) subprocess.check_call([ 'objcopy', diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py index b46d775a47..b33c8cf744 100755 --- a/src/ukify/ukify.py +++ b/src/ukify/ukify.py @@ -846,9 +846,6 @@ uki,1,UKI,uki,1,https://www.freedesktop.org/software/systemd/man/systemd-stub.ht print(f"Wrote {'signed' if sign_args_present else 'unsigned'} {opts.output}") -ONE_DAY = datetime.timedelta(1, 0, 0) - - @contextlib.contextmanager def temporary_umask(mask: int): # Drop <mask> bits from umask @@ -888,7 +885,7 @@ def generate_key_cert_pair( ).not_valid_before( now, ).not_valid_after( - now + ONE_DAY * valid_days + now + datetime.timedelta(days=valid_days) ).serial_number( x509.random_serial_number() ).public_key( @@ -935,6 +932,8 @@ def generate_priv_pub_key_pair(keylength : int = 2048) -> tuple[bytes]: def generate_keys(opts): + work = False + # This will generate keys and certificates and write them to the paths that # are specified as input paths. if opts.sb_key or opts.sb_cert: @@ -950,6 +949,8 @@ def generate_keys(opts): print(f'Writing SecureBoot certificate to {opts.sb_cert}') opts.sb_cert.write_bytes(cert_pem) + work = True + for priv_key, pub_key, _ in key_path_groups(opts): priv_key_pem, pub_key_pem = generate_priv_pub_key_pair() @@ -960,6 +961,11 @@ def generate_keys(opts): print(f'Writing public key for PCR signing to {pub_key}') pub_key.write_bytes(pub_key_pem) + work = True + + if not work: + raise ValueError('genkey: --secureboot-private-key=/--secureboot-certificate= or --pcr-private-key/--pcr-public-key must be specified') + def inspect_section(opts, section): name = section.Name.rstrip(b"\x00").decode() @@ -1335,6 +1341,7 @@ CONFIG_ITEMS = [ ConfigItem( '--secureboot-certificate-validity', metavar = 'DAYS', + type = int, dest = 'sb_cert_validity', default = 365 * 10, help = "period of validity (in days) for a certificate created by 'genkey'", diff --git a/src/userdb/20-systemd-userdb.conf.in b/src/userdb/20-systemd-userdb.conf.in new file mode 100644 index 0000000000..031fc3a4b8 --- /dev/null +++ b/src/userdb/20-systemd-userdb.conf.in @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Make sure SSH authorized keys recorded in user records can be consumed by SSH +# +AuthorizedKeysCommand {{BINDIR}}/userdbctl ssh-authorized-keys %u +AuthorizedKeysCommandUser root diff --git a/src/userdb/meson.build b/src/userdb/meson.build index 2d701c8ba7..260dbab2c8 100644 --- a/src/userdb/meson.build +++ b/src/userdb/meson.build @@ -23,3 +23,16 @@ executables += [ 'dependencies' : threads, }, ] + +custom_target( + '20-systemd-userdb.conf', + input : '20-systemd-userdb.conf.in', + output : '20-systemd-userdb.conf', + command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'], + install : conf.get('ENABLE_USERDB') == 1 and sshdconfdir != 'no', + install_dir : libexecdir / 'sshd_config.d') + +install_emptydir(sshdconfdir) + +meson.add_install_script(sh, '-c', + ln_s.format(libexecdir / 'sshd_config.d' / '20-systemd-userdb.conf', sshdconfdir / '20-systemd-userdb.conf')) |