summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyze/analyze-srk.c10
-rw-r--r--src/basic/fd-util.c63
-rw-r--r--src/basic/fd-util.h2
-rw-r--r--src/basic/log.c22
-rw-r--r--src/basic/missing_stat.h2
-rw-r--r--src/basic/recurse-dir.c13
-rw-r--r--src/basic/recurse-dir.h1
-rw-r--r--src/basic/stat-util.c6
-rw-r--r--src/basic/terminal-util.h11
-rw-r--r--src/basic/user-util.c19
-rw-r--r--src/basic/user-util.h5
-rw-r--r--src/core/automount.c2
-rw-r--r--src/core/dbus-unit.c11
-rw-r--r--src/core/exec-invoke.c69
-rw-r--r--src/core/execute-serialize.c19
-rw-r--r--src/core/executor.c4
-rw-r--r--src/core/import-creds.c7
-rw-r--r--src/core/load-fragment-gperf.gperf.in3
-rw-r--r--src/core/load-fragment.c27
-rw-r--r--src/core/load-fragment.h2
-rw-r--r--src/core/main.c77
-rw-r--r--src/core/manager.c11
-rw-r--r--src/core/manager.h10
-rw-r--r--src/core/mount.c51
-rw-r--r--src/core/path.c2
-rw-r--r--src/core/show-status.c2
-rw-r--r--src/core/socket.c2
-rw-r--r--src/core/swap.c2
-rw-r--r--src/core/system.conf.in1
-rw-r--r--src/core/timer.c2
-rw-r--r--src/core/unit-serialize.c25
-rw-r--r--src/core/unit.c169
-rw-r--r--src/core/unit.h21
-rw-r--r--src/creds/creds.c7
-rw-r--r--src/cryptsetup/cryptsetup-generator.c23
-rw-r--r--src/fstab-generator/fstab-generator.c13
-rw-r--r--src/home/homed-home.c18
-rw-r--r--src/home/homework-fscrypt.c18
-rw-r--r--src/hostname/hostnamectl.c65
-rw-r--r--src/hostname/hostnamed.c52
-rw-r--r--src/journal-remote/journal-gatewayd.c176
-rw-r--r--src/journal/journalctl.c9
-rw-r--r--src/libsystemd/sd-journal/catalog.c50
-rw-r--r--src/libsystemd/sd-journal/journal-verify.c2
-rw-r--r--src/login/loginctl.c16
-rw-r--r--src/login/logind-action.c119
-rw-r--r--src/login/logind-action.h21
-rw-r--r--src/login/logind-core.c2
-rw-r--r--src/login/logind-dbus.c205
-rw-r--r--src/login/logind-gperf.gperf1
-rw-r--r--src/login/logind-wall.c12
-rw-r--r--src/login/logind.c19
-rw-r--r--src/login/logind.conf.in1
-rw-r--r--src/login/logind.h4
-rw-r--r--src/login/test-login-tables.c20
-rw-r--r--src/network/networkctl.c62
-rw-r--r--src/network/networkd-address.c2
-rw-r--r--src/network/networkd-bridge-vlan.c441
-rw-r--r--src/network/networkd-bridge-vlan.h20
-rw-r--r--src/network/networkd-link.c7
-rw-r--r--src/network/networkd-link.h7
-rw-r--r--src/network/networkd-lldp-rx.c4
-rw-r--r--src/network/networkd-manager.c14
-rw-r--r--src/network/networkd-neighbor.c14
-rw-r--r--src/network/networkd-network-gperf.gperf6
-rw-r--r--src/network/networkd-network.c2
-rw-r--r--src/network/networkd-network.h7
-rw-r--r--src/network/networkd-queue.c3
-rw-r--r--src/network/networkd-queue.h1
-rw-r--r--src/network/networkd-setlink.c66
-rw-r--r--src/notify/notify.c4
-rw-r--r--src/nspawn/nspawn-setuid.c13
-rw-r--r--src/partition/definitions/confext.repart.d/10-root.conf1
-rw-r--r--src/partition/definitions/confext.repart.d/20-root-verity.conf1
-rw-r--r--src/partition/definitions/portable.repart.d/10-root.conf1
-rw-r--r--src/partition/definitions/portable.repart.d/20-root-verity.conf1
-rw-r--r--src/partition/definitions/sysext.repart.d/10-root.conf4
-rw-r--r--src/partition/definitions/sysext.repart.d/20-root-verity.conf1
-rw-r--r--src/partition/repart.c44
-rw-r--r--src/run/run.c40
-rw-r--r--src/shared/bus-unit-util.c1
-rw-r--r--src/shared/fdset.c12
-rw-r--r--src/shared/format-table.c127
-rw-r--r--src/shared/format-table.h5
-rw-r--r--src/shared/killall.c2
-rw-r--r--src/shared/mkfs-util.c7
-rw-r--r--src/shared/ptyfwd.c33
-rw-r--r--src/shared/seccomp-util.c85
-rw-r--r--src/shared/selinux-util.c2
-rw-r--r--src/shared/switch-root.c6
-rw-r--r--src/stdio-bridge/stdio-bridge.c10
-rw-r--r--src/systemctl/systemctl-list-units.c81
-rw-r--r--src/systemctl/systemctl-logind.c5
-rw-r--r--src/systemctl/systemctl-start-special.c3
-rw-r--r--src/systemctl/systemctl-start-unit.c3
-rw-r--r--src/systemctl/systemctl-whoami.c164
-rw-r--r--src/systemctl/systemctl.c4
-rw-r--r--src/systemctl/systemctl.h1
-rw-r--r--src/test/test-fd-util.c18
-rw-r--r--src/test/test-fdset.c5
-rw-r--r--src/test/test-macro.c18
-rw-r--r--src/test/test-socket-util.c5
-rw-r--r--src/tpm2-setup/tpm2-setup.c6
-rwxr-xr-xsrc/ukify/test/test_ukify.py19
-rwxr-xr-xsrc/ukify/ukify.py15
-rw-r--r--src/userdb/20-systemd-userdb.conf.in6
-rw-r--r--src/userdb/meson.build13
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, &params->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 *)&params->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, &params->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'))