summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyze/analyze-verify.c17
-rw-r--r--src/backlight/backlight.c2
-rw-r--r--src/basic/architecture.c10
-rw-r--r--src/basic/architecture.h12
-rw-r--r--src/basic/audit-util.c7
-rw-r--r--src/basic/capability-util.c6
-rw-r--r--src/basic/def.h2
-rw-r--r--src/basic/escape.c2
-rw-r--r--src/basic/exit-status.c33
-rw-r--r--src/basic/exit-status.h14
-rw-r--r--src/basic/fileio.c8
-rw-r--r--src/basic/fileio.h2
-rw-r--r--src/basic/fs-util.c187
-rw-r--r--src/basic/fs-util.h2
-rw-r--r--src/basic/list.h2
-rw-r--r--src/basic/missing.h4
-rw-r--r--src/basic/mount-util.c71
-rw-r--r--src/basic/mount-util.h2
-rw-r--r--src/basic/path-util.c68
-rw-r--r--src/basic/path-util.h2
-rw-r--r--src/basic/socket-util.c19
-rw-r--r--src/basic/socket-util.h2
-rw-r--r--src/basic/string-util.c12
-rw-r--r--src/basic/strv.h5
-rw-r--r--src/basic/time-util.c4
-rw-r--r--src/basic/time-util.h1
-rw-r--r--src/basic/user-util.c68
-rw-r--r--src/basic/user-util.h3
-rw-r--r--src/basic/util.c2
-rw-r--r--src/boot/efi/measure.c23
-rw-r--r--src/core/automount.c5
-rw-r--r--src/core/busname.c2
-rw-r--r--src/core/dbus-execute.c9
-rw-r--r--src/core/dbus-unit.c2
-rw-r--r--src/core/dbus.c4
-rw-r--r--src/core/dynamic-user.c2
-rw-r--r--src/core/execute.c408
-rw-r--r--src/core/execute.h2
-rw-r--r--src/core/load-fragment-gperf.gperf.m42
-rw-r--r--src/core/main.c37
-rw-r--r--src/core/manager.c165
-rw-r--r--src/core/manager.h17
-rw-r--r--src/core/mount.c48
-rw-r--r--src/core/namespace.c586
-rw-r--r--src/core/namespace.h3
-rw-r--r--src/core/service.c15
-rw-r--r--src/core/socket.c11
-rw-r--r--src/core/swap.c2
-rw-r--r--src/core/system.conf1
-rw-r--r--src/core/umount.c2
-rw-r--r--src/core/unit.c11
-rw-r--r--src/coredump/coredump.c78
-rw-r--r--src/coredump/coredumpctl.c350
-rw-r--r--src/hostname/hostnamectl.c2
-rw-r--r--src/hostname/hostnamed.c8
-rw-r--r--src/hwdb/hwdb.c56
-rw-r--r--src/journal-remote/journal-gatewayd.c37
-rw-r--r--src/journal-remote/journal-remote.c38
-rw-r--r--src/journal-remote/journal-upload.c2
-rw-r--r--src/journal-remote/microhttpd-util.c20
-rw-r--r--src/journal-remote/microhttpd-util.h3
-rw-r--r--src/journal/journal-file.c21
-rw-r--r--src/journal/journal-verify.c11
-rw-r--r--src/journal/journalctl.c6
-rw-r--r--src/journal/journald-rate-limit.c4
-rw-r--r--src/journal/journald-server.c4
-rw-r--r--src/journal/journald-stream.c3
-rw-r--r--src/journal/test-catalog.c4
-rw-r--r--src/journal/test-compress.c4
-rw-r--r--src/journal/test-journal-interleaving.c7
-rw-r--r--src/journal/test-mmap-cache.c6
-rw-r--r--src/kernel-install/kernel-install14
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.c22
-rw-r--r--src/libsystemd/sd-bus/busctl.c3
-rw-r--r--src/libsystemd/sd-bus/test-bus-chat.c2
-rw-r--r--src/libsystemd/sd-bus/test-bus-creds.c7
-rw-r--r--src/libsystemd/sd-device/sd-device.c5
-rw-r--r--src/libsystemd/sd-hwdb/hwdb-internal.h8
-rw-r--r--src/libsystemd/sd-hwdb/sd-hwdb.c58
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.c74
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.h1
-rw-r--r--src/locale/localed.c6
-rw-r--r--src/login/logind-session.c2
-rw-r--r--src/login/logind-user.c6
-rw-r--r--src/login/logind.c2
-rw-r--r--src/login/systemd-user.m44
-rw-r--r--src/machine/machinectl.c82
-rw-r--r--src/machine/machined-dbus.c4
-rw-r--r--src/network/networkctl.c2
-rw-r--r--src/network/networkd-address.c59
-rw-r--r--src/network/networkd-address.h6
-rw-r--r--src/network/networkd-conf.c2
-rw-r--r--src/network/networkd-dhcp4.c3
-rw-r--r--src/network/networkd-fdb.c12
-rw-r--r--src/network/networkd-link.c77
-rw-r--r--src/network/networkd-ndisc.c6
-rw-r--r--src/network/networkd-netdev-bridge.c2
-rw-r--r--src/network/networkd-netdev-gperf.gperf5
-rw-r--r--src/network/networkd-netdev-vcan.c25
-rw-r--r--src/network/networkd-netdev-vcan.h34
-rw-r--r--src/network/networkd-netdev-vxlan.c8
-rw-r--r--src/network/networkd-netdev-vxlan.h2
-rw-r--r--src/network/networkd-netdev.c19
-rw-r--r--src/network/networkd-netdev.h1
-rw-r--r--src/network/networkd-network-gperf.gperf7
-rw-r--r--src/network/networkd-network.c70
-rw-r--r--src/network/networkd-network.h3
-rw-r--r--src/network/networkd.h1
-rw-r--r--src/nspawn/nspawn-mount.c34
-rw-r--r--src/nspawn/nspawn.c1102
-rw-r--r--src/nss-resolve/nss-resolve.c13
-rw-r--r--src/remount-fs/remount-fs.c2
-rw-r--r--src/resolve/resolve-tool.c40
-rw-r--r--src/resolve/resolved-conf.c13
-rw-r--r--src/resolve/resolved-conf.h17
-rw-r--r--src/resolve/resolved-dns-packet.c3
-rw-r--r--src/resolve/resolved-dns-packet.h1
-rw-r--r--src/resolve/resolved-dns-scope.c8
-rw-r--r--src/resolve/resolved-dns-scope.h3
-rw-r--r--src/resolve/resolved-dns-server.c21
-rw-r--r--src/resolve/resolved-dns-server.h2
-rw-r--r--src/resolve/resolved-dns-stream.h1
-rw-r--r--src/resolve/resolved-dns-stub.c162
-rw-r--r--src/resolve/resolved-dns-stub.h3
-rw-r--r--src/resolve/resolved-dns-transaction.h2
-rw-r--r--src/resolve/resolved-gperf.gperf13
-rw-r--r--src/resolve/resolved-link.h1
-rw-r--r--src/resolve/resolved-manager.c1
-rw-r--r--src/resolve/resolved-manager.h2
-rw-r--r--src/resolve/resolved-resolv-conf.c10
-rw-r--r--src/resolve/resolved.conf.in1
-rw-r--r--src/run/run.c18
-rw-r--r--src/shared/ask-password-api.c2
-rw-r--r--src/shared/bus-unit-util.c2
-rw-r--r--src/shared/condition.c42
-rw-r--r--src/shared/conf-parser.c72
-rw-r--r--src/shared/conf-parser.h48
-rw-r--r--src/shared/dns-domain.c4
-rw-r--r--src/shared/gcrypt-util.h8
-rw-r--r--src/shared/install.c7
-rw-r--r--src/shared/seccomp-util.c22
-rw-r--r--src/shared/sleep-config.c2
-rw-r--r--src/sysctl/sysctl.c18
-rw-r--r--src/systemctl/systemctl.c201
-rw-r--r--src/systemd/sd-messages.h1
-rw-r--r--src/sysv-generator/sysv-generator.c8
-rw-r--r--src/test/test-acl-util.c2
-rw-r--r--src/test/test-async.c2
-rw-r--r--src/test/test-clock.c2
-rw-r--r--src/test/test-copy.c12
-rw-r--r--src/test/test-dns-domain.c3
-rw-r--r--src/test/test-engine.c2
-rw-r--r--src/test/test-execute.c28
-rw-r--r--src/test/test-fd-util.c8
-rw-r--r--src/test/test-fdset.c16
-rw-r--r--src/test/test-fileio.c24
-rw-r--r--src/test/test-fs-util.c124
-rw-r--r--src/test/test-glob-util.c2
-rw-r--r--src/test/test-hostname-util.c2
-rw-r--r--src/test/test-list.c23
-rw-r--r--src/test/test-ns.c14
-rw-r--r--src/test/test-path-util.c19
-rw-r--r--src/test/test-path.c2
-rw-r--r--src/test/test-sched-prio.c2
-rw-r--r--src/test/test-stat-util.c4
-rw-r--r--src/test/test-strv.c21
-rw-r--r--src/test/test-terminal-util.c2
-rw-r--r--src/test/test-tmpfiles.c2
-rw-r--r--src/test/test-unit-file.c18
-rw-r--r--src/timesync/timesyncd-conf.c2
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c2
-rw-r--r--src/udev/net/ethtool-util.c6
-rw-r--r--src/udev/net/link-config.c16
-rw-r--r--src/udev/udev-builtin-path_id.c9
-rw-r--r--src/udev/udevadm-control.c29
-rw-r--r--src/udev/udevd.c9
-rw-r--r--src/update-done/update-done.c76
-rw-r--r--src/vconsole/vconsole-setup.c2
178 files changed, 3899 insertions, 1730 deletions
diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c
index 5fd3ee49eb..0ce0276d92 100644
--- a/src/analyze/analyze-verify.c
+++ b/src/analyze/analyze-verify.c
@@ -71,6 +71,7 @@ static int prepare_filename(const char *filename, char **ret) {
}
static int generate_path(char **var, char **filenames) {
+ const char *old;
char **filename;
_cleanup_strv_free_ char **ans = NULL;
@@ -90,9 +91,19 @@ static int generate_path(char **var, char **filenames) {
assert_se(strv_uniq(ans));
- r = strv_extend(&ans, "");
- if (r < 0)
- return r;
+ /* First, prepend our directories. Second, if some path was specified, use that, and
+ * otherwise use the defaults. Any duplicates will be filtered out in path-lookup.c.
+ * Treat explicit empty path to mean that nothing should be appended.
+ */
+ old = getenv("SYSTEMD_UNIT_PATH");
+ if (!streq_ptr(old, "")) {
+ if (!old)
+ old = ":";
+
+ r = strv_extend(&ans, old);
+ if (r < 0)
+ return r;
+ }
*var = strv_join(ans, ":");
if (!*var)
diff --git a/src/backlight/backlight.c b/src/backlight/backlight.c
index 45be135a23..7c59f60d5f 100644
--- a/src/backlight/backlight.c
+++ b/src/backlight/backlight.c
@@ -167,7 +167,7 @@ static bool validate_device(struct udev *udev, struct udev_device *device) {
continue;
v = udev_device_get_sysattr_value(other, "type");
- if (!streq_ptr(v, "platform") && !streq_ptr(v, "firmware"))
+ if (!STRPTR_IN_SET(v, "platform", "firmware"))
continue;
/* OK, so there's another backlight device, and it's a
diff --git a/src/basic/architecture.c b/src/basic/architecture.c
index b1c8e91f50..b74dc0db78 100644
--- a/src/basic/architecture.c
+++ b/src/basic/architecture.c
@@ -123,6 +123,14 @@ int uname_architecture(void) {
{ "crisv32", ARCHITECTURE_CRIS },
#elif defined(__nios2__)
{ "nios2", ARCHITECTURE_NIOS2 },
+#elif defined(__riscv__)
+ { "riscv32", ARCHITECTURE_RISCV32 },
+ { "riscv64", ARCHITECTURE_RISCV64 },
+# if __SIZEOF_POINTER__ == 4
+ { "riscv", ARCHITECTURE_RISCV32 },
+# elif __SIZEOF_POINTER__ == 8
+ { "riscv", ARCHITECTURE_RISCV64 },
+# endif
#else
#error "Please register your architecture here!"
#endif
@@ -174,6 +182,8 @@ static const char *const architecture_table[_ARCHITECTURE_MAX] = {
[ARCHITECTURE_TILEGX] = "tilegx",
[ARCHITECTURE_CRIS] = "cris",
[ARCHITECTURE_NIOS2] = "nios2",
+ [ARCHITECTURE_RISCV32] = "riscv32",
+ [ARCHITECTURE_RISCV64] = "riscv64",
};
DEFINE_STRING_TABLE_LOOKUP(architecture, int);
diff --git a/src/basic/architecture.h b/src/basic/architecture.h
index b3e4d85906..5a77c31932 100644
--- a/src/basic/architecture.h
+++ b/src/basic/architecture.h
@@ -58,6 +58,8 @@ enum {
ARCHITECTURE_TILEGX,
ARCHITECTURE_CRIS,
ARCHITECTURE_NIOS2,
+ ARCHITECTURE_RISCV32,
+ ARCHITECTURE_RISCV64,
_ARCHITECTURE_MAX,
_ARCHITECTURE_INVALID = -1
};
@@ -191,6 +193,16 @@ int uname_architecture(void);
#elif defined(__nios2__)
# define native_architecture() ARCHITECTURE_NIOS2
# define LIB_ARCH_TUPLE "nios2-linux-gnu"
+#elif defined(__riscv__)
+# if __SIZEOF_POINTER__ == 4
+# define native_architecture() ARCHITECTURE_RISCV32
+# define LIB_ARCH_TUPLE "riscv32-linux-gnu"
+# elif __SIZEOF_POINTER__ == 8
+# define native_architecture() ARCHITECTURE_RISCV64
+# define LIB_ARCH_TUPLE "riscv64-linux-gnu"
+# else
+# error "Unrecognized riscv architecture variant"
+# endif
#else
# error "Please register your architecture here!"
#endif
diff --git a/src/basic/audit-util.c b/src/basic/audit-util.c
index 5741fecdd6..d1c9695973 100644
--- a/src/basic/audit-util.c
+++ b/src/basic/audit-util.c
@@ -92,8 +92,11 @@ bool use_audit(void) {
int fd;
fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT);
- if (fd < 0)
- cached_use = errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT;
+ if (fd < 0) {
+ cached_use = !IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT, EPERM);
+ if (errno == EPERM)
+ log_debug_errno(errno, "Audit access prohibited, won't talk to audit");
+ }
else {
cached_use = true;
safe_close(fd);
diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c
index d4c5bd6937..c3de20a0e8 100644
--- a/src/basic/capability-util.c
+++ b/src/basic/capability-util.c
@@ -31,6 +31,7 @@
#include "log.h"
#include "macro.h"
#include "parse-util.h"
+#include "user-util.h"
#include "util.h"
int have_effective_cap(int value) {
@@ -295,8 +296,9 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
if (setresgid(gid, gid, gid) < 0)
return log_error_errno(errno, "Failed to change group ID: %m");
- if (setgroups(0, NULL) < 0)
- return log_error_errno(errno, "Failed to drop auxiliary groups list: %m");
+ r = maybe_setgroups(0, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to drop auxiliary groups list: %m");
/* Ensure we keep the permitted caps across the setresuid() */
if (prctl(PR_SET_KEEPCAPS, 1) < 0)
diff --git a/src/basic/def.h b/src/basic/def.h
index 1a7a0f4928..2266eff650 100644
--- a/src/basic/def.h
+++ b/src/basic/def.h
@@ -79,7 +79,7 @@
#endif
/* Return a nulstr for a standard cascade of configuration paths,
- * suitable to pass to conf_files_list_nulstr() or config_parse_many()
+ * suitable to pass to conf_files_list_nulstr() or config_parse_many_nulstr()
* to implement drop-in directories for extending configuration
* files. */
#define CONF_PATHS_NULSTR(n) \
diff --git a/src/basic/escape.c b/src/basic/escape.c
index 01daf11ce7..4a1ec4505e 100644
--- a/src/basic/escape.c
+++ b/src/basic/escape.c
@@ -333,7 +333,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
assert(remaining > 0);
if (*f != '\\') {
- /* A literal literal, copy verbatim */
+ /* A literal, copy verbatim */
*(t++) = *f;
continue;
}
diff --git a/src/basic/exit-status.c b/src/basic/exit-status.c
index d488cfc59f..59557f8afe 100644
--- a/src/basic/exit-status.c
+++ b/src/basic/exit-status.c
@@ -24,12 +24,12 @@
#include "macro.h"
#include "set.h"
-const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
+const char* exit_status_to_string(int status, ExitStatusLevel level) {
/* We cast to int here, so that -Wenum doesn't complain that
* EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */
- switch ((int) status) {
+ switch (status) {
case EXIT_SUCCESS:
return "SUCCESS";
@@ -39,7 +39,7 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
}
if (IN_SET(level, EXIT_STATUS_SYSTEMD, EXIT_STATUS_LSB)) {
- switch ((int) status) {
+ switch (status) {
case EXIT_CHDIR:
return "CHDIR";
@@ -140,19 +140,19 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
case EXIT_RUNTIME_DIRECTORY:
return "RUNTIME_DIRECTORY";
- case EXIT_CHOWN:
- return "CHOWN";
-
case EXIT_MAKE_STARTER:
return "MAKE_STARTER";
+ case EXIT_CHOWN:
+ return "CHOWN";
+
case EXIT_SMACK_PROCESS_LABEL:
return "SMACK_PROCESS_LABEL";
}
}
if (level == EXIT_STATUS_LSB) {
- switch ((int) status) {
+ switch (status) {
case EXIT_INVALIDARGUMENT:
return "INVALIDARGUMENT";
@@ -177,34 +177,23 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
return NULL;
}
-
-bool is_clean_exit(int code, int status, ExitStatusSet *success_status) {
+bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status) {
if (code == CLD_EXITED)
return status == 0 ||
(success_status &&
set_contains(success_status->status, INT_TO_PTR(status)));
- /* If a daemon does not implement handlers for some of the
- * signals that's not considered an unclean shutdown */
+ /* If a daemon does not implement handlers for some of the signals that's not considered an unclean shutdown */
if (code == CLD_KILLED)
- return IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE) ||
+ return
+ (clean == EXIT_CLEAN_DAEMON && IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE)) ||
(success_status &&
set_contains(success_status->signal, INT_TO_PTR(status)));
return false;
}
-bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status) {
-
- if (is_clean_exit(code, status, success_status))
- return true;
-
- return
- code == CLD_EXITED &&
- IN_SET(status, EXIT_NOTINSTALLED, EXIT_NOTCONFIGURED);
-}
-
void exit_status_set_free(ExitStatusSet *x) {
assert(x);
diff --git a/src/basic/exit-status.h b/src/basic/exit-status.h
index 2309f68815..0cfdfd7891 100644
--- a/src/basic/exit-status.h
+++ b/src/basic/exit-status.h
@@ -31,7 +31,7 @@
* https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
*/
-typedef enum ExitStatus {
+enum {
/* EXIT_SUCCESS defined by libc */
/* EXIT_FAILURE defined by libc */
EXIT_INVALIDARGUMENT = 2,
@@ -82,7 +82,7 @@ typedef enum ExitStatus {
EXIT_MAKE_STARTER,
EXIT_CHOWN,
EXIT_SMACK_PROCESS_LABEL,
-} ExitStatus;
+};
typedef enum ExitStatusLevel {
EXIT_STATUS_MINIMAL, /* only cover libc EXIT_STATUS/EXIT_FAILURE */
@@ -96,10 +96,14 @@ typedef struct ExitStatusSet {
Set *signal;
} ExitStatusSet;
-const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) _const_;
+const char* exit_status_to_string(int status, ExitStatusLevel level) _const_;
-bool is_clean_exit(int code, int status, ExitStatusSet *success_status);
-bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status);
+typedef enum ExitClean {
+ EXIT_CLEAN_DAEMON,
+ EXIT_CLEAN_COMMAND,
+} ExitClean;
+
+bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status);
void exit_status_set_free(ExitStatusSet *x);
bool exit_status_set_is_empty(ExitStatusSet *x);
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index a5920e7d36..1cfb7a98f5 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -1043,7 +1043,7 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
if (r < 0)
return r;
- fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC);
+ fd = mkostemp_safe(t);
if (fd < 0) {
free(t);
return -errno;
@@ -1076,7 +1076,7 @@ int fflush_and_check(FILE *f) {
}
/* This is much like mkostemp() but is subject to umask(). */
-int mkostemp_safe(char *pattern, int flags) {
+int mkostemp_safe(char *pattern) {
_cleanup_umask_ mode_t u = 0;
int fd;
@@ -1084,7 +1084,7 @@ int mkostemp_safe(char *pattern, int flags) {
u = umask(077);
- fd = mkostemp(pattern, flags);
+ fd = mkostemp(pattern, O_CLOEXEC);
if (fd < 0)
return -errno;
@@ -1289,7 +1289,7 @@ int open_tmpfile_unlinkable(const char *directory, int flags) {
/* Fall back to unguessable name + unlinking */
p = strjoina(directory, "/systemd-tmp-XXXXXX");
- fd = mkostemp_safe(p, flags);
+ fd = mkostemp_safe(p);
if (fd < 0)
return fd;
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index 9ac497d9eb..b58c83e64a 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -71,7 +71,7 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root
int fflush_and_check(FILE *f);
int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
-int mkostemp_safe(char *pattern, int flags);
+int mkostemp_safe(char *pattern);
int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
int tempfn_random(const char *p, const char *extra, char **ret);
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index ce87257bc1..86d9ad7e36 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -597,3 +597,190 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
return r;
}
+
+int chase_symlinks(const char *path, const char *_root, char **ret) {
+ _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
+ _cleanup_close_ int fd = -1;
+ unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */
+ char *todo;
+ int r;
+
+ assert(path);
+
+ /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
+ * symlinks relative to a root directory, instead of the root of the host.
+ *
+ * Note that "root" matters only if we encounter an absolute symlink, it's unused otherwise. Most importantly
+ * this means the path parameter passed in is not prefixed by it.
+ *
+ * Algorithmically this operates on two path buffers: "done" are the components of the path we already
+ * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to
+ * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning
+ * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no
+ * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races
+ * at a minimum. */
+
+ r = path_make_absolute_cwd(path, &buffer);
+ if (r < 0)
+ return r;
+
+ if (_root) {
+ r = path_make_absolute_cwd(_root, &root);
+ if (r < 0)
+ return r;
+ }
+
+ fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
+ if (fd < 0)
+ return -errno;
+
+ todo = buffer;
+ for (;;) {
+ _cleanup_free_ char *first = NULL;
+ _cleanup_close_ int child = -1;
+ struct stat st;
+ size_t n, m;
+
+ /* Determine length of first component in the path */
+ n = strspn(todo, "/"); /* The slashes */
+ m = n + strcspn(todo + n, "/"); /* The entire length of the component */
+
+ /* Extract the first component. */
+ first = strndup(todo, m);
+ if (!first)
+ return -ENOMEM;
+
+ todo += m;
+
+ /* Just a single slash? Then we reached the end. */
+ if (isempty(first) || path_equal(first, "/"))
+ break;
+
+ /* Just a dot? Then let's eat this up. */
+ if (path_equal(first, "/."))
+ continue;
+
+ /* Two dots? Then chop off the last bit of what we already found out. */
+ if (path_equal(first, "/..")) {
+ _cleanup_free_ char *parent = NULL;
+ int fd_parent = -1;
+
+ if (isempty(done) || path_equal(done, "/"))
+ return -EINVAL;
+
+ parent = dirname_malloc(done);
+ if (!parent)
+ return -ENOMEM;
+
+ /* Don't allow this to leave the root dir */
+ if (root &&
+ path_startswith(done, root) &&
+ !path_startswith(parent, root))
+ return -EINVAL;
+
+ free(done);
+ done = parent;
+ parent = NULL;
+
+ fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH);
+ if (fd_parent < 0)
+ return -errno;
+
+ safe_close(fd);
+ fd = fd_parent;
+
+ continue;
+ }
+
+ /* Otherwise let's see what this is. */
+ child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH);
+ if (child < 0)
+ return -errno;
+
+ if (fstat(child, &st) < 0)
+ return -errno;
+
+ if (S_ISLNK(st.st_mode)) {
+ _cleanup_free_ char *destination = NULL;
+
+ /* This is a symlink, in this case read the destination. But let's make sure we don't follow
+ * symlinks without bounds. */
+ if (--max_follow <= 0)
+ return -ELOOP;
+
+ r = readlinkat_malloc(fd, first + n, &destination);
+ if (r < 0)
+ return r;
+ if (isempty(destination))
+ return -EINVAL;
+
+ if (path_is_absolute(destination)) {
+
+ /* An absolute destination. Start the loop from the beginning, but use the root
+ * directory as base. */
+
+ safe_close(fd);
+ fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
+ if (fd < 0)
+ return -errno;
+
+ free(buffer);
+ buffer = destination;
+ destination = NULL;
+
+ todo = buffer;
+ free(done);
+
+ /* Note that we do not revalidate the root, we take it as is. */
+ if (isempty(root))
+ done = NULL;
+ else {
+ done = strdup(root);
+ if (!done)
+ return -ENOMEM;
+ }
+
+ } else {
+ char *joined;
+
+ /* A relative destination. If so, this is what we'll prefix what's left to do with what
+ * we just read, and start the loop again, but remain in the current directory. */
+
+ joined = strjoin("/", destination, todo, NULL);
+ if (!joined)
+ return -ENOMEM;
+
+ free(buffer);
+ todo = buffer = joined;
+ }
+
+ continue;
+ }
+
+ /* If this is not a symlink, then let's just add the name we read to what we already verified. */
+ if (!done) {
+ done = first;
+ first = NULL;
+ } else {
+ if (!strextend(&done, first, NULL))
+ return -ENOMEM;
+ }
+
+ /* And iterate again, but go one directory further down. */
+ safe_close(fd);
+ fd = child;
+ child = -1;
+ }
+
+ if (!done) {
+ /* Special case, turn the empty string into "/", to indicate the root directory. */
+ done = strdup("/");
+ if (!done)
+ return -ENOMEM;
+ }
+
+ *ret = done;
+ done = NULL;
+
+ return 0;
+}
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index 2c3b9a1c74..31df47cf1e 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -77,3 +77,5 @@ union inotify_event_buffer {
};
int inotify_add_watch_fd(int fd, int what, uint32_t mask);
+
+int chase_symlinks(const char *path, const char *_root, char **ret);
diff --git a/src/basic/list.h b/src/basic/list.h
index 5962aa4211..c3771a177f 100644
--- a/src/basic/list.h
+++ b/src/basic/list.h
@@ -142,6 +142,8 @@
} else { \
if ((_b->name##_prev = _a->name##_prev)) \
_b->name##_prev->name##_next = _b; \
+ else \
+ *_head = _b; \
_b->name##_next = _a; \
_a->name##_prev = _b; \
} \
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 13ff51cd35..4a78269e33 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -1052,6 +1052,10 @@ typedef int32_t key_serial_t;
#define ETHERTYPE_LLDP 0x88cc
#endif
+#ifndef IFA_F_MCAUTOJOIN
+#define IFA_F_MCAUTOJOIN 0x400
+#endif
+
#endif
#include "missing_syscall.h"
diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c
index bfa04394fe..b9affb4e70 100644
--- a/src/basic/mount-util.c
+++ b/src/basic/mount-util.c
@@ -36,6 +36,7 @@
#include "set.h"
#include "stdio-util.h"
#include "string-util.h"
+#include "strv.h"
static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
@@ -287,10 +288,12 @@ int umount_recursive(const char *prefix, int flags) {
continue;
if (umount2(p, flags) < 0) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to umount %s: %m", p);
continue;
}
+ log_debug("Successfully unmounted %s", p);
+
again = true;
n++;
@@ -311,24 +314,21 @@ static int get_mount_flags(const char *path, unsigned long *flags) {
return 0;
}
-int bind_remount_recursive(const char *prefix, bool ro) {
+int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) {
_cleanup_set_free_free_ Set *done = NULL;
_cleanup_free_ char *cleaned = NULL;
int r;
- /* Recursively remount a directory (and all its submounts)
- * read-only or read-write. If the directory is already
- * mounted, we reuse the mount and simply mark it
- * MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
- * operation). If it isn't we first make it one. Afterwards we
- * apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to all
- * submounts we can access, too. When mounts are stacked on
- * the same mount point we only care for each individual
- * "top-level" mount on each point, as we cannot
- * influence/access the underlying mounts anyway. We do not
- * have any effect on future submounts that might get
- * propagated, they migt be writable. This includes future
- * submounts that have been triggered via autofs. */
+ /* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already
+ * mounted, we reuse the mount and simply mark it MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
+ * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to
+ * all submounts we can access, too. When mounts are stacked on the same mount point we only care for each
+ * individual "top-level" mount on each point, as we cannot influence/access the underlying mounts anyway. We
+ * do not have any effect on future submounts that might get propagated, they migt be writable. This includes
+ * future submounts that have been triggered via autofs.
+ *
+ * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the
+ * remount operation. Note that we'll ignore the blacklist for the top-level path. */
cleaned = strdup(prefix);
if (!cleaned)
@@ -385,6 +385,33 @@ int bind_remount_recursive(const char *prefix, bool ro) {
if (r < 0)
return r;
+ if (!path_startswith(p, cleaned))
+ continue;
+
+ /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount we shall
+ * operate on. */
+ if (!path_equal(cleaned, p)) {
+ bool blacklisted = false;
+ char **i;
+
+ STRV_FOREACH(i, blacklist) {
+
+ if (path_equal(*i, cleaned))
+ continue;
+
+ if (!path_startswith(*i, cleaned))
+ continue;
+
+ if (path_startswith(p, *i)) {
+ blacklisted = true;
+ log_debug("Not remounting %s, because blacklisted by %s, called for %s", p, *i, cleaned);
+ break;
+ }
+ }
+ if (blacklisted)
+ continue;
+ }
+
/* Let's ignore autofs mounts. If they aren't
* triggered yet, we want to avoid triggering
* them, as we don't make any guarantees for
@@ -396,12 +423,9 @@ int bind_remount_recursive(const char *prefix, bool ro) {
continue;
}
- if (path_startswith(p, cleaned) &&
- !set_contains(done, p)) {
-
+ if (!set_contains(done, p)) {
r = set_consume(todo, p);
p = NULL;
-
if (r == -EEXIST)
continue;
if (r < 0)
@@ -418,8 +442,7 @@ int bind_remount_recursive(const char *prefix, bool ro) {
if (!set_contains(done, cleaned) &&
!set_contains(todo, cleaned)) {
- /* The prefix directory itself is not yet a
- * mount, make it one. */
+ /* The prefix directory itself is not yet a mount, make it one. */
if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0)
return -errno;
@@ -430,6 +453,8 @@ int bind_remount_recursive(const char *prefix, bool ro) {
if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
return -errno;
+ log_debug("Made top-level directory %s a mount point.", prefix);
+
x = strdup(cleaned);
if (!x)
return -ENOMEM;
@@ -447,8 +472,7 @@ int bind_remount_recursive(const char *prefix, bool ro) {
if (r < 0)
return r;
- /* Deal with mount points that are obstructed by a
- * later mount */
+ /* Deal with mount points that are obstructed by a later mount */
r = path_is_mount_point(x, 0);
if (r == -ENOENT || r == 0)
continue;
@@ -463,6 +487,7 @@ int bind_remount_recursive(const char *prefix, bool ro) {
if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
return -errno;
+ log_debug("Remounted %s read-only.", x);
}
}
}
diff --git a/src/basic/mount-util.h b/src/basic/mount-util.h
index f46989ebb3..74730de663 100644
--- a/src/basic/mount-util.h
+++ b/src/basic/mount-util.h
@@ -35,7 +35,7 @@ int path_is_mount_point(const char *path, int flags);
int repeat_unmount(const char *path, int flags);
int umount_recursive(const char *target, int flags);
-int bind_remount_recursive(const char *prefix, bool ro);
+int bind_remount_recursive(const char *prefix, bool ro, char **blacklist);
int mount_move_root(const char *path);
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index b2fa81a294..c32e961af4 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -34,9 +34,11 @@
#include "alloc-util.h"
#include "extract-word.h"
#include "fs-util.h"
+#include "glob-util.h"
#include "log.h"
#include "macro.h"
#include "missing.h"
+#include "parse-util.h"
#include "path-util.h"
#include "stat-util.h"
#include "string-util.h"
@@ -814,3 +816,69 @@ bool is_device_path(const char *path) {
path_startswith(path, "/dev/") ||
path_startswith(path, "/sys/");
}
+
+int systemd_installation_has_version(const char *root, unsigned minimal_version) {
+ const char *pattern;
+ int r;
+
+ /* Try to guess if systemd installation is later than the specified version. This
+ * is hacky and likely to yield false negatives, particularly if the installation
+ * is non-standard. False positives should be relatively rare.
+ */
+
+ NULSTR_FOREACH(pattern,
+ /* /lib works for systems without usr-merge, and for systems with a sane
+ * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary
+ * for Gentoo which does a merge without making /lib a symlink.
+ */
+ "lib/systemd/libsystemd-shared-*.so\0"
+ "usr/lib/systemd/libsystemd-shared-*.so\0") {
+
+ _cleanup_strv_free_ char **names = NULL;
+ _cleanup_free_ char *path = NULL;
+ char *c, **name;
+
+ path = prefix_root(root, pattern);
+ if (!path)
+ return -ENOMEM;
+
+ r = glob_extend(&names, path);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return r;
+
+ assert_se((c = endswith(path, "*.so")));
+ *c = '\0'; /* truncate the glob part */
+
+ STRV_FOREACH(name, names) {
+ /* This is most likely to run only once, hence let's not optimize anything. */
+ char *t, *t2;
+ unsigned version;
+
+ t = startswith(*name, path);
+ if (!t)
+ continue;
+
+ t2 = endswith(t, ".so");
+ if (!t2)
+ continue;
+
+ t2[0] = '\0'; /* truncate the suffix */
+
+ r = safe_atou(t, &version);
+ if (r < 0) {
+ log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name);
+ continue;
+ }
+
+ log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).",
+ *name, version,
+ version >= minimal_version ? "OK" : "too old");
+ if (version >= minimal_version)
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index a27c13fcc3..78472f0961 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -125,3 +125,5 @@ char *file_in_same_dir(const char *path, const char *filename);
bool hidden_or_backup_file(const char *filename) _pure_;
bool is_device_path(const char *path);
+
+int systemd_installation_has_version(const char *root, unsigned minimal_version);
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index 6093e47172..1662c04705 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -441,7 +441,7 @@ const char* socket_address_get_path(const SocketAddress *a) {
}
bool socket_ipv6_is_supported(void) {
- if (access("/proc/net/sockstat6", F_OK) != 0)
+ if (access("/proc/net/if_inet6", F_OK) != 0)
return false;
return true;
@@ -1060,3 +1060,20 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng
return NULL;
}
+
+int socket_ioctl_fd(void) {
+ int fd;
+
+ /* Create a socket to invoke the various network interface ioctl()s on. Traditionally only AF_INET was good for
+ * that. Since kernel 4.6 AF_NETLINK works for this too. We first try to use AF_INET hence, but if that's not
+ * available (for example, because it is made unavailable via SECCOMP or such), we'll fall back to the more
+ * generic AF_NETLINK. */
+
+ fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_GENERIC);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index 2536b085f9..2ef572badb 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -154,3 +154,5 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng
1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \
strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \
})
+
+int socket_ioctl_fd(void);
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
index 5d4510e1b3..dc7de5dab8 100644
--- a/src/basic/string-util.c
+++ b/src/basic/string-util.c
@@ -443,7 +443,7 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le
if (old_length <= 3 || old_length <= new_length)
return strndup(s, old_length);
- r = new0(char, new_length+1);
+ r = new0(char, new_length+3);
if (!r)
return NULL;
@@ -453,12 +453,12 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le
x = new_length - 3;
memcpy(r, s, x);
- r[x] = '.';
- r[x+1] = '.';
- r[x+2] = '.';
+ r[x] = 0xe2; /* tri-dot ellipsis: … */
+ r[x+1] = 0x80;
+ r[x+2] = 0xa6;
memcpy(r + x + 3,
- s + old_length - (new_length - x - 3),
- new_length - x - 3);
+ s + old_length - (new_length - x - 1),
+ new_length - x - 1);
return r;
}
diff --git a/src/basic/strv.h b/src/basic/strv.h
index 683ce83a2a..fec2597db0 100644
--- a/src/basic/strv.h
+++ b/src/basic/strv.h
@@ -141,6 +141,11 @@ void strv_print(char **l);
})
#define STR_IN_SET(x, ...) strv_contains(STRV_MAKE(__VA_ARGS__), x)
+#define STRPTR_IN_SET(x, ...) \
+ ({ \
+ const char* _x = (x); \
+ _x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \
+ })
#define FOREACH_STRING(x, ...) \
for (char **_l = ({ \
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index 0ef1f6393e..fedff1362c 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -40,8 +40,6 @@
#include "strv.h"
#include "time-util.h"
-static nsec_t timespec_load_nsec(const struct timespec *ts);
-
static clockid_t map_clock_id(clockid_t c) {
/* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will
@@ -198,7 +196,7 @@ usec_t timespec_load(const struct timespec *ts) {
(usec_t) ts->tv_nsec / NSEC_PER_USEC;
}
-static nsec_t timespec_load_nsec(const struct timespec *ts) {
+nsec_t timespec_load_nsec(const struct timespec *ts) {
assert(ts);
if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1)
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
index 99be5ce6ee..558b0b5b7f 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -111,6 +111,7 @@ static inline bool triple_timestamp_is_set(triple_timestamp *ts) {
usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock);
usec_t timespec_load(const struct timespec *ts) _pure_;
+nsec_t timespec_load_nsec(const struct timespec *ts) _pure_;
struct timespec *timespec_store(struct timespec *ts, usec_t u);
usec_t timeval_load(const struct timeval *tv) _pure_;
diff --git a/src/basic/user-util.c b/src/basic/user-util.c
index 122d9a0c7c..de6c93056e 100644
--- a/src/basic/user-util.c
+++ b/src/basic/user-util.c
@@ -31,14 +31,16 @@
#include <unistd.h>
#include <utmp.h>
-#include "missing.h"
#include "alloc-util.h"
#include "fd-util.h"
+#include "fileio.h"
#include "formats-util.h"
#include "macro.h"
+#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
#include "string-util.h"
+#include "strv.h"
#include "user-util.h"
#include "utf8.h"
@@ -175,6 +177,35 @@ int get_user_creds(
return 0;
}
+int get_user_creds_clean(
+ const char **username,
+ uid_t *uid, gid_t *gid,
+ const char **home,
+ const char **shell) {
+
+ int r;
+
+ /* Like get_user_creds(), but resets home/shell to NULL if they don't contain anything relevant. */
+
+ r = get_user_creds(username, uid, gid, home, shell);
+ if (r < 0)
+ return r;
+
+ if (shell &&
+ (isempty(*shell) || PATH_IN_SET(*shell,
+ "/bin/nologin",
+ "/sbin/nologin",
+ "/usr/bin/nologin",
+ "/usr/sbin/nologin")))
+ *shell = NULL;
+
+ if (home &&
+ (isempty(*home) || path_equal(*home, "/")))
+ *home = NULL;
+
+ return 0;
+}
+
int get_group_creds(const char **groupname, gid_t *gid) {
struct group *g;
gid_t id;
@@ -429,9 +460,11 @@ int get_shell(char **_s) {
}
int reset_uid_gid(void) {
+ int r;
- if (setgroups(0, NULL) < 0)
- return -errno;
+ r = maybe_setgroups(0, NULL);
+ if (r < 0)
+ return r;
if (setresgid(0, 0, 0) < 0)
return -errno;
@@ -572,3 +605,32 @@ bool valid_home(const char *p) {
return true;
}
+
+int maybe_setgroups(size_t size, const gid_t *list) {
+ int r;
+
+ /* Check if setgroups is allowed before we try to drop all the auxiliary groups */
+ if (size == 0) { /* Dropping all aux groups? */
+ _cleanup_free_ char *setgroups_content = NULL;
+ bool can_setgroups;
+
+ r = read_one_line_file("/proc/self/setgroups", &setgroups_content);
+ if (r == -ENOENT)
+ /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */
+ can_setgroups = true;
+ else if (r < 0)
+ return r;
+ else
+ can_setgroups = streq(setgroups_content, "allow");
+
+ if (!can_setgroups) {
+ log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'");
+ return 0;
+ }
+ }
+
+ if (setgroups(size, list) < 0)
+ return -errno;
+
+ return 0;
+}
diff --git a/src/basic/user-util.h b/src/basic/user-util.h
index f569363811..dfea561bde 100644
--- a/src/basic/user-util.h
+++ b/src/basic/user-util.h
@@ -40,6 +40,7 @@ char* getlogname_malloc(void);
char* getusername_malloc(void);
int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell);
+int get_user_creds_clean(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell);
int get_group_creds(const char **groupname, gid_t *gid);
char* uid_to_name(uid_t uid);
@@ -85,3 +86,5 @@ bool valid_user_group_name(const char *u);
bool valid_user_group_name_or_id(const char *u);
bool valid_gecos(const char *d);
bool valid_home(const char *p);
+
+int maybe_setgroups(size_t size, const gid_t *list);
diff --git a/src/basic/util.c b/src/basic/util.c
index 9d66d28eb7..ec7939dc83 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -467,7 +467,7 @@ bool in_initrd(void) {
* 2. the root file system must be a memory file system
*
* The second check is extra paranoia, since misdetecting an
- * initrd can have bad bad consequences due the initrd
+ * initrd can have bad consequences due the initrd
* emptying when transititioning to the main systemd.
*/
diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c
index 7c016387c1..4ac11a9bb0 100644
--- a/src/boot/efi/measure.c
+++ b/src/boot/efi/measure.c
@@ -209,12 +209,35 @@ static EFI_STATUS tpm1_measure_to_pcr_and_event_log(const EFI_TCG *tcg, UINT32 p
return EFI_SUCCESS;
}
+/*
+ * According to TCG EFI Protocol Specification for TPM 2.0 family,
+ * all events generated after the invocation of EFI_TCG2_GET_EVENT_LOG
+ * shall be stored in an instance of an EFI_CONFIGURATION_TABLE aka
+ * EFI TCG 2.0 final events table. Hence, it is necessary to trigger the
+ * internal switch through calling get_event_log() in order to allow
+ * to retrieve the logs from OS runtime.
+ */
+static EFI_STATUS trigger_tcg2_final_events_table(const EFI_TCG2 *tcg)
+{
+ return uefi_call_wrapper(tcg->GetEventLog, 5, tcg,
+ EFI_TCG2_EVENT_LOG_FORMAT_TCG_2, NULL,
+ NULL, NULL);
+}
static EFI_STATUS tpm2_measure_to_pcr_and_event_log(const EFI_TCG2 *tcg, UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer,
UINT64 buffer_size, const CHAR16 *description) {
EFI_STATUS status;
EFI_TCG2_EVENT *tcg_event;
UINTN desc_len;
+ static BOOLEAN triggered = FALSE;
+
+ if (triggered == FALSE) {
+ status = trigger_tcg2_final_events_table(tcg);
+ if (EFI_ERROR(status))
+ return status;
+
+ triggered = TRUE;
+ }
desc_len = StrLen(description) * sizeof(CHAR16);
diff --git a/src/core/automount.c b/src/core/automount.c
index 00295cf769..bdc0e06965 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -271,6 +271,11 @@ static int automount_coldplug(Unit *u) {
return r;
(void) sd_event_source_set_description(a->pipe_event_source, "automount-io");
+ if (a->deserialized_state == AUTOMOUNT_RUNNING) {
+ r = automount_start_expire(a);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m");
+ }
}
automount_set_state(a, a->deserialized_state);
diff --git a/src/core/busname.c b/src/core/busname.c
index 7952cd31aa..a69e3831f6 100644
--- a/src/core/busname.c
+++ b/src/core/busname.c
@@ -868,7 +868,7 @@ static void busname_sigchld_event(Unit *u, pid_t pid, int code, int status) {
n->control_pid = 0;
- if (is_clean_exit(code, status, NULL))
+ if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
f = BUSNAME_SUCCESS;
else if (code == CLD_EXITED)
f = BUSNAME_FAILURE_EXIT_CODE;
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index 7e33a2d201..eec4500c8c 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -707,6 +707,8 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("MountFlags", "t", bus_property_get_ulong, offsetof(ExecContext, mount_flags), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PrivateTmp", "b", bus_property_get_bool, offsetof(ExecContext, private_tmp), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PrivateDevices", "b", bus_property_get_bool, offsetof(ExecContext, private_devices), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ProtectKernelTunables", "b", bus_property_get_bool, offsetof(ExecContext, protect_kernel_tunables), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ProtectControlGroups", "b", bus_property_get_bool, offsetof(ExecContext, protect_control_groups), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PrivateNetwork", "b", bus_property_get_bool, offsetof(ExecContext, private_network), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PrivateUsers", "b", bus_property_get_bool, offsetof(ExecContext, private_users), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProtectHome", "s", bus_property_get_protect_home, offsetof(ExecContext, protect_home), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1072,7 +1074,8 @@ int bus_exec_context_set_transient_property(
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset",
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
"NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute",
- "RestrictRealtime", "DynamicUser", "RemoveIPC")) {
+ "RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables",
+ "ProtectControlGroups")) {
int b;
r = sd_bus_message_read(message, "b", &b);
@@ -1106,6 +1109,10 @@ int bus_exec_context_set_transient_property(
c->dynamic_user = b;
else if (streq(name, "RemoveIPC"))
c->remove_ipc = b;
+ else if (streq(name, "ProtectKernelTunables"))
+ c->protect_kernel_tunables = b;
+ else if (streq(name, "ProtectControlGroups"))
+ c->protect_control_groups = b;
unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, yes_no(b));
}
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 1b86bdde43..5020dfba4b 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -1167,7 +1167,7 @@ void bus_unit_send_removed_signal(Unit *u) {
int r;
assert(u);
- if (!u->sent_dbus_new_signal)
+ if (!u->sent_dbus_new_signal || u->in_dbus_queue)
bus_unit_send_change_signal(u);
if (!u->id)
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 1e41a42aa6..070974fe66 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -964,10 +964,6 @@ static int bus_init_private(Manager *m) {
if (m->private_listen_fd >= 0)
return 0;
- /* We don't need the private socket if we have kdbus */
- if (m->kdbus_fd >= 0)
- return 0;
-
if (MANAGER_IS_SYSTEM(m)) {
/* We want the private bus only when running as init */
diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c
index 310aaa94e1..1043da3eb7 100644
--- a/src/core/dynamic-user.c
+++ b/src/core/dynamic-user.c
@@ -233,7 +233,7 @@ static int pick_uid(const char *name, uid_t *ret_uid) {
if (st.st_nlink > 0)
break;
- /* Oh, bummer, we got got the lock, but the file was unlinked between the time we opened it and
+ /* Oh, bummer, we got the lock, but the file was unlinked between the time we opened it and
* got the lock. Close it, and try again. */
lock_fd = safe_close(lock_fd);
}
diff --git a/src/core/execute.c b/src/core/execute.c
index 2026137721..d5c4e60796 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -781,9 +781,10 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_
k++;
}
- if (setgroups(k, gids) < 0) {
+ r = maybe_setgroups(k, gids);
+ if (r < 0) {
free(gids);
- return -errno;
+ return r;
}
free(gids);
@@ -837,14 +838,19 @@ static int null_conv(
return PAM_CONV_ERR;
}
+#endif
+
static int setup_pam(
const char *name,
const char *user,
uid_t uid,
+ gid_t gid,
const char *tty,
char ***env,
int fds[], unsigned n_fds) {
+#ifdef HAVE_PAM
+
static const struct pam_conv conv = {
.conv = null_conv,
.appdata_ptr = NULL
@@ -944,8 +950,14 @@ static int setup_pam(
* and this will make 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);
+ 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_error_errno(r, "Error: Failed to setresuid() in sd-pam: %m");
+ log_warning_errno(errno, "Failed to setresuid() in sd-pam: %m");
(void) ignore_signals(SIGPIPE, -1);
@@ -1038,8 +1050,10 @@ fail:
closelog();
return r;
-}
+#else
+ return 0;
#endif
+}
static void rename_process_from_path(const char *path) {
char process_name[11];
@@ -1273,6 +1287,10 @@ static int apply_memory_deny_write_execute(const Unit* u, const ExecContext *c)
if (!seccomp)
return -ENOMEM;
+ r = seccomp_add_secondary_archs(seccomp);
+ if (r < 0)
+ goto finish;
+
r = seccomp_rule_add(
seccomp,
SCMP_ACT_ERRNO(EPERM),
@@ -1322,6 +1340,10 @@ static int apply_restrict_realtime(const Unit* u, const ExecContext *c) {
if (!seccomp)
return -ENOMEM;
+ r = seccomp_add_secondary_archs(seccomp);
+ if (r < 0)
+ goto finish;
+
/* Determine the highest policy constant we want to allow */
for (i = 0; i < ELEMENTSOF(permitted_policies); i++)
if (permitted_policies[i] > max_policy)
@@ -1375,12 +1397,121 @@ finish:
return r;
}
+static int apply_protect_sysctl(Unit *u, const ExecContext *c) {
+ scmp_filter_ctx *seccomp;
+ int r;
+
+ assert(c);
+
+ /* Turn off the legacy sysctl() system call. Many distributions turn this off while building the kernel, but
+ * let's protect even those systems where this is left on in the kernel. */
+
+ if (skip_seccomp_unavailable(u, "ProtectKernelTunables="))
+ return 0;
+
+ seccomp = seccomp_init(SCMP_ACT_ALLOW);
+ if (!seccomp)
+ return -ENOMEM;
+
+ r = seccomp_add_secondary_archs(seccomp);
+ if (r < 0)
+ goto finish;
+
+ r = seccomp_rule_add(
+ seccomp,
+ SCMP_ACT_ERRNO(EPERM),
+ SCMP_SYS(_sysctl),
+ 0);
+ if (r < 0)
+ goto finish;
+
+ r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
+ if (r < 0)
+ goto finish;
+
+ r = seccomp_load(seccomp);
+
+finish:
+ seccomp_release(seccomp);
+ return r;
+}
+
+static int apply_private_devices(Unit *u, const ExecContext *c) {
+ const SystemCallFilterSet *set;
+ scmp_filter_ctx *seccomp;
+ const char *sys;
+ bool syscalls_found = false;
+ int r;
+
+ assert(c);
+
+ /* If PrivateDevices= is set, also turn off iopl and all @raw-io syscalls. */
+
+ if (skip_seccomp_unavailable(u, "PrivateDevices="))
+ return 0;
+
+ seccomp = seccomp_init(SCMP_ACT_ALLOW);
+ if (!seccomp)
+ return -ENOMEM;
+
+ r = seccomp_add_secondary_archs(seccomp);
+ if (r < 0)
+ goto finish;
+
+ for (set = syscall_filter_sets; set->set_name; set++)
+ if (streq(set->set_name, "@raw-io")) {
+ syscalls_found = true;
+ break;
+ }
+
+ /* We should never fail here */
+ if (!syscalls_found) {
+ r = -EOPNOTSUPP;
+ goto finish;
+ }
+
+ NULSTR_FOREACH(sys, set->value) {
+ int id;
+ bool add = true;
+
+#ifndef __NR_s390_pci_mmio_read
+ if (streq(sys, "s390_pci_mmio_read"))
+ add = false;
+#endif
+#ifndef __NR_s390_pci_mmio_write
+ if (streq(sys, "s390_pci_mmio_write"))
+ add = false;
+#endif
+
+ if (!add)
+ continue;
+
+ id = seccomp_syscall_resolve_name(sys);
+
+ r = seccomp_rule_add(
+ seccomp,
+ SCMP_ACT_ERRNO(EPERM),
+ id, 0);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
+ if (r < 0)
+ goto finish;
+
+ r = seccomp_load(seccomp);
+
+finish:
+ seccomp_release(seccomp);
+ return r;
+}
+
#endif
static void do_idle_pipe_dance(int idle_pipe[4]) {
assert(idle_pipe);
-
idle_pipe[1] = safe_close(idle_pipe[1]);
idle_pipe[2] = safe_close(idle_pipe[2]);
@@ -1581,7 +1712,9 @@ static bool exec_needs_mount_namespace(
if (context->private_devices ||
context->protect_system != PROTECT_SYSTEM_NO ||
- context->protect_home != PROTECT_HOME_NO)
+ context->protect_home != PROTECT_HOME_NO ||
+ context->protect_kernel_tunables ||
+ context->protect_control_groups)
return true;
return false;
@@ -1740,6 +1873,111 @@ static int setup_private_users(uid_t uid, gid_t gid) {
return 0;
}
+static int setup_runtime_directory(
+ const ExecContext *context,
+ const ExecParameters *params,
+ uid_t uid,
+ gid_t gid) {
+
+ char **rt;
+ int r;
+
+ assert(context);
+ assert(params);
+
+ STRV_FOREACH(rt, context->runtime_directory) {
+ _cleanup_free_ char *p;
+
+ p = strjoin(params->runtime_prefix, "/", *rt, NULL);
+ if (!p)
+ return -ENOMEM;
+
+ r = mkdir_p_label(p, context->runtime_directory_mode);
+ if (r < 0)
+ return r;
+
+ r = chmod_and_chown(p, context->runtime_directory_mode, uid, gid);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int setup_smack(
+ const ExecContext *context,
+ const ExecCommand *command) {
+
+#ifdef HAVE_SMACK
+ int r;
+
+ assert(context);
+ assert(command);
+
+ if (!mac_smack_use())
+ return 0;
+
+ if (context->smack_process_label) {
+ r = mac_smack_apply_pid(0, context->smack_process_label);
+ if (r < 0)
+ return r;
+ }
+#ifdef SMACK_DEFAULT_PROCESS_LABEL
+ else {
+ _cleanup_free_ char *exec_label = NULL;
+
+ r = mac_smack_read(command->path, SMACK_ATTR_EXEC, &exec_label);
+ if (r < 0 && r != -ENODATA && r != -EOPNOTSUPP)
+ return r;
+
+ r = mac_smack_apply_pid(0, exec_label ? : SMACK_DEFAULT_PROCESS_LABEL);
+ if (r < 0)
+ return r;
+ }
+#endif
+#endif
+
+ return 0;
+}
+
+static int compile_read_write_paths(
+ const ExecContext *context,
+ const ExecParameters *params,
+ char ***ret) {
+
+ _cleanup_strv_free_ char **l = NULL;
+ char **rt;
+
+ /* Compile the list of writable paths. This is the combination of the explicitly configured paths, plus all
+ * runtime directories. */
+
+ if (strv_isempty(context->read_write_paths) &&
+ strv_isempty(context->runtime_directory)) {
+ *ret = NULL; /* NOP if neither is set */
+ return 0;
+ }
+
+ l = strv_copy(context->read_write_paths);
+ if (!l)
+ return -ENOMEM;
+
+ STRV_FOREACH(rt, context->runtime_directory) {
+ char *s;
+
+ s = strjoin(params->runtime_prefix, "/", *rt, NULL);
+ if (!s)
+ return -ENOMEM;
+
+ if (strv_consume(&l, s) < 0)
+ return -ENOMEM;
+ }
+
+ *ret = l;
+ l = NULL;
+
+ return 0;
+}
+
static void append_socket_pair(int *array, unsigned *n, int pair[2]) {
assert(array);
assert(n);
@@ -1796,6 +2034,37 @@ static int close_remaining_fds(
return close_all_fds(dont_close, n_dont_close);
}
+static bool context_has_address_families(const ExecContext *c) {
+ assert(c);
+
+ return c->address_families_whitelist ||
+ !set_isempty(c->address_families);
+}
+
+static bool context_has_syscall_filters(const ExecContext *c) {
+ assert(c);
+
+ return c->syscall_whitelist ||
+ !set_isempty(c->syscall_filter) ||
+ !set_isempty(c->syscall_archs);
+}
+
+static bool context_has_no_new_privileges(const ExecContext *c) {
+ assert(c);
+
+ if (c->no_new_privileges)
+ return true;
+
+ if (have_effective_cap(CAP_SYS_ADMIN)) /* if we are privileged, we don't need NNP */
+ return false;
+
+ return context_has_address_families(c) || /* we need NNP if we have any form of seccomp and are unprivileged */
+ c->memory_deny_write_execute ||
+ c->restrict_realtime ||
+ c->protect_kernel_tunables ||
+ context_has_syscall_filters(c);
+}
+
static int send_user_lookup(
Unit *unit,
int user_lookup_fd,
@@ -1940,22 +2209,14 @@ static int exec_child(
} else {
if (context->user) {
username = context->user;
- r = get_user_creds(&username, &uid, &gid, &home, &shell);
+ r = get_user_creds_clean(&username, &uid, &gid, &home, &shell);
if (r < 0) {
*exit_status = EXIT_USER;
return r;
}
- /* Don't set $HOME or $SHELL if they are are not particularly enlightening anyway. */
- if (isempty(home) || path_equal(home, "/"))
- home = NULL;
-
- if (isempty(shell) || PATH_IN_SET(shell,
- "/bin/nologin",
- "/sbin/nologin",
- "/usr/bin/nologin",
- "/usr/sbin/nologin"))
- shell = NULL;
+ /* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway
+ * (i.e. are "/" or "/bin/nologin"). */
}
if (context->group) {
@@ -2108,28 +2369,10 @@ static int exec_child(
}
if (!strv_isempty(context->runtime_directory) && params->runtime_prefix) {
- char **rt;
-
- STRV_FOREACH(rt, context->runtime_directory) {
- _cleanup_free_ char *p;
-
- p = strjoin(params->runtime_prefix, "/", *rt, NULL);
- if (!p) {
- *exit_status = EXIT_RUNTIME_DIRECTORY;
- return -ENOMEM;
- }
-
- r = mkdir_p_label(p, context->runtime_directory_mode);
- if (r < 0) {
- *exit_status = EXIT_RUNTIME_DIRECTORY;
- return r;
- }
-
- r = chmod_and_chown(p, context->runtime_directory_mode, uid, gid);
- if (r < 0) {
- *exit_status = EXIT_RUNTIME_DIRECTORY;
- return r;
- }
+ r = setup_runtime_directory(context, params, uid, gid);
+ if (r < 0) {
+ *exit_status = EXIT_RUNTIME_DIRECTORY;
+ return r;
}
}
@@ -2168,49 +2411,22 @@ static int exec_child(
}
accum_env = strv_env_clean(accum_env);
- umask(context->umask);
+ (void) umask(context->umask);
if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) {
- r = enforce_groups(context, username, gid);
+ r = setup_smack(context, command);
if (r < 0) {
- *exit_status = EXIT_GROUP;
+ *exit_status = EXIT_SMACK_PROCESS_LABEL;
return r;
}
-#ifdef HAVE_SMACK
- if (context->smack_process_label) {
- r = mac_smack_apply_pid(0, context->smack_process_label);
- if (r < 0) {
- *exit_status = EXIT_SMACK_PROCESS_LABEL;
- return r;
- }
- }
-#ifdef SMACK_DEFAULT_PROCESS_LABEL
- else {
- _cleanup_free_ char *exec_label = NULL;
-
- r = mac_smack_read(command->path, SMACK_ATTR_EXEC, &exec_label);
- if (r < 0 && r != -ENODATA && r != -EOPNOTSUPP) {
- *exit_status = EXIT_SMACK_PROCESS_LABEL;
- return r;
- }
- r = mac_smack_apply_pid(0, exec_label ? : SMACK_DEFAULT_PROCESS_LABEL);
- if (r < 0) {
- *exit_status = EXIT_SMACK_PROCESS_LABEL;
- return r;
- }
- }
-#endif
-#endif
-#ifdef HAVE_PAM
if (context->pam_name && username) {
- r = setup_pam(context->pam_name, username, uid, context->tty_path, &accum_env, fds, n_fds);
+ r = setup_pam(context->pam_name, username, uid, gid, context->tty_path, &accum_env, fds, n_fds);
if (r < 0) {
*exit_status = EXIT_PAM;
return r;
}
}
-#endif
}
if (context->private_network && runtime && runtime->netns_storage_socket[0] >= 0) {
@@ -2222,8 +2438,8 @@ static int exec_child(
}
needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime);
-
if (needs_mount_namespace) {
+ _cleanup_free_ char **rw = NULL;
char *tmp = NULL, *var = NULL;
/* The runtime struct only contains the parent
@@ -2239,14 +2455,22 @@ static int exec_child(
var = strjoina(runtime->var_tmp_dir, "/tmp");
}
+ r = compile_read_write_paths(context, params, &rw);
+ if (r < 0) {
+ *exit_status = EXIT_NAMESPACE;
+ return r;
+ }
+
r = setup_namespace(
(params->flags & EXEC_APPLY_CHROOT) ? context->root_directory : NULL,
- context->read_write_paths,
+ rw,
context->read_only_paths,
context->inaccessible_paths,
tmp,
var,
context->private_devices,
+ context->protect_kernel_tunables,
+ context->protect_control_groups,
context->protect_home,
context->protect_system,
context->mount_flags);
@@ -2264,6 +2488,14 @@ static int exec_child(
}
}
+ if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) {
+ r = enforce_groups(context, username, gid);
+ if (r < 0) {
+ *exit_status = EXIT_GROUP;
+ return r;
+ }
+ }
+
if (context->working_directory_home)
wd = home;
else if (context->working_directory)
@@ -2335,11 +2567,6 @@ static int exec_child(
if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) {
- bool use_address_families = context->address_families_whitelist ||
- !set_isempty(context->address_families);
- bool use_syscall_filter = context->syscall_whitelist ||
- !set_isempty(context->syscall_filter) ||
- !set_isempty(context->syscall_archs);
int secure_bits = context->secure_bits;
for (i = 0; i < _RLIMIT_MAX; i++) {
@@ -2416,15 +2643,14 @@ static int exec_child(
return -errno;
}
- if (context->no_new_privileges ||
- (!have_effective_cap(CAP_SYS_ADMIN) && (use_address_families || context->memory_deny_write_execute || context->restrict_realtime || use_syscall_filter)))
+ if (context_has_no_new_privileges(context))
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
*exit_status = EXIT_NO_NEW_PRIVILEGES;
return -errno;
}
#ifdef HAVE_SECCOMP
- if (use_address_families) {
+ if (context_has_address_families(context)) {
r = apply_address_families(unit, context);
if (r < 0) {
*exit_status = EXIT_ADDRESS_FAMILIES;
@@ -2448,7 +2674,23 @@ static int exec_child(
}
}
- if (use_syscall_filter) {
+ if (context->protect_kernel_tunables) {
+ r = apply_protect_sysctl(unit, context);
+ if (r < 0) {
+ *exit_status = EXIT_SECCOMP;
+ return r;
+ }
+ }
+
+ if (context->private_devices) {
+ r = apply_private_devices(unit, context);
+ if (r < 0) {
+ *exit_status = EXIT_SECCOMP;
+ return r;
+ }
+ }
+
+ if (context_has_syscall_filters(context)) {
r = apply_seccomp(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
@@ -2880,6 +3122,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
"%sNonBlocking: %s\n"
"%sPrivateTmp: %s\n"
"%sPrivateDevices: %s\n"
+ "%sProtectKernelTunables: %s\n"
+ "%sProtectControlGroups: %s\n"
"%sPrivateNetwork: %s\n"
"%sPrivateUsers: %s\n"
"%sProtectHome: %s\n"
@@ -2893,6 +3137,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
prefix, yes_no(c->non_blocking),
prefix, yes_no(c->private_tmp),
prefix, yes_no(c->private_devices),
+ prefix, yes_no(c->protect_kernel_tunables),
+ prefix, yes_no(c->protect_control_groups),
prefix, yes_no(c->private_network),
prefix, yes_no(c->private_users),
prefix, protect_home_to_string(c->protect_home),
diff --git a/src/core/execute.h b/src/core/execute.h
index 6082c42aba..449180c903 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -174,6 +174,8 @@ struct ExecContext {
bool private_users;
ProtectSystem protect_system;
ProtectHome protect_home;
+ bool protect_kernel_tunables;
+ bool protect_control_groups;
bool no_new_privileges;
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 2e6c965aec..c49c1d6732 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -89,6 +89,8 @@ $1.ReadOnlyPaths, config_parse_namespace_path_strv, 0,
$1.InaccessiblePaths, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.inaccessible_paths)
$1.PrivateTmp, config_parse_bool, 0, offsetof($1, exec_context.private_tmp)
$1.PrivateDevices, config_parse_bool, 0, offsetof($1, exec_context.private_devices)
+$1.ProtectKernelTunables, config_parse_bool, 0, offsetof($1, exec_context.protect_kernel_tunables)
+$1.ProtectControlGroups, config_parse_bool, 0, offsetof($1, exec_context.protect_control_groups)
$1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network)
$1.PrivateUsers, config_parse_bool, 0, offsetof($1, exec_context.private_users)
$1.ProtectSystem, config_parse_protect_system, 0, offsetof($1, exec_context)
diff --git a/src/core/main.c b/src/core/main.c
index 7d8322ebd8..4b82a57b3c 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -131,6 +131,7 @@ static bool arg_default_memory_accounting = false;
static bool arg_default_tasks_accounting = true;
static uint64_t arg_default_tasks_max = UINT64_MAX;
static sd_id128_t arg_machine_id = {};
+static CADBurstAction arg_cad_burst_action = CAD_BURST_ACTION_REBOOT;
noreturn static void freeze_or_reboot(void) {
@@ -202,7 +203,7 @@ noreturn static void crash(int sig) {
pid, sigchld_code_to_string(status.si_code),
status.si_status,
strna(status.si_code == CLD_EXITED
- ? exit_status_to_string(status.si_status, EXIT_STATUS_FULL)
+ ? exit_status_to_string(status.si_status, EXIT_STATUS_MINIMAL)
: signal_to_string(status.si_status)));
else
log_emergency("Caught <%s>, dumped core as pid "PID_FMT".", signal_to_string(sig), pid);
@@ -648,6 +649,8 @@ static int config_parse_join_controllers(const char *unit,
return 0;
}
+static DEFINE_CONFIG_PARSE_ENUM(config_parse_cad_burst_action, cad_burst_action, CADBurstAction, "Failed to parse service restart specifier");
+
static int parse_config_file(void) {
const ConfigTableItem items[] = {
@@ -702,6 +705,7 @@ static int parse_config_file(void) {
{ "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting },
{ "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_default_tasks_accounting },
{ "Manager", "DefaultTasksMax", config_parse_tasks_max, 0, &arg_default_tasks_max },
+ { "Manager", "CtrlAltDelBurstAction", config_parse_cad_burst_action, 0, &arg_cad_burst_action},
{}
};
@@ -715,7 +719,7 @@ static int parse_config_file(void) {
CONF_PATHS_NULSTR("systemd/system.conf.d") :
CONF_PATHS_NULSTR("systemd/user.conf.d");
- config_parse_many(fn, conf_dirs_nulstr, "Manager\0", config_item_table_lookup, items, false, NULL);
+ config_parse_many_nulstr(fn, conf_dirs_nulstr, "Manager\0", config_item_table_lookup, items, false, NULL);
/* Traditionally "0" was used to turn off the default unit timeouts. Fix this up so that we used USEC_INFINITY
* like everywhere else. */
@@ -996,10 +1000,8 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_MACHINE_ID:
r = set_machine_id(optarg);
- if (r < 0) {
- log_error("MachineID '%s' is not valid.", optarg);
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "MachineID '%s' is not valid.", optarg);
break;
case 'h':
@@ -1530,15 +1532,9 @@ int main(int argc, char *argv[]) {
* need to do that for user instances since they never log
* into the console. */
log_show_color(colors_enabled());
- make_null_stdio();
- }
-
- /* Initialize default unit */
- r = free_and_strdup(&arg_default_unit, SPECIAL_DEFAULT_TARGET);
- if (r < 0) {
- log_emergency_errno(r, "Failed to set default unit %s: %m", SPECIAL_DEFAULT_TARGET);
- error_message = "Failed to set default unit";
- goto finish;
+ r = make_null_stdio();
+ if (r < 0)
+ log_warning_errno(r, "Failed to redirect standard streams to /dev/null: %m");
}
r = initialize_join_controllers();
@@ -1588,6 +1584,16 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ /* Initialize default unit */
+ if (!arg_default_unit) {
+ arg_default_unit = strdup(SPECIAL_DEFAULT_TARGET);
+ if (!arg_default_unit) {
+ r = log_oom();
+ error_message = "Failed to set default unit";
+ goto finish;
+ }
+ }
+
if (arg_action == ACTION_TEST &&
geteuid() == 0) {
log_error("Don't run test mode as root.");
@@ -1796,6 +1802,7 @@ int main(int argc, char *argv[]) {
m->initrd_timestamp = initrd_timestamp;
m->security_start_timestamp = security_start_timestamp;
m->security_finish_timestamp = security_finish_timestamp;
+ m->cad_burst_action = arg_cad_burst_action;
manager_set_defaults(m);
manager_set_show_status(m, arg_show_status);
diff --git a/src/core/manager.c b/src/core/manager.c
index b58f68fa7a..c1dce62a18 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -590,7 +590,7 @@ int manager_new(UnitFileScope scope, bool test_run, Manager **_m) {
m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
m->pin_cgroupfs_fd = m->notify_fd = m->cgroups_agent_fd = m->signal_fd = m->time_change_fd =
- m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd =
+ m->dev_autofs_fd = m->private_listen_fd = m->cgroup_inotify_fd =
m->ask_password_inotify_fd = -1;
m->user_lookup_fds[0] = m->user_lookup_fds[1] = -1;
@@ -661,9 +661,8 @@ int manager_new(UnitFileScope scope, bool test_run, Manager **_m) {
goto fail;
}
- /* Note that we set up neither kdbus, nor the notify fd
- * here. We do that after deserialization, since they might
- * have gotten serialized across the reexec. */
+ /* Note that we do not set up the notify fd here. We do that after deserialization,
+ * since they might have gotten serialized across the reexec. */
m->taint_usr = dir_is_empty("/usr") > 0;
@@ -879,7 +878,6 @@ static int manager_connect_bus(Manager *m, bool reexecuting) {
return 0;
try_bus_connect =
- m->kdbus_fd >= 0 ||
reexecuting ||
(MANAGER_IS_USER(m) && getenv("DBUS_SESSION_BUS_ADDRESS"));
@@ -1084,7 +1082,6 @@ Manager* manager_free(Manager *m) {
safe_close(m->notify_fd);
safe_close(m->cgroups_agent_fd);
safe_close(m->time_change_fd);
- safe_close(m->kdbus_fd);
safe_close_pair(m->user_lookup_fds);
manager_close_ask_password(m);
@@ -1239,9 +1236,11 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
return r;
/* Make sure the transient directory always exists, so that it remains in the search path */
- r = mkdir_p_label(m->lookup_paths.transient, 0755);
- if (r < 0)
- return r;
+ if (!m->test_run) {
+ r = mkdir_p_label(m->lookup_paths.transient, 0755);
+ if (r < 0)
+ return r;
+ }
dual_timestamp_get(&m->generators_start_timestamp);
r = manager_run_generators(m);
@@ -1287,7 +1286,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
if (q < 0 && r == 0)
r = q;
- /* We might have deserialized the kdbus control fd, but if we didn't, then let's create the bus now. */
+ /* Let's connect to the bus now. */
(void) manager_connect_bus(m, !!serialization);
(void) bus_track_coldplug(m, &m->subscribed, false, m->deserialized_subscribed);
@@ -1660,13 +1659,12 @@ static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, ui
return 0;
}
-static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const char *buf, size_t n, FDSet *fds) {
+static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const char *buf, FDSet *fds) {
_cleanup_strv_free_ char **tags = NULL;
assert(m);
assert(u);
assert(buf);
- assert(n > 0);
tags = strv_split(buf, "\n\r");
if (!tags) {
@@ -1676,8 +1674,14 @@ static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const
if (UNIT_VTABLE(u)->notify_message)
UNIT_VTABLE(u)->notify_message(u, pid, tags, fds);
- else
- log_unit_debug(u, "Got notification message for unit. Ignoring.");
+ else if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
+ _cleanup_free_ char *x = NULL, *y = NULL;
+
+ x = cescape(buf);
+ if (x)
+ y = ellipsize(x, 20, 90);
+ log_unit_debug(u, "Got notification message \"%s\", ignoring.", strnull(y));
+ }
}
static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
@@ -1703,7 +1707,6 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
struct cmsghdr *cmsg;
struct ucred *ucred = NULL;
- bool found = false;
Unit *u1, *u2, *u3;
int r, *fd_array = NULL;
unsigned n_fds = 0;
@@ -1717,12 +1720,15 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
return 0;
}
- n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
+ n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC|MSG_TRUNC);
if (n < 0) {
- if (errno == EAGAIN || errno == EINTR)
- return 0;
+ if (IN_SET(errno, EAGAIN, EINTR))
+ return 0; /* Spurious wakeup, try again */
- return -errno;
+ /* If this is any other, real error, then let's stop processing this socket. This of course means we
+ * won't take notification messages anymore, but that's still better than busy looping around this:
+ * being woken up over and over again but being unable to actually read the message off the socket. */
+ return log_error_errno(errno, "Failed to receive notification message: %m");
}
CMSG_FOREACH(cmsg, &msghdr) {
@@ -1745,7 +1751,8 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
r = fdset_new_array(&fds, fd_array, n_fds);
if (r < 0) {
close_many(fd_array, n_fds);
- return log_oom();
+ log_oom();
+ return 0;
}
}
@@ -1754,38 +1761,40 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
return 0;
}
- if ((size_t) n >= sizeof(buf)) {
+ if ((size_t) n >= sizeof(buf) || (msghdr.msg_flags & MSG_TRUNC)) {
log_warning("Received notify message exceeded maximum size. Ignoring.");
return 0;
}
+ /* As extra safety check, let's make sure the string we get doesn't contain embedded NUL bytes. We permit one
+ * trailing NUL byte in the message, but don't expect it. */
+ if (n > 1 && memchr(buf, 0, n-1)) {
+ log_warning("Received notify message with embedded NUL bytes. Ignoring.");
+ return 0;
+ }
+
+ /* Make sure it's NUL-terminated. */
buf[n] = 0;
/* Notify every unit that might be interested, but try
* to avoid notifying the same one multiple times. */
u1 = manager_get_unit_by_pid_cgroup(m, ucred->pid);
- if (u1) {
- manager_invoke_notify_message(m, u1, ucred->pid, buf, n, fds);
- found = true;
- }
+ if (u1)
+ manager_invoke_notify_message(m, u1, ucred->pid, buf, fds);
u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(ucred->pid));
- if (u2 && u2 != u1) {
- manager_invoke_notify_message(m, u2, ucred->pid, buf, n, fds);
- found = true;
- }
+ if (u2 && u2 != u1)
+ manager_invoke_notify_message(m, u2, ucred->pid, buf, fds);
u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(ucred->pid));
- if (u3 && u3 != u2 && u3 != u1) {
- manager_invoke_notify_message(m, u3, ucred->pid, buf, n, fds);
- found = true;
- }
+ if (u3 && u3 != u2 && u3 != u1)
+ manager_invoke_notify_message(m, u3, ucred->pid, buf, fds);
- if (!found)
+ if (!u1 && !u2 && !u3)
log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid);
if (fdset_size(fds) > 0)
- log_warning("Got auxiliary fds with notification message, closing all.");
+ log_warning("Got extra auxiliary fds with notification message, closing them.");
return 0;
}
@@ -1890,6 +1899,35 @@ static int manager_start_target(Manager *m, const char *name, JobMode mode) {
return r;
}
+static void manager_handle_ctrl_alt_del(Manager *m) {
+ /* If the user presses C-A-D more than
+ * 7 times within 2s, we reboot/shutdown immediately,
+ * unless it was disabled in system.conf */
+
+ if (ratelimit_test(&m->ctrl_alt_del_ratelimit) || m->cad_burst_action == CAD_BURST_ACTION_IGNORE)
+ manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY);
+ else {
+ switch (m->cad_burst_action) {
+
+ case CAD_BURST_ACTION_REBOOT:
+ m->exit_code = MANAGER_REBOOT;
+ break;
+
+ case CAD_BURST_ACTION_POWEROFF:
+ m->exit_code = MANAGER_POWEROFF;
+ break;
+
+ default:
+ assert_not_reached("Unknown action.");
+ }
+
+ log_notice("Ctrl-Alt-Del was pressed more than 7 times within 2s, performing immediate %s.",
+ cad_burst_action_to_string(m->cad_burst_action));
+ status_printf(NULL, true, false, "Ctrl-Alt-Del was pressed more than 7 times within 2s, performing immediate %s.",
+ cad_burst_action_to_string(m->cad_burst_action));
+ }
+}
+
static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
Manager *m = userdata;
ssize_t n;
@@ -1908,14 +1946,17 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
for (;;) {
n = read(m->signal_fd, &sfsi, sizeof(sfsi));
if (n != sizeof(sfsi)) {
+ if (n >= 0) {
+ log_warning("Truncated read from signal fd (%zu bytes)!", n);
+ return 0;
+ }
- if (n >= 0)
- return -EIO;
-
- if (errno == EINTR || errno == EAGAIN)
+ if (IN_SET(errno, EINTR, EAGAIN))
break;
- return -errno;
+ /* We return an error here, which will kill this handler,
+ * to avoid a busy loop on read error. */
+ return log_error_errno(errno, "Reading from signal fd failed: %m");
}
log_received_signal(sfsi.ssi_signo == SIGCHLD ||
@@ -1941,19 +1982,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
case SIGINT:
if (MANAGER_IS_SYSTEM(m)) {
-
- /* If the user presses C-A-D more than
- * 7 times within 2s, we reboot
- * immediately. */
-
- if (ratelimit_test(&m->ctrl_alt_del_ratelimit))
- manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY);
- else {
- log_notice("Ctrl-Alt-Del was pressed more than 7 times within 2s, rebooting immediately.");
- status_printf(NULL, true, false, "Ctrl-Alt-Del was pressed more than 7 times within 2s, rebooting immediately.");
- m->exit_code = MANAGER_REBOOT;
- }
-
+ manager_handle_ctrl_alt_del(m);
break;
}
@@ -2481,16 +2510,6 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
fprintf(f, "user-lookup=%i %i\n", copy0, copy1);
}
- if (m->kdbus_fd >= 0) {
- int copy;
-
- copy = fdset_put_dup(fds, m->kdbus_fd);
- if (copy < 0)
- return copy;
-
- fprintf(f, "kdbus-fd=%i\n", copy);
- }
-
bus_track_serialize(m->subscribed, f, "subscribed");
r = dynamic_user_serialize(m, f, fds);
@@ -2678,16 +2697,6 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
m->user_lookup_fds[1] = fdset_remove(fds, fd1);
}
- } else if (startswith(l, "kdbus-fd=")) {
- int fd;
-
- if (safe_atoi(l + 9, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
- log_debug("Failed to parse kdbus fd: %s", l + 9);
- else {
- safe_close(m->kdbus_fd);
- m->kdbus_fd = fdset_remove(fds, fd);
- }
-
} else if (startswith(l, "dynamic-user="))
dynamic_user_deserialize_one(m, l + 13, fds);
else if (startswith(l, "destroy-ipc-uid="))
@@ -2699,7 +2708,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
if (strv_extend(&m->deserialized_subscribed, l+11) < 0)
log_oom();
- } else
+ } else if (!startswith(l, "kdbus-fd=")) /* ignore this one */
log_debug("Unknown serialization item '%s'", l);
}
@@ -3560,3 +3569,11 @@ static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP(manager_state, ManagerState);
+
+static const char *const cad_burst_action_table[_CAD_BURST_ACTION_MAX] = {
+ [CAD_BURST_ACTION_IGNORE] = "ignore",
+ [CAD_BURST_ACTION_REBOOT] = "reboot-force",
+ [CAD_BURST_ACTION_POWEROFF] = "poweroff-force",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(cad_burst_action, CADBurstAction);
diff --git a/src/core/manager.h b/src/core/manager.h
index b9f2e4b5a1..495440b446 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -62,6 +62,14 @@ typedef enum ManagerExitCode {
_MANAGER_EXIT_CODE_INVALID = -1
} ManagerExitCode;
+typedef enum CADBurstAction {
+ CAD_BURST_ACTION_IGNORE,
+ CAD_BURST_ACTION_REBOOT,
+ CAD_BURST_ACTION_POWEROFF,
+ _CAD_BURST_ACTION_MAX,
+ _CAD_BURST_ACTION_INVALID = -1
+} CADBurstAction;
+
typedef enum StatusType {
STATUS_TYPE_EPHEMERAL,
STATUS_TYPE_NORMAL,
@@ -294,9 +302,6 @@ struct Manager {
* value where Unit objects are contained. */
Hashmap *units_requiring_mounts_for;
- /* Reference to the kdbus bus control fd */
- int kdbus_fd;
-
/* Used for processing polkit authorization responses */
Hashmap *polkit_registry;
@@ -307,8 +312,9 @@ struct Manager {
Hashmap *uid_refs;
Hashmap *gid_refs;
- /* When the user hits C-A-D more than 7 times per 2s, reboot immediately... */
+ /* When the user hits C-A-D more than 7 times per 2s, do something immediately... */
RateLimit ctrl_alt_del_ratelimit;
+ CADBurstAction cad_burst_action;
const char *unit_log_field;
const char *unit_log_format_string;
@@ -401,3 +407,6 @@ void manager_deserialize_gid_refs_one(Manager *m, const char *value);
const char *manager_state_to_string(ManagerState m) _const_;
ManagerState manager_state_from_string(const char *s) _pure_;
+
+const char *cad_burst_action_to_string(CADBurstAction a) _const_;
+CADBurstAction cad_burst_action_from_string(const char *s) _pure_;
diff --git a/src/core/mount.c b/src/core/mount.c
index 04025b83b9..f5e67b1d78 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -830,7 +830,7 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
fail:
log_unit_warning_errno(UNIT(m), r, "Failed to kill processes: %m");
- if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL)
+ if (IN_SET(state, MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL))
mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
else
mount_enter_dead(m, MOUNT_FAILURE_RESOURCES);
@@ -986,18 +986,19 @@ static int mount_start(Unit *u) {
/* We cannot fulfill this request right now, try again later
* please! */
- if (m->state == MOUNT_UNMOUNTING ||
- m->state == MOUNT_UNMOUNTING_SIGTERM ||
- m->state == MOUNT_UNMOUNTING_SIGKILL ||
- m->state == MOUNT_MOUNTING_SIGTERM ||
- m->state == MOUNT_MOUNTING_SIGKILL)
+ if (IN_SET(m->state,
+ MOUNT_UNMOUNTING,
+ MOUNT_UNMOUNTING_SIGTERM,
+ MOUNT_UNMOUNTING_SIGKILL,
+ MOUNT_MOUNTING_SIGTERM,
+ MOUNT_MOUNTING_SIGKILL))
return -EAGAIN;
/* Already on it! */
if (m->state == MOUNT_MOUNTING)
return 0;
- assert(m->state == MOUNT_DEAD || m->state == MOUNT_FAILED);
+ assert(IN_SET(m->state, MOUNT_DEAD, MOUNT_FAILED));
r = unit_start_limit_test(u);
if (r < 0) {
@@ -1019,19 +1020,21 @@ static int mount_stop(Unit *u) {
assert(m);
/* Already on it */
- if (m->state == MOUNT_UNMOUNTING ||
- m->state == MOUNT_UNMOUNTING_SIGKILL ||
- m->state == MOUNT_UNMOUNTING_SIGTERM ||
- m->state == MOUNT_MOUNTING_SIGTERM ||
- m->state == MOUNT_MOUNTING_SIGKILL)
+ if (IN_SET(m->state,
+ MOUNT_UNMOUNTING,
+ MOUNT_UNMOUNTING_SIGKILL,
+ MOUNT_UNMOUNTING_SIGTERM,
+ MOUNT_MOUNTING_SIGTERM,
+ MOUNT_MOUNTING_SIGKILL))
return 0;
- assert(m->state == MOUNT_MOUNTING ||
- m->state == MOUNT_MOUNTING_DONE ||
- m->state == MOUNT_MOUNTED ||
- m->state == MOUNT_REMOUNTING ||
- m->state == MOUNT_REMOUNTING_SIGTERM ||
- m->state == MOUNT_REMOUNTING_SIGKILL);
+ assert(IN_SET(m->state,
+ MOUNT_MOUNTING,
+ MOUNT_MOUNTING_DONE,
+ MOUNT_MOUNTED,
+ MOUNT_REMOUNTING,
+ MOUNT_REMOUNTING_SIGTERM,
+ MOUNT_REMOUNTING_SIGKILL));
mount_enter_unmounting(m);
return 1;
@@ -1159,7 +1162,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
m->control_pid = 0;
- if (is_clean_exit(code, status, NULL))
+ if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
f = MOUNT_SUCCESS;
else if (code == CLD_EXITED)
f = MOUNT_FAILURE_EXIT_CODE;
@@ -1197,9 +1200,10 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
case MOUNT_MOUNTING_SIGKILL:
case MOUNT_MOUNTING_SIGTERM:
- if (f == MOUNT_SUCCESS)
- mount_enter_mounted(m, f);
- else if (m->from_proc_self_mountinfo)
+ if (f == MOUNT_SUCCESS || m->from_proc_self_mountinfo)
+ /* If /bin/mount returned success, or if we see the mount point in /proc/self/mountinfo we are
+ * happy. If we see the first condition first, we should see the the second condition
+ * immediately after – or /bin/mount lies to us and is broken. */
mount_enter_mounted(m, f);
else
mount_enter_dead(m, f);
diff --git a/src/core/namespace.c b/src/core/namespace.c
index 52a2505d94..43a2f4ba6e 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -29,6 +29,7 @@
#include "alloc-util.h"
#include "dev-setup.h"
#include "fd-util.h"
+#include "fs-util.h"
#include "loopback-setup.h"
#include "missing.h"
#include "mkdir.h"
@@ -53,61 +54,230 @@ typedef enum MountMode {
PRIVATE_TMP,
PRIVATE_VAR_TMP,
PRIVATE_DEV,
- READWRITE
+ READWRITE,
} MountMode;
typedef struct BindMount {
- const char *path;
+ const char *path; /* stack memory, doesn't need to be freed explicitly */
+ char *chased; /* malloc()ed memory, needs to be freed */
MountMode mode;
- bool done;
- bool ignore;
+ bool ignore; /* Ignore if path does not exist */
} BindMount;
+typedef struct TargetMount {
+ const char *path;
+ MountMode mode;
+ bool ignore; /* Ignore if path does not exist */
+} TargetMount;
+
+/*
+ * The following Protect tables are to protect paths and mark some of them
+ * READONLY, in case a path is covered by an option from another table, then
+ * it is marked READWRITE in the current one, and the more restrictive mode is
+ * applied from that other table. This way all options can be combined in a
+ * safe and comprehensible way for users.
+ */
+
+/* ProtectKernelTunables= option and the related filesystem APIs */
+static const TargetMount protect_kernel_tunables_table[] = {
+ { "/proc/sys", READONLY, false },
+ { "/proc/sysrq-trigger", READONLY, true },
+ { "/proc/latency_stats", READONLY, true },
+ { "/proc/mtrr", READONLY, true },
+ { "/proc/apm", READONLY, true },
+ { "/proc/acpi", READONLY, true },
+ { "/proc/timer_stats", READONLY, true },
+ { "/proc/asound", READONLY, true },
+ { "/proc/bus", READONLY, true },
+ { "/proc/fs", READONLY, true },
+ { "/proc/irq", READONLY, true },
+ { "/sys", READONLY, false },
+ { "/sys/kernel/debug", READONLY, true },
+ { "/sys/kernel/tracing", READONLY, true },
+ { "/sys/fs/cgroup", READWRITE, false }, /* READONLY is set by ProtectControlGroups= option */
+};
+
+/*
+ * ProtectHome=read-only table, protect $HOME and $XDG_RUNTIME_DIR and rest of
+ * system should be protected by ProtectSystem=
+ */
+static const TargetMount protect_home_read_only_table[] = {
+ { "/home", READONLY, true },
+ { "/run/user", READONLY, true },
+ { "/root", READONLY, true },
+};
+
+/* ProtectHome=yes table */
+static const TargetMount protect_home_yes_table[] = {
+ { "/home", INACCESSIBLE, true },
+ { "/run/user", INACCESSIBLE, true },
+ { "/root", INACCESSIBLE, true },
+};
+
+/* ProtectSystem=yes table */
+static const TargetMount protect_system_yes_table[] = {
+ { "/usr", READONLY, false },
+ { "/boot", READONLY, true },
+ { "/efi", READONLY, true },
+};
+
+/* ProtectSystem=full includes ProtectSystem=yes */
+static const TargetMount protect_system_full_table[] = {
+ { "/usr", READONLY, false },
+ { "/boot", READONLY, true },
+ { "/efi", READONLY, true },
+ { "/etc", READONLY, false },
+};
+
+/*
+ * ProtectSystem=strict table. In this strict mode, we mount everything
+ * read-only, except for /proc, /dev, /sys which are the kernel API VFS,
+ * which are left writable, but PrivateDevices= + ProtectKernelTunables=
+ * protect those, and these options should be fully orthogonal.
+ * (And of course /home and friends are also left writable, as ProtectHome=
+ * shall manage those, orthogonally).
+ */
+static const TargetMount protect_system_strict_table[] = {
+ { "/", READONLY, false },
+ { "/proc", READWRITE, false }, /* ProtectKernelTunables= */
+ { "/sys", READWRITE, false }, /* ProtectKernelTunables= */
+ { "/dev", READWRITE, false }, /* PrivateDevices= */
+ { "/home", READWRITE, true }, /* ProtectHome= */
+ { "/run/user", READWRITE, true }, /* ProtectHome= */
+ { "/root", READWRITE, true }, /* ProtectHome= */
+};
+
+static void set_bind_mount(BindMount **p, const char *path, MountMode mode, bool ignore) {
+ (*p)->path = path;
+ (*p)->mode = mode;
+ (*p)->ignore = ignore;
+}
+
static int append_mounts(BindMount **p, char **strv, MountMode mode) {
char **i;
assert(p);
STRV_FOREACH(i, strv) {
+ bool ignore = false;
- (*p)->ignore = false;
- (*p)->done = false;
-
- if ((mode == INACCESSIBLE || mode == READONLY || mode == READWRITE) && (*i)[0] == '-') {
- (*p)->ignore = true;
+ if (IN_SET(mode, INACCESSIBLE, READONLY, READWRITE) && startswith(*i, "-")) {
(*i)++;
+ ignore = true;
}
if (!path_is_absolute(*i))
return -EINVAL;
- (*p)->path = *i;
- (*p)->mode = mode;
+ set_bind_mount(p, *i, mode, ignore);
(*p)++;
}
return 0;
}
-static int mount_path_compare(const void *a, const void *b) {
- const BindMount *p = a, *q = b;
- int d;
+static int append_target_mounts(BindMount **p, const char *root_directory, const TargetMount *mounts, const size_t size) {
+ unsigned i;
- d = path_compare(p->path, q->path);
+ assert(p);
+ assert(mounts);
+
+ for (i = 0; i < size; i++) {
+ /*
+ * Here we assume that the ignore field is set during
+ * declaration we do not support "-" at the beginning.
+ */
+ const TargetMount *m = &mounts[i];
+ const char *path = prefix_roota(root_directory, m->path);
+
+ if (!path_is_absolute(path))
+ return -EINVAL;
+
+ set_bind_mount(p, path, m->mode, m->ignore);
+ (*p)++;
+ }
+
+ return 0;
+}
+
+static int append_protect_kernel_tunables(BindMount **p, const char *root_directory) {
+ assert(p);
+
+ return append_target_mounts(p, root_directory, protect_kernel_tunables_table,
+ ELEMENTSOF(protect_kernel_tunables_table));
+}
- if (d == 0) {
- /* If the paths are equal, check the mode */
- if (p->mode < q->mode)
- return -1;
+static int append_protect_home(BindMount **p, const char *root_directory, ProtectHome protect_home) {
+ int r = 0;
- if (p->mode > q->mode)
- return 1;
+ assert(p);
+ if (protect_home == PROTECT_HOME_NO)
return 0;
+
+ switch (protect_home) {
+ case PROTECT_HOME_READ_ONLY:
+ r = append_target_mounts(p, root_directory, protect_home_read_only_table,
+ ELEMENTSOF(protect_home_read_only_table));
+ break;
+ case PROTECT_HOME_YES:
+ r = append_target_mounts(p, root_directory, protect_home_yes_table,
+ ELEMENTSOF(protect_home_yes_table));
+ break;
+ default:
+ r = -EINVAL;
+ break;
}
+ return r;
+}
+
+static int append_protect_system(BindMount **p, const char *root_directory, ProtectSystem protect_system) {
+ int r = 0;
+
+ assert(p);
+
+ if (protect_system == PROTECT_SYSTEM_NO)
+ return 0;
+
+ switch (protect_system) {
+ case PROTECT_SYSTEM_STRICT:
+ r = append_target_mounts(p, root_directory, protect_system_strict_table,
+ ELEMENTSOF(protect_system_strict_table));
+ break;
+ case PROTECT_SYSTEM_YES:
+ r = append_target_mounts(p, root_directory, protect_system_yes_table,
+ ELEMENTSOF(protect_system_yes_table));
+ break;
+ case PROTECT_SYSTEM_FULL:
+ r = append_target_mounts(p, root_directory, protect_system_full_table,
+ ELEMENTSOF(protect_system_full_table));
+ break;
+ default:
+ r = -EINVAL;
+ break;
+ }
+
+ return r;
+}
+
+static int mount_path_compare(const void *a, const void *b) {
+ const BindMount *p = a, *q = b;
+ int d;
+
/* If the paths are not equal, then order prefixes first */
- return d;
+ d = path_compare(p->path, q->path);
+ if (d != 0)
+ return d;
+
+ /* If the paths are equal, check the mode */
+ if (p->mode < q->mode)
+ return -1;
+
+ if (p->mode > q->mode)
+ return 1;
+
+ return 0;
}
static void drop_duplicates(BindMount *m, unsigned *n) {
@@ -116,16 +286,110 @@ static void drop_duplicates(BindMount *m, unsigned *n) {
assert(m);
assert(n);
+ /* Drops duplicate entries. Expects that the array is properly ordered already. */
+
for (f = m, t = m, previous = NULL; f < m+*n; f++) {
- /* The first one wins */
- if (previous && path_equal(f->path, previous->path))
+ /* The first one wins (which is the one with the more restrictive mode), see mount_path_compare()
+ * above. */
+ if (previous && path_equal(f->path, previous->path)) {
+ log_debug("%s is duplicate.", f->path);
continue;
+ }
*t = *f;
-
previous = t;
+ t++;
+ }
+
+ *n = t - m;
+}
+
+static void drop_inaccessible(BindMount *m, unsigned *n) {
+ BindMount *f, *t;
+ const char *clear = NULL;
+
+ assert(m);
+ assert(n);
+
+ /* Drops all entries obstructed by another entry further up the tree. Expects that the array is properly
+ * ordered already. */
+ for (f = m, t = m; f < m+*n; f++) {
+
+ /* If we found a path set for INACCESSIBLE earlier, and this entry has it as prefix we should drop
+ * it, as inaccessible paths really should drop the entire subtree. */
+ if (clear && path_startswith(f->path, clear)) {
+ log_debug("%s is masked by %s.", f->path, clear);
+ continue;
+ }
+
+ clear = f->mode == INACCESSIBLE ? f->path : NULL;
+
+ *t = *f;
+ t++;
+ }
+
+ *n = t - m;
+}
+
+static void drop_nop(BindMount *m, unsigned *n) {
+ BindMount *f, *t;
+
+ assert(m);
+ assert(n);
+
+ /* Drops all entries which have an immediate parent that has the same type, as they are redundant. Assumes the
+ * list is ordered by prefixes. */
+
+ for (f = m, t = m; f < m+*n; f++) {
+
+ /* Only suppress such subtrees for READONLY and READWRITE entries */
+ if (IN_SET(f->mode, READONLY, READWRITE)) {
+ BindMount *p;
+ bool found = false;
+
+ /* Now let's find the first parent of the entry we are looking at. */
+ for (p = t-1; p >= m; p--) {
+ if (path_startswith(f->path, p->path)) {
+ found = true;
+ break;
+ }
+ }
+
+ /* We found it, let's see if it's the same mode, if so, we can drop this entry */
+ if (found && p->mode == f->mode) {
+ log_debug("%s is redundant by %s", f->path, p->path);
+ continue;
+ }
+ }
+
+ *t = *f;
+ t++;
+ }
+
+ *n = t - m;
+}
+
+static void drop_outside_root(const char *root_directory, BindMount *m, unsigned *n) {
+ BindMount *f, *t;
+
+ assert(m);
+ assert(n);
+
+ if (!root_directory)
+ return;
+
+ /* Drops all mounts that are outside of the root directory. */
+
+ for (f = m, t = m; f < m+*n; f++) {
+
+ if (!path_startswith(f->path, root_directory)) {
+ log_debug("%s is outside of root directory.", f->path);
+ continue;
+ }
+
+ *t = *f;
t++;
}
@@ -278,24 +542,23 @@ static int apply_mount(
const char *what;
int r;
- struct stat target;
assert(m);
+ log_debug("Applying namespace mount on %s", m->path);
+
switch (m->mode) {
- case INACCESSIBLE:
+ case INACCESSIBLE: {
+ struct stat target;
/* First, get rid of everything that is below if there
* is anything... Then, overmount it with an
* inaccessible path. */
- umount_recursive(m->path, 0);
+ (void) umount_recursive(m->path, 0);
- if (lstat(m->path, &target) < 0) {
- if (m->ignore && errno == ENOENT)
- return 0;
- return -errno;
- }
+ if (lstat(m->path, &target) < 0)
+ return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m", m->path);
what = mode_to_inaccessible_node(target.st_mode);
if (!what) {
@@ -303,11 +566,20 @@ static int apply_mount(
return -ELOOP;
}
break;
+ }
+
case READONLY:
case READWRITE:
- /* Nothing to mount here, we just later toggle the
- * MS_RDONLY bit for the mount point */
- return 0;
+
+ r = path_is_mount_point(m->path, 0);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", m->path);
+ if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY bit for the mount point if needed. */
+ return 0;
+
+ /* This isn't a mount point yet, let's make it one. */
+ what = m->path;
+ break;
case PRIVATE_TMP:
what = tmp_dir;
@@ -326,38 +598,104 @@ static int apply_mount(
assert(what);
- r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL);
- if (r >= 0) {
- log_debug("Successfully mounted %s to %s", what, m->path);
- return r;
- } else {
- if (m->ignore && errno == ENOENT)
- return 0;
+ if (mount(what, m->path, NULL, MS_BIND|MS_REC, NULL) < 0)
return log_debug_errno(errno, "Failed to mount %s to %s: %m", what, m->path);
- }
+
+ log_debug("Successfully mounted %s to %s", what, m->path);
+ return 0;
}
-static int make_read_only(BindMount *m) {
- int r;
+static int make_read_only(BindMount *m, char **blacklist) {
+ int r = 0;
assert(m);
if (IN_SET(m->mode, INACCESSIBLE, READONLY))
- r = bind_remount_recursive(m->path, true);
- else if (IN_SET(m->mode, READWRITE, PRIVATE_TMP, PRIVATE_VAR_TMP, PRIVATE_DEV)) {
- r = bind_remount_recursive(m->path, false);
- if (r == 0 && m->mode == PRIVATE_DEV) /* can be readonly but the submounts can't*/
- if (mount(NULL, m->path, NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0)
- r = -errno;
+ r = bind_remount_recursive(m->path, true, blacklist);
+ else if (m->mode == PRIVATE_DEV) { /* Can be readonly but the submounts can't*/
+ if (mount(NULL, m->path, NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0)
+ r = -errno;
} else
- r = 0;
-
- if (m->ignore && r == -ENOENT)
return 0;
+ /* Not that we only turn on the MS_RDONLY flag here, we never turn it off. Something that was marked read-only
+ * already stays this way. This improves compatibility with container managers, where we won't attempt to undo
+ * read-only mounts already applied. */
+
return r;
}
+static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned *n) {
+ BindMount *f, *t;
+ int r;
+
+ assert(m);
+ assert(n);
+
+ /* Since mount() will always follow symlinks and we need to take the different root directory into account we
+ * chase the symlinks on our own first. This call wil do so for all entries and remove all entries where we
+ * can't resolve the path, and which have been marked for such removal. */
+
+ for (f = m, t = m; f < m+*n; f++) {
+
+ r = chase_symlinks(f->path, root_directory, &f->chased);
+ if (r == -ENOENT && f->ignore) /* Doesn't exist? Then remove it! */
+ continue;
+ if (r < 0)
+ return log_debug_errno(r, "Failed to chase symlinks for %s: %m", f->path);
+
+ if (path_equal(f->path, f->chased))
+ f->chased = mfree(f->chased);
+ else {
+ log_debug("Chased %s → %s", f->path, f->chased);
+ f->path = f->chased;
+ }
+
+ *t = *f;
+ t++;
+ }
+
+ *n = t - m;
+ return 0;
+}
+
+static unsigned namespace_calculate_mounts(
+ char** read_write_paths,
+ char** read_only_paths,
+ char** inaccessible_paths,
+ const char* tmp_dir,
+ const char* var_tmp_dir,
+ bool private_dev,
+ bool protect_sysctl,
+ bool protect_cgroups,
+ ProtectHome protect_home,
+ ProtectSystem protect_system) {
+
+ unsigned protect_home_cnt;
+ unsigned protect_system_cnt =
+ (protect_system == PROTECT_SYSTEM_STRICT ?
+ ELEMENTSOF(protect_system_strict_table) :
+ ((protect_system == PROTECT_SYSTEM_FULL) ?
+ ELEMENTSOF(protect_system_full_table) :
+ ((protect_system == PROTECT_SYSTEM_YES) ?
+ ELEMENTSOF(protect_system_yes_table) : 0)));
+
+ protect_home_cnt =
+ (protect_home == PROTECT_HOME_YES ?
+ ELEMENTSOF(protect_home_yes_table) :
+ ((protect_home == PROTECT_HOME_READ_ONLY) ?
+ ELEMENTSOF(protect_home_read_only_table) : 0));
+
+ return !!tmp_dir + !!var_tmp_dir +
+ strv_length(read_write_paths) +
+ strv_length(read_only_paths) +
+ strv_length(inaccessible_paths) +
+ private_dev +
+ (protect_sysctl ? ELEMENTSOF(protect_kernel_tunables_table) : 0) +
+ (protect_cgroups ? 1 : 0) +
+ protect_home_cnt + protect_system_cnt;
+}
+
int setup_namespace(
const char* root_directory,
char** read_write_paths,
@@ -366,28 +704,31 @@ int setup_namespace(
const char* tmp_dir,
const char* var_tmp_dir,
bool private_dev,
+ bool protect_sysctl,
+ bool protect_cgroups,
ProtectHome protect_home,
ProtectSystem protect_system,
unsigned long mount_flags) {
BindMount *m, *mounts = NULL;
+ bool make_slave = false;
unsigned n;
int r = 0;
if (mount_flags == 0)
mount_flags = MS_SHARED;
- if (unshare(CLONE_NEWNS) < 0)
- return -errno;
+ n = namespace_calculate_mounts(read_write_paths,
+ read_only_paths,
+ inaccessible_paths,
+ tmp_dir, var_tmp_dir,
+ private_dev, protect_sysctl,
+ protect_cgroups, protect_home,
+ protect_system);
- n = !!tmp_dir + !!var_tmp_dir +
- strv_length(read_write_paths) +
- strv_length(read_only_paths) +
- strv_length(inaccessible_paths) +
- private_dev +
- (protect_home != PROTECT_HOME_NO ? 3 : 0) +
- (protect_system != PROTECT_SYSTEM_NO ? 2 : 0) +
- (protect_system == PROTECT_SYSTEM_FULL ? 1 : 0);
+ /* Set mount slave mode */
+ if (root_directory || n > 0)
+ make_slave = true;
if (n > 0) {
m = mounts = (BindMount *) alloca0(n * sizeof(BindMount));
@@ -421,94 +762,112 @@ int setup_namespace(
m++;
}
- if (protect_home != PROTECT_HOME_NO) {
- const char *home_dir, *run_user_dir, *root_dir;
+ if (protect_sysctl)
+ append_protect_kernel_tunables(&m, root_directory);
- home_dir = prefix_roota(root_directory, "/home");
- home_dir = strjoina("-", home_dir);
- run_user_dir = prefix_roota(root_directory, "/run/user");
- run_user_dir = strjoina("-", run_user_dir);
- root_dir = prefix_roota(root_directory, "/root");
- root_dir = strjoina("-", root_dir);
-
- r = append_mounts(&m, STRV_MAKE(home_dir, run_user_dir, root_dir),
- protect_home == PROTECT_HOME_READ_ONLY ? READONLY : INACCESSIBLE);
- if (r < 0)
- return r;
+ if (protect_cgroups) {
+ m->path = prefix_roota(root_directory, "/sys/fs/cgroup");
+ m->mode = READONLY;
+ m++;
}
- if (protect_system != PROTECT_SYSTEM_NO) {
- const char *usr_dir, *boot_dir, *etc_dir;
-
- usr_dir = prefix_roota(root_directory, "/usr");
- boot_dir = prefix_roota(root_directory, "/boot");
- boot_dir = strjoina("-", boot_dir);
- etc_dir = prefix_roota(root_directory, "/etc");
+ r = append_protect_home(&m, root_directory, protect_home);
+ if (r < 0)
+ return r;
- r = append_mounts(&m, protect_system == PROTECT_SYSTEM_FULL
- ? STRV_MAKE(usr_dir, boot_dir, etc_dir)
- : STRV_MAKE(usr_dir, boot_dir), READONLY);
- if (r < 0)
- return r;
- }
+ r = append_protect_system(&m, root_directory, protect_system);
+ if (r < 0)
+ return r;
assert(mounts + n == m);
+ /* Resolve symlinks manually first, as mount() will always follow them relative to the host's
+ * root. Moreover we want to suppress duplicates based on the resolved paths. This of course is a bit
+ * racy. */
+ r = chase_all_symlinks(root_directory, mounts, &n);
+ if (r < 0)
+ goto finish;
+
qsort(mounts, n, sizeof(BindMount), mount_path_compare);
+
drop_duplicates(mounts, &n);
+ drop_outside_root(root_directory, mounts, &n);
+ drop_inaccessible(mounts, &n);
+ drop_nop(mounts, &n);
+ }
+
+ if (unshare(CLONE_NEWNS) < 0) {
+ r = -errno;
+ goto finish;
}
- if (n > 0 || root_directory) {
+ if (make_slave) {
/* Remount / as SLAVE so that nothing now mounted in the namespace
shows up in the parent */
- if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
- return -errno;
+ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
+ r = -errno;
+ goto finish;
+ }
}
if (root_directory) {
- /* Turn directory into bind mount */
- if (mount(root_directory, root_directory, NULL, MS_BIND|MS_REC, NULL) < 0)
- return -errno;
+ /* Turn directory into bind mount, if it isn't one yet */
+ r = path_is_mount_point(root_directory, AT_SYMLINK_FOLLOW);
+ if (r < 0)
+ goto finish;
+ if (r == 0) {
+ if (mount(root_directory, root_directory, NULL, MS_BIND|MS_REC, NULL) < 0) {
+ r = -errno;
+ goto finish;
+ }
+ }
}
if (n > 0) {
+ char **blacklist;
+ unsigned j;
+
+ /* First round, add in all special mounts we need */
for (m = mounts; m < mounts + n; ++m) {
r = apply_mount(m, tmp_dir, var_tmp_dir);
if (r < 0)
- goto fail;
+ goto finish;
}
+ /* Create a blacklist we can pass to bind_mount_recursive() */
+ blacklist = newa(char*, n+1);
+ for (j = 0; j < n; j++)
+ blacklist[j] = (char*) mounts[j].path;
+ blacklist[j] = NULL;
+
+ /* Second round, flip the ro bits if necessary. */
for (m = mounts; m < mounts + n; ++m) {
- r = make_read_only(m);
+ r = make_read_only(m, blacklist);
if (r < 0)
- goto fail;
+ goto finish;
}
}
if (root_directory) {
/* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */
r = mount_move_root(root_directory);
-
- /* at this point, we cannot rollback */
if (r < 0)
- return r;
+ goto finish;
}
/* Remount / as the desired mode. Not that this will not
* reestablish propagation from our side to the host, since
* what's disconnected is disconnected. */
- if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0)
- /* at this point, we cannot rollback */
- return -errno;
+ if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
+ r = -errno;
+ goto finish;
+ }
- return 0;
+ r = 0;
-fail:
- if (n > 0) {
- for (m = mounts; m < mounts + n; ++m)
- if (m->done)
- (void) umount2(m->path, MNT_DETACH);
- }
+finish:
+ for (m = mounts; m < mounts + n; m++)
+ free(m->chased);
return r;
}
@@ -658,6 +1017,7 @@ static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = {
[PROTECT_SYSTEM_NO] = "no",
[PROTECT_SYSTEM_YES] = "yes",
[PROTECT_SYSTEM_FULL] = "full",
+ [PROTECT_SYSTEM_STRICT] = "strict",
};
DEFINE_STRING_TABLE_LOOKUP(protect_system, ProtectSystem);
diff --git a/src/core/namespace.h b/src/core/namespace.h
index 1aedf5f208..6505bcc499 100644
--- a/src/core/namespace.h
+++ b/src/core/namespace.h
@@ -35,6 +35,7 @@ typedef enum ProtectSystem {
PROTECT_SYSTEM_NO,
PROTECT_SYSTEM_YES,
PROTECT_SYSTEM_FULL,
+ PROTECT_SYSTEM_STRICT,
_PROTECT_SYSTEM_MAX,
_PROTECT_SYSTEM_INVALID = -1
} ProtectSystem;
@@ -46,6 +47,8 @@ int setup_namespace(const char *chroot,
const char *tmp_dir,
const char *var_tmp_dir,
bool private_dev,
+ bool protect_sysctl,
+ bool protect_cgroups,
ProtectHome protect_home,
ProtectSystem protect_system,
unsigned long mount_flags);
diff --git a/src/core/service.c b/src/core/service.c
index 969c62bd83..98edc437a2 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -1256,10 +1256,16 @@ static int service_spawn(
socklen_t salen = sizeof(sa);
r = getpeername(s->socket_fd, &sa.sa, &salen);
- if (r < 0)
- return -errno;
+ if (r < 0) {
+ r = -errno;
+
+ /* ENOTCONN is legitimate if the endpoint disappeared on shutdown.
+ * This connection is over, but the socket unit lives on. */
+ if (r != -ENOTCONN || !IN_SET(s->control_command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST))
+ return r;
+ }
- if (IN_SET(sa.sa.sa_family, AF_INET, AF_INET6)) {
+ if (r == 0 && IN_SET(sa.sa.sa_family, AF_INET, AF_INET6)) {
_cleanup_free_ char *addr = NULL;
char *t;
int port;
@@ -2594,8 +2600,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
assert(s);
assert(pid >= 0);
- if (UNIT(s)->fragment_path ? is_clean_exit(code, status, &s->success_status) :
- is_clean_exit_lsb(code, status, &s->success_status))
+ if (is_clean_exit(code, status, s->type == SERVICE_ONESHOT ? EXIT_CLEAN_COMMAND : EXIT_CLEAN_DAEMON, &s->success_status))
f = SERVICE_SUCCESS;
else if (code == CLD_EXITED)
f = SERVICE_FAILURE_EXIT_CODE;
diff --git a/src/core/socket.c b/src/core/socket.c
index 70d55dd9ed..1b4a1b3dc3 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -1334,14 +1334,9 @@ static int usbffs_select_ep(const struct dirent *d) {
static int usbffs_dispatch_eps(SocketPort *p) {
_cleanup_free_ struct dirent **ent = NULL;
- _cleanup_free_ char *path = NULL;
int r, i, n, k;
- path = dirname_malloc(p->path);
- if (!path)
- return -ENOMEM;
-
- r = scandir(path, &ent, usbffs_select_ep, alphasort);
+ r = scandir(p->path, &ent, usbffs_select_ep, alphasort);
if (r < 0)
return -errno;
@@ -1356,7 +1351,7 @@ static int usbffs_dispatch_eps(SocketPort *p) {
for (i = 0; i < n; ++i) {
_cleanup_free_ char *ep = NULL;
- ep = path_make_absolute(ent[i]->d_name, path);
+ ep = path_make_absolute(ent[i]->d_name, p->path);
if (!ep)
return -ENOMEM;
@@ -2748,7 +2743,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_pid = 0;
- if (is_clean_exit(code, status, NULL))
+ if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
f = SOCKET_SUCCESS;
else if (code == CLD_EXITED)
f = SOCKET_FAILURE_EXIT_CODE;
diff --git a/src/core/swap.c b/src/core/swap.c
index fb222b6858..fee9e7b0e6 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -988,7 +988,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_pid = 0;
- if (is_clean_exit(code, status, NULL))
+ if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
f = SWAP_SUCCESS;
else if (code == CLD_EXITED)
f = SWAP_FAILURE_EXIT_CODE;
diff --git a/src/core/system.conf b/src/core/system.conf
index c6bb050aac..746572b7ff 100644
--- a/src/core/system.conf
+++ b/src/core/system.conf
@@ -21,6 +21,7 @@
#CrashChangeVT=no
#CrashShell=no
#CrashReboot=no
+#CtrlAltDelBurstAction=reboot-force
#CPUAffinity=1 2
#JoinControllers=cpu,cpuacct net_cls,net_prio
#RuntimeWatchdogSec=0
diff --git a/src/core/umount.c b/src/core/umount.c
index c21a2be54e..1e5459ed80 100644
--- a/src/core/umount.c
+++ b/src/core/umount.c
@@ -375,7 +375,7 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e
/* If we are in a container, don't attempt to
read-only mount anything as that brings no real
benefits, but might confuse the host, as we remount
- the superblock here, not the bind mound. */
+ the superblock here, not the bind mount. */
if (detect_container() <= 0) {
_cleanup_free_ char *options = NULL;
/* MS_REMOUNT requires that the data parameter
diff --git a/src/core/unit.c b/src/core/unit.c
index de22f657c6..693f75c928 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -2225,6 +2225,11 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen
return 0;
}
+ if (d == UNIT_BEFORE && other->type == UNIT_DEVICE) {
+ log_unit_warning(u, "Dependency Before=%s ignored (.device units cannot be delayed)", other->id);
+ return 0;
+ }
+
r = set_ensure_allocated(&u->dependencies[d], NULL);
if (r < 0)
return r;
@@ -3377,8 +3382,14 @@ int unit_patch_contexts(Unit *u) {
return -ENOMEM;
}
+ /* If the dynamic user option is on, let's make sure that the unit can't leave its UID/GID
+ * around in the file system or on IPC objects. Hence enforce a strict sandbox. */
+
ec->private_tmp = true;
ec->remove_ipc = true;
+ ec->protect_system = PROTECT_SYSTEM_STRICT;
+ if (ec->protect_home == PROTECT_HOME_NO)
+ ec->protect_home = PROTECT_HOME_READ_ONLY;
}
}
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index be724aed4e..db60d0af7a 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -28,9 +28,10 @@
#include <elfutils/libdwfl.h>
#endif
+#include "sd-daemon.h"
#include "sd-journal.h"
#include "sd-login.h"
-#include "sd-daemon.h"
+#include "sd-messages.h"
#include "acl-util.h"
#include "alloc-util.h"
@@ -93,7 +94,6 @@ typedef enum CoredumpStorage {
COREDUMP_STORAGE_NONE,
COREDUMP_STORAGE_EXTERNAL,
COREDUMP_STORAGE_JOURNAL,
- COREDUMP_STORAGE_BOTH,
_COREDUMP_STORAGE_MAX,
_COREDUMP_STORAGE_INVALID = -1
} CoredumpStorage;
@@ -102,7 +102,6 @@ static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
[COREDUMP_STORAGE_NONE] = "none",
[COREDUMP_STORAGE_EXTERNAL] = "external",
[COREDUMP_STORAGE_JOURNAL] = "journal",
- [COREDUMP_STORAGE_BOTH] = "both",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
@@ -128,13 +127,17 @@ static int parse_config(void) {
{}
};
- return config_parse_many(PKGSYSCONFDIR "/coredump.conf",
+ return config_parse_many_nulstr(PKGSYSCONFDIR "/coredump.conf",
CONF_PATHS_NULSTR("systemd/coredump.conf.d"),
"Coredump\0",
config_item_table_lookup, items,
false, NULL);
}
+static inline uint64_t storage_size_max(void) {
+ return arg_storage == COREDUMP_STORAGE_EXTERNAL ? arg_external_size_max : arg_journal_size_max;
+}
+
static int fix_acl(int fd, uid_t uid) {
#ifdef HAVE_ACL
@@ -247,7 +250,7 @@ static int maybe_remove_external_coredump(const char *filename, uint64_t size) {
/* Returns 1 if might remove, 0 if will not remove, < 0 on error. */
- if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
+ if (arg_storage == COREDUMP_STORAGE_EXTERNAL &&
size <= arg_external_size_max)
return 0;
@@ -331,12 +334,13 @@ static int save_external_coredump(
/* Is coredumping disabled? Then don't bother saving/processing the coredump.
* Anything below PAGE_SIZE cannot give a readable coredump (the kernel uses
* ELF_EXEC_PAGESIZE which is not easily accessible, but is usually the same as PAGE_SIZE. */
- log_info("Core dumping has been disabled for process %s (%s).", context[CONTEXT_PID], context[CONTEXT_COMM]);
+ log_info("Resource limits disable core dumping for process %s (%s).",
+ context[CONTEXT_PID], context[CONTEXT_COMM]);
return -EBADSLT;
}
/* Never store more than the process configured, or than we actually shall keep or process */
- max_size = MIN(rlimit, MAX(arg_process_size_max, arg_external_size_max));
+ max_size = MIN(rlimit, MAX(arg_process_size_max, storage_size_max()));
r = make_filename(context, &fn);
if (r < 0)
@@ -349,19 +353,18 @@ static int save_external_coredump(
return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn);
r = copy_bytes(input_fd, fd, max_size, false);
- if (r == -EFBIG) {
- log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]);
- goto fail;
- } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
- log_error("Not enough disk space for coredump of %s (%s), refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]);
- goto fail;
- } else if (r < 0) {
- log_error_errno(r, "Failed to dump coredump to file: %m");
+ if (r < 0) {
+ log_error_errno(r, "Cannot store coredump of %s (%s): %m", context[CONTEXT_PID], context[CONTEXT_COMM]);
goto fail;
- }
+ } else if (r == 1)
+ log_struct(LOG_INFO,
+ LOG_MESSAGE("Core file was truncated to %zu bytes.", max_size),
+ "SIZE_LIMIT=%zu", max_size,
+ LOG_MESSAGE_ID(SD_MESSAGE_TRUNCATED_CORE),
+ NULL);
if (fstat(fd, &st) < 0) {
- log_error_errno(errno, "Failed to fstat coredump %s: %m", coredump_tmpfile_name(tmp));
+ log_error_errno(errno, "Failed to fstat core file %s: %m", coredump_tmpfile_name(tmp));
goto fail;
}
@@ -372,8 +375,7 @@ static int save_external_coredump(
#if defined(HAVE_XZ) || defined(HAVE_LZ4)
/* If we will remove the coredump anyway, do not compress. */
- if (maybe_remove_external_coredump(NULL, st.st_size) == 0
- && arg_compress) {
+ if (arg_compress && !maybe_remove_external_coredump(NULL, st.st_size)) {
_cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
_cleanup_close_ int fd_compressed = -1;
@@ -678,7 +680,7 @@ static int submit_coredump(
_cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
_cleanup_free_ char *core_message = NULL, *filename = NULL, *coredump_data = NULL;
- uint64_t coredump_size;
+ uint64_t coredump_size = UINT64_MAX;
int r;
assert(context);
@@ -705,7 +707,9 @@ static int submit_coredump(
coredump_filename = strjoina("COREDUMP_FILENAME=", filename);
IOVEC_SET_STRING(iovec[n_iovec++], coredump_filename);
- }
+ } else if (arg_storage == COREDUMP_STORAGE_EXTERNAL)
+ log_info("The core will not be stored: size %zu is greater than %zu (the configured maximum)",
+ coredump_size, arg_external_size_max);
/* Vacuum again, but exclude the coredump we just created */
(void) coredump_vacuum(coredump_node_fd >= 0 ? coredump_node_fd : coredump_fd, arg_keep_free, arg_max_use);
@@ -730,7 +734,9 @@ static int submit_coredump(
log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
else
log_warning_errno(r, "Failed to generate stack trace: %m");
- }
+ } else
+ log_debug("Not generating stack trace: core size %zu is greater than %zu (the configured maximum)",
+ coredump_size, arg_process_size_max);
if (!core_message)
#endif
@@ -740,18 +746,22 @@ log:
IOVEC_SET_STRING(iovec[n_iovec++], core_message);
/* Optionally store the entire coredump in the journal */
- if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
- coredump_size <= arg_journal_size_max) {
- size_t sz = 0;
-
- /* Store the coredump itself in the journal */
-
- r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
- if (r >= 0) {
- iovec[n_iovec].iov_base = coredump_data;
- iovec[n_iovec].iov_len = sz;
- n_iovec++;
- }
+ if (arg_storage == COREDUMP_STORAGE_JOURNAL) {
+ if (coredump_size <= arg_journal_size_max) {
+ size_t sz = 0;
+
+ /* Store the coredump itself in the journal */
+
+ r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
+ if (r >= 0) {
+ iovec[n_iovec].iov_base = coredump_data;
+ iovec[n_iovec].iov_len = sz;
+ n_iovec++;
+ } else
+ log_warning_errno(r, "Failed to attach the core to the journal entry: %m");
+ } else
+ log_info("The core will not be stored: size %zu is greater than %zu (the configured maximum)",
+ coredump_size, arg_journal_size_max);
}
assert(n_iovec <= n_iovec_allocated);
diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c
index bbf8793e57..0e5351e621 100644
--- a/src/coredump/coredumpctl.c
+++ b/src/coredump/coredumpctl.c
@@ -280,11 +280,10 @@ static int retrieve(const void *data,
free(*var);
*var = v;
- return 0;
+ return 1;
}
-static void print_field(FILE* file, sd_journal *j) {
- _cleanup_free_ char *value = NULL;
+static int print_field(FILE* file, sd_journal *j) {
const void *d;
size_t l;
@@ -293,37 +292,59 @@ static void print_field(FILE* file, sd_journal *j) {
assert(arg_field);
- SD_JOURNAL_FOREACH_DATA(j, d, l)
- retrieve(d, l, arg_field, &value);
+ /* A (user-specified) field may appear more than once for a given entry.
+ * We will print all of the occurences.
+ * This is different below for fields that systemd-coredump uses,
+ * because they cannot meaningfully appear more than once.
+ */
+ SD_JOURNAL_FOREACH_DATA(j, d, l) {
+ _cleanup_free_ char *value = NULL;
+ int r;
+
+ r = retrieve(d, l, arg_field, &value);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ fprintf(file, "%s\n", value);
+ }
- if (value)
- fprintf(file, "%s\n", value);
+ return 0;
}
+#define RETRIEVE(d, l, name, arg) \
+ { \
+ int _r = retrieve(d, l, name, &arg); \
+ if (_r < 0) \
+ return _r; \
+ if (_r > 0) \
+ continue; \
+ }
+
static int print_list(FILE* file, sd_journal *j, int had_legend) {
_cleanup_free_ char
*pid = NULL, *uid = NULL, *gid = NULL,
*sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
- *filename = NULL;
+ *filename = NULL, *coredump = NULL;
const void *d;
size_t l;
usec_t t;
char buf[FORMAT_TIMESTAMP_MAX];
int r;
- bool present;
+ const char *present;
assert(file);
assert(j);
SD_JOURNAL_FOREACH_DATA(j, d, l) {
- retrieve(d, l, "COREDUMP_PID", &pid);
- retrieve(d, l, "COREDUMP_UID", &uid);
- retrieve(d, l, "COREDUMP_GID", &gid);
- retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
- retrieve(d, l, "COREDUMP_EXE", &exe);
- retrieve(d, l, "COREDUMP_COMM", &comm);
- retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
- retrieve(d, l, "COREDUMP_FILENAME", &filename);
+ RETRIEVE(d, l, "COREDUMP_PID", pid);
+ RETRIEVE(d, l, "COREDUMP_UID", uid);
+ RETRIEVE(d, l, "COREDUMP_GID", gid);
+ RETRIEVE(d, l, "COREDUMP_SIGNAL", sgnl);
+ RETRIEVE(d, l, "COREDUMP_EXE", exe);
+ RETRIEVE(d, l, "COREDUMP_COMM", comm);
+ RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline);
+ RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
+ RETRIEVE(d, l, "COREDUMP", coredump);
}
if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename) {
@@ -336,7 +357,6 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) {
return log_error_errno(r, "Failed to get realtime timestamp: %m");
format_timestamp(buf, sizeof(buf), t);
- present = filename && access(filename, F_OK) == 0;
if (!had_legend && !arg_no_legend)
fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
@@ -345,16 +365,28 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) {
5, "UID",
5, "GID",
3, "SIG",
- 1, "PRESENT",
+ 8, "COREFILE",
"EXE");
- fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
+ if (filename)
+ if (access(filename, R_OK) == 0)
+ present = "present";
+ else if (errno == ENOENT)
+ present = "missing";
+ else
+ present = "error";
+ else if (coredump)
+ present = "journal";
+ else
+ present = "none";
+
+ fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n",
FORMAT_TIMESTAMP_WIDTH, buf,
6, strna(pid),
5, strna(uid),
5, strna(gid),
3, strna(sgnl),
- 1, present ? "*" : "",
+ 8, present,
strna(exe ?: (comm ?: cmdline)));
return 0;
@@ -367,7 +399,8 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
*unit = NULL, *user_unit = NULL, *session = NULL,
*boot_id = NULL, *machine_id = NULL, *hostname = NULL,
*slice = NULL, *cgroup = NULL, *owner_uid = NULL,
- *message = NULL, *timestamp = NULL, *filename = NULL;
+ *message = NULL, *timestamp = NULL, *filename = NULL,
+ *coredump = NULL;
const void *d;
size_t l;
int r;
@@ -376,25 +409,26 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
assert(j);
SD_JOURNAL_FOREACH_DATA(j, d, l) {
- retrieve(d, l, "COREDUMP_PID", &pid);
- retrieve(d, l, "COREDUMP_UID", &uid);
- retrieve(d, l, "COREDUMP_GID", &gid);
- retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
- retrieve(d, l, "COREDUMP_EXE", &exe);
- retrieve(d, l, "COREDUMP_COMM", &comm);
- retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
- retrieve(d, l, "COREDUMP_UNIT", &unit);
- retrieve(d, l, "COREDUMP_USER_UNIT", &user_unit);
- retrieve(d, l, "COREDUMP_SESSION", &session);
- retrieve(d, l, "COREDUMP_OWNER_UID", &owner_uid);
- retrieve(d, l, "COREDUMP_SLICE", &slice);
- retrieve(d, l, "COREDUMP_CGROUP", &cgroup);
- retrieve(d, l, "COREDUMP_TIMESTAMP", &timestamp);
- retrieve(d, l, "COREDUMP_FILENAME", &filename);
- retrieve(d, l, "_BOOT_ID", &boot_id);
- retrieve(d, l, "_MACHINE_ID", &machine_id);
- retrieve(d, l, "_HOSTNAME", &hostname);
- retrieve(d, l, "MESSAGE", &message);
+ RETRIEVE(d, l, "COREDUMP_PID", pid);
+ RETRIEVE(d, l, "COREDUMP_UID", uid);
+ RETRIEVE(d, l, "COREDUMP_GID", gid);
+ RETRIEVE(d, l, "COREDUMP_SIGNAL", sgnl);
+ RETRIEVE(d, l, "COREDUMP_EXE", exe);
+ RETRIEVE(d, l, "COREDUMP_COMM", comm);
+ RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline);
+ RETRIEVE(d, l, "COREDUMP_UNIT", unit);
+ RETRIEVE(d, l, "COREDUMP_USER_UNIT", user_unit);
+ RETRIEVE(d, l, "COREDUMP_SESSION", session);
+ RETRIEVE(d, l, "COREDUMP_OWNER_UID", owner_uid);
+ RETRIEVE(d, l, "COREDUMP_SLICE", slice);
+ RETRIEVE(d, l, "COREDUMP_CGROUP", cgroup);
+ RETRIEVE(d, l, "COREDUMP_TIMESTAMP", timestamp);
+ RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
+ RETRIEVE(d, l, "COREDUMP", coredump);
+ RETRIEVE(d, l, "_BOOT_ID", boot_id);
+ RETRIEVE(d, l, "_MACHINE_ID", machine_id);
+ RETRIEVE(d, l, "_HOSTNAME", hostname);
+ RETRIEVE(d, l, "MESSAGE", message);
}
if (need_space)
@@ -477,7 +511,7 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
if (unit)
fprintf(file, " Unit: %s\n", unit);
if (user_unit)
- fprintf(file, " User Unit: %s\n", unit);
+ fprintf(file, " User Unit: %s\n", user_unit);
if (slice)
fprintf(file, " Slice: %s\n", slice);
if (session)
@@ -505,8 +539,13 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
if (hostname)
fprintf(file, " Hostname: %s\n", hostname);
- if (filename && access(filename, F_OK) == 0)
- fprintf(file, " Coredump: %s\n", filename);
+ if (filename)
+ fprintf(file, " Storage: %s%s\n", filename,
+ access(filename, R_OK) < 0 ? " (inaccessible)" : "");
+ else if (coredump)
+ fprintf(file, " Storage: journal\n");
+ else
+ fprintf(file, " Storage: none\n");
if (message) {
_cleanup_free_ char *m = NULL;
@@ -534,15 +573,15 @@ static int focus(sd_journal *j) {
return r;
}
-static void print_entry(sd_journal *j, unsigned n_found) {
+static int print_entry(sd_journal *j, unsigned n_found) {
assert(j);
if (arg_action == ACTION_INFO)
- print_info(stdout, j, n_found);
+ return print_info(stdout, j, n_found);
else if (arg_field)
- print_field(stdout, j);
+ return print_field(stdout, j);
else
- print_list(stdout, j, n_found);
+ return print_list(stdout, j, n_found);
}
static int dump_list(sd_journal *j) {
@@ -561,10 +600,13 @@ static int dump_list(sd_journal *j) {
if (r < 0)
return r;
- print_entry(j, 0);
+ return print_entry(j, 0);
} else {
- SD_JOURNAL_FOREACH(j)
- print_entry(j, n_found++);
+ SD_JOURNAL_FOREACH(j) {
+ r = print_entry(j, n_found++);
+ if (r < 0)
+ return r;
+ }
if (!arg_field && n_found <= 0) {
log_notice("No coredumps found.");
@@ -575,122 +617,142 @@ static int dump_list(sd_journal *j) {
return 0;
}
-static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) {
+static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) {
const char *data;
_cleanup_free_ char *filename = NULL;
size_t len;
- int r;
+ int r, fd;
+ _cleanup_close_ int fdt = -1;
+ char *temp = NULL;
- assert((fd >= 0) != !!path);
- assert(!!path == !!unlink_temp);
+ assert(!(file && path)); /* At most one can be specified */
+ assert(!!path == !!unlink_temp); /* Those must be specified together */
- /* Prefer uncompressed file to journal (probably cached) to
- * compressed file (probably uncached). */
+ /* Look for a coredump on disk first. */
r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len);
- if (r < 0 && r != -ENOENT)
- log_warning_errno(r, "Failed to retrieve COREDUMP_FILENAME: %m");
- else if (r == 0)
+ if (r == 0)
retrieve(data, len, "COREDUMP_FILENAME", &filename);
+ else {
+ if (r != -ENOENT)
+ return log_error_errno(r, "Failed to retrieve COREDUMP_FILENAME field: %m");
+ /* Check that we can have a COREDUMP field. We still haven't set a high
+ * data threshold, so we'll get a few kilobytes at most.
+ */
- if (filename && access(filename, R_OK) < 0) {
- log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
- "File %s is not readable: %m", filename);
- filename = mfree(filename);
+ r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
+ if (r == -ENOENT)
+ return log_error_errno(r, "Coredump entry has no core attached (neither internally in the journal nor externally on disk).");
+ if (r < 0)
+ return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
}
- if (filename && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) {
- if (path) {
+ if (filename) {
+ if (access(filename, R_OK) < 0)
+ return log_error_errno(errno, "File \"%s\" is not readable: %m", filename);
+
+ if (path && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) {
*path = filename;
filename = NULL;
+
+ return 0;
}
+ }
- return 0;
- } else {
- _cleanup_close_ int fdt = -1;
- char *temp = NULL;
+ if (path) {
+ const char *vt;
- if (fd < 0) {
- const char *vt;
+ /* Create a temporary file to write the uncompressed core to. */
- r = var_tmp_dir(&vt);
- if (r < 0)
- return log_error_errno(r, "Failed to acquire temporary directory path: %m");
+ r = var_tmp_dir(&vt);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire temporary directory path: %m");
- temp = strjoin(vt, "/coredump-XXXXXX", NULL);
- if (!temp)
- return log_oom();
+ temp = strjoin(vt, "/coredump-XXXXXX", NULL);
+ if (!temp)
+ return log_oom();
- fdt = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
- if (fdt < 0)
- return log_error_errno(fdt, "Failed to create temporary file: %m");
- log_debug("Created temporary file %s", temp);
+ fdt = mkostemp_safe(temp);
+ if (fdt < 0)
+ return log_error_errno(fdt, "Failed to create temporary file: %m");
+ log_debug("Created temporary file %s", temp);
- fd = fdt;
+ fd = fdt;
+ } else {
+ /* If neither path or file are specified, we will write to stdout. Let's now check
+ * if stdout is connected to a tty. We checked that the file exists, or that the
+ * core might be stored in the journal. In this second case, if we found the entry,
+ * in all likelyhood we will be able to access the COREDUMP= field. In either case,
+ * we stop before doing any "real" work, i.e. before starting decompression or
+ * reading from the file or creating temporary files.
+ */
+ if (!file) {
+ if (on_tty())
+ return log_error_errno(ENOTTY, "Refusing to dump core to tty"
+ " (use shell redirection or specify --output).");
+ file = stdout;
}
- r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
- if (r == 0) {
- ssize_t sz;
-
- assert(len >= 9);
- data += 9;
- len -= 9;
-
- sz = write(fdt, data, len);
- if (sz < 0) {
- r = log_error_errno(errno,
- "Failed to write temporary file: %m");
- goto error;
- }
- if (sz != (ssize_t) len) {
- log_error("Short write to temporary file.");
- r = -EIO;
- goto error;
- }
- } else if (filename) {
+ fd = fileno(file);
+ }
+
+ if (filename) {
#if defined(HAVE_XZ) || defined(HAVE_LZ4)
- _cleanup_close_ int fdf;
-
- fdf = open(filename, O_RDONLY | O_CLOEXEC);
- if (fdf < 0) {
- r = log_error_errno(errno,
- "Failed to open %s: %m",
- filename);
- goto error;
- }
+ _cleanup_close_ int fdf;
- r = decompress_stream(filename, fdf, fd, -1);
- if (r < 0) {
- log_error_errno(r, "Failed to decompress %s: %m", filename);
- goto error;
- }
-#else
- log_error("Cannot decompress file. Compiled without compression support.");
- r = -EOPNOTSUPP;
+ fdf = open(filename, O_RDONLY | O_CLOEXEC);
+ if (fdf < 0) {
+ r = log_error_errno(errno, "Failed to open %s: %m", filename);
goto error;
-#endif
- } else {
- if (r == -ENOENT)
- log_error("Cannot retrieve coredump from journal or disk.");
- else
- log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
+ }
+
+ r = decompress_stream(filename, fdf, fd, -1);
+ if (r < 0) {
+ log_error_errno(r, "Failed to decompress %s: %m", filename);
goto error;
}
+#else
+ log_error("Cannot decompress file. Compiled without compression support.");
+ r = -EOPNOTSUPP;
+ goto error;
+#endif
+ } else {
+ ssize_t sz;
+
+ /* We want full data, nothing truncated. */
+ sd_journal_set_data_threshold(j, 0);
+
+ r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
+ if (r < 0)
+ return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
- if (temp) {
- *path = temp;
- *unlink_temp = true;
+ assert(len >= 9);
+ data += 9;
+ len -= 9;
+
+ sz = write(fd, data, len);
+ if (sz < 0) {
+ r = log_error_errno(errno, "Failed to write output: %m");
+ goto error;
+ }
+ if (sz != (ssize_t) len) {
+ log_error("Short write to output.");
+ r = -EIO;
+ goto error;
}
+ }
- return 0;
+ if (temp) {
+ *path = temp;
+ *unlink_temp = true;
+ }
+ return 0;
error:
- if (temp) {
- unlink(temp);
- log_debug("Removed temporary file %s", temp);
- }
- return r;
+ if (temp) {
+ unlink(temp);
+ log_debug("Removed temporary file %s", temp);
}
+ return r;
}
static int dump_core(sd_journal* j) {
@@ -704,17 +766,12 @@ static int dump_core(sd_journal* j) {
print_info(arg_output ? stdout : stderr, j, false);
- if (on_tty() && !arg_output) {
- log_error("Refusing to dump core to tty.");
- return -ENOTTY;
- }
-
- r = save_core(j, arg_output ? fileno(arg_output) : STDOUT_FILENO, NULL, NULL);
+ r = save_core(j, arg_output, NULL, NULL);
if (r < 0)
- return log_error_errno(r, "Coredump retrieval failed: %m");
+ return r;
r = sd_journal_previous(j);
- if (r >= 0)
+ if (r > 0)
log_warning("More than one entry matches, ignoring rest.");
return 0;
@@ -760,9 +817,9 @@ static int run_gdb(sd_journal *j) {
return -ENOENT;
}
- r = save_core(j, -1, &path, &unlink_path);
+ r = save_core(j, NULL, &path, &unlink_path);
if (r < 0)
- return log_error_errno(r, "Failed to retrieve core: %m");
+ return r;
pid = fork();
if (pid < 0) {
@@ -836,9 +893,6 @@ int main(int argc, char *argv[]) {
}
}
- /* We want full data, nothing truncated. */
- sd_journal_set_data_threshold(j, 0);
-
SET_FOREACH(match, matches, it) {
r = sd_journal_add_match(j, match, strlen(match));
if (r != 0) {
diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c
index 4795324667..07c57fb567 100644
--- a/src/hostname/hostnamectl.c
+++ b/src/hostname/hostnamectl.c
@@ -278,7 +278,7 @@ static int set_hostname(sd_bus *bus, char **args, unsigned n) {
/* Now that we set the pretty hostname, let's clean up the parameter and use that as static
* hostname. If the hostname was already valid as static hostname, this will only chop off the trailing
* dot if there is one. If it was not valid, then it will be made fully valid by truncating, dropping
- * multiple dots, and and dropping weird chars. Note that we clean the name up only if we also are
+ * multiple dots, and dropping weird chars. Note that we clean the name up only if we also are
* supposed to set the pretty name. If the pretty name is not being set we assume the user knows what
* he does and pass the name as-is. */
h = strdup(hostname);
diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c
index 080a2cd138..197f905b7d 100644
--- a/src/hostname/hostnamed.c
+++ b/src/hostname/hostnamed.c
@@ -451,7 +451,7 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *
r = context_update_kernel_hostname(c);
if (r < 0) {
log_error_errno(r, "Failed to set host name: %m");
- return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r));
+ return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
}
log_info("Changed host name to '%s'", strna(c->data[PROP_HOSTNAME]));
@@ -512,13 +512,13 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_
r = context_update_kernel_hostname(c);
if (r < 0) {
log_error_errno(r, "Failed to set host name: %m");
- return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r));
+ return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
}
r = context_write_data_static_hostname(c);
if (r < 0) {
log_error_errno(r, "Failed to write static host name: %m");
- return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %s", strerror(-r));
+ return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %m");
}
log_info("Changed static host name to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
@@ -593,7 +593,7 @@ static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_mess
r = context_write_data_machine_info(c);
if (r < 0) {
log_error_errno(r, "Failed to write machine info: %m");
- return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %s", strerror(-r));
+ return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %m");
}
log_info("Changed %s to '%s'",
diff --git a/src/hwdb/hwdb.c b/src/hwdb/hwdb.c
index e12cd93d1c..be4ef5f9e9 100644
--- a/src/hwdb/hwdb.c
+++ b/src/hwdb/hwdb.c
@@ -85,6 +85,8 @@ struct trie_child_entry {
struct trie_value_entry {
size_t key_off;
size_t value_off;
+ size_t filename_off;
+ size_t line_number;
};
static int trie_children_cmp(const void *v1, const void *v2) {
@@ -157,9 +159,11 @@ static int trie_values_cmp(const void *v1, const void *v2, void *arg) {
}
static int trie_node_add_value(struct trie *trie, struct trie_node *node,
- const char *key, const char *value) {
- ssize_t k, v;
+ const char *key, const char *value,
+ const char *filename, size_t line_number) {
+ ssize_t k, v, fn;
struct trie_value_entry *val;
+ int r;
k = strbuf_add_string(trie->strings, key, strlen(key));
if (k < 0)
@@ -167,6 +171,9 @@ static int trie_node_add_value(struct trie *trie, struct trie_node *node,
v = strbuf_add_string(trie->strings, value, strlen(value));
if (v < 0)
return v;
+ fn = strbuf_add_string(trie->strings, filename, strlen(filename));
+ if (v < 0)
+ return v;
if (node->values_count) {
struct trie_value_entry search = {
@@ -176,8 +183,20 @@ static int trie_node_add_value(struct trie *trie, struct trie_node *node,
val = xbsearch_r(&search, node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie);
if (val) {
+ /*
+ * At this point we have 2 identical properties on the same match-string. We
+ * strictly order them by filename+line-number, since we know the dynamic
+ * runtime lookup does the same for multiple matching nodes.
+ */
+ r = strcmp(filename, trie->strings->buf + val->filename_off);
+ if (r < 0 ||
+ (r == 0 && line_number < val->line_number))
+ return 0;
+
/* replace existing earlier key with new value */
val->value_off = v;
+ val->filename_off = fn;
+ val->line_number = line_number;
return 0;
}
}
@@ -190,13 +209,16 @@ static int trie_node_add_value(struct trie *trie, struct trie_node *node,
node->values = val;
node->values[node->values_count].key_off = k;
node->values[node->values_count].value_off = v;
+ node->values[node->values_count].filename_off = fn;
+ node->values[node->values_count].line_number = line_number;
node->values_count++;
qsort_r(node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie);
return 0;
}
static int trie_insert(struct trie *trie, struct trie_node *node, const char *search,
- const char *key, const char *value) {
+ const char *key, const char *value,
+ const char *filename, uint64_t line_number) {
size_t i = 0;
int err = 0;
@@ -250,7 +272,7 @@ static int trie_insert(struct trie *trie, struct trie_node *node, const char *se
c = search[i];
if (c == '\0')
- return trie_node_add_value(trie, node, key, value);
+ return trie_node_add_value(trie, node, key, value, filename, line_number);
child = node_lookup(node, c);
if (!child) {
@@ -274,7 +296,7 @@ static int trie_insert(struct trie *trie, struct trie_node *node, const char *se
return err;
}
- return trie_node_add_value(trie, child, key, value);
+ return trie_node_add_value(trie, child, key, value, filename, line_number);
}
node = child;
@@ -303,7 +325,7 @@ static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node) {
for (i = 0; i < node->children_count; i++)
trie->strings_off += sizeof(struct trie_child_entry_f);
for (i = 0; i < node->values_count; i++)
- trie->strings_off += sizeof(struct trie_value_entry_f);
+ trie->strings_off += sizeof(struct trie_value_entry2_f);
}
static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) {
@@ -349,12 +371,14 @@ static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) {
/* append values array */
for (i = 0; i < node->values_count; i++) {
- struct trie_value_entry_f v = {
+ struct trie_value_entry2_f v = {
.key_off = htole64(trie->strings_off + node->values[i].key_off),
.value_off = htole64(trie->strings_off + node->values[i].value_off),
+ .filename_off = htole64(trie->strings_off + node->values[i].filename_off),
+ .line_number = htole64(node->values[i].line_number),
};
- fwrite(&v, sizeof(struct trie_value_entry_f), 1, trie->f);
+ fwrite(&v, sizeof(struct trie_value_entry2_f), 1, trie->f);
trie->values_count++;
}
@@ -375,7 +399,7 @@ static int trie_store(struct trie *trie, const char *filename) {
.header_size = htole64(sizeof(struct trie_header_f)),
.node_size = htole64(sizeof(struct trie_node_f)),
.child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
- .value_entry_size = htole64(sizeof(struct trie_value_entry_f)),
+ .value_entry_size = htole64(sizeof(struct trie_value_entry2_f)),
};
int err;
@@ -431,14 +455,15 @@ static int trie_store(struct trie *trie, const char *filename) {
log_debug("child pointers: %8"PRIu64" bytes (%8"PRIu64")",
t.children_count * sizeof(struct trie_child_entry_f), t.children_count);
log_debug("value pointers: %8"PRIu64" bytes (%8"PRIu64")",
- t.values_count * sizeof(struct trie_value_entry_f), t.values_count);
+ t.values_count * sizeof(struct trie_value_entry2_f), t.values_count);
log_debug("string store: %8zu bytes", trie->strings->len);
log_debug("strings start: %8"PRIu64, t.strings_off);
return 0;
}
-static int insert_data(struct trie *trie, char **match_list, char *line, const char *filename) {
+static int insert_data(struct trie *trie, char **match_list, char *line,
+ const char *filename, size_t line_number) {
char *value, **entry;
value = strchr(line, '=');
@@ -460,7 +485,7 @@ static int insert_data(struct trie *trie, char **match_list, char *line, const c
}
STRV_FOREACH(entry, match_list)
- trie_insert(trie, trie->root, *entry, line, value);
+ trie_insert(trie, trie->root, *entry, line, value, filename, line_number);
return 0;
}
@@ -474,6 +499,7 @@ static int import_file(struct trie *trie, const char *filename) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
_cleanup_strv_free_ char **match_list = NULL;
+ size_t line_number = 0;
char *match = NULL;
int r;
@@ -485,6 +511,8 @@ static int import_file(struct trie *trie, const char *filename) {
size_t len;
char *pos;
+ ++line_number;
+
/* comment line */
if (line[0] == '#')
continue;
@@ -546,7 +574,7 @@ static int import_file(struct trie *trie, const char *filename) {
/* first data */
state = HW_DATA;
- insert_data(trie, match_list, line, filename);
+ insert_data(trie, match_list, line, filename, line_number);
break;
case HW_DATA:
@@ -564,7 +592,7 @@ static int import_file(struct trie *trie, const char *filename) {
break;
}
- insert_data(trie, match_list, line, filename);
+ insert_data(trie, match_list, line, filename, line_number);
break;
};
}
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index c2b5a5f205..7325adee8f 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -475,20 +475,20 @@ static int request_handler_entries(
r = open_journal(m);
if (r < 0)
- return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r));
+ return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %m");
if (request_parse_accept(m, connection) < 0)
- return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.\n");
+ return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.");
if (request_parse_range(m, connection) < 0)
- return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Range header.\n");
+ return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Range header.");
if (request_parse_arguments(m, connection) < 0)
- return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse URL arguments.\n");
+ return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse URL arguments.");
if (m->discrete) {
if (!m->cursor)
- return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Discrete seeks require a cursor specification.\n");
+ return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Discrete seeks require a cursor specification.");
m->n_entries = 1;
m->n_entries_set = true;
@@ -501,7 +501,7 @@ static int request_handler_entries(
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.\n");
+ return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to seek in journal.");
response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_entries, m, NULL);
if (!response)
@@ -633,14 +633,14 @@ static int request_handler_fields(
r = open_journal(m);
if (r < 0)
- return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r));
+ return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %m");
if (request_parse_accept(m, connection) < 0)
- return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.\n");
+ return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.");
r = sd_journal_query_unique(m->journal, field);
if (r < 0)
- return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to query unique fields.\n");
+ return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to query unique fields.");
response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_fields, m, NULL);
if (!response)
@@ -699,10 +699,10 @@ static int request_handler_file(
fd = open(path, O_RDONLY|O_CLOEXEC);
if (fd < 0)
- return mhd_respondf(connection, MHD_HTTP_NOT_FOUND, "Failed to open file %s: %m\n", path);
+ return mhd_respondf(connection, errno, MHD_HTTP_NOT_FOUND, "Failed to open file %s: %m", path);
if (fstat(fd, &st) < 0)
- return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to stat file: %m\n");
+ return mhd_respondf(connection, errno, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to stat file: %m");
response = MHD_create_response_from_fd_at_offset64(st.st_size, fd, 0);
if (!response)
@@ -766,15 +766,15 @@ static int request_handler_machine(
r = open_journal(m);
if (r < 0)
- return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r));
+ return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %m");
r = sd_id128_get_machine(&mid);
if (r < 0)
- return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine machine ID: %s\n", strerror(-r));
+ return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine machine ID: %m");
r = sd_id128_get_boot(&bid);
if (r < 0)
- return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine boot ID: %s\n", strerror(-r));
+ return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine boot ID: %m");
hostname = gethostname_malloc();
if (!hostname)
@@ -782,11 +782,11 @@ static int request_handler_machine(
r = sd_journal_get_usage(m->journal, &usage);
if (r < 0)
- return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r));
+ return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %m");
r = sd_journal_get_cutoff_realtime_usec(m->journal, &cutoff_from, &cutoff_to);
if (r < 0)
- return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r));
+ return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %m");
if (parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL) == -ENOENT)
(void) parse_env_file("/usr/lib/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL);
@@ -844,8 +844,7 @@ static int request_handler(
assert(method);
if (!streq(method, "GET"))
- return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE,
- "Unsupported method.\n");
+ return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, "Unsupported method.");
if (!*connection_cls) {
@@ -875,7 +874,7 @@ static int request_handler(
if (streq(url, "/machine"))
return request_handler_machine(connection, *connection_cls);
- return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found.\n");
+ return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found.");
}
static void help(void) {
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index 80e2adb100..d86c3681b1 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -127,6 +127,10 @@ static int spawn_child(const char* child, char** argv) {
if (r < 0)
log_warning_errno(errno, "Failed to close write end of pipe: %m");
+ r = fd_nonblock(fd[0], true);
+ if (r < 0)
+ log_warning_errno(errno, "Failed to set child pipe to non-blocking: %m");
+
return fd[0];
}
@@ -524,13 +528,12 @@ static int process_http_upload(
log_warning("Failed to process data for connection %p", connection);
if (r == -E2BIG)
return mhd_respondf(connection,
- MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
- "Entry is too large, maximum is %u bytes.\n",
- DATA_SIZE_MAX);
+ r, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
+ "Entry is too large, maximum is " STRINGIFY(DATA_SIZE_MAX) " bytes.");
else
return mhd_respondf(connection,
- MHD_HTTP_UNPROCESSABLE_ENTITY,
- "Processing failed: %s.", strerror(-r));
+ r, MHD_HTTP_UNPROCESSABLE_ENTITY,
+ "Processing failed: %m.");
}
}
@@ -541,13 +544,14 @@ static int process_http_upload(
remaining = source_non_empty(source);
if (remaining > 0) {
- log_warning("Premature EOFbyte. %zu bytes lost.", remaining);
- return mhd_respondf(connection, MHD_HTTP_EXPECTATION_FAILED,
+ log_warning("Premature EOF byte. %zu bytes lost.", remaining);
+ return mhd_respondf(connection,
+ 0, MHD_HTTP_EXPECTATION_FAILED,
"Premature EOF. %zu bytes of trailing data not processed.",
remaining);
}
- return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
+ return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.");
};
static int request_handler(
@@ -577,19 +581,16 @@ static int request_handler(
*connection_cls);
if (!streq(method, "POST"))
- return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE,
- "Unsupported method.\n");
+ return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, "Unsupported method.");
if (!streq(url, "/upload"))
- return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
- "Not found.\n");
+ return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found.");
header = MHD_lookup_connection_value(connection,
MHD_HEADER_KIND, "Content-Type");
if (!header || !streq(header, "application/vnd.fdo.journal"))
return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
- "Content-Type: application/vnd.fdo.journal"
- " is required.\n");
+ "Content-Type: application/vnd.fdo.journal is required.");
{
const union MHD_ConnectionInfo *ci;
@@ -599,7 +600,7 @@ static int request_handler(
if (!ci) {
log_error("MHD_get_connection_info failed: cannot get remote fd");
return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
- "Cannot check remote address");
+ "Cannot check remote address.");
}
fd = ci->connect_fd;
@@ -614,7 +615,7 @@ static int request_handler(
r = getpeername_pretty(fd, false, &hostname);
if (r < 0)
return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
- "Cannot check remote hostname");
+ "Cannot check remote hostname.");
}
assert(hostname);
@@ -623,8 +624,7 @@ static int request_handler(
if (r == -ENOMEM)
return respond_oom(connection);
else if (r < 0)
- return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
- strerror(-r));
+ return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "%m");
hostname = NULL;
return MHD_YES;
@@ -1198,7 +1198,7 @@ static int parse_config(void) {
{ "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
{}};
- return config_parse_many(PKGSYSCONFDIR "/journal-remote.conf",
+ return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-remote.conf",
CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"),
"Remote\0", config_item_table_lookup, items,
false, NULL);
diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c
index 4647cfdeb3..c0f967ab94 100644
--- a/src/journal-remote/journal-upload.c
+++ b/src/journal-remote/journal-upload.c
@@ -542,7 +542,7 @@ static int parse_config(void) {
{ "Upload", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
{}};
- return config_parse_many(PKGSYSCONFDIR "/journal-upload.conf",
+ return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-upload.conf",
CONF_PATHS_NULSTR("systemd/journal-upload.conf.d"),
"Upload\0", config_item_table_lookup, items,
false, NULL);
diff --git a/src/journal-remote/microhttpd-util.c b/src/journal-remote/microhttpd-util.c
index 2f16b02e9a..cae10203c6 100644
--- a/src/journal-remote/microhttpd-util.c
+++ b/src/journal-remote/microhttpd-util.c
@@ -48,7 +48,7 @@ void microhttpd_logger(void *arg, const char *fmt, va_list ap) {
static int mhd_respond_internal(struct MHD_Connection *connection,
enum MHD_RequestTerminationCode code,
- char *buffer,
+ const char *buffer,
size_t size,
enum MHD_ResponseMemoryMode mode) {
struct MHD_Response *response;
@@ -56,7 +56,7 @@ static int mhd_respond_internal(struct MHD_Connection *connection,
assert(connection);
- response = MHD_create_response_from_buffer(size, buffer, mode);
+ response = MHD_create_response_from_buffer(size, (char*) buffer, mode);
if (!response)
return MHD_NO;
@@ -72,19 +72,25 @@ int mhd_respond(struct MHD_Connection *connection,
enum MHD_RequestTerminationCode code,
const char *message) {
+ const char *fmt;
+
+ fmt = strjoina(message, "\n");
+
return mhd_respond_internal(connection, code,
- (char*) message, strlen(message),
+ fmt, strlen(message) + 1,
MHD_RESPMEM_PERSISTENT);
}
int mhd_respond_oom(struct MHD_Connection *connection) {
- return mhd_respond(connection, MHD_HTTP_SERVICE_UNAVAILABLE, "Out of memory.\n");
+ return mhd_respond(connection, MHD_HTTP_SERVICE_UNAVAILABLE, "Out of memory.");
}
int mhd_respondf(struct MHD_Connection *connection,
+ int error,
enum MHD_RequestTerminationCode code,
const char *format, ...) {
+ const char *fmt;
char *m;
int r;
va_list ap;
@@ -92,8 +98,12 @@ int mhd_respondf(struct MHD_Connection *connection,
assert(connection);
assert(format);
+ if (error < 0)
+ error = -error;
+ errno = -error;
+ fmt = strjoina(format, "\n");
va_start(ap, format);
- r = vasprintf(&m, format, ap);
+ r = vasprintf(&m, fmt, ap);
va_end(ap);
if (r < 0)
diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h
index ea160f212b..af26ab69fe 100644
--- a/src/journal-remote/microhttpd-util.h
+++ b/src/journal-remote/microhttpd-util.h
@@ -39,8 +39,9 @@ void microhttpd_logger(void *arg, const char *fmt, va_list ap) _printf_(2, 0);
#define respond_oom(connection) log_oom(), mhd_respond_oom(connection)
int mhd_respondf(struct MHD_Connection *connection,
+ int error,
unsigned code,
- const char *format, ...) _printf_(3,4);
+ const char *format, ...) _printf_(4,5);
int mhd_respond(struct MHD_Connection *connection,
unsigned code,
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index 7504326bff..349ef74e81 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -333,8 +333,13 @@ JournalFile* journal_file_close(JournalFile *f) {
#ifdef HAVE_GCRYPT
/* Write the final tag */
- if (f->seal && f->writable)
- journal_file_append_tag(f);
+ if (f->seal && f->writable) {
+ int r;
+
+ r = journal_file_append_tag(f);
+ if (r < 0)
+ log_error_errno(r, "Failed to append tag when closing journal: %m");
+ }
#endif
if (f->post_change_timer) {
@@ -1369,6 +1374,12 @@ static int journal_file_append_data(
if (r < 0)
return r;
+#ifdef HAVE_GCRYPT
+ r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p);
+ if (r < 0)
+ return r;
+#endif
+
/* The linking might have altered the window, so let's
* refresh our pointer */
r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
@@ -1393,12 +1404,6 @@ static int journal_file_append_data(
fo->field.head_data_offset = le64toh(p);
}
-#ifdef HAVE_GCRYPT
- r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p);
- if (r < 0)
- return r;
-#endif
-
if (ret)
*ret = o;
diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c
index 4105abfccc..9e4d8a28a5 100644
--- a/src/journal/journal-verify.c
+++ b/src/journal/journal-verify.c
@@ -118,6 +118,11 @@ static void flush_progress(void) {
log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
} while (0)
+#define error_errno(_offset, error, _fmt, ...) do { \
+ flush_progress(); \
+ log_error_errno(error, OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
+ } while (0)
+
static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) {
uint64_t i;
@@ -168,8 +173,8 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
le64toh(o->object.size) - offsetof(Object, data.payload),
&b, &alloc, &b_size, 0);
if (r < 0) {
- error(offset, "%s decompression failed: %s",
- object_compressed_to_string(compression), strerror(-r));
+ error_errno(offset, r, "%s decompression failed: %m",
+ object_compressed_to_string(compression));
return r;
}
@@ -912,7 +917,7 @@ int journal_file_verify(
r = journal_file_object_verify(f, p, o);
if (r < 0) {
- error(p, "Invalid object contents: %s", strerror(-r));
+ error_errno(p, r, "Invalid object contents: %m");
goto fail;
}
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 381e219390..4350925fb0 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -1632,7 +1632,7 @@ static int setup_keys(void) {
n /= arg_interval;
safe_close(fd);
- fd = mkostemp_safe(k, O_WRONLY|O_CLOEXEC);
+ fd = mkostemp_safe(k);
if (fd < 0) {
r = log_error_errno(fd, "Failed to open %s: %m", k);
goto finish;
@@ -1684,9 +1684,9 @@ static int setup_keys(void) {
"at a safe location and should not be saved locally on disk.\n"
"\n\t%s",
ansi_highlight(), ansi_normal(),
+ p,
ansi_highlight(), ansi_normal(),
- ansi_highlight_red(),
- p);
+ ansi_highlight_red());
fflush(stderr);
}
for (i = 0; i < seed_size; i++) {
diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c
index fce799a6ce..f48639cf58 100644
--- a/src/journal/journald-rate-limit.c
+++ b/src/journal/journald-rate-limit.c
@@ -190,7 +190,7 @@ static unsigned burst_modulate(unsigned burst, uint64_t available) {
if (k <= 20)
return burst;
- burst = (burst * (k-20)) / 4;
+ burst = (burst * (k-16)) / 4;
/*
* Example:
@@ -261,7 +261,7 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u
return 1 + s;
}
- if (p->num <= burst) {
+ if (p->num < burst) {
p->num++;
return 1;
}
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 3507910919..a762558e3d 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -1474,7 +1474,7 @@ static int server_parse_proc_cmdline(Server *s) {
static int server_parse_config_file(Server *s) {
assert(s);
- return config_parse_many(PKGSYSCONFDIR "/journald.conf",
+ return config_parse_many_nulstr(PKGSYSCONFDIR "/journald.conf",
CONF_PATHS_NULSTR("systemd/journald.conf.d"),
"Journal\0",
config_item_perf_lookup, journald_gperf_lookup,
@@ -1587,7 +1587,7 @@ static int dispatch_notify_event(sd_event_source *es, int fd, uint32_t revents,
assert(s->notify_fd == fd);
/* The $NOTIFY_SOCKET is writable again, now send exactly one
- * message on it. Either it's the wtachdog event, the initial
+ * message on it. Either it's the watchdog event, the initial
* READY=1 event or an stdout stream event. If there's nothing
* to write anymore, turn our event source off. The next time
* there's something to send it will be turned on again. */
diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c
index 4ad16ee41c..bc092f3c12 100644
--- a/src/journal/journald-stream.c
+++ b/src/journal/journald-stream.c
@@ -393,6 +393,9 @@ static int stdout_stream_scan(StdoutStream *s, bool force_flush) {
p = s->buffer;
remaining = s->length;
+
+ /* XXX: This function does nothing if (s->length == 0) */
+
for (;;) {
char *end;
size_t skip;
diff --git a/src/journal/test-catalog.c b/src/journal/test-catalog.c
index 898c876450..b7d9e7bffa 100644
--- a/src/journal/test-catalog.c
+++ b/src/journal/test-catalog.c
@@ -55,7 +55,7 @@ static Hashmap * test_import(const char* contents, ssize_t size, int code) {
assert_se(h = hashmap_new(&catalog_hash_ops));
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se(write(fd, contents, size) == size);
@@ -182,7 +182,7 @@ static void test_catalog_update(void) {
static char name[] = "/tmp/test-catalog.XXXXXX";
int r;
- r = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ r = mkostemp_safe(name);
assert_se(r >= 0);
database = name;
diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c
index 68c9a4d76c..00e5222a1c 100644
--- a/src/journal/test-compress.c
+++ b/src/journal/test-compress.c
@@ -167,7 +167,7 @@ static void test_compress_stream(int compression,
log_debug("/* test compression */");
- assert_se((dst = mkostemp_safe(pattern, O_RDWR|O_CLOEXEC)) >= 0);
+ assert_se((dst = mkostemp_safe(pattern)) >= 0);
assert_se(compress(src, dst, -1) == 0);
@@ -178,7 +178,7 @@ static void test_compress_stream(int compression,
log_debug("/* test decompression */");
- assert_se((dst2 = mkostemp_safe(pattern2, O_RDWR|O_CLOEXEC)) >= 0);
+ assert_se((dst2 = mkostemp_safe(pattern2)) >= 0);
assert_se(stat(srcfile, &st) == 0);
diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c
index 5e063f4d04..35cae23bf8 100644
--- a/src/journal/test-journal-interleaving.c
+++ b/src/journal/test-journal-interleaving.c
@@ -36,10 +36,9 @@
static bool arg_keep = false;
-noreturn static void log_assert_errno(const char *text, int eno, const char *file, int line, const char *func) {
- log_internal(LOG_CRIT, 0, file, line, func,
- "'%s' failed at %s:%u (%s): %s.",
- text, file, line, func, strerror(eno));
+noreturn static void log_assert_errno(const char *text, int error, const char *file, int line, const char *func) {
+ log_internal(LOG_CRIT, error, file, line, func,
+ "'%s' failed at %s:%u (%s): %m", text, file, line, func);
abort();
}
diff --git a/src/journal/test-mmap-cache.c b/src/journal/test-mmap-cache.c
index 009aabf55e..0ad49aeb5f 100644
--- a/src/journal/test-mmap-cache.c
+++ b/src/journal/test-mmap-cache.c
@@ -36,15 +36,15 @@ int main(int argc, char *argv[]) {
assert_se(m = mmap_cache_new());
- x = mkostemp_safe(px, O_RDWR|O_CLOEXEC);
+ x = mkostemp_safe(px);
assert_se(x >= 0);
unlink(px);
- y = mkostemp_safe(py, O_RDWR|O_CLOEXEC);
+ y = mkostemp_safe(py);
assert_se(y >= 0);
unlink(py);
- z = mkostemp_safe(pz, O_RDWR|O_CLOEXEC);
+ z = mkostemp_safe(pz);
assert_se(z >= 0);
unlink(pz);
diff --git a/src/kernel-install/kernel-install b/src/kernel-install/kernel-install
index c66bcfc092..0c0ee718ac 100644
--- a/src/kernel-install/kernel-install
+++ b/src/kernel-install/kernel-install
@@ -19,6 +19,8 @@
# You should have received a copy of the GNU Lesser General Public License
# along with systemd; If not, see <http://www.gnu.org/licenses/>.
+SKIP_REMAINING=77
+
usage()
{
echo "Usage:"
@@ -123,7 +125,11 @@ case $COMMAND in
for f in "${PLUGINS[@]}"; do
if [[ -x $f ]]; then
"$f" add "$KERNEL_VERSION" "$BOOT_DIR_ABS" "$KERNEL_IMAGE"
- ((ret+=$?))
+ x=$?
+ if [[ $x == $SKIP_REMAINING ]]; then
+ return 0
+ fi
+ ((ret+=$x))
fi
done
;;
@@ -132,7 +138,11 @@ case $COMMAND in
for f in "${PLUGINS[@]}"; do
if [[ -x $f ]]; then
"$f" remove "$KERNEL_VERSION" "$BOOT_DIR_ABS"
- ((ret+=$?))
+ x=$?
+ if [[ $x == $SKIP_REMAINING ]]; then
+ return 0
+ fi
+ ((ret+=$x))
fi
done
diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c
index a69193aa32..9cc28ed564 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.c
+++ b/src/libsystemd/sd-bus/bus-common-errors.c
@@ -52,6 +52,8 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_NO_MACHINE_FOR_PID, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_MACHINE_EXISTS, EEXIST),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRIVATE_NETWORKING, ENOSYS),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_USER_MAPPING, ENXIO),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_GROUP_MAPPING, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SESSION, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SESSION_FOR_PID, ENXIO),
@@ -64,6 +66,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_NOT_TAKEN, EINVAL),
SD_BUS_ERROR_MAP(BUS_ERROR_OPERATION_IN_PROGRESS, EINPROGRESS),
SD_BUS_ERROR_MAP(BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, EOPNOTSUPP),
+ SD_BUS_ERROR_MAP(BUS_ERROR_SESSION_BUSY, EBUSY),
SD_BUS_ERROR_MAP(BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, EALREADY),
@@ -82,6 +85,25 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_LINK_BUSY, EBUSY),
SD_BUS_ERROR_MAP(BUS_ERROR_NETWORK_DOWN, ENETDOWN),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "FORMERR", EBADMSG),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "SERVFAIL", EHOSTDOWN),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "NXDOMAIN", ENXIO),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "NOTIMP", ENOSYS),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "REFUSED", EACCES),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "YXDOMAIN", EEXIST),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "YRRSET", EEXIST),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "NXRRSET", ENOENT),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "NOTAUTH", EACCES),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "NOTZONE", EREMOTE),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADVERS", EBADMSG),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADKEY", EKEYREJECTED),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADTIME", EBADMSG),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADMODE", EBADMSG),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADNAME", EBADMSG),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADALG", EBADMSG),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADTRUNC", EBADMSG),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADCOOKIE", EBADR),
+
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY),
diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c
index eb042e9c81..2c3f591053 100644
--- a/src/libsystemd/sd-bus/busctl.c
+++ b/src/libsystemd/sd-bus/busctl.c
@@ -2003,8 +2003,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (streq_ptr(argv[optind], "monitor") ||
- streq_ptr(argv[optind], "capture")) {
+ if (STRPTR_IN_SET(argv[optind], "monitor", "capture")) {
r = sd_bus_set_monitor(bus, true);
if (r < 0) {
diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c
index 048c0d19e2..fc60830059 100644
--- a/src/libsystemd/sd-bus/test-bus-chat.c
+++ b/src/libsystemd/sd-bus/test-bus-chat.c
@@ -351,7 +351,7 @@ finish:
static int quit_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
bool *x = userdata;
- log_error("Quit callback: %s", strerror(sd_bus_message_get_errno(m)));
+ log_error_errno(sd_bus_message_get_errno(m), "Quit callback: %m");
*x = 1;
return 1;
diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c
index 82237af115..6fdcfa4128 100644
--- a/src/libsystemd/sd-bus/test-bus-creds.c
+++ b/src/libsystemd/sd-bus/test-bus-creds.c
@@ -27,12 +27,17 @@ int main(int argc, char *argv[]) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
int r;
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
if (cg_all_unified() == -ENOMEDIUM) {
- puts("Skipping test: /sys/fs/cgroup/ not available");
+ log_info("Skipping test: /sys/fs/cgroup/ not available");
return EXIT_TEST_SKIP;
}
r = sd_bus_creds_new_from_pid(&creds, 0, _SD_BUS_CREDS_ALL);
+ log_full_errno(r < 0 ? LOG_ERR : LOG_DEBUG, r, "sd_bus_creds_new_from_pid: %m");
assert_se(r >= 0);
bus_creds_dump(creds, NULL, true);
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index 0c4ad966bd..411453e08d 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -36,6 +36,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "set.h"
+#include "socket-util.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
@@ -629,9 +630,9 @@ _public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
if (r < 0)
return r;
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ sk = socket_ioctl_fd();
if (sk < 0)
- return -errno;
+ return sk;
r = ioctl(sk, SIOCGIFNAME, &ifr);
if (r < 0)
diff --git a/src/libsystemd/sd-hwdb/hwdb-internal.h b/src/libsystemd/sd-hwdb/hwdb-internal.h
index 8ffb5e5c74..4fff94ec76 100644
--- a/src/libsystemd/sd-hwdb/hwdb-internal.h
+++ b/src/libsystemd/sd-hwdb/hwdb-internal.h
@@ -70,3 +70,11 @@ struct trie_value_entry_f {
le64_t key_off;
le64_t value_off;
} _packed_;
+
+/* v2 extends v1 with filename and line-number */
+struct trie_value_entry2_f {
+ le64_t key_off;
+ le64_t value_off;
+ le64_t filename_off;
+ le64_t line_number;
+} _packed_;
diff --git a/src/libsystemd/sd-hwdb/sd-hwdb.c b/src/libsystemd/sd-hwdb/sd-hwdb.c
index 062fa97b17..488e101ea8 100644
--- a/src/libsystemd/sd-hwdb/sd-hwdb.c
+++ b/src/libsystemd/sd-hwdb/sd-hwdb.c
@@ -97,15 +97,20 @@ static void linebuf_rem_char(struct linebuf *buf) {
linebuf_rem(buf, 1);
}
-static const struct trie_child_entry_f *trie_node_children(sd_hwdb *hwdb, const struct trie_node_f *node) {
- return (const struct trie_child_entry_f *)((const char *)node + le64toh(hwdb->head->node_size));
+static const struct trie_child_entry_f *trie_node_child(sd_hwdb *hwdb, const struct trie_node_f *node, size_t idx) {
+ const char *base = (const char *)node;
+
+ base += le64toh(hwdb->head->node_size);
+ base += idx * le64toh(hwdb->head->child_entry_size);
+ return (const struct trie_child_entry_f *)base;
}
-static const struct trie_value_entry_f *trie_node_values(sd_hwdb *hwdb, const struct trie_node_f *node) {
+static const struct trie_value_entry_f *trie_node_value(sd_hwdb *hwdb, const struct trie_node_f *node, size_t idx) {
const char *base = (const char *)node;
base += le64toh(hwdb->head->node_size);
base += node->children_count * le64toh(hwdb->head->child_entry_size);
+ base += idx * le64toh(hwdb->head->value_entry_size);
return (const struct trie_value_entry_f *)base;
}
@@ -129,19 +134,20 @@ static const struct trie_node_f *node_lookup_f(sd_hwdb *hwdb, const struct trie_
struct trie_child_entry_f search;
search.c = c;
- child = bsearch(&search, trie_node_children(hwdb, node), node->children_count,
+ child = bsearch(&search, (const char *)node + le64toh(hwdb->head->node_size), node->children_count,
le64toh(hwdb->head->child_entry_size), trie_children_cmp_f);
if (child)
return trie_node_from_off(hwdb, child->child_off);
return NULL;
}
-static int hwdb_add_property(sd_hwdb *hwdb, const char *key, const char *value) {
+static int hwdb_add_property(sd_hwdb *hwdb, const struct trie_value_entry_f *entry) {
+ const char *key;
int r;
assert(hwdb);
- assert(key);
- assert(value);
+
+ key = trie_string(hwdb, entry->key_off);
/*
* Silently ignore all properties which do not start with a
@@ -152,11 +158,25 @@ static int hwdb_add_property(sd_hwdb *hwdb, const char *key, const char *value)
key++;
+ if (le64toh(hwdb->head->value_entry_size) >= sizeof(struct trie_value_entry2_f)) {
+ const struct trie_value_entry2_f *old, *entry2;
+
+ entry2 = (const struct trie_value_entry2_f *)entry;
+ old = ordered_hashmap_get(hwdb->properties, key);
+ if (old) {
+ /* on duplicates, we order by filename and line-number */
+ r = strcmp(trie_string(hwdb, entry2->filename_off), trie_string(hwdb, old->filename_off));
+ if (r < 0 ||
+ (r == 0 && entry2->line_number < old->line_number))
+ return 0;
+ }
+ }
+
r = ordered_hashmap_ensure_allocated(&hwdb->properties, &string_hash_ops);
if (r < 0)
return r;
- r = ordered_hashmap_replace(hwdb->properties, key, (char*)value);
+ r = ordered_hashmap_replace(hwdb->properties, key, (void *)entry);
if (r < 0)
return r;
@@ -177,7 +197,7 @@ static int trie_fnmatch_f(sd_hwdb *hwdb, const struct trie_node_f *node, size_t
linebuf_add(buf, prefix + p, len);
for (i = 0; i < node->children_count; i++) {
- const struct trie_child_entry_f *child = &trie_node_children(hwdb, node)[i];
+ const struct trie_child_entry_f *child = trie_node_child(hwdb, node, i);
linebuf_add_char(buf, child->c);
err = trie_fnmatch_f(hwdb, trie_node_from_off(hwdb, child->child_off), 0, buf, search);
@@ -188,8 +208,7 @@ static int trie_fnmatch_f(sd_hwdb *hwdb, const struct trie_node_f *node, size_t
if (le64toh(node->values_count) && fnmatch(linebuf_get(buf), search, 0) == 0)
for (i = 0; i < le64toh(node->values_count); i++) {
- err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[i].key_off),
- trie_string(hwdb, trie_node_values(hwdb, node)[i].value_off));
+ err = hwdb_add_property(hwdb, trie_node_value(hwdb, node, i));
if (err < 0)
return err;
}
@@ -254,8 +273,7 @@ static int trie_search_f(sd_hwdb *hwdb, const char *search) {
size_t n;
for (n = 0; n < le64toh(node->values_count); n++) {
- err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[n].key_off),
- trie_string(hwdb, trie_node_values(hwdb, node)[n].value_off));
+ err = hwdb_add_property(hwdb, trie_node_value(hwdb, node, n));
if (err < 0)
return err;
}
@@ -410,7 +428,7 @@ static int properties_prepare(sd_hwdb *hwdb, const char *modalias) {
}
_public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **_value) {
- const char *value;
+ const struct trie_value_entry_f *entry;
int r;
assert_return(hwdb, -EINVAL);
@@ -422,11 +440,11 @@ _public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, c
if (r < 0)
return r;
- value = ordered_hashmap_get(hwdb->properties, key);
- if (!value)
+ entry = ordered_hashmap_get(hwdb->properties, key);
+ if (!entry)
return -ENOENT;
- *_value = value;
+ *_value = trie_string(hwdb, entry->value_off);
return 0;
}
@@ -449,8 +467,8 @@ _public_ int sd_hwdb_seek(sd_hwdb *hwdb, const char *modalias) {
}
_public_ int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **value) {
+ const struct trie_value_entry_f *entry;
const void *k;
- void *v;
assert_return(hwdb, -EINVAL);
assert_return(key, -EINVAL);
@@ -459,12 +477,12 @@ _public_ int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **val
if (hwdb->properties_modified)
return -EAGAIN;
- ordered_hashmap_iterate(hwdb->properties, &hwdb->properties_iterator, &v, &k);
+ ordered_hashmap_iterate(hwdb->properties, &hwdb->properties_iterator, (void **)&entry, &k);
if (!k)
return 0;
*key = k;
- *value = v;
+ *value = trie_string(hwdb, entry->value_off);
return 1;
}
diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c
index 566a050432..1c10dd55a7 100644
--- a/src/libsystemd/sd-netlink/netlink-types.c
+++ b/src/libsystemd/sd-netlink/netlink-types.c
@@ -21,6 +21,7 @@
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
+#include <linux/can/netlink.h>
#include <linux/in6.h>
#include <linux/veth.h>
#include <linux/if_bridge.h>
@@ -303,49 +304,48 @@ static const char* const nl_union_link_info_data_table[] = {
[NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = "vti6",
[NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = "ip6tnl",
[NL_UNION_LINK_INFO_DATA_VRF] = "vrf",
+ [NL_UNION_LINK_INFO_DATA_VCAN] = "vcan",
};
DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData);
static const NLTypeSystem rtnl_link_info_data_type_systems[] = {
- [NL_UNION_LINK_INFO_DATA_BOND] = { .count = ELEMENTSOF(rtnl_link_info_data_bond_types),
- .types = rtnl_link_info_data_bond_types },
- [NL_UNION_LINK_INFO_DATA_BRIDGE] = { .count = ELEMENTSOF(rtnl_link_info_data_bridge_types),
- .types = rtnl_link_info_data_bridge_types },
- [NL_UNION_LINK_INFO_DATA_VLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vlan_types),
- .types = rtnl_link_info_data_vlan_types },
- [NL_UNION_LINK_INFO_DATA_VETH] = { .count = ELEMENTSOF(rtnl_link_info_data_veth_types),
- .types = rtnl_link_info_data_veth_types },
- [NL_UNION_LINK_INFO_DATA_MACVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types),
- .types = rtnl_link_info_data_macvlan_types },
- [NL_UNION_LINK_INFO_DATA_MACVTAP] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types),
- .types = rtnl_link_info_data_macvlan_types },
- [NL_UNION_LINK_INFO_DATA_IPVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvlan_types),
- .types = rtnl_link_info_data_ipvlan_types },
- [NL_UNION_LINK_INFO_DATA_VXLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vxlan_types),
- .types = rtnl_link_info_data_vxlan_types },
- [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types),
- .types = rtnl_link_info_data_iptun_types },
- [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
- .types = rtnl_link_info_data_ipgre_types },
+ [NL_UNION_LINK_INFO_DATA_BOND] = { .count = ELEMENTSOF(rtnl_link_info_data_bond_types),
+ .types = rtnl_link_info_data_bond_types },
+ [NL_UNION_LINK_INFO_DATA_BRIDGE] = { .count = ELEMENTSOF(rtnl_link_info_data_bridge_types),
+ .types = rtnl_link_info_data_bridge_types },
+ [NL_UNION_LINK_INFO_DATA_VLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vlan_types),
+ .types = rtnl_link_info_data_vlan_types },
+ [NL_UNION_LINK_INFO_DATA_VETH] = { .count = ELEMENTSOF(rtnl_link_info_data_veth_types),
+ .types = rtnl_link_info_data_veth_types },
+ [NL_UNION_LINK_INFO_DATA_MACVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types),
+ .types = rtnl_link_info_data_macvlan_types },
+ [NL_UNION_LINK_INFO_DATA_MACVTAP] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types),
+ .types = rtnl_link_info_data_macvlan_types },
+ [NL_UNION_LINK_INFO_DATA_IPVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvlan_types),
+ .types = rtnl_link_info_data_ipvlan_types },
+ [NL_UNION_LINK_INFO_DATA_VXLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vxlan_types),
+ .types = rtnl_link_info_data_vxlan_types },
+ [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types),
+ .types = rtnl_link_info_data_iptun_types },
+ [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
+ .types = rtnl_link_info_data_ipgre_types },
[NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
- .types = rtnl_link_info_data_ipgre_types },
- [NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
- .types = rtnl_link_info_data_ipgre_types },
- [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
- .types = rtnl_link_info_data_ipgre_types },
- [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types),
- .types = rtnl_link_info_data_iptun_types },
- [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types),
- .types = rtnl_link_info_data_ipvti_types },
- [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types),
- .types = rtnl_link_info_data_ipvti_types },
- [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ip6tnl_types),
- .types = rtnl_link_info_data_ip6tnl_types },
-
- [NL_UNION_LINK_INFO_DATA_VRF] = { .count = ELEMENTSOF(rtnl_link_info_data_vrf_types),
- .types = rtnl_link_info_data_vrf_types },
-
+ .types = rtnl_link_info_data_ipgre_types },
+ [NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
+ .types = rtnl_link_info_data_ipgre_types },
+ [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
+ .types = rtnl_link_info_data_ipgre_types },
+ [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types),
+ .types = rtnl_link_info_data_iptun_types },
+ [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types),
+ .types = rtnl_link_info_data_ipvti_types },
+ [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types),
+ .types = rtnl_link_info_data_ipvti_types },
+ [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ip6tnl_types),
+ .types = rtnl_link_info_data_ip6tnl_types },
+ [NL_UNION_LINK_INFO_DATA_VRF] = { .count = ELEMENTSOF(rtnl_link_info_data_vrf_types),
+ .types = rtnl_link_info_data_vrf_types },
};
static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = {
diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h
index 7c0e598b26..42e96173de 100644
--- a/src/libsystemd/sd-netlink/netlink-types.h
+++ b/src/libsystemd/sd-netlink/netlink-types.h
@@ -87,6 +87,7 @@ typedef enum NLUnionLinkInfoData {
NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL,
NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL,
NL_UNION_LINK_INFO_DATA_VRF,
+ NL_UNION_LINK_INFO_DATA_VCAN,
_NL_UNION_LINK_INFO_DATA_MAX,
_NL_UNION_LINK_INFO_DATA_INVALID = -1
} NLUnionLinkInfoData;
diff --git a/src/locale/localed.c b/src/locale/localed.c
index 298f176e40..1cb049e74a 100644
--- a/src/locale/localed.c
+++ b/src/locale/localed.c
@@ -334,7 +334,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
r = locale_write_data(c, &settings);
if (r < 0) {
log_error_errno(r, "Failed to set locale: %m");
- return sd_bus_error_set_errnof(error, r, "Failed to set locale: %s", strerror(-r));
+ return sd_bus_error_set_errnof(error, r, "Failed to set locale: %m");
}
locale_update_system_manager(c, sd_bus_message_get_bus(m));
@@ -403,7 +403,7 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro
r = vconsole_write_data(c);
if (r < 0) {
log_error_errno(r, "Failed to set virtual console keymap: %m");
- return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %s", strerror(-r));
+ return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %m");
}
log_info("Changed virtual console keymap to '%s' toggle '%s'",
@@ -592,7 +592,7 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err
r = x11_write_data(c);
if (r < 0) {
log_error_errno(r, "Failed to set X11 keyboard layout: %m");
- return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %s", strerror(-r));
+ return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %m");
}
log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'",
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index b6da237397..ba1bcc2630 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -611,7 +611,7 @@ static int session_stop_scope(Session *s, bool force) {
return 0;
/* Let's always abandon the scope first. This tells systemd that we are not interested anymore, and everything
- * that is left in in the scope is "left-over". Informing systemd about this has the benefit that it will log
+ * that is left in the scope is "left-over". Informing systemd about this has the benefit that it will log
* when killing any processes left after this point. */
r = manager_abandon_scope(s->manager, s->scope, &error);
if (r < 0)
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
index e0e73b034d..2dc5fa7665 100644
--- a/src/login/logind-user.c
+++ b/src/login/logind-user.c
@@ -354,14 +354,12 @@ static int user_mkdir_runtime_path(User *u) {
r = mount("tmpfs", u->runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, t);
if (r < 0) {
- if (errno != EPERM) {
+ if (errno != EPERM && errno != EACCES) {
r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", u->runtime_path);
goto fail;
}
- /* Lacking permissions, maybe
- * CAP_SYS_ADMIN-less container? In this case,
- * just use a normal directory. */
+ log_debug_errno(errno, "Failed to mount per-user tmpfs directory %s, assuming containerized execution, ignoring: %m", u->runtime_path);
r = chmod_and_chown(u->runtime_path, 0700, u->uid, u->gid);
if (r < 0) {
diff --git a/src/login/logind.c b/src/login/logind.c
index bbbf4aef57..a9a06f5e28 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -1002,7 +1002,7 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us
static int manager_parse_config_file(Manager *m) {
assert(m);
- return config_parse_many(PKGSYSCONFDIR "/logind.conf",
+ return config_parse_many_nulstr(PKGSYSCONFDIR "/logind.conf",
CONF_PATHS_NULSTR("systemd/logind.conf.d"),
"Login\0",
config_item_perf_lookup, logind_gperf_lookup,
diff --git a/src/login/systemd-user.m4 b/src/login/systemd-user.m4
index f188a8e548..fe38b24fef 100644
--- a/src/login/systemd-user.m4
+++ b/src/login/systemd-user.m4
@@ -2,11 +2,9 @@
#
# Used by systemd --user instances.
-account include system-auth
-
m4_ifdef(`HAVE_SELINUX',
session required pam_selinux.so close
session required pam_selinux.so nottys open
)m4_dnl
session required pam_loginuid.so
-session include system-auth
+session optional pam_systemd.so
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index 74e1a349bc..7b9be3b425 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -1326,10 +1326,12 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, PT
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
- if (streq(name, ".host"))
- log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
- else
- log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name);
+ if (!arg_quiet) {
+ if (streq(name, ".host"))
+ log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
+ else
+ log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name);
+ }
sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
@@ -1353,17 +1355,54 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, PT
if (last_char != '\n')
fputc('\n', stdout);
- if (machine_died)
- log_info("Machine %s terminated.", name);
- else if (streq(name, ".host"))
- log_info("Connection to the local host terminated.");
- else
- log_info("Connection to machine %s terminated.", name);
+ if (!arg_quiet) {
+ if (machine_died)
+ log_info("Machine %s terminated.", name);
+ else if (streq(name, ".host"))
+ log_info("Connection to the local host terminated.");
+ else
+ log_info("Connection to machine %s terminated.", name);
+ }
sd_event_get_exit_code(event, &ret);
return ret;
}
+static int parse_machine_uid(const char *spec, const char **machine, char **uid) {
+ /*
+ * Whatever is specified in the spec takes priority over global arguments.
+ */
+ char *_uid = NULL;
+ const char *_machine = NULL;
+
+ if (spec) {
+ const char *at;
+
+ at = strchr(spec, '@');
+ if (at) {
+ if (at == spec)
+ /* Do the same as ssh and refuse "@host". */
+ return -EINVAL;
+
+ _machine = at + 1;
+ _uid = strndup(spec, at - spec);
+ if (!_uid)
+ return -ENOMEM;
+ } else
+ _machine = spec;
+ };
+
+ if (arg_uid && !_uid) {
+ _uid = strdup(arg_uid);
+ if (!_uid)
+ return -ENOMEM;
+ }
+
+ *uid = _uid;
+ *machine = isempty(_machine) ? ".host" : _machine;
+ return 0;
+}
+
static int login_machine(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -1439,7 +1478,8 @@ static int shell_machine(int argc, char *argv[], void *userdata) {
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
int master = -1, r;
sd_bus *bus = userdata;
- const char *pty, *match, *machine, *path, *uid = NULL;
+ const char *pty, *match, *machine, *path;
+ _cleanup_free_ char *uid = NULL;
assert(bus);
@@ -1470,22 +1510,9 @@ static int shell_machine(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
- machine = argc < 2 || isempty(argv[1]) ? NULL : argv[1];
-
- if (arg_uid)
- uid = arg_uid;
- else if (machine) {
- const char *at;
-
- at = strchr(machine, '@');
- if (at) {
- uid = strndupa(machine, at - machine);
- machine = at + 1;
- }
- }
-
- if (isempty(machine))
- machine = ".host";
+ r = parse_machine_uid(argc >= 2 ? argv[1] : NULL, &machine, &uid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse machine specification: %m");
match = strjoina("type='signal',"
"sender='org.freedesktop.machine1',"
@@ -2602,6 +2629,7 @@ static int clean_images(int argc, char *argv[], void *userdata) {
}
static int help(int argc, char *argv[], void *userdata) {
+ pager_open(arg_no_pager, false);
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Send control commands to or query the virtual machine and container\n"
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 5e2462cba2..e40f40a263 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -444,7 +444,9 @@ static int method_register_machine_internal(sd_bus_message *message, bool read_n
r = cg_pid_get_unit(m->leader, &m->unit);
if (r < 0) {
- r = sd_bus_error_set_errnof(error, r, "Failed to determine unit of process "PID_FMT" : %s", m->leader, strerror(-r));
+ r = sd_bus_error_set_errnof(error, r,
+ "Failed to determine unit of process "PID_FMT" : %m",
+ m->leader);
goto fail;
}
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index d2df9b7560..6f7f41bf7d 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -122,7 +122,7 @@ static void setup_state_to_color(const char *state, const char **on, const char
} else if (streq_ptr(state, "configuring")) {
*on = ansi_highlight_yellow();
*off = ansi_normal();
- } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) {
+ } else if (STRPTR_IN_SET(state, "failed", "linger")) {
*on = ansi_highlight_red();
*off = ansi_normal();
} else
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 5498e352d8..ed52d5e42d 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -571,6 +571,21 @@ int address_configure(
address->flags |= IFA_F_PERMANENT;
+ if (address->home_address)
+ address->flags |= IFA_F_HOMEADDRESS;
+
+ if (address->duplicate_address_detection)
+ address->flags |= IFA_F_NODAD;
+
+ if (address->manage_temporary_address)
+ address->flags |= IFA_F_MANAGETEMPADDR;
+
+ if (address->prefix_route)
+ address->flags |= IFA_F_NOPREFIXROUTE;
+
+ if (address->autojoin)
+ address->flags |= IFA_F_MCAUTOJOIN;
+
r = sd_rtnl_message_addr_set_flags(req, (address->flags & 0xff));
if (r < 0)
return log_error_errno(r, "Could not set flags: %m");
@@ -856,6 +871,50 @@ int config_parse_lifetime(const char *unit,
return 0;
}
+int config_parse_address_flags(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Network *network = userdata;
+ _cleanup_address_free_ Address *n = NULL;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = address_new_static(network, section_line, &n);
+ if (r < 0)
+ return r;
+
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (streq(lvalue, "HomeAddress"))
+ n->home_address = r;
+ else if (streq(lvalue, "DuplicateAddressDetection"))
+ n->duplicate_address_detection = r;
+ else if (streq(lvalue, "ManageTemporaryAddress"))
+ n->manage_temporary_address = r;
+ else if (streq(lvalue, "PrefixRoute"))
+ n->prefix_route = r;
+ else if (streq(lvalue, "AutoJoin"))
+ n->autojoin = r;
+
+ return 0;
+}
+
bool address_is_ready(const Address *a) {
assert(a);
diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h
index 03c4bea7c6..bc3b4fc7f3 100644
--- a/src/network/networkd-address.h
+++ b/src/network/networkd-address.h
@@ -53,6 +53,11 @@ struct Address {
union in_addr_union in_addr_peer;
bool ip_masquerade_done:1;
+ bool duplicate_address_detection;
+ bool manage_temporary_address;
+ bool home_address;
+ bool prefix_route;
+ bool autojoin;
LIST_FIELDS(Address, addresses);
};
@@ -77,3 +82,4 @@ int config_parse_address(const char *unit, const char *filename, unsigned line,
int config_parse_broadcast(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_address_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/networkd-conf.c b/src/network/networkd-conf.c
index c03e2b2ebf..49bb8c18f6 100644
--- a/src/network/networkd-conf.c
+++ b/src/network/networkd-conf.c
@@ -29,7 +29,7 @@
int manager_parse_config_file(Manager *m) {
assert(m);
- return config_parse_many(PKGSYSCONFDIR "/networkd.conf",
+ return config_parse_many_nulstr(PKGSYSCONFDIR "/networkd.conf",
CONF_PATHS_NULSTR("systemd/networkd.conf.d"),
"DHCP\0",
config_item_perf_lookup, networkd_gperf_lookup,
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 12fb8e3fce..76d3d132ea 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -95,6 +95,7 @@ static int link_set_dhcp_routes(Link *link) {
route_gw->scope = RT_SCOPE_LINK;
route_gw->protocol = RTPROT_DHCP;
route_gw->priority = link->network->dhcp_route_metric;
+ route_gw->table = link->network->dhcp_route_table;
r = route_configure(route_gw, link, dhcp4_route_handler);
if (r < 0)
@@ -106,6 +107,7 @@ static int link_set_dhcp_routes(Link *link) {
route->gw.in = gateway;
route->prefsrc.in = address;
route->priority = link->network->dhcp_route_metric;
+ route->table = link->network->dhcp_route_table;
r = route_configure(route, link, dhcp4_route_handler);
if (r < 0) {
@@ -136,6 +138,7 @@ static int link_set_dhcp_routes(Link *link) {
assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0);
assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
route->priority = link->network->dhcp_route_metric;
+ route->table = link->network->dhcp_route_table;
r = route_configure(route, link, dhcp4_route_handler);
if (r < 0)
diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c
index be8aebee2d..ed5a47589e 100644
--- a/src/network/networkd-fdb.c
+++ b/src/network/networkd-fdb.c
@@ -107,20 +107,28 @@ int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
sd_netlink *rtnl;
int r;
+ uint8_t flags;
+ Bridge *bridge;
assert(link);
+ assert(link->network);
assert(link->manager);
assert(fdb_entry);
rtnl = link->manager->rtnl;
+ bridge = BRIDGE(link->network->bridge);
/* create new RTM message */
r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, PF_BRIDGE);
if (r < 0)
return rtnl_log_create_error(r);
- /* only NTF_SELF flag supported. */
- r = sd_rtnl_message_neigh_set_flags(req, NTF_SELF);
+ if (bridge)
+ flags = NTF_MASTER;
+ else
+ flags = NTF_SELF;
+
+ r = sd_rtnl_message_neigh_set_flags(req, flags);
if (r < 0)
return rtnl_log_create_error(r);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index aab40a0eb1..d9e060b6cf 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -942,6 +942,19 @@ static int link_push_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) {
return sd_dhcp_server_set_ntp(s, addresses, n_addresses);
}
+static int link_set_bridge_fdb(Link *link) {
+ FdbEntry *fdb_entry;
+ int r;
+
+ LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) {
+ r = fdb_entry_configure(link, fdb_entry);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m");
+ }
+
+ return 0;
+}
+
static int link_enter_set_addresses(Link *link) {
Address *ad;
int r;
@@ -950,6 +963,10 @@ static int link_enter_set_addresses(Link *link) {
assert(link->network);
assert(link->state != _LINK_STATE_INVALID);
+ r = link_set_bridge_fdb(link);
+ if (r < 0)
+ return r;
+
link_set_state(link, LINK_STATE_SETTING_ADDRESSES);
LIST_FOREACH(addresses, ad, link->network->static_addresses) {
@@ -1119,21 +1136,6 @@ static int link_set_bridge_vlan(Link *link) {
return r;
}
-static int link_set_bridge_fdb(Link *link) {
- FdbEntry *fdb_entry;
- int r = 0;
-
- LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) {
- r = fdb_entry_configure(link, fdb_entry);
- if (r < 0) {
- log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m");
- break;
- }
- }
-
- return r;
-}
-
static int link_set_proxy_arp(Link *link) {
const char *p = NULL;
int r;
@@ -1787,6 +1789,31 @@ static int link_down(Link *link) {
return 0;
}
+static int link_up_can(Link *link) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ int r;
+
+ assert(link);
+
+ log_link_debug(link, "Bringing CAN link up");
+
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
+
+ r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set link flags: %m");
+
+ r = sd_netlink_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+ link_ref(link);
+
+ return 0;
+}
+
static int link_handle_bound_to_list(Link *link) {
Link *l;
Iterator i;
@@ -2431,6 +2458,19 @@ static int link_configure(Link *link) {
assert(link->network);
assert(link->state == LINK_STATE_PENDING);
+ if (streq_ptr(link->kind, "vcan")) {
+
+ if (!(link->flags & IFF_UP)) {
+ r = link_up_can(link);
+ if (r < 0) {
+ link_enter_failed(link);
+ return r;
+ }
+ }
+
+ return 0;
+ }
+
/* Drop foreign config, but ignore loopback or critical devices.
* We do not want to remove loopback address or addresses used for root NFS. */
if (!(link->flags & IFF_LOOPBACK) && !(link->network->dhcp_critical)) {
@@ -2439,10 +2479,6 @@ static int link_configure(Link *link) {
return r;
}
- r = link_set_bridge_fdb(link);
- if (r < 0)
- return r;
-
r = link_set_proxy_arp(link);
if (r < 0)
return r;
@@ -2957,7 +2993,8 @@ static int link_carrier_lost(Link *link) {
if (r < 0)
return r;
- if (link->state != LINK_STATE_UNMANAGED) {
+ if (!IN_SET(link->state, LINK_STATE_UNMANAGED, LINK_STATE_PENDING)) {
+ log_link_debug(link, "State is %s, dropping config", link_state_to_string(link->state));
r = link_drop_foreign_config(link);
if (r < 0)
return r;
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
index d9c18b32a5..c2b7970623 100644
--- a/src/network/networkd-ndisc.c
+++ b/src/network/networkd-ndisc.c
@@ -94,7 +94,7 @@ static void ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
}
route->family = AF_INET6;
- route->table = RT_TABLE_MAIN;
+ route->table = link->network->ipv6_accept_ra_route_table;
route->protocol = RTPROT_RA;
route->pref = preference;
route->gw.in6 = gateway;
@@ -214,7 +214,7 @@ static void ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt)
}
route->family = AF_INET6;
- route->table = RT_TABLE_MAIN;
+ route->table = link->network->ipv6_accept_ra_route_table;
route->protocol = RTPROT_RA;
route->flags = RTM_F_PREFIX;
route->dst_prefixlen = prefixlen;
@@ -285,7 +285,7 @@ static void ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
}
route->family = AF_INET6;
- route->table = RT_TABLE_MAIN;
+ route->table = link->network->ipv6_accept_ra_route_table;
route->protocol = RTPROT_RA;
route->pref = preference;
route->gw.in6 = gateway;
diff --git a/src/network/networkd-netdev-bridge.c b/src/network/networkd-netdev-bridge.c
index bdbea7d770..002ad94210 100644
--- a/src/network/networkd-netdev-bridge.c
+++ b/src/network/networkd-netdev-bridge.c
@@ -39,7 +39,7 @@ static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, vo
return 1;
}
- log_netdev_debug(netdev, "Bridge parametres set success");
+ log_netdev_debug(netdev, "Bridge parameters set success");
return 1;
}
diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf
index 6dbb627f15..323eaa8032 100644
--- a/src/network/networkd-netdev-gperf.gperf
+++ b/src/network/networkd-netdev-gperf.gperf
@@ -63,8 +63,13 @@ VXLAN.L2MissNotification, config_parse_bool, 0,
VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss)
VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit)
VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
+VXLAN.UDPChecksum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
+VXLAN.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
+VXLAN.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
+VXLAN.RemoteChecksumTx, config_parse_bool, 0, offsetof(VxLan, remote_csum_tx)
+VXLAN.RemoteChecksumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx)
VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing)
VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy)
VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb)
diff --git a/src/network/networkd-netdev-vcan.c b/src/network/networkd-netdev-vcan.c
new file mode 100644
index 0000000000..bfce6e1962
--- /dev/null
+++ b/src/network/networkd-netdev-vcan.c
@@ -0,0 +1,25 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Susant Sahani
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "networkd-netdev-vcan.h"
+
+const NetDevVTable vcan_vtable = {
+ .object_size = sizeof(VCan),
+ .create_type = NETDEV_CREATE_INDEPENDENT,
+};
diff --git a/src/network/networkd-netdev-vcan.h b/src/network/networkd-netdev-vcan.h
new file mode 100644
index 0000000000..6ba47fd70e
--- /dev/null
+++ b/src/network/networkd-netdev-vcan.h
@@ -0,0 +1,34 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Susant Sahani
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct VCan VCan;
+
+#include <linux/can/netlink.h>
+
+#include "networkd-netdev.h"
+
+struct VCan {
+ NetDev meta;
+};
+
+DEFINE_NETDEV_CAST(VCAN, VCan);
+
+extern const NetDevVTable vcan_vtable;
diff --git a/src/network/networkd-netdev-vxlan.c b/src/network/networkd-netdev-vxlan.c
index 724f9861be..706e52b698 100644
--- a/src/network/networkd-netdev-vxlan.c
+++ b/src/network/networkd-netdev-vxlan.c
@@ -112,6 +112,14 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_UDP_ZERO_CSUM6_RX attribute: %m");
+ r = sd_netlink_message_append_u8(m, IFLA_VXLAN_REMCSUM_TX, v->remote_csum_tx);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_REMCSUM_TX attribute: %m");
+
+ r = sd_netlink_message_append_u8(m, IFLA_VXLAN_REMCSUM_RX, v->remote_csum_rx);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_REMCSUM_RX attribute: %m");
+
r = sd_netlink_message_append_u16(m, IFLA_VXLAN_PORT, htobe16(v->dest_port));
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_PORT attribute: %m");
diff --git a/src/network/networkd-netdev-vxlan.h b/src/network/networkd-netdev-vxlan.h
index 4614c66fd1..3906820afb 100644
--- a/src/network/networkd-netdev-vxlan.h
+++ b/src/network/networkd-netdev-vxlan.h
@@ -50,6 +50,8 @@ struct VxLan {
bool udpcsum;
bool udp6zerocsumtx;
bool udp6zerocsumrx;
+ bool remote_csum_tx;
+ bool remote_csum_rx;
bool group_policy;
struct ifla_vxlan_port_range port_range;
diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c
index e7edc366af..a210ba1242 100644
--- a/src/network/networkd-netdev.c
+++ b/src/network/networkd-netdev.c
@@ -34,7 +34,6 @@
#include "string-util.h"
const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
-
[NETDEV_KIND_BRIDGE] = &bridge_vtable,
[NETDEV_KIND_BOND] = &bond_vtable,
[NETDEV_KIND_VLAN] = &vlan_vtable,
@@ -56,7 +55,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_TAP] = &tap_vtable,
[NETDEV_KIND_IP6TNL] = &ip6tnl_vtable,
[NETDEV_KIND_VRF] = &vrf_vtable,
-
+ [NETDEV_KIND_VCAN] = &vcan_vtable,
};
static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
@@ -81,7 +80,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_TAP] = "tap",
[NETDEV_KIND_IP6TNL] = "ip6tnl",
[NETDEV_KIND_VRF] = "vrf",
-
+ [NETDEV_KIND_VCAN] = "vcan",
};
DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
@@ -516,7 +515,7 @@ static int netdev_create(NetDev *netdev, Link *link,
r = sd_netlink_message_close_container(m);
if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
r = sd_netlink_message_close_container(m);
if (r < 0)
@@ -577,6 +576,7 @@ static int netdev_load_one(Manager *manager, const char *filename) {
_cleanup_netdev_unref_ NetDev *netdev = NULL;
_cleanup_free_ NetDev *netdev_raw = NULL;
_cleanup_fclose_ FILE *file = NULL;
+ const char *dropin_dirname;
int r;
assert(manager);
@@ -600,11 +600,12 @@ static int netdev_load_one(Manager *manager, const char *filename) {
return log_oom();
netdev_raw->kind = _NETDEV_KIND_INVALID;
+ dropin_dirname = strjoina(basename(filename), ".d");
- r = config_parse(NULL, filename, file,
- "Match\0NetDev\0",
- config_item_perf_lookup, network_netdev_gperf_lookup,
- true, false, true, netdev_raw);
+ r = config_parse_many(filename, network_dirs, dropin_dirname,
+ "Match\0NetDev\0",
+ config_item_perf_lookup, network_netdev_gperf_lookup,
+ true, netdev_raw);
if (r < 0)
return r;
@@ -620,7 +621,7 @@ static int netdev_load_one(Manager *manager, const char *filename) {
return 0;
if (netdev_raw->kind == _NETDEV_KIND_INVALID) {
- log_warning("NetDev with invalid Kind configured in %s. Ignoring", filename);
+ log_warning("NetDev has no Kind configured in %s. Ignoring", filename);
return 0;
}
diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h
index 09863e72b4..31b55e2791 100644
--- a/src/network/networkd-netdev.h
+++ b/src/network/networkd-netdev.h
@@ -56,6 +56,7 @@ typedef enum NetDevKind {
NETDEV_KIND_TUN,
NETDEV_KIND_TAP,
NETDEV_KIND_VRF,
+ NETDEV_KIND_VCAN,
_NETDEV_KIND_MAX,
_NETDEV_KIND_INVALID = -1
} NetDevKind;
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index b96f0b7210..5587961b9f 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -70,6 +70,11 @@ Address.Peer, config_parse_address,
Address.Broadcast, config_parse_broadcast, 0, 0
Address.Label, config_parse_label, 0, 0
Address.PreferredLifetime, config_parse_lifetime, 0, 0
+Address.HomeAddress, config_parse_address_flags, 0, 0
+Address.DuplicateAddressDetection, config_parse_address_flags, 0, 0
+Address.ManageTemporaryAddress, config_parse_address_flags, 0, 0
+Address.PrefixRoute, config_parse_address_flags, 0, 0
+Address.AutoJoin, config_parse_address_flags, 0, 0
Route.Gateway, config_parse_gateway, 0, 0
Route.Destination, config_parse_destination, 0, 0
Route.Source, config_parse_destination, 0, 0
@@ -92,10 +97,12 @@ DHCP.VendorClassIdentifier, config_parse_string,
DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid.type)
DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid)
DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
+DHCP.RouteTable, config_parse_dhcp_route_table, 0, offsetof(Network, dhcp_route_table)
DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid)
IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains)
+IPv6AcceptRA.RouteTable, config_parse_dhcp_route_table, 0, offsetof(Network, ipv6_accept_ra_route_table)
DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec)
DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec)
DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 49faba5b12..584cb96979 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -40,6 +40,7 @@ static int network_load_one(Manager *manager, const char *filename) {
_cleanup_network_free_ Network *network = NULL;
_cleanup_fclose_ FILE *file = NULL;
char *d;
+ const char *dropin_dirname;
Route *route;
Address *address;
int r;
@@ -110,6 +111,7 @@ static int network_load_one(Manager *manager, const char *filename) {
network->dhcp_send_hostname = true;
network->dhcp_route_metric = DHCP_ROUTE_METRIC;
network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID;
+ network->dhcp_route_table = RT_TABLE_MAIN;
network->dhcp_server_emit_dns = true;
network->dhcp_server_emit_ntp = true;
@@ -136,22 +138,25 @@ static int network_load_one(Manager *manager, const char *filename) {
network->proxy_arp = -1;
network->arp = -1;
network->ipv6_accept_ra_use_dns = true;
-
- r = config_parse(NULL, filename, file,
- "Match\0"
- "Link\0"
- "Network\0"
- "Address\0"
- "Route\0"
- "DHCP\0"
- "DHCPv4\0" /* compat */
- "DHCPServer\0"
- "IPv6AcceptRA\0"
- "Bridge\0"
- "BridgeFDB\0"
- "BridgeVLAN\0",
- config_item_perf_lookup, network_network_gperf_lookup,
- false, false, true, network);
+ network->ipv6_accept_ra_route_table = RT_TABLE_MAIN;
+
+ dropin_dirname = strjoina(network->name, ".network.d");
+
+ r = config_parse_many(filename, network_dirs, dropin_dirname,
+ "Match\0"
+ "Link\0"
+ "Network\0"
+ "Address\0"
+ "Route\0"
+ "DHCP\0"
+ "DHCPv4\0" /* compat */
+ "DHCPServer\0"
+ "IPv6AcceptRA\0"
+ "Bridge\0"
+ "BridgeFDB\0"
+ "BridgeVLAN\0",
+ config_item_perf_lookup, network_network_gperf_lookup,
+ false, network);
if (r < 0)
return r;
@@ -479,9 +484,10 @@ int config_parse_netdev(const char *unit,
case NETDEV_KIND_MACVTAP:
case NETDEV_KIND_IPVLAN:
case NETDEV_KIND_VXLAN:
+ case NETDEV_KIND_VCAN:
r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Can not add VLAN '%s' to network: %m", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Can not add NetDev '%s' to network: %m", rvalue);
return 0;
}
@@ -1029,6 +1035,36 @@ int config_parse_dnssec_negative_trust_anchors(
return 0;
}
+int config_parse_dhcp_route_table(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ uint32_t rt;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = safe_atou32(rvalue, &rt);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Unable to read RouteTable, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ *((uint32_t *)data) = rt;
+
+ return 0;
+}
+
DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse DHCP use domains setting");
static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 5460eb4d1c..ef4b499ab9 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -123,6 +123,7 @@ struct Network {
bool dhcp_use_routes;
bool dhcp_use_timezone;
unsigned dhcp_route_metric;
+ uint32_t dhcp_route_table;
/* DHCP Server Support */
bool dhcp_server;
@@ -166,6 +167,7 @@ struct Network {
bool ipv6_accept_ra_use_dns;
DHCPUseDomains ipv6_accept_ra_use_domains;
+ uint32_t ipv6_accept_ra_route_table;
union in_addr_union ipv6_token;
IPv6PrivacyExtensions ipv6_privacy_extensions;
@@ -228,6 +230,7 @@ int config_parse_dhcp_server_ntp(const char *unit, const char *filename, unsigne
int config_parse_dnssec_negative_trust_anchors(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_dhcp_use_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_lldp_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_dhcp_route_table(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);
/* Legacy IPv4LL support */
int config_parse_ipv4ll(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/networkd.h b/src/network/networkd.h
index c4bd712147..cb1b73145e 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -43,6 +43,7 @@
#include "networkd-netdev-vlan.h"
#include "networkd-netdev-vrf.h"
#include "networkd-netdev-vxlan.h"
+#include "networkd-netdev-vcan.h"
#include "networkd-network.h"
#include "networkd-util.h"
diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c
index 295b75341f..25d38aa742 100644
--- a/src/nspawn/nspawn-mount.c
+++ b/src/nspawn/nspawn-mount.c
@@ -314,19 +314,21 @@ int mount_all(const char *dest,
} MountPoint;
static const MountPoint mount_table[] = {
- { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true, true, false },
- { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, true, true, false }, /* Bind mount first ...*/
- { "/proc/sys/net", "/proc/sys/net", NULL, NULL, MS_BIND, true, true, true }, /* (except for this) */
- { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, true, true, false }, /* ... then, make it r/o */
- { "tmpfs", "/sys", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV, true, false, true },
- { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, true, false, false },
- { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true, false, false },
- { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false, false },
- { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false, false },
- { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, true, false, false },
+ { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true, true, false },
+ { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, true, true, false }, /* Bind mount first ...*/
+ { "/proc/sys/net", "/proc/sys/net", NULL, NULL, MS_BIND, true, true, true }, /* (except for this) */
+ { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, true, true, false }, /* ... then, make it r/o */
+ { "/proc/sysrq-trigger", "/proc/sysrq-trigger", NULL, NULL, MS_BIND, false, true, false }, /* Bind mount first ...*/
+ { NULL, "/proc/sysrq-trigger", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, false, true, false }, /* ... then, make it r/o */
+ { "tmpfs", "/sys", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV, true, false, true },
+ { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, true, false, false },
+ { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true, false, false },
+ { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false, false },
+ { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false, false },
+ { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, true, false, false },
#ifdef HAVE_SELINUX
- { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, false, false, false }, /* Bind mount first */
- { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, false, false, false }, /* Then, make it r/o */
+ { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, false, false, false }, /* Bind mount first */
+ { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, false, false, false }, /* Then, make it r/o */
#endif
};
@@ -356,7 +358,7 @@ int mount_all(const char *dest,
continue;
r = mkdir_p(where, 0755);
- if (r < 0) {
+ if (r < 0 && r != -EEXIST) {
if (mount_table[k].fatal)
return log_error_errno(r, "Failed to create directory %s: %m", where);
@@ -476,7 +478,7 @@ static int mount_bind(const char *dest, CustomMount *m) {
return log_error_errno(errno, "mount(%s) failed: %m", where);
if (m->read_only) {
- r = bind_remount_recursive(where, true);
+ r = bind_remount_recursive(where, true, NULL);
if (r < 0)
return log_error_errno(r, "Read-only bind mount failed: %m");
}
@@ -990,7 +992,7 @@ int setup_volatile_state(
/* --volatile=state means we simply overmount /var
with a tmpfs, and the rest read-only. */
- r = bind_remount_recursive(directory, true);
+ r = bind_remount_recursive(directory, true, NULL);
if (r < 0)
return log_error_errno(r, "Failed to remount %s read-only: %m", directory);
@@ -1065,7 +1067,7 @@ int setup_volatile(
bind_mounted = true;
- r = bind_remount_recursive(t, true);
+ r = bind_remount_recursive(t, true, NULL);
if (r < 0) {
log_error_errno(r, "Failed to remount %s read-only: %m", t);
goto fail;
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 6d0420965a..a173d171e1 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -316,17 +316,10 @@ static int custom_mounts_prepare(void) {
return 0;
}
-static int detect_unified_cgroup_hierarchy(void) {
+static int detect_unified_cgroup_hierarchy(const char *directory) {
const char *e;
int r, all_unified, systemd_unified;
- all_unified = cg_all_unified();
- systemd_unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
-
- if (all_unified < 0 || systemd_unified < 0)
- return log_error_errno(all_unified < 0 ? all_unified : systemd_unified,
- "Failed to determine whether the unified cgroups hierarchy is used: %m");
-
/* Allow the user to control whether the unified hierarchy is used */
e = getenv("UNIFIED_CGROUP_HIERARCHY");
if (e) {
@@ -341,12 +334,34 @@ static int detect_unified_cgroup_hierarchy(void) {
return 0;
}
+ all_unified = cg_all_unified();
+ systemd_unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
+
+ if (all_unified < 0 || systemd_unified < 0)
+ return log_error_errno(all_unified < 0 ? all_unified : systemd_unified,
+ "Failed to determine whether the unified cgroups hierarchy is used: %m");
+
/* Otherwise inherit the default from the host system */
- if (all_unified > 0)
- arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL;
- else if (systemd_unified > 0)
- arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_SYSTEMD;
- else
+ if (all_unified > 0) {
+ /* Unified cgroup hierarchy support was added in 230. Unfortunately the detection
+ * routine only detects 231, so we'll have a false negative here for 230. */
+ r = systemd_installation_has_version(directory, 230);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine systemd version in container: %m");
+ if (r > 0)
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL;
+ else
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE;
+ } else if (systemd_unified > 0) {
+ /* Mixed cgroup hierarchy support was added in 232 */
+ r = systemd_installation_has_version(directory, 232);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine systemd version in container: %m");
+ if (r > 0)
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_SYSTEMD;
+ else
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE;
+ } else
arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE;
return 0;
@@ -400,52 +415,52 @@ static int parse_argv(int argc, char *argv[]) {
};
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "directory", required_argument, NULL, 'D' },
- { "template", required_argument, NULL, ARG_TEMPLATE },
- { "ephemeral", no_argument, NULL, 'x' },
- { "user", required_argument, NULL, 'u' },
- { "private-network", no_argument, NULL, ARG_PRIVATE_NETWORK },
- { "as-pid2", no_argument, NULL, 'a' },
- { "boot", no_argument, NULL, 'b' },
- { "uuid", required_argument, NULL, ARG_UUID },
- { "read-only", no_argument, NULL, ARG_READ_ONLY },
- { "capability", required_argument, NULL, ARG_CAPABILITY },
- { "drop-capability", required_argument, NULL, ARG_DROP_CAPABILITY },
- { "link-journal", required_argument, NULL, ARG_LINK_JOURNAL },
- { "bind", required_argument, NULL, ARG_BIND },
- { "bind-ro", required_argument, NULL, ARG_BIND_RO },
- { "tmpfs", required_argument, NULL, ARG_TMPFS },
- { "overlay", required_argument, NULL, ARG_OVERLAY },
- { "overlay-ro", required_argument, NULL, ARG_OVERLAY_RO },
- { "machine", required_argument, NULL, 'M' },
- { "slice", required_argument, NULL, 'S' },
- { "setenv", required_argument, NULL, 'E' },
- { "selinux-context", required_argument, NULL, 'Z' },
- { "selinux-apifs-context", required_argument, NULL, 'L' },
- { "quiet", no_argument, NULL, 'q' },
- { "share-system", no_argument, NULL, ARG_SHARE_SYSTEM }, /* not documented */
- { "register", required_argument, NULL, ARG_REGISTER },
- { "keep-unit", no_argument, NULL, ARG_KEEP_UNIT },
- { "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE },
- { "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN },
- { "network-ipvlan", required_argument, NULL, ARG_NETWORK_IPVLAN },
- { "network-veth", no_argument, NULL, 'n' },
- { "network-veth-extra", required_argument, NULL, ARG_NETWORK_VETH_EXTRA},
- { "network-bridge", required_argument, NULL, ARG_NETWORK_BRIDGE },
- { "network-zone", required_argument, NULL, ARG_NETWORK_ZONE },
- { "personality", required_argument, NULL, ARG_PERSONALITY },
- { "image", required_argument, NULL, 'i' },
- { "volatile", optional_argument, NULL, ARG_VOLATILE },
- { "port", required_argument, NULL, 'p' },
- { "property", required_argument, NULL, ARG_PROPERTY },
- { "private-users", optional_argument, NULL, ARG_PRIVATE_USERS },
- { "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN},
- { "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL },
- { "settings", required_argument, NULL, ARG_SETTINGS },
- { "chdir", required_argument, NULL, ARG_CHDIR },
- { "notify-ready", required_argument, NULL, ARG_NOTIFY_READY },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "directory", required_argument, NULL, 'D' },
+ { "template", required_argument, NULL, ARG_TEMPLATE },
+ { "ephemeral", no_argument, NULL, 'x' },
+ { "user", required_argument, NULL, 'u' },
+ { "private-network", no_argument, NULL, ARG_PRIVATE_NETWORK },
+ { "as-pid2", no_argument, NULL, 'a' },
+ { "boot", no_argument, NULL, 'b' },
+ { "uuid", required_argument, NULL, ARG_UUID },
+ { "read-only", no_argument, NULL, ARG_READ_ONLY },
+ { "capability", required_argument, NULL, ARG_CAPABILITY },
+ { "drop-capability", required_argument, NULL, ARG_DROP_CAPABILITY },
+ { "link-journal", required_argument, NULL, ARG_LINK_JOURNAL },
+ { "bind", required_argument, NULL, ARG_BIND },
+ { "bind-ro", required_argument, NULL, ARG_BIND_RO },
+ { "tmpfs", required_argument, NULL, ARG_TMPFS },
+ { "overlay", required_argument, NULL, ARG_OVERLAY },
+ { "overlay-ro", required_argument, NULL, ARG_OVERLAY_RO },
+ { "machine", required_argument, NULL, 'M' },
+ { "slice", required_argument, NULL, 'S' },
+ { "setenv", required_argument, NULL, 'E' },
+ { "selinux-context", required_argument, NULL, 'Z' },
+ { "selinux-apifs-context", required_argument, NULL, 'L' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "share-system", no_argument, NULL, ARG_SHARE_SYSTEM }, /* not documented */
+ { "register", required_argument, NULL, ARG_REGISTER },
+ { "keep-unit", no_argument, NULL, ARG_KEEP_UNIT },
+ { "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE },
+ { "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN },
+ { "network-ipvlan", required_argument, NULL, ARG_NETWORK_IPVLAN },
+ { "network-veth", no_argument, NULL, 'n' },
+ { "network-veth-extra", required_argument, NULL, ARG_NETWORK_VETH_EXTRA },
+ { "network-bridge", required_argument, NULL, ARG_NETWORK_BRIDGE },
+ { "network-zone", required_argument, NULL, ARG_NETWORK_ZONE },
+ { "personality", required_argument, NULL, ARG_PERSONALITY },
+ { "image", required_argument, NULL, 'i' },
+ { "volatile", optional_argument, NULL, ARG_VOLATILE },
+ { "port", required_argument, NULL, 'p' },
+ { "property", required_argument, NULL, ARG_PROPERTY },
+ { "private-users", optional_argument, NULL, ARG_PRIVATE_USERS },
+ { "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN },
+ { "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL },
+ { "settings", required_argument, NULL, ARG_SETTINGS },
+ { "chdir", required_argument, NULL, ARG_CHDIR },
+ { "notify-ready", required_argument, NULL, ARG_NOTIFY_READY },
{}
};
@@ -898,15 +913,21 @@ static int parse_argv(int argc, char *argv[]) {
break;
- case ARG_PRIVATE_USERS:
+ case ARG_PRIVATE_USERS: {
+ int boolean = -1;
- r = optarg ? parse_boolean(optarg) : 1;
- if (r == 0) {
+ if (!optarg)
+ boolean = true;
+ else if (!in_charset(optarg, DIGITS))
+ /* do *not* parse numbers as booleans */
+ boolean = parse_boolean(optarg);
+
+ if (boolean == false) {
/* no: User namespacing off */
arg_userns_mode = USER_NAMESPACE_NO;
arg_uid_shift = UID_INVALID;
arg_uid_range = UINT32_C(0x10000);
- } else if (r > 0) {
+ } else if (boolean == true) {
/* yes: User namespacing on, UID range is read from root dir */
arg_userns_mode = USER_NAMESPACE_FIXED;
arg_uid_shift = UID_INVALID;
@@ -930,23 +951,27 @@ static int parse_argv(int argc, char *argv[]) {
shift = buffer;
range++;
- if (safe_atou32(range, &arg_uid_range) < 0 || arg_uid_range <= 0) {
- log_error("Failed to parse UID range: %s", range);
- return -EINVAL;
- }
+ r = safe_atou32(range, &arg_uid_range);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse UID range \"%s\": %m", range);
} else
shift = optarg;
- if (parse_uid(shift, &arg_uid_shift) < 0) {
- log_error("Failed to parse UID: %s", optarg);
- return -EINVAL;
- }
+ r = parse_uid(shift, &arg_uid_shift);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse UID \"%s\": %m", optarg);
arg_userns_mode = USER_NAMESPACE_FIXED;
}
+ if (arg_uid_range <= 0) {
+ log_error("UID range cannot be 0.");
+ return -EINVAL;
+ }
+
arg_settings_mask |= SETTING_USERNS;
break;
+ }
case 'U':
if (userns_supported()) {
@@ -1045,7 +1070,8 @@ static int parse_argv(int argc, char *argv[]) {
parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_UTS", CLONE_NEWUTS);
parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_SYSTEM", CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS);
- if (arg_clone_ns_flags != (CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS)) {
+ if (!(arg_clone_ns_flags & CLONE_NEWPID) ||
+ !(arg_clone_ns_flags & CLONE_NEWUTS)) {
arg_register = false;
if (arg_start_mode != START_PID1) {
log_error("--boot cannot be used without namespacing.");
@@ -1124,10 +1150,6 @@ static int parse_argv(int argc, char *argv[]) {
arg_caps_retain = (arg_caps_retain | plus | (arg_private_network ? 1ULL << CAP_NET_ADMIN : 0)) & ~minus;
- r = detect_unified_cgroup_hierarchy();
- if (r < 0)
- return r;
-
e = getenv("SYSTEMD_NSPAWN_CONTAINER_SERVICE");
if (e)
arg_container_service_name = e;
@@ -1219,7 +1241,13 @@ static int setup_timezone(const char *dest) {
/* Fix the timezone, if possible */
r = readlink_malloc("/etc/localtime", &p);
if (r < 0) {
- log_warning("/etc/localtime is not a symlink, not updating container timezone.");
+ log_warning("host's /etc/localtime is not a symlink, not updating container timezone.");
+ /* to handle warning, delete /etc/localtime and replace it
+ * with a symbolic link to a time zone data file.
+ *
+ * Example:
+ * ln -s /usr/share/zoneinfo/UTC /etc/localtime
+ */
return 0;
}
@@ -1388,6 +1416,12 @@ static int copy_devnodes(const char *dest) {
} else {
if (mknod(to, st.st_mode, st.st_rdev) < 0) {
+ /*
+ * This is some sort of protection too against
+ * recursive userns chown on shared /dev/
+ */
+ if (errno == EEXIST)
+ log_notice("%s/dev/ should be an empty directory", dest);
if (errno != EPERM)
return log_error_errno(errno, "mknod(%s) failed: %m", to);
@@ -1748,6 +1782,11 @@ static int setup_propagate(const char *root) {
if (mount(NULL, q, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0)
return log_error_errno(errno, "Failed to make propagation mount read-only");
+ /* machined will MS_MOVE into that directory, and that's only
+ * supported for non-shared mounts. */
+ if (mount(NULL, q, NULL, MS_SLAVE, NULL) < 0)
+ return log_error_errno(errno, "Failed to make propagation mount slave");
+
return 0;
}
@@ -2952,6 +2991,10 @@ static int outer_child(
if (r < 0)
return r;
+ r = detect_unified_cgroup_hierarchy(directory);
+ if (r < 0)
+ return r;
+
if (arg_userns_mode != USER_NAMESPACE_NO) {
/* Let the parent know which UID shift we read from the image */
l = send(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL);
@@ -2983,6 +3026,15 @@ static int outer_child(
if (mount(directory, directory, NULL, MS_BIND|MS_REC, NULL) < 0)
return log_error_errno(errno, "Failed to make bind mount: %m");
+ /* Mark everything as shared so our mounts get propagated down. This is
+ * required to make new bind mounts available in systemd services
+ * inside the containter that create a new mount namespace.
+ * See https://github.com/systemd/systemd/issues/3860
+ * Further submounts (such as /dev) done after this will inherit the
+ * shared propagation mode.*/
+ if (mount(NULL, directory, NULL, MS_SHARED|MS_REC, NULL) < 0)
+ return log_error_errno(errno, "MS_SHARED|MS_REC failed: %m");
+
r = recursive_chown(directory, arg_uid_shift, arg_uid_range);
if (r < 0)
return r;
@@ -3012,7 +3064,7 @@ static int outer_child(
return r;
if (arg_read_only) {
- r = bind_remount_recursive(directory, true);
+ r = bind_remount_recursive(directory, true, NULL);
if (r < 0)
return log_error_errno(r, "Failed to make tree read-only: %m");
}
@@ -3548,18 +3600,437 @@ static int load_settings(void) {
return 0;
}
+static int run(int master,
+ const char* console,
+ const char *root_device, bool root_device_rw,
+ const char *home_device, bool home_device_rw,
+ const char *srv_device, bool srv_device_rw,
+ const char *esp_device,
+ bool interactive,
+ bool secondary,
+ FDSet *fds,
+ char veth_name[IFNAMSIZ], bool *veth_created,
+ union in_addr_union *exposed,
+ pid_t *pid, int *ret) {
+
+ static const struct sigaction sa = {
+ .sa_handler = nop_signal_handler,
+ .sa_flags = SA_NOCLDSTOP,
+ };
+
+ _cleanup_release_lock_file_ LockFile uid_shift_lock = LOCK_FILE_INIT;
+ _cleanup_close_ int etc_passwd_lock = -1;
+ _cleanup_close_pair_ int
+ kmsg_socket_pair[2] = { -1, -1 },
+ rtnl_socket_pair[2] = { -1, -1 },
+ pid_socket_pair[2] = { -1, -1 },
+ uuid_socket_pair[2] = { -1, -1 },
+ notify_socket_pair[2] = { -1, -1 },
+ uid_shift_socket_pair[2] = { -1, -1 };
+ _cleanup_close_ int notify_socket= -1;
+ _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ ContainerStatus container_status = 0;
+ char last_char = 0;
+ int ifi = 0, r;
+ ssize_t l;
+ sigset_t mask_chld;
+
+ assert_se(sigemptyset(&mask_chld) == 0);
+ assert_se(sigaddset(&mask_chld, SIGCHLD) == 0);
+
+ if (arg_userns_mode == USER_NAMESPACE_PICK) {
+ /* When we shall pick the UID/GID range, let's first lock /etc/passwd, so that we can safely
+ * check with getpwuid() if the specific user already exists. Note that /etc might be
+ * read-only, in which case this will fail with EROFS. But that's really OK, as in that case we
+ * can be reasonably sure that no users are going to be added. Note that getpwuid() checks are
+ * really just an extra safety net. We kinda assume that the UID range we allocate from is
+ * really ours. */
+
+ etc_passwd_lock = take_etc_passwd_lock(NULL);
+ if (etc_passwd_lock < 0 && etc_passwd_lock != -EROFS)
+ return log_error_errno(etc_passwd_lock, "Failed to take /etc/passwd lock: %m");
+ }
+
+ r = barrier_create(&barrier);
+ if (r < 0)
+ return log_error_errno(r, "Cannot initialize IPC barrier: %m");
+
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create kmsg socket pair: %m");
+
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, rtnl_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create rtnl socket pair: %m");
+
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pid_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create pid socket pair: %m");
+
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uuid_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create id socket pair: %m");
+
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, notify_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create notify socket pair: %m");
+
+ if (arg_userns_mode != USER_NAMESPACE_NO)
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create uid shift socket pair: %m");
+
+ /* Child can be killed before execv(), so handle SIGCHLD in order to interrupt
+ * parent's blocking calls and give it a chance to call wait() and terminate. */
+ r = sigprocmask(SIG_UNBLOCK, &mask_chld, NULL);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to change the signal mask: %m");
+
+ r = sigaction(SIGCHLD, &sa, NULL);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
+
+ *pid = raw_clone(SIGCHLD|CLONE_NEWNS);
+ if (*pid < 0)
+ return log_error_errno(errno, "clone() failed%s: %m",
+ errno == EINVAL ?
+ ", do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in)" : "");
+
+ if (*pid == 0) {
+ /* The outer child only has a file system namespace. */
+ barrier_set_role(&barrier, BARRIER_CHILD);
+
+ master = safe_close(master);
+
+ kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]);
+ rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
+ pid_socket_pair[0] = safe_close(pid_socket_pair[0]);
+ uuid_socket_pair[0] = safe_close(uuid_socket_pair[0]);
+ notify_socket_pair[0] = safe_close(notify_socket_pair[0]);
+ uid_shift_socket_pair[0] = safe_close(uid_shift_socket_pair[0]);
+
+ (void) reset_all_signal_handlers();
+ (void) reset_signal_mask();
+
+ r = outer_child(&barrier,
+ arg_directory,
+ console,
+ root_device, root_device_rw,
+ home_device, home_device_rw,
+ srv_device, srv_device_rw,
+ esp_device,
+ interactive,
+ secondary,
+ pid_socket_pair[1],
+ uuid_socket_pair[1],
+ notify_socket_pair[1],
+ kmsg_socket_pair[1],
+ rtnl_socket_pair[1],
+ uid_shift_socket_pair[1],
+ fds);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ barrier_set_role(&barrier, BARRIER_PARENT);
+
+ fds = fdset_free(fds);
+
+ kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]);
+ rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]);
+ pid_socket_pair[1] = safe_close(pid_socket_pair[1]);
+ uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]);
+ notify_socket_pair[1] = safe_close(notify_socket_pair[1]);
+ uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]);
+
+ if (arg_userns_mode != USER_NAMESPACE_NO) {
+ /* The child just let us know the UID shift it might have read from the image. */
+ l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, 0);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to read UID shift: %m");
+
+ if (l != sizeof arg_uid_shift) {
+ log_error("Short read while reading UID shift.");
+ return -EIO;
+ }
+
+ if (arg_userns_mode == USER_NAMESPACE_PICK) {
+ /* If we are supposed to pick the UID shift, let's try to use the shift read from the
+ * image, but if that's already in use, pick a new one, and report back to the child,
+ * which one we now picked. */
+
+ r = uid_shift_pick(&arg_uid_shift, &uid_shift_lock);
+ if (r < 0)
+ return log_error_errno(r, "Failed to pick suitable UID/GID range: %m");
+
+ l = send(uid_shift_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, MSG_NOSIGNAL);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to send UID shift: %m");
+ if (l != sizeof arg_uid_shift) {
+ log_error("Short write while writing UID shift.");
+ return -EIO;
+ }
+ }
+ }
+
+ /* Wait for the outer child. */
+ r = wait_for_terminate_and_warn("namespace helper", *pid, NULL);
+ if (r != 0)
+ return r < 0 ? r : -EIO;
+
+ /* And now retrieve the PID of the inner child. */
+ l = recv(pid_socket_pair[0], pid, sizeof *pid, 0);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to read inner child PID: %m");
+ if (l != sizeof *pid) {
+ log_error("Short read while reading inner child PID.");
+ return -EIO;
+ }
+
+ /* We also retrieve container UUID in case it was generated by outer child */
+ l = recv(uuid_socket_pair[0], &arg_uuid, sizeof arg_uuid, 0);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to read container machine ID: %m");
+ if (l != sizeof(arg_uuid)) {
+ log_error("Short read while reading container machined ID.");
+ return -EIO;
+ }
+
+ /* We also retrieve the socket used for notifications generated by outer child */
+ notify_socket = receive_one_fd(notify_socket_pair[0], 0);
+ if (notify_socket < 0)
+ return log_error_errno(notify_socket,
+ "Failed to receive notification socket from the outer child: %m");
+
+ log_debug("Init process invoked as PID "PID_FMT, *pid);
+
+ if (arg_userns_mode != USER_NAMESPACE_NO) {
+ if (!barrier_place_and_sync(&barrier)) { /* #1 */
+ log_error("Child died too early.");
+ return -ESRCH;
+ }
+
+ r = setup_uid_map(*pid);
+ if (r < 0)
+ return r;
+
+ (void) barrier_place(&barrier); /* #2 */
+ }
+
+ if (arg_private_network) {
+
+ r = move_network_interfaces(*pid, arg_network_interfaces);
+ if (r < 0)
+ return r;
+
+ if (arg_network_veth) {
+ r = setup_veth(arg_machine, *pid, veth_name,
+ arg_network_bridge || arg_network_zone);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ ifi = r;
+
+ if (arg_network_bridge) {
+ /* Add the interface to a bridge */
+ r = setup_bridge(veth_name, arg_network_bridge, false);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ ifi = r;
+ } else if (arg_network_zone) {
+ /* Add the interface to a bridge, possibly creating it */
+ r = setup_bridge(veth_name, arg_network_zone, true);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ ifi = r;
+ }
+ }
+
+ r = setup_veth_extra(arg_machine, *pid, arg_network_veth_extra);
+ if (r < 0)
+ return r;
+
+ /* We created the primary and extra veth links now; let's remember this, so that we know to
+ remove them later on. Note that we don't bother with removing veth links that were created
+ here when their setup failed half-way, because in that case the kernel should be able to
+ remove them on its own, since they cannot be referenced by anything yet. */
+ *veth_created = true;
+
+ r = setup_macvlan(arg_machine, *pid, arg_network_macvlan);
+ if (r < 0)
+ return r;
+
+ r = setup_ipvlan(arg_machine, *pid, arg_network_ipvlan);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_register) {
+ r = register_machine(
+ arg_machine,
+ *pid,
+ arg_directory,
+ arg_uuid,
+ ifi,
+ arg_slice,
+ arg_custom_mounts, arg_n_custom_mounts,
+ arg_kill_signal,
+ arg_property,
+ arg_keep_unit,
+ arg_container_service_name);
+ if (r < 0)
+ return r;
+ }
+
+ r = sync_cgroup(*pid, arg_unified_cgroup_hierarchy);
+ if (r < 0)
+ return r;
+
+ if (arg_keep_unit) {
+ r = create_subcgroup(*pid, arg_unified_cgroup_hierarchy);
+ if (r < 0)
+ return r;
+ }
+
+ r = chown_cgroup(*pid, arg_uid_shift);
+ if (r < 0)
+ return r;
+
+ /* Notify the child that the parent is ready with all
+ * its setup (including cgroup-ification), and that
+ * the child can now hand over control to the code to
+ * run inside the container. */
+ (void) barrier_place(&barrier); /* #3 */
+
+ /* Block SIGCHLD here, before notifying child.
+ * process_pty() will handle it with the other signals. */
+ assert_se(sigprocmask(SIG_BLOCK, &mask_chld, NULL) >= 0);
+
+ /* Reset signal to default */
+ r = default_signals(SIGCHLD, -1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to reset SIGCHLD: %m");
+
+ r = sd_event_new(&event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get default event source: %m");
+
+ r = setup_sd_notify_parent(event, notify_socket, PID_TO_PTR(*pid));
+ if (r < 0)
+ return r;
+
+ /* Let the child know that we are ready and wait that the child is completely ready now. */
+ if (!barrier_place_and_sync(&barrier)) { /* #4 */
+ log_error("Child died too early.");
+ return -ESRCH;
+ }
+
+ /* At this point we have made use of the UID we picked, and thus nss-mymachines
+ * will make them appear in getpwuid(), thus we can release the /etc/passwd lock. */
+ etc_passwd_lock = safe_close(etc_passwd_lock);
+
+ sd_notifyf(false,
+ "STATUS=Container running.\n"
+ "X_NSPAWN_LEADER_PID=" PID_FMT, *pid);
+ if (!arg_notify_ready)
+ sd_notify(false, "READY=1\n");
+
+ if (arg_kill_signal > 0) {
+ /* Try to kill the init system on SIGINT or SIGTERM */
+ sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, PID_TO_PTR(*pid));
+ sd_event_add_signal(event, NULL, SIGTERM, on_orderly_shutdown, PID_TO_PTR(*pid));
+ } else {
+ /* Immediately exit */
+ sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
+ sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+ }
+
+ /* simply exit on sigchld */
+ sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL);
+
+ if (arg_expose_ports) {
+ r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, exposed, &rtnl);
+ if (r < 0)
+ return r;
+
+ (void) expose_port_execute(rtnl, arg_expose_ports, exposed);
+ }
+
+ rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
+
+ r = pty_forward_new(event, master,
+ PTY_FORWARD_IGNORE_VHANGUP | (interactive ? 0 : PTY_FORWARD_READ_ONLY),
+ &forward);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create PTY forwarder: %m");
+
+ r = sd_event_loop(event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
+
+ pty_forward_get_last_char(forward, &last_char);
+
+ forward = pty_forward_free(forward);
+
+ if (!arg_quiet && last_char != '\n')
+ putc('\n', stdout);
+
+ /* Kill if it is not dead yet anyway */
+ if (arg_register && !arg_keep_unit)
+ terminate_machine(*pid);
+
+ /* Normally redundant, but better safe than sorry */
+ kill(*pid, SIGKILL);
+
+ r = wait_for_container(*pid, &container_status);
+ *pid = 0;
+
+ if (r < 0)
+ /* We failed to wait for the container, or the container exited abnormally. */
+ return r;
+ if (r > 0 || container_status == CONTAINER_TERMINATED) {
+ /* r > 0 → The container exited with a non-zero status.
+ * As a special case, we need to replace 133 with a different value,
+ * because 133 is special-cased in the service file to reboot the container.
+ * otherwise → The container exited with zero status and a reboot was not requested.
+ */
+ if (r == 133)
+ r = EXIT_FAILURE; /* replace 133 with the general failure code */
+ *ret = r;
+ return 0; /* finito */
+ }
+
+ /* CONTAINER_REBOOTED, loop again */
+
+ if (arg_keep_unit) {
+ /* Special handling if we are running as a service: instead of simply
+ * restarting the machine we want to restart the entire service, so let's
+ * inform systemd about this with the special exit code 133. The service
+ * file uses RestartForceExitStatus=133 so that this results in a full
+ * nspawn restart. This is necessary since we might have cgroup parameters
+ * set we want to have flushed out. */
+ *ret = 0;
+ return 133;
+ }
+
+ expose_port_flush(arg_expose_ports, exposed);
+
+ (void) remove_veth_links(veth_name, arg_network_veth_extra);
+ *veth_created = false;
+ return 1; /* loop again */
+}
+
int main(int argc, char *argv[]) {
_cleanup_free_ char *device_path = NULL, *root_device = NULL, *home_device = NULL, *srv_device = NULL, *esp_device = NULL, *console = NULL;
bool root_device_rw = true, home_device_rw = true, srv_device_rw = true;
_cleanup_close_ int master = -1, image_fd = -1;
_cleanup_fdset_free_ FDSet *fds = NULL;
- int r, n_fd_passed, loop_nr = -1;
+ int r, n_fd_passed, loop_nr = -1, ret = EXIT_FAILURE;
char veth_name[IFNAMSIZ] = "";
bool secondary = false, remove_subvol = false;
- sigset_t mask_chld;
pid_t pid = 0;
- int ret = EXIT_SUCCESS;
union in_addr_union exposed = {};
_cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT;
bool interactive, veth_created = false;
@@ -3775,470 +4246,25 @@ int main(int argc, char *argv[]) {
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
- assert_se(sigemptyset(&mask_chld) == 0);
- assert_se(sigaddset(&mask_chld, SIGCHLD) == 0);
-
if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) {
r = log_error_errno(errno, "Failed to become subreaper: %m");
goto finish;
}
for (;;) {
- static const struct sigaction sa = {
- .sa_handler = nop_signal_handler,
- .sa_flags = SA_NOCLDSTOP,
- };
-
- _cleanup_release_lock_file_ LockFile uid_shift_lock = LOCK_FILE_INIT;
- _cleanup_close_ int etc_passwd_lock = -1;
- _cleanup_close_pair_ int
- kmsg_socket_pair[2] = { -1, -1 },
- rtnl_socket_pair[2] = { -1, -1 },
- pid_socket_pair[2] = { -1, -1 },
- uuid_socket_pair[2] = { -1, -1 },
- notify_socket_pair[2] = { -1, -1 },
- uid_shift_socket_pair[2] = { -1, -1 };
- _cleanup_close_ int notify_socket= -1;
- _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
- _cleanup_(sd_event_unrefp) sd_event *event = NULL;
- _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
- _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
- ContainerStatus container_status;
- char last_char = 0;
- int ifi = 0;
- ssize_t l;
-
- if (arg_userns_mode == USER_NAMESPACE_PICK) {
- /* When we shall pick the UID/GID range, let's first lock /etc/passwd, so that we can safely
- * check with getpwuid() if the specific user already exists. Note that /etc might be
- * read-only, in which case this will fail with EROFS. But that's really OK, as in that case we
- * can be reasonably sure that no users are going to be added. Note that getpwuid() checks are
- * really just an extra safety net. We kinda assume that the UID range we allocate from is
- * really ours. */
-
- etc_passwd_lock = take_etc_passwd_lock(NULL);
- if (etc_passwd_lock < 0 && etc_passwd_lock != -EROFS) {
- log_error_errno(r, "Failed to take /etc/passwd lock: %m");
- goto finish;
- }
- }
-
- r = barrier_create(&barrier);
- if (r < 0) {
- log_error_errno(r, "Cannot initialize IPC barrier: %m");
- goto finish;
- }
-
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) {
- r = log_error_errno(errno, "Failed to create kmsg socket pair: %m");
- goto finish;
- }
-
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, rtnl_socket_pair) < 0) {
- r = log_error_errno(errno, "Failed to create rtnl socket pair: %m");
- goto finish;
- }
-
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pid_socket_pair) < 0) {
- r = log_error_errno(errno, "Failed to create pid socket pair: %m");
- goto finish;
- }
-
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uuid_socket_pair) < 0) {
- r = log_error_errno(errno, "Failed to create id socket pair: %m");
- goto finish;
- }
-
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, notify_socket_pair) < 0) {
- r = log_error_errno(errno, "Failed to create notify socket pair: %m");
- goto finish;
- }
-
- if (arg_userns_mode != USER_NAMESPACE_NO)
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0) {
- r = log_error_errno(errno, "Failed to create uid shift socket pair: %m");
- goto finish;
- }
-
- /* Child can be killed before execv(), so handle SIGCHLD
- * in order to interrupt parent's blocking calls and
- * give it a chance to call wait() and terminate. */
- r = sigprocmask(SIG_UNBLOCK, &mask_chld, NULL);
- if (r < 0) {
- r = log_error_errno(errno, "Failed to change the signal mask: %m");
- goto finish;
- }
-
- r = sigaction(SIGCHLD, &sa, NULL);
- if (r < 0) {
- r = log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
- goto finish;
- }
-
- pid = raw_clone(SIGCHLD|CLONE_NEWNS);
- if (pid < 0) {
- if (errno == EINVAL)
- r = log_error_errno(errno, "clone() failed, do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in): %m");
- else
- r = log_error_errno(errno, "clone() failed: %m");
-
- goto finish;
- }
-
- if (pid == 0) {
- /* The outer child only has a file system namespace. */
- barrier_set_role(&barrier, BARRIER_CHILD);
-
- master = safe_close(master);
-
- kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]);
- rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
- pid_socket_pair[0] = safe_close(pid_socket_pair[0]);
- uuid_socket_pair[0] = safe_close(uuid_socket_pair[0]);
- notify_socket_pair[0] = safe_close(notify_socket_pair[0]);
- uid_shift_socket_pair[0] = safe_close(uid_shift_socket_pair[0]);
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
- r = outer_child(&barrier,
- arg_directory,
- console,
- root_device, root_device_rw,
- home_device, home_device_rw,
- srv_device, srv_device_rw,
- esp_device,
- interactive,
- secondary,
- pid_socket_pair[1],
- uuid_socket_pair[1],
- notify_socket_pair[1],
- kmsg_socket_pair[1],
- rtnl_socket_pair[1],
- uid_shift_socket_pair[1],
- fds);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
- _exit(EXIT_SUCCESS);
- }
-
- barrier_set_role(&barrier, BARRIER_PARENT);
-
- fds = fdset_free(fds);
-
- kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]);
- rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]);
- pid_socket_pair[1] = safe_close(pid_socket_pair[1]);
- uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]);
- notify_socket_pair[1] = safe_close(notify_socket_pair[1]);
- uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]);
-
- if (arg_userns_mode != USER_NAMESPACE_NO) {
- /* The child just let us know the UID shift it might have read from the image. */
- l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), 0);
- if (l < 0) {
- r = log_error_errno(errno, "Failed to read UID shift: %m");
- goto finish;
- }
- if (l != sizeof(arg_uid_shift)) {
- log_error("Short read while reading UID shift.");
- r = EIO;
- goto finish;
- }
-
- if (arg_userns_mode == USER_NAMESPACE_PICK) {
- /* If we are supposed to pick the UID shift, let's try to use the shift read from the
- * image, but if that's already in use, pick a new one, and report back to the child,
- * which one we now picked. */
-
- r = uid_shift_pick(&arg_uid_shift, &uid_shift_lock);
- if (r < 0) {
- log_error_errno(r, "Failed to pick suitable UID/GID range: %m");
- goto finish;
- }
-
- l = send(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL);
- if (l < 0) {
- r = log_error_errno(errno, "Failed to send UID shift: %m");
- goto finish;
- }
- if (l != sizeof(arg_uid_shift)) {
- log_error("Short write while writing UID shift.");
- r = -EIO;
- goto finish;
- }
- }
- }
-
- /* Wait for the outer child. */
- r = wait_for_terminate_and_warn("namespace helper", pid, NULL);
- if (r < 0)
- goto finish;
- if (r != 0) {
- r = -EIO;
- goto finish;
- }
- pid = 0;
-
- /* And now retrieve the PID of the inner child. */
- l = recv(pid_socket_pair[0], &pid, sizeof(pid), 0);
- if (l < 0) {
- r = log_error_errno(errno, "Failed to read inner child PID: %m");
- goto finish;
- }
- if (l != sizeof(pid)) {
- log_error("Short read while reading inner child PID.");
- r = EIO;
- goto finish;
- }
-
- /* We also retrieve container UUID in case it was generated by outer child */
- l = recv(uuid_socket_pair[0], &arg_uuid, sizeof(arg_uuid), 0);
- if (l < 0) {
- r = log_error_errno(errno, "Failed to read container machine ID: %m");
- goto finish;
- }
- if (l != sizeof(arg_uuid)) {
- log_error("Short read while reading container machined ID.");
- r = EIO;
- goto finish;
- }
-
- /* We also retrieve the socket used for notifications generated by outer child */
- notify_socket = receive_one_fd(notify_socket_pair[0], 0);
- if (notify_socket < 0) {
- r = log_error_errno(errno, "Failed to receive notification socket from the outer child: %m");
- goto finish;
- }
-
- log_debug("Init process invoked as PID " PID_FMT, pid);
-
- if (arg_userns_mode != USER_NAMESPACE_NO) {
- if (!barrier_place_and_sync(&barrier)) { /* #1 */
- log_error("Child died too early.");
- r = -ESRCH;
- goto finish;
- }
-
- r = setup_uid_map(pid);
- if (r < 0)
- goto finish;
-
- (void) barrier_place(&barrier); /* #2 */
- }
-
- if (arg_private_network) {
-
- r = move_network_interfaces(pid, arg_network_interfaces);
- if (r < 0)
- goto finish;
-
- if (arg_network_veth) {
- r = setup_veth(arg_machine, pid, veth_name,
- arg_network_bridge || arg_network_zone);
- if (r < 0)
- goto finish;
- else if (r > 0)
- ifi = r;
-
- if (arg_network_bridge) {
- /* Add the interface to a bridge */
- r = setup_bridge(veth_name, arg_network_bridge, false);
- if (r < 0)
- goto finish;
- if (r > 0)
- ifi = r;
- } else if (arg_network_zone) {
- /* Add the interface to a bridge, possibly creating it */
- r = setup_bridge(veth_name, arg_network_zone, true);
- if (r < 0)
- goto finish;
- if (r > 0)
- ifi = r;
- }
- }
-
- r = setup_veth_extra(arg_machine, pid, arg_network_veth_extra);
- if (r < 0)
- goto finish;
-
- /* We created the primary and extra veth links now; let's remember this, so that we know to
- remove them later on. Note that we don't bother with removing veth links that were created
- here when their setup failed half-way, because in that case the kernel should be able to
- remove them on its own, since they cannot be referenced by anything yet. */
- veth_created = true;
-
- r = setup_macvlan(arg_machine, pid, arg_network_macvlan);
- if (r < 0)
- goto finish;
-
- r = setup_ipvlan(arg_machine, pid, arg_network_ipvlan);
- if (r < 0)
- goto finish;
- }
-
- if (arg_register) {
- r = register_machine(
- arg_machine,
- pid,
- arg_directory,
- arg_uuid,
- ifi,
- arg_slice,
- arg_custom_mounts, arg_n_custom_mounts,
- arg_kill_signal,
- arg_property,
- arg_keep_unit,
- arg_container_service_name);
- if (r < 0)
- goto finish;
- }
-
- r = sync_cgroup(pid, arg_unified_cgroup_hierarchy);
- if (r < 0)
- goto finish;
-
- if (arg_keep_unit) {
- r = create_subcgroup(pid, arg_unified_cgroup_hierarchy);
- if (r < 0)
- goto finish;
- }
-
- r = chown_cgroup(pid, arg_uid_shift);
- if (r < 0)
- goto finish;
-
- /* Notify the child that the parent is ready with all
- * its setup (including cgroup-ification), and that
- * the child can now hand over control to the code to
- * run inside the container. */
- (void) barrier_place(&barrier); /* #3 */
-
- /* Block SIGCHLD here, before notifying child.
- * process_pty() will handle it with the other signals. */
- assert_se(sigprocmask(SIG_BLOCK, &mask_chld, NULL) >= 0);
-
- /* Reset signal to default */
- r = default_signals(SIGCHLD, -1);
- if (r < 0) {
- log_error_errno(r, "Failed to reset SIGCHLD: %m");
- goto finish;
- }
-
- r = sd_event_new(&event);
- if (r < 0) {
- log_error_errno(r, "Failed to get default event source: %m");
- goto finish;
- }
-
- r = setup_sd_notify_parent(event, notify_socket, PID_TO_PTR(pid));
- if (r < 0)
- goto finish;
-
- /* Let the child know that we are ready and wait that the child is completely ready now. */
- if (!barrier_place_and_sync(&barrier)) { /* #4 */
- log_error("Child died too early.");
- r = -ESRCH;
- goto finish;
- }
-
- /* At this point we have made use of the UID we picked, and thus nss-mymachines will make them appear
- * in getpwuid(), thus we can release the /etc/passwd lock. */
- etc_passwd_lock = safe_close(etc_passwd_lock);
-
- sd_notifyf(false,
- "STATUS=Container running.\n"
- "X_NSPAWN_LEADER_PID=" PID_FMT, pid);
- if (!arg_notify_ready)
- sd_notify(false, "READY=1\n");
-
- if (arg_kill_signal > 0) {
- /* Try to kill the init system on SIGINT or SIGTERM */
- sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, PID_TO_PTR(pid));
- sd_event_add_signal(event, NULL, SIGTERM, on_orderly_shutdown, PID_TO_PTR(pid));
- } else {
- /* Immediately exit */
- sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
- sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
- }
-
- /* simply exit on sigchld */
- sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL);
-
- if (arg_expose_ports) {
- r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, &exposed, &rtnl);
- if (r < 0)
- goto finish;
-
- (void) expose_port_execute(rtnl, arg_expose_ports, &exposed);
- }
-
- rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
-
- r = pty_forward_new(event, master, PTY_FORWARD_IGNORE_VHANGUP | (interactive ? 0 : PTY_FORWARD_READ_ONLY), &forward);
- if (r < 0) {
- log_error_errno(r, "Failed to create PTY forwarder: %m");
- goto finish;
- }
-
- r = sd_event_loop(event);
- if (r < 0) {
- log_error_errno(r, "Failed to run event loop: %m");
- goto finish;
- }
-
- pty_forward_get_last_char(forward, &last_char);
-
- forward = pty_forward_free(forward);
-
- if (!arg_quiet && last_char != '\n')
- putc('\n', stdout);
-
- /* Kill if it is not dead yet anyway */
- if (arg_register && !arg_keep_unit)
- terminate_machine(pid);
-
- /* Normally redundant, but better safe than sorry */
- kill(pid, SIGKILL);
-
- r = wait_for_container(pid, &container_status);
- pid = 0;
-
- if (r < 0)
- /* We failed to wait for the container, or the
- * container exited abnormally */
- goto finish;
- else if (r > 0 || container_status == CONTAINER_TERMINATED) {
- /* The container exited with a non-zero
- * status, or with zero status and no reboot
- * was requested. */
- ret = r;
- break;
- }
-
- /* CONTAINER_REBOOTED, loop again */
-
- if (arg_keep_unit) {
- /* Special handling if we are running as a
- * service: instead of simply restarting the
- * machine we want to restart the entire
- * service, so let's inform systemd about this
- * with the special exit code 133. The service
- * file uses RestartForceExitStatus=133 so
- * that this results in a full nspawn
- * restart. This is necessary since we might
- * have cgroup parameters set we want to have
- * flushed out. */
- ret = 133;
- r = 0;
+ r = run(master,
+ console,
+ root_device, root_device_rw,
+ home_device, home_device_rw,
+ srv_device, srv_device_rw,
+ esp_device,
+ interactive, secondary,
+ fds,
+ veth_name, &veth_created,
+ &exposed,
+ &pid, &ret);
+ if (r <= 0)
break;
- }
-
- expose_port_flush(arg_expose_ports, &exposed);
-
- (void) remove_veth_links(veth_name, arg_network_veth_extra);
- veth_created = false;
}
finish:
diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c
index 5ce10f1cbd..eea91e3e88 100644
--- a/src/nss-resolve/nss-resolve.c
+++ b/src/nss-resolve/nss-resolve.c
@@ -279,9 +279,12 @@ fallback:
}
fail:
+ /* When we arrive here, resolved runs and has answered (fallback to
+ * "dns" is handled earlier). So we have a definitive "no" answer and
+ * should not fall back to subsequent NSS modules via "UNAVAIL". */
*errnop = -r;
*h_errnop = NO_RECOVERY;
- return NSS_STATUS_UNAVAIL;
+ return NSS_STATUS_NOTFOUND;
}
enum nss_status _nss_resolve_gethostbyname3_r(
@@ -476,7 +479,7 @@ fallback:
fail:
*errnop = -r;
*h_errnop = NO_RECOVERY;
- return NSS_STATUS_UNAVAIL;
+ return NSS_STATUS_NOTFOUND;
}
enum nss_status _nss_resolve_gethostbyaddr2_r(
@@ -558,9 +561,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
goto fallback;
- *errnop = -r;
- *h_errnop = NO_RECOVERY;
- return NSS_STATUS_UNAVAIL;
+ goto fail;
}
r = sd_bus_message_enter_container(reply, 'a', "(is)");
@@ -668,7 +669,7 @@ fallback:
fail:
*errnop = -r;
*h_errnop = NO_RECOVERY;
- return NSS_STATUS_UNAVAIL;
+ return NSS_STATUS_NOTFOUND;
}
NSS_GETHOSTBYNAME_FALLBACKS(resolve);
diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c
index 6468d1eecd..c3bdcaf1da 100644
--- a/src/remount-fs/remount-fs.c
+++ b/src/remount-fs/remount-fs.c
@@ -137,7 +137,7 @@ int main(int argc, char *argv[]) {
s = hashmap_remove(pids, PID_TO_PTR(si.si_pid));
if (s) {
- if (!is_clean_exit(si.si_code, si.si_status, NULL)) {
+ if (!is_clean_exit(si.si_code, si.si_status, EXIT_CLEAN_COMMAND, NULL)) {
if (si.si_code == CLD_EXITED)
log_error(MOUNT_PATH " for %s exited with exit status %i.", s, si.si_status);
else
diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c
index 07e4cd7d1d..8aa79049b6 100644
--- a/src/resolve/resolve-tool.c
+++ b/src/resolve/resolve-tool.c
@@ -395,7 +395,7 @@ static int output_rr_packet(const void *d, size_t l, int ifindex) {
return 0;
}
-static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type) {
+static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type, bool warn_missing) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char ifname[IF_NAMESIZE] = "";
@@ -430,7 +430,8 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_
r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
- log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
+ if (warn_missing || r != -ENXIO)
+ log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
return r;
}
@@ -488,7 +489,8 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_
return bus_log_parse_error(r);
if (n == 0) {
- log_error("%s: no records found", name);
+ if (warn_missing)
+ log_error("%s: no records found", name);
return -ESRCH;
}
@@ -618,7 +620,7 @@ static int resolve_rfc4501(sd_bus *bus, const char *name) {
if (type == 0)
type = arg_type ?: DNS_TYPE_A;
- return resolve_record(bus, n, class, type);
+ return resolve_record(bus, n, class, type, true);
invalid:
log_error("Invalid DNS URI: %s", name);
@@ -840,16 +842,34 @@ static int resolve_openpgp(sd_bus *bus, const char *address) {
}
domain++;
- r = string_hashsum_sha224(address, domain - 1 - address, &hashed);
+ r = string_hashsum_sha256(address, domain - 1 - address, &hashed);
if (r < 0)
return log_error_errno(r, "Hashing failed: %m");
+ strshorten(hashed, 56);
+
full = strjoina(hashed, "._openpgpkey.", domain);
log_debug("Looking up \"%s\".", full);
- return resolve_record(bus, full,
- arg_class ?: DNS_CLASS_IN,
- arg_type ?: DNS_TYPE_OPENPGPKEY);
+ r = resolve_record(bus, full,
+ arg_class ?: DNS_CLASS_IN,
+ arg_type ?: DNS_TYPE_OPENPGPKEY, false);
+
+ if (IN_SET(r, -ENXIO, -ESRCH)) { /* NXDOMAIN or NODATA? */
+ hashed = NULL;
+ r = string_hashsum_sha224(address, domain - 1 - address, &hashed);
+ if (r < 0)
+ return log_error_errno(r, "Hashing failed: %m");
+
+ full = strjoina(hashed, "._openpgpkey.", domain);
+ log_debug("Looking up \"%s\".", full);
+
+ return resolve_record(bus, full,
+ arg_class ?: DNS_CLASS_IN,
+ arg_type ?: DNS_TYPE_OPENPGPKEY, true);
+ }
+
+ return r;
}
static int resolve_tlsa(sd_bus *bus, const char *address) {
@@ -881,7 +901,7 @@ static int resolve_tlsa(sd_bus *bus, const char *address) {
return resolve_record(bus, full,
arg_class ?: DNS_CLASS_IN,
- arg_type ?: DNS_TYPE_TLSA);
+ arg_type ?: DNS_TYPE_TLSA, true);
}
static int show_statistics(sd_bus *bus) {
@@ -1877,7 +1897,7 @@ int main(int argc, char **argv) {
while (argv[optind]) {
int k;
- k = resolve_record(bus, argv[optind], arg_class, arg_type);
+ k = resolve_record(bus, argv[optind], arg_class, arg_type, true);
if (r == 0)
r = k;
diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c
index dd233e7c4a..abf3263178 100644
--- a/src/resolve/resolved-conf.c
+++ b/src/resolve/resolved-conf.c
@@ -23,8 +23,19 @@
#include "extract-word.h"
#include "parse-util.h"
#include "resolved-conf.h"
+#include "string-table.h"
#include "string-util.h"
+DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_stub_listener_mode, dns_stub_listener_mode, DnsStubListenerMode, "Failed to parse DNS stub listener mode setting");
+
+static const char* const dns_stub_listener_mode_table[_DNS_STUB_LISTENER_MODE_MAX] = {
+ [DNS_STUB_LISTENER_NO] = "no",
+ [DNS_STUB_LISTENER_UDP] = "udp",
+ [DNS_STUB_LISTENER_TCP] = "tcp",
+ [DNS_STUB_LISTENER_YES] = "yes",
+};
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_stub_listener_mode, DnsStubListenerMode, DNS_STUB_LISTENER_YES);
+
int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) {
union in_addr_union address;
int family, r, ifindex = 0;
@@ -221,7 +232,7 @@ int manager_parse_config_file(Manager *m) {
assert(m);
- r = config_parse_many(PKGSYSCONFDIR "/resolved.conf",
+ r = config_parse_many_nulstr(PKGSYSCONFDIR "/resolved.conf",
CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
"Resolve\0",
config_item_perf_lookup, resolved_gperf_lookup,
diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h
index e1fd2cceec..fc425a36b2 100644
--- a/src/resolve/resolved-conf.h
+++ b/src/resolve/resolved-conf.h
@@ -19,7 +19,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+typedef enum DnsStubListenerMode DnsStubListenerMode;
+
+enum DnsStubListenerMode {
+ DNS_STUB_LISTENER_NO,
+ DNS_STUB_LISTENER_UDP,
+ DNS_STUB_LISTENER_TCP,
+ DNS_STUB_LISTENER_YES,
+ _DNS_STUB_LISTENER_MODE_MAX,
+ _DNS_STUB_LISTENER_MODE_INVALID = -1
+};
+
#include "resolved-manager.h"
+#include "resolved-dns-server.h"
int manager_parse_config_file(Manager *m);
@@ -33,4 +45,7 @@ const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned len
int config_parse_dns_servers(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_search_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_dnssec(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_dns_stub_listener_mode(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);
+
+const char* dns_stub_listener_mode_to_string(DnsStubListenerMode p) _const_;
+DnsStubListenerMode dns_stub_listener_mode_from_string(const char *s) _pure_;
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index a8ad8fe342..337a8c473f 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -2143,7 +2143,7 @@ int dns_packet_extract(DnsPacket *p) {
for (i = 0; i < n; i++) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- bool cache_flush;
+ bool cache_flush = false;
r = dns_packet_read_rr(p, &rr, &cache_flush, NULL);
if (r < 0)
@@ -2289,6 +2289,7 @@ static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = {
[DNS_RCODE_BADNAME] = "BADNAME",
[DNS_RCODE_BADALG] = "BADALG",
[DNS_RCODE_BADTRUNC] = "BADTRUNC",
+ [DNS_RCODE_BADCOOKIE] = "BADCOOKIE",
};
DEFINE_STRING_TABLE_LOOKUP(dns_rcode, int);
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index 7b7d4e14c9..054dc88a85 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -263,6 +263,7 @@ enum {
DNS_RCODE_BADNAME = 20,
DNS_RCODE_BADALG = 21,
DNS_RCODE_BADTRUNC = 22,
+ DNS_RCODE_BADCOOKIE = 23,
_DNS_RCODE_MAX_DEFINED,
_DNS_RCODE_MAX = 4095 /* 4 bit rcode in the header plus 8 bit rcode in OPT, makes 12 bit */
};
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index ed0c6aa105..03811ac8e7 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -407,6 +407,7 @@ int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *add
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
DnsSearchDomain *d;
+ DnsServer *dns_server;
assert(s);
assert(domain);
@@ -447,6 +448,13 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
if (dns_name_endswith(domain, d->name) > 0)
return DNS_SCOPE_YES;
+ /* If the DNS server has route-only domains, don't send other requests
+ * to it. This would be a privacy violation, will most probably fail
+ * anyway, and adds unnecessary load. */
+ dns_server = dns_scope_get_dns_server(s);
+ if (dns_server && dns_server_limited_domains(dns_server))
+ return DNS_SCOPE_NO;
+
switch (s->protocol) {
case DNS_PROTOCOL_DNS:
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index 538bc61f81..01a83a76b2 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -26,7 +26,10 @@ typedef struct DnsScope DnsScope;
#include "resolved-dns-cache.h"
#include "resolved-dns-dnssec.h"
#include "resolved-dns-packet.h"
+#include "resolved-dns-query.h"
+#include "resolved-dns-search-domain.h"
#include "resolved-dns-server.h"
+#include "resolved-dns-stream.h"
#include "resolved-dns-zone.h"
#include "resolved-link.h"
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 9b7b471600..97cc8c0e09 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -576,6 +576,27 @@ void dns_server_warn_downgrade(DnsServer *server) {
server->warned_downgrade = true;
}
+bool dns_server_limited_domains(DnsServer *server)
+{
+ DnsSearchDomain *domain;
+ bool domain_restricted = false;
+
+ /* Check if the server has route-only domains without ~., i. e. whether
+ * it should only be used for particular domains */
+ if (!server->link)
+ return false;
+
+ LIST_FOREACH(domains, domain, server->link->search_domains)
+ if (domain->route_only) {
+ domain_restricted = true;
+ /* ~. means "any domain", thus it is a global server */
+ if (streq(DNS_SEARCH_DOMAIN_NAME(domain), "."))
+ return false;
+ }
+
+ return domain_restricted;
+}
+
static void dns_server_hash_func(const void *p, struct siphash *state) {
const DnsServer *s = p;
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index c1732faffd..83e288a202 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -128,6 +128,8 @@ bool dns_server_dnssec_supported(DnsServer *server);
void dns_server_warn_downgrade(DnsServer *server);
+bool dns_server_limited_domains(DnsServer *server);
+
DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex);
void dns_server_unlink_all(DnsServer *first);
diff --git a/src/resolve/resolved-dns-stream.h b/src/resolve/resolved-dns-stream.h
index e6569678fa..4cdb4f6806 100644
--- a/src/resolve/resolved-dns-stream.h
+++ b/src/resolve/resolved-dns-stream.h
@@ -25,6 +25,7 @@ typedef struct DnsStream DnsStream;
#include "resolved-dns-packet.h"
#include "resolved-dns-transaction.h"
+#include "resolved-manager.h"
/* Streams are used by three subsystems:
*
diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c
index d263cedcd9..e76de6c06a 100644
--- a/src/resolve/resolved-dns-stub.c
+++ b/src/resolve/resolved-dns-stub.c
@@ -25,6 +25,9 @@
* IP and UDP header sizes */
#define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U)
+static int manager_dns_stub_udp_fd(Manager *m);
+static int manager_dns_stub_tcp_fd(Manager *m);
+
static int dns_stub_make_reply_packet(
uint16_t id,
int rcode,
@@ -354,66 +357,48 @@ static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
-int manager_dns_stub_udp_fd(Manager *m) {
+static int manager_dns_stub_udp_fd(Manager *m) {
static const int one = 1;
-
union sockaddr_union sa = {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(53),
.in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
};
-
+ _cleanup_close_ int fd = -1;
int r;
if (m->dns_stub_udp_fd >= 0)
return m->dns_stub_udp_fd;
- m->dns_stub_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->dns_stub_udp_fd < 0)
+ fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0)
return -errno;
- r = setsockopt(m->dns_stub_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0)
+ return -errno;
- r = setsockopt(m->dns_stub_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0)
+ return -errno;
- r = setsockopt(m->dns_stub_udp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0)
+ return -errno;
/* Make sure no traffic from outside the local host can leak to onto this socket */
- r = setsockopt(m->dns_stub_udp_fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3);
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3) < 0)
+ return -errno;
- r = bind(m->dns_stub_udp_fd, &sa.sa, sizeof(sa.in));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
+ return -errno;
- r = sd_event_add_io(m->event, &m->dns_stub_udp_event_source, m->dns_stub_udp_fd, EPOLLIN, on_dns_stub_packet, m);
+ r = sd_event_add_io(m->event, &m->dns_stub_udp_event_source, fd, EPOLLIN, on_dns_stub_packet, m);
if (r < 0)
- goto fail;
+ return r;
(void) sd_event_source_set_description(m->dns_stub_udp_event_source, "dns-stub-udp");
+ m->dns_stub_udp_fd = fd;
+ fd = -1;
return m->dns_stub_udp_fd;
-
-fail:
- m->dns_stub_udp_fd = safe_close(m->dns_stub_udp_fd);
- return r;
}
static int on_dns_stub_stream_packet(DnsStream *s) {
@@ -461,102 +446,83 @@ static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
-int manager_dns_stub_tcp_fd(Manager *m) {
+static int manager_dns_stub_tcp_fd(Manager *m) {
static const int one = 1;
-
union sockaddr_union sa = {
.in.sin_family = AF_INET,
.in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
.in.sin_port = htobe16(53),
};
-
+ _cleanup_close_ int fd = -1;
int r;
if (m->dns_stub_tcp_fd >= 0)
return m->dns_stub_tcp_fd;
- m->dns_stub_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->dns_stub_tcp_fd < 0)
+ fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0)
return -errno;
- r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof one) < 0)
+ return -errno;
- r = setsockopt(m->dns_stub_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0)
+ return -errno;
- r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0)
+ return -errno;
- r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0)
+ return -errno;
/* Make sure no traffic from outside the local host can leak to onto this socket */
- r = setsockopt(m->dns_stub_tcp_fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3);
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3) < 0)
+ return -errno;
- r = bind(m->dns_stub_tcp_fd, &sa.sa, sizeof(sa.in));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
+ return -errno;
- r = listen(m->dns_stub_tcp_fd, SOMAXCONN);
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (listen(fd, SOMAXCONN) < 0)
+ return -errno;
- r = sd_event_add_io(m->event, &m->dns_stub_tcp_event_source, m->dns_stub_tcp_fd, EPOLLIN, on_dns_stub_stream, m);
+ r = sd_event_add_io(m->event, &m->dns_stub_tcp_event_source, fd, EPOLLIN, on_dns_stub_stream, m);
if (r < 0)
- goto fail;
+ return r;
(void) sd_event_source_set_description(m->dns_stub_tcp_event_source, "dns-stub-tcp");
+ m->dns_stub_tcp_fd = fd;
+ fd = -1;
return m->dns_stub_tcp_fd;
-
-fail:
- m->dns_stub_tcp_fd = safe_close(m->dns_stub_tcp_fd);
- return r;
}
int manager_dns_stub_start(Manager *m) {
- int r;
+ const char *t = "UDP";
+ int r = 0;
assert(m);
- r = manager_dns_stub_udp_fd(m);
- if (r == -EADDRINUSE)
- goto eaddrinuse;
- if (r < 0)
- return r;
-
- r = manager_dns_stub_tcp_fd(m);
- if (r == -EADDRINUSE)
- goto eaddrinuse;
- if (r < 0)
- return r;
+ if (IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_UDP))
+ r = manager_dns_stub_udp_fd(m);
- return 0;
+ if (r >= 0 &&
+ IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_TCP)) {
+ t = "TCP";
+ r = manager_dns_stub_tcp_fd(m);
+ }
-eaddrinuse:
- log_warning("Another process is already listening on 127.0.0.53:53. Turning off local DNS stub support.");
- manager_dns_stub_stop(m);
+ if (IN_SET(r, -EADDRINUSE, -EPERM)) {
+ if (r == -EADDRINUSE)
+ log_warning_errno(r,
+ "Another process is already listening on %s socket 127.0.0.53:53.\n"
+ "Turning off local DNS stub support.", t);
+ else
+ log_warning_errno(r,
+ "Failed to listen on %s socket 127.0.0.53:53: %m.\n"
+ "Turning off local DNS stub support.", t);
+ manager_dns_stub_stop(m);
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to listen on %s socket 127.0.0.53:53: %m", t);
return 0;
}
diff --git a/src/resolve/resolved-dns-stub.h b/src/resolve/resolved-dns-stub.h
index fce4d25ede..12b86f6753 100644
--- a/src/resolve/resolved-dns-stub.h
+++ b/src/resolve/resolved-dns-stub.h
@@ -24,8 +24,5 @@
/* 127.0.0.53 in native endian */
#define INADDR_DNS_STUB ((in_addr_t) 0x7f000035U)
-int manager_dns_stub_udp_fd(Manager *m);
-int manager_dns_stub_tcp_fd(Manager *m);
-
void manager_dns_stub_stop(Manager *m);
int manager_dns_stub_start(Manager *m);
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index 96b066845d..5a1df70422 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -59,6 +59,8 @@ enum DnsTransactionSource {
#include "resolved-dns-packet.h"
#include "resolved-dns-question.h"
#include "resolved-dns-scope.h"
+#include "resolved-dns-server.h"
+#include "resolved-dns-stream.h"
struct DnsTransaction {
DnsScope *scope;
diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf
index 2fd56bce26..446f85cdf4 100644
--- a/src/resolve/resolved-gperf.gperf
+++ b/src/resolve/resolved-gperf.gperf
@@ -14,9 +14,10 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0
-Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0
-Resolve.Domains, config_parse_search_domains, 0, 0
-Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support)
-Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode)
-Resolve.Cache, config_parse_bool, 0, offsetof(Manager, enable_cache)
+Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0
+Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0
+Resolve.Domains, config_parse_search_domains, 0, 0
+Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support)
+Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode)
+Resolve.Cache, config_parse_bool, 0, offsetof(Manager, enable_cache)
+Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0, offsetof(Manager, dns_stub_listener_mode)
diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h
index 6a2343f9f7..c9b2a58c34 100644
--- a/src/resolve/resolved-link.h
+++ b/src/resolve/resolved-link.h
@@ -29,6 +29,7 @@ typedef struct Link Link;
typedef struct LinkAddress LinkAddress;
#include "resolved-dns-rr.h"
+#include "resolved-dns-scope.h"
#include "resolved-dns-search-domain.h"
#include "resolved-dns-server.h"
#include "resolved-manager.h"
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 92ade820ac..40f08e8044 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -501,6 +501,7 @@ int manager_new(Manager **ret) {
m->mdns_support = RESOLVE_SUPPORT_NO;
m->dnssec_mode = DEFAULT_DNSSEC_MODE;
m->enable_cache = true;
+ m->dns_stub_listener_mode = DNS_STUB_LISTENER_UDP;
m->read_resolv_conf = true;
m->need_builtin_fallbacks = true;
m->etc_hosts_last = m->etc_hosts_mtime = USEC_INFINITY;
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index deebd8e484..6b2208ed94 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -30,6 +30,7 @@
typedef struct Manager Manager;
+#include "resolved-conf.h"
#include "resolved-dns-query.h"
#include "resolved-dns-search-domain.h"
#include "resolved-dns-server.h"
@@ -47,6 +48,7 @@ struct Manager {
ResolveSupport mdns_support;
DnssecMode dnssec_mode;
bool enable_cache;
+ DnsStubListenerMode dns_stub_listener_mode;
/* Network */
Hashmap *links;
diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c
index 31b25ca50f..801014caf5 100644
--- a/src/resolve/resolved-resolv-conf.c
+++ b/src/resolve/resolved-resolv-conf.c
@@ -154,6 +154,16 @@ static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
return;
}
+ /* Check if the DNS server is limited to particular domains;
+ * resolv.conf does not have a syntax to express that, so it must not
+ * appear as a global name server to avoid routing unrelated domains to
+ * it (which is a privacy violation, will most probably fail anyway,
+ * and adds unnecessary load) */
+ if (dns_server_limited_domains(s)) {
+ log_debug("DNS server %s has route-only domains, not using as global name server", dns_server_string(s));
+ return;
+ }
+
if (*count == MAXNS)
fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f);
(*count)++;
diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in
index 3bd8389c88..60afa151e3 100644
--- a/src/resolve/resolved.conf.in
+++ b/src/resolve/resolved.conf.in
@@ -18,3 +18,4 @@
#LLMNR=yes
#DNSSEC=@DEFAULT_DNSSEC_MODE@
#Cache=yes
+#DNSStubListener=udp
diff --git a/src/run/run.c b/src/run/run.c
index 2dd229868c..81b53fdfab 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -1168,17 +1168,21 @@ static int start_transient_scope(
uid_t uid;
gid_t gid;
- r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell);
+ r = get_user_creds_clean(&arg_exec_user, &uid, &gid, &home, &shell);
if (r < 0)
return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
- r = strv_extendf(&user_env, "HOME=%s", home);
- if (r < 0)
- return log_oom();
+ if (home) {
+ r = strv_extendf(&user_env, "HOME=%s", home);
+ if (r < 0)
+ return log_oom();
+ }
- r = strv_extendf(&user_env, "SHELL=%s", shell);
- if (r < 0)
- return log_oom();
+ if (shell) {
+ r = strv_extendf(&user_env, "SHELL=%s", shell);
+ if (r < 0)
+ return log_oom();
+ }
r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
if (r < 0)
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index 65151b19a6..2597cfc648 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -484,7 +484,7 @@ int ask_password_agent(
(void) mkdir_p_label("/run/systemd/ask-password", 0755);
- fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
+ fd = mkostemp_safe(temp);
if (fd < 0) {
r = fd;
goto finish;
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index feb4a06737..c6bd2f145c 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -204,7 +204,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges",
"SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute",
- "RestrictRealtime", "DynamicUser", "RemoveIPC")) {
+ "RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables", "ProtectControlGroups")) {
r = parse_boolean(eq);
if (r < 0)
diff --git a/src/shared/condition.c b/src/shared/condition.c
index 6bb42c0692..f13fa6a9fd 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -37,6 +37,7 @@
#include "condition.h"
#include "extract-word.h"
#include "fd-util.h"
+#include "fileio.h"
#include "glob-util.h"
#include "hostname-util.h"
#include "ima-util.h"
@@ -309,8 +310,45 @@ static int condition_test_needs_update(Condition *c) {
if (lstat("/usr/", &usr) < 0)
return true;
- return usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
- (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec);
+ /*
+ * First, compare seconds as they are always accurate...
+ */
+ if (usr.st_mtim.tv_sec != other.st_mtim.tv_sec)
+ return usr.st_mtim.tv_sec > other.st_mtim.tv_sec;
+
+ /*
+ * ...then compare nanoseconds.
+ *
+ * A false positive is only possible when /usr's nanoseconds > 0
+ * (otherwise /usr cannot be strictly newer than the target file)
+ * AND the target file's nanoseconds == 0
+ * (otherwise the filesystem supports nsec timestamps, see stat(2)).
+ */
+ if (usr.st_mtim.tv_nsec > 0 && other.st_mtim.tv_nsec == 0) {
+ _cleanup_free_ char *timestamp_str = NULL;
+ uint64_t timestamp;
+ int r;
+
+ r = parse_env_file(p, NULL, "TimestampNSec", &timestamp_str, NULL);
+ if (r < 0) {
+ log_error_errno(-r, "Failed to parse timestamp file '%s', using mtime: %m", p);
+ return true;
+ } else if (r == 0) {
+ log_debug("No data in timestamp file '%s', using mtime", p);
+ return true;
+ }
+
+ r = safe_atou64(timestamp_str, &timestamp);
+ if (r < 0) {
+ log_error_errno(-r, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m",
+ timestamp_str, p);
+ return true;
+ }
+
+ other.st_mtim.tv_nsec = timestamp % NSEC_PER_SEC;
+ }
+
+ return usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec;
}
static int condition_test_first_boot(Condition *c) {
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index f31d219418..2ec0155b71 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -396,22 +396,18 @@ int config_parse(const char *unit,
return 0;
}
-/* Parse each config file in the specified directories. */
-int config_parse_many(const char *conf_file,
- const char *conf_file_dirs,
- const char *sections,
- ConfigItemLookup lookup,
- const void *table,
- bool relaxed,
- void *userdata) {
- _cleanup_strv_free_ char **files = NULL;
+static int config_parse_many_files(
+ const char *conf_file,
+ char **files,
+ const char *sections,
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ void *userdata) {
+
char **fn;
int r;
- r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
- if (r < 0)
- return r;
-
if (conf_file) {
r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata);
if (r < 0)
@@ -427,6 +423,56 @@ int config_parse_many(const char *conf_file,
return 0;
}
+/* Parse each config file in the directories specified as nulstr. */
+int config_parse_many_nulstr(
+ const char *conf_file,
+ const char *conf_file_dirs,
+ const char *sections,
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ void *userdata) {
+
+ _cleanup_strv_free_ char **files = NULL;
+ int r;
+
+ r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
+ if (r < 0)
+ return r;
+
+ return config_parse_many_files(conf_file, files,
+ sections, lookup, table, relaxed, userdata);
+}
+
+/* Parse each config file in the directories specified as strv. */
+int config_parse_many(
+ const char *conf_file,
+ const char* const* conf_file_dirs,
+ const char *dropin_dirname,
+ const char *sections,
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ void *userdata) {
+
+ _cleanup_strv_free_ char **dropin_dirs = NULL;
+ _cleanup_strv_free_ char **files = NULL;
+ const char *suffix;
+ int r;
+
+ suffix = strjoina("/", dropin_dirname);
+ r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix);
+ if (r < 0)
+ return r;
+
+ r = conf_files_list_strv(&files, ".conf", NULL, (const char* const*) dropin_dirs);
+ if (r < 0)
+ return r;
+
+ return config_parse_many_files(conf_file, files,
+ sections, lookup, table, relaxed, userdata);
+}
+
#define DEFINE_PARSER(type, vartype, conv_func) \
int config_parse_##type( \
const char *unit, \
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
index 3298dc0cea..26ff3df16f 100644
--- a/src/shared/conf-parser.h
+++ b/src/shared/conf-parser.h
@@ -84,24 +84,36 @@ int config_item_table_lookup(const void *table, const char *section, const char
* ConfigPerfItem tables */
int config_item_perf_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
-int config_parse(const char *unit,
- const char *filename,
- FILE *f,
- const char *sections, /* nulstr */
- ConfigItemLookup lookup,
- const void *table,
- bool relaxed,
- bool allow_include,
- bool warn,
- void *userdata);
-
-int config_parse_many(const char *conf_file, /* possibly NULL */
- const char *conf_file_dirs, /* nulstr */
- const char *sections, /* nulstr */
- ConfigItemLookup lookup,
- const void *table,
- bool relaxed,
- void *userdata);
+int config_parse(
+ const char *unit,
+ const char *filename,
+ FILE *f,
+ const char *sections, /* nulstr */
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ bool allow_include,
+ bool warn,
+ void *userdata);
+
+int config_parse_many_nulstr(
+ const char *conf_file, /* possibly NULL */
+ const char *conf_file_dirs, /* nulstr */
+ const char *sections, /* nulstr */
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ void *userdata);
+
+int config_parse_many(
+ const char *conf_file, /* possibly NULL */
+ const char* const* conf_file_dirs,
+ const char *dropin_dirname,
+ const char *sections, /* nulstr */
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ void *userdata);
/* Generic parsers */
int config_parse_int(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c
index 835557c6b2..892f0aadf5 100644
--- a/src/shared/dns-domain.c
+++ b/src/shared/dns-domain.c
@@ -131,6 +131,10 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
if (r == 0 && *n)
return -EINVAL;
+ /* More than one trailing dot? */
+ if (*n == '.')
+ return -EINVAL;
+
if (sz >= 1 && d)
*d = 0;
diff --git a/src/shared/gcrypt-util.h b/src/shared/gcrypt-util.h
index cf33b3c59c..1da12a32be 100644
--- a/src/shared/gcrypt-util.h
+++ b/src/shared/gcrypt-util.h
@@ -37,3 +37,11 @@ static inline int string_hashsum_sha224(const char *s, size_t len, char **out) {
return -EOPNOTSUPP;
#endif
}
+
+static inline int string_hashsum_sha256(const char *s, size_t len, char **out) {
+#ifdef HAVE_GCRYPT
+ return string_hashsum(s, len, GCRY_MD_SHA256, out);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
diff --git a/src/shared/install.c b/src/shared/install.c
index 11770d887f..60a6d1312d 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -403,6 +403,9 @@ static bool chroot_symlinks_same(const char *root, const char *wd, const char *a
/* This will give incorrect results if the paths are relative and go outside
* of the chroot. False negatives are possible. */
+ if (!root)
+ root = "/";
+
a = strjoina(path_is_absolute(a) ? root : wd, "/", a);
b = strjoina(path_is_absolute(b) ? root : wd, "/", b);
return path_equal_or_files_same(a, b);
@@ -1017,7 +1020,7 @@ static int config_parse_alias(
type = unit_name_to_type(name);
if (!unit_type_may_alias(type))
return log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Aliases are not allowed for %s units, ignoring.",
+ "Alias= is not allowed for %s units, ignoring.",
unit_type_to_string(type));
return config_parse_strv(unit, filename, line, section, section_line,
@@ -1095,7 +1098,7 @@ static int config_parse_default_instance(
return 0;
if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE))
return log_syntax(unit, LOG_WARNING, filename, line, 0,
- "DefaultInstance only makes sense for template units, ignoring.");
+ "DefaultInstance= only makes sense for template units, ignoring.");
r = install_full_printf(i, rvalue, &printed);
if (r < 0)
diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c
index 2f42381fc1..8116c7671f 100644
--- a/src/shared/seccomp-util.c
+++ b/src/shared/seccomp-util.c
@@ -39,6 +39,10 @@ const char* seccomp_arch_to_string(uint32_t c) {
return "x32";
if (c == SCMP_ARCH_ARM)
return "arm";
+ if (c == SCMP_ARCH_S390)
+ return "s390";
+ if (c == SCMP_ARCH_S390X)
+ return "s390x";
return NULL;
}
@@ -59,6 +63,10 @@ int seccomp_arch_from_string(const char *n, uint32_t *ret) {
*ret = SCMP_ARCH_X32;
else if (streq(n, "arm"))
*ret = SCMP_ARCH_ARM;
+ else if (streq(n, "s390"))
+ *ret = SCMP_ARCH_S390;
+ else if (streq(n, "s390x"))
+ *ret = SCMP_ARCH_S390X;
else
return -EINVAL;
@@ -85,6 +93,20 @@ int seccomp_add_secondary_archs(scmp_filter_ctx *c) {
if (r < 0 && r != -EEXIST)
return r;
+#elif defined(__s390__) || defined(__s390x__)
+ int r;
+
+ /* Add in all possible secondary archs we are aware of that
+ * this kernel might support. */
+
+ r = seccomp_arch_add(c, SCMP_ARCH_S390);
+ if (r < 0 && r != -EEXIST)
+ return r;
+
+ r = seccomp_arch_add(c, SCMP_ARCH_S390X);
+ if (r < 0 && r != -EEXIST)
+ return r;
+
#endif
return 0;
diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c
index f00624d0f2..ed31a80c8d 100644
--- a/src/shared/sleep-config.c
+++ b/src/shared/sleep-config.c
@@ -58,7 +58,7 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states) {
{}
};
- config_parse_many(PKGSYSCONFDIR "/sleep.conf",
+ config_parse_many_nulstr(PKGSYSCONFDIR "/sleep.conf",
CONF_PATHS_NULSTR("systemd/sleep.conf.d"),
"Sleep\0", config_item_table_lookup, items,
false, NULL);
diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c
index ce7c26e7d3..fbc1e0eb1a 100644
--- a/src/sysctl/sysctl.c
+++ b/src/sysctl/sysctl.c
@@ -41,12 +41,12 @@ static char **arg_prefixes = NULL;
static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysctl.d");
-static int apply_all(Hashmap *sysctl_options) {
+static int apply_all(OrderedHashmap *sysctl_options) {
char *property, *value;
Iterator i;
int r = 0;
- HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) {
+ ORDERED_HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) {
int k;
k = sysctl_write(property, value);
@@ -62,7 +62,7 @@ static int apply_all(Hashmap *sysctl_options) {
return r;
}
-static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) {
+static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ignore_enoent) {
_cleanup_fclose_ FILE *f = NULL;
int r;
@@ -125,13 +125,13 @@ static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_eno
}
found:
- existing = hashmap_get2(sysctl_options, p, &v);
+ existing = ordered_hashmap_get2(sysctl_options, p, &v);
if (existing) {
if (streq(value, existing))
continue;
log_debug("Overwriting earlier assignment of %s in file '%s'.", p, path);
- free(hashmap_remove(sysctl_options, p));
+ free(ordered_hashmap_remove(sysctl_options, p));
free(v);
}
@@ -145,7 +145,7 @@ found:
return log_oom();
}
- k = hashmap_put(sysctl_options, property, new_value);
+ k = ordered_hashmap_put(sysctl_options, property, new_value);
if (k < 0) {
log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", property);
free(property);
@@ -230,7 +230,7 @@ static int parse_argv(int argc, char *argv[]) {
int main(int argc, char *argv[]) {
int r = 0, k;
- Hashmap *sysctl_options;
+ OrderedHashmap *sysctl_options;
r = parse_argv(argc, argv);
if (r <= 0)
@@ -242,7 +242,7 @@ int main(int argc, char *argv[]) {
umask(0022);
- sysctl_options = hashmap_new(&string_hash_ops);
+ sysctl_options = ordered_hashmap_new(&string_hash_ops);
if (!sysctl_options) {
r = log_oom();
goto finish;
@@ -280,7 +280,7 @@ int main(int argc, char *argv[]) {
r = k;
finish:
- hashmap_free_free_free(sysctl_options);
+ ordered_hashmap_free_free_free(sysctl_options);
strv_free(arg_prefixes);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 5912441168..18a8a4f248 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -118,6 +118,7 @@ static enum dependency {
} arg_dependency = DEPENDENCY_FORWARD;
static const char *arg_job_mode = "replace";
static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
+static bool arg_wait = false;
static bool arg_no_block = false;
static bool arg_no_legend = false;
static bool arg_no_pager = false;
@@ -2679,13 +2680,86 @@ static const char *method_to_verb(const char *method) {
return "n/a";
}
+typedef struct {
+ sd_bus_slot *match;
+ sd_event *event;
+ Set *unit_paths;
+ bool any_failed;
+} WaitContext;
+
+static void wait_context_free(WaitContext *c) {
+ c->match = sd_bus_slot_unref(c->match);
+ c->event = sd_event_unref(c->event);
+ c->unit_paths = set_free(c->unit_paths);
+}
+
+static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ WaitContext *c = userdata;
+ const char *path;
+ int r;
+
+ path = sd_bus_message_get_path(m);
+ if (!set_contains(c->unit_paths, path))
+ return 0;
+
+ /* Check if ActiveState changed to inactive/failed */
+ /* (s interface, a{sv} changed_properties, as invalidated_properties) */
+ r = sd_bus_message_skip(m, "s");
+ if (r < 0)
+ return bus_log_parse_error(r);
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
+ const char *s;
+ bool is_failed;
+
+ r = sd_bus_message_read(m, "s", &s);
+ if (r < 0)
+ return bus_log_parse_error(r);
+ if (streq(s, "ActiveState")) {
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, "s");
+ if (r < 0)
+ return bus_log_parse_error(r);
+ r = sd_bus_message_read(m, "s", &s);
+ if (r < 0)
+ return bus_log_parse_error(r);
+ is_failed = streq(s, "failed");
+ if (streq(s, "inactive") || is_failed) {
+ log_debug("%s became %s, dropping from --wait tracking", path, s);
+ set_remove(c->unit_paths, path);
+ c->any_failed |= is_failed;
+ } else
+ log_debug("ActiveState on %s changed to %s", path, s);
+ break; /* no need to dissect the rest of the message */
+ } else {
+ /* other property */
+ r = sd_bus_message_skip(m, "v");
+ if (r < 0)
+ return bus_log_parse_error(r);
+ }
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (set_isempty(c->unit_paths))
+ sd_event_exit(c->event, EXIT_SUCCESS);
+
+ return 0;
+}
+
static int start_unit_one(
sd_bus *bus,
const char *method,
const char *name,
const char *mode,
sd_bus_error *error,
- BusWaitForJobs *w) {
+ BusWaitForJobs *w,
+ WaitContext *wait_context) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *path;
@@ -2696,6 +2770,40 @@ static int start_unit_one(
assert(mode);
assert(error);
+ if (wait_context) {
+ _cleanup_free_ char *unit_path = NULL;
+ const char* mt;
+
+ log_debug("Watching for property changes of %s", name);
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "RefUnit",
+ error,
+ NULL,
+ "s", name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to RefUnit %s: %s", name, bus_error_message(error, r));
+
+ unit_path = unit_dbus_path_from_name(name);
+ if (!unit_path)
+ return log_oom();
+
+ r = set_put_strdup(wait_context->unit_paths, unit_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add unit path %s to set: %m", unit_path);
+
+ mt = strjoina("type='signal',"
+ "interface='org.freedesktop.DBus.Properties',"
+ "path='", unit_path, "',"
+ "member='PropertiesChanged'");
+ r = sd_bus_add_match(bus, &wait_context->match, mt, on_properties_changed, wait_context);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add match for PropertiesChanged signal: %m");
+ }
+
log_debug("Calling manager for %s on %s, %s", method, name, mode);
r = sd_bus_call_method(
@@ -2841,10 +2949,18 @@ static int start_unit(int argc, char *argv[], void *userdata) {
const char *method, *mode, *one_name, *suffix = NULL;
_cleanup_strv_free_ char **names = NULL;
sd_bus *bus;
+ _cleanup_(wait_context_free) WaitContext wait_context = {};
char **name;
int r = 0;
- r = acquire_bus(BUS_MANAGER, &bus);
+ if (arg_wait && !strstr(argv[0], "start")) {
+ log_error("--wait may only be used with a command that starts units.");
+ return -EINVAL;
+ }
+
+ /* we cannot do sender tracking on the private bus, so we need the full
+ * one for RefUnit to implement --wait */
+ r = acquire_bus(arg_wait ? BUS_FULL : BUS_MANAGER, &bus);
if (r < 0)
return r;
@@ -2888,11 +3004,36 @@ static int start_unit(int argc, char *argv[], void *userdata) {
return log_error_errno(r, "Could not watch jobs: %m");
}
+ if (arg_wait) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ wait_context.unit_paths = set_new(&string_hash_ops);
+ if (!wait_context.unit_paths)
+ return log_oom();
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "Subscribe",
+ &error,
+ NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable subscription: %s", bus_error_message(&error, r));
+ r = sd_event_default(&wait_context.event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate event loop: %m");
+ r = sd_bus_attach_event(bus, wait_context.event, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach bus to event loop: %m");
+ }
+
STRV_FOREACH(name, names) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int q;
- q = start_unit_one(bus, method, *name, mode, &error, w);
+ q = start_unit_one(bus, method, *name, mode, &error, w, arg_wait ? &wait_context : NULL);
if (r >= 0 && q < 0)
r = translate_bus_error_to_exit_status(q, &error);
}
@@ -2924,6 +3065,15 @@ static int start_unit(int argc, char *argv[], void *userdata) {
check_triggering_units(bus, *name);
}
+ if (r >= 0 && arg_wait) {
+ int q;
+ q = sd_event_loop(wait_context.event);
+ if (q < 0)
+ return log_error_errno(q, "Failed to run event loop: %m");
+ if (wait_context.any_failed)
+ r = EXIT_FAILURE;
+ }
+
return r;
}
@@ -3121,7 +3271,7 @@ static int logind_check_inhibitors(enum action a) {
if (sd_session_get_class(*s, &class) < 0 || !streq(class, "user"))
continue;
- if (sd_session_get_type(*s, &type) < 0 || (!streq(type, "x11") && !streq(type, "tty")))
+ if (sd_session_get_type(*s, &type) < 0 || !STR_IN_SET(type, "x11", "tty"))
continue;
sd_session_get_tty(*s, &tty);
@@ -3622,7 +3772,7 @@ static void print_status_info(
if (streq_ptr(i->active_state, "failed")) {
active_on = ansi_highlight_red();
active_off = ansi_normal();
- } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
+ } else if (STRPTR_IN_SET(i->active_state, "active", "reloading")) {
active_on = ansi_highlight_green();
active_off = ansi_normal();
} else
@@ -3703,12 +3853,10 @@ static void print_status_info(
if (!isempty(i->result) && !streq(i->result, "success"))
printf(" (Result: %s)", i->result);
- timestamp = (streq_ptr(i->active_state, "active") ||
- streq_ptr(i->active_state, "reloading")) ? i->active_enter_timestamp :
- (streq_ptr(i->active_state, "inactive") ||
- streq_ptr(i->active_state, "failed")) ? i->inactive_enter_timestamp :
- streq_ptr(i->active_state, "activating") ? i->inactive_exit_timestamp :
- i->active_exit_timestamp;
+ timestamp = STRPTR_IN_SET(i->active_state, "active", "reloading") ? i->active_enter_timestamp :
+ STRPTR_IN_SET(i->active_state, "inactive", "failed") ? i->inactive_enter_timestamp :
+ STRPTR_IN_SET(i->active_state, "activating") ? i->inactive_exit_timestamp :
+ i->active_exit_timestamp;
s1 = format_timestamp_relative(since1, sizeof(since1), timestamp);
s2 = format_timestamp(since2, sizeof(since2), timestamp);
@@ -3788,7 +3936,7 @@ static void print_status_info(
argv = strv_join(p->argv, " ");
printf(" Process: "PID_FMT" %s=%s ", p->pid, p->name, strna(argv));
- good = is_clean_exit_lsb(p->code, p->status, NULL);
+ good = is_clean_exit(p->code, p->status, EXIT_CLEAN_DAEMON, NULL);
if (!good) {
on = ansi_highlight_red();
off = ansi_normal();
@@ -4583,7 +4731,8 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
return 0;
- } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && (streq(name, "IODeviceWeight") || streq(name, "BlockIODeviceWeight"))) {
+ } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN &&
+ STR_IN_SET(name, "IODeviceWeight", "BlockIODeviceWeight")) {
const char *path;
uint64_t weight;
@@ -4602,8 +4751,9 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
return 0;
- } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && (cgroup_io_limit_type_from_string(name) >= 0 ||
- streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth"))) {
+ } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN &&
+ (cgroup_io_limit_type_from_string(name) >= 0 ||
+ STR_IN_SET(name, "BlockIOReadBandwidth", "BlockIOWriteBandwidth"))) {
const char *path;
uint64_t bandwidth;
@@ -4695,12 +4845,14 @@ static int show_one(
return log_error_errno(r, "Failed to map properties: %s", bus_error_message(&error, r));
if (streq_ptr(info.load_state, "not-found") && streq_ptr(info.active_state, "inactive")) {
- log_error("Unit %s could not be found.", unit);
+ log_full(streq(verb, "status") ? LOG_ERR : LOG_DEBUG,
+ "Unit %s could not be found.", unit);
if (streq(verb, "status"))
return EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN;
- return -ENOENT;
+ if (!streq(verb, "show"))
+ return -ENOENT;
}
r = sd_bus_message_rewind(reply, true);
@@ -4765,10 +4917,11 @@ static int show_one(
r = 0;
if (show_properties) {
char **pp;
+ int not_found_level = streq(verb, "show") ? LOG_DEBUG : LOG_WARNING;
STRV_FOREACH(pp, arg_properties)
if (!set_contains(found_properties, *pp)) {
- log_warning("Property %s does not exist.", *pp);
+ log_full(not_found_level, "Property %s does not exist.", *pp);
r = -ENXIO;
}
@@ -6584,6 +6737,7 @@ static void systemctl_help(void) {
" -s --signal=SIGNAL Which signal to send\n"
" --now Start or stop unit in addition to enabling or disabling it\n"
" -q --quiet Suppress output\n"
+ " --wait For (re)start, wait until service stopped again\n"
" --no-block Do not wait until operation finished\n"
" --no-wall Don't send wall message before halt/power-off/reboot\n"
" --no-reload Don't reload daemon after en-/dis-abling unit files\n"
@@ -6854,6 +7008,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
ARG_FIRMWARE_SETUP,
ARG_NOW,
ARG_MESSAGE,
+ ARG_WAIT,
};
static const struct option options[] = {
@@ -6877,6 +7032,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "user", no_argument, NULL, ARG_USER },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "global", no_argument, NULL, ARG_GLOBAL },
+ { "wait", no_argument, NULL, ARG_WAIT },
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
@@ -7057,6 +7213,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_scope = UNIT_FILE_GLOBAL;
break;
+ case ARG_WAIT:
+ arg_wait = true;
+ break;
+
case ARG_NO_BLOCK:
arg_no_block = true;
break;
@@ -7232,6 +7392,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
+ if (arg_wait && arg_no_block) {
+ log_error("--wait may not be combined with --no-block.");
+ return -EINVAL;
+ }
+
return 1;
}
diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h
index 3c44d63021..79246ae060 100644
--- a/src/systemd/sd-messages.h
+++ b/src/systemd/sd-messages.h
@@ -40,6 +40,7 @@ _SD_BEGIN_DECLARATIONS;
#define SD_MESSAGE_JOURNAL_USAGE SD_ID128_MAKE(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6)
#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)
+#define SD_MESSAGE_TRUNCATED_CORE SD_ID128_MAKE(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37)
#define SD_MESSAGE_SESSION_START SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66)
#define SD_MESSAGE_SESSION_STOP SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a)
diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c
index 39821687b9..c2c80175a2 100644
--- a/src/sysv-generator/sysv-generator.c
+++ b/src/sysv-generator/sysv-generator.c
@@ -25,6 +25,7 @@
#include "alloc-util.h"
#include "dirent-util.h"
+#include "exit-status.h"
#include "fd-util.h"
#include "fileio.h"
#include "hashmap.h"
@@ -199,6 +200,13 @@ static int generate_unit_file(SysvStub *s) {
if (s->pid_file)
fprintf(f, "PIDFile=%s\n", s->pid_file);
+ /* Consider two special LSB exit codes a clean exit */
+ if (s->has_lsb)
+ fprintf(f,
+ "SuccessExitStatus=%i %i\n",
+ EXIT_NOTINSTALLED,
+ EXIT_NOTCONFIGURED);
+
fprintf(f,
"ExecStart=%s start\n"
"ExecStop=%s stop\n",
diff --git a/src/test/test-acl-util.c b/src/test/test-acl-util.c
index 430dda8e78..5b572bb0bf 100644
--- a/src/test/test-acl-util.c
+++ b/src/test/test-acl-util.c
@@ -35,7 +35,7 @@ static void test_add_acls_for_user(void) {
uid_t uid;
int r;
- fd = mkostemp_safe(fn, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(fn);
assert_se(fd >= 0);
/* Use the mode that user journal files use */
diff --git a/src/test/test-async.c b/src/test/test-async.c
index ada6d67c42..4ebc27f0bd 100644
--- a/src/test/test-async.c
+++ b/src/test/test-async.c
@@ -36,7 +36,7 @@ int main(int argc, char *argv[]) {
int fd;
char name[] = "/tmp/test-asynchronous_close.XXXXXX";
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
asynchronous_close(fd);
diff --git a/src/test/test-clock.c b/src/test/test-clock.c
index 84f775e5bc..7d97328416 100644
--- a/src/test/test-clock.c
+++ b/src/test/test-clock.c
@@ -55,7 +55,7 @@ static void test_clock_is_localtime(void) {
/* without an adjtime file we default to UTC */
assert_se(clock_is_localtime("/nonexisting/adjtime") == 0);
- fd = mkostemp_safe(adjtime, O_WRONLY|O_CLOEXEC);
+ fd = mkostemp_safe(adjtime);
assert_se(fd >= 0);
log_info("adjtime test file: %s", adjtime);
f = fdopen(fd, "w");
diff --git a/src/test/test-copy.c b/src/test/test-copy.c
index 68154fc4e8..ed1ea51dbd 100644
--- a/src/test/test-copy.c
+++ b/src/test/test-copy.c
@@ -42,11 +42,11 @@ static void test_copy_file(void) {
log_info("%s", __func__);
- fd = mkostemp_safe(fn, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(fn);
assert_se(fd >= 0);
close(fd);
- fd = mkostemp_safe(fn_copy, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(fn_copy);
assert_se(fd >= 0);
close(fd);
@@ -71,9 +71,9 @@ static void test_copy_file_fd(void) {
log_info("%s", __func__);
- in_fd = mkostemp_safe(in_fn, O_RDWR);
+ in_fd = mkostemp_safe(in_fn);
assert_se(in_fd >= 0);
- out_fd = mkostemp_safe(out_fn, O_RDWR);
+ out_fd = mkostemp_safe(out_fn);
assert_se(out_fd >= 0);
assert_se(write_string_file(in_fn, text, WRITE_STRING_FILE_CREATE) == 0);
@@ -207,10 +207,10 @@ static void test_copy_bytes_regular_file(const char *src, bool try_reflink, uint
fd = open(src, O_RDONLY | O_CLOEXEC | O_NOCTTY);
assert_se(fd >= 0);
- fd2 = mkostemp_safe(fn2, O_RDWR);
+ fd2 = mkostemp_safe(fn2);
assert_se(fd2 >= 0);
- fd3 = mkostemp_safe(fn3, O_WRONLY);
+ fd3 = mkostemp_safe(fn3);
assert_se(fd3 >= 0);
r = copy_bytes(fd, fd2, max_bytes, try_reflink);
diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c
index a9d09f59bc..e2f097c95e 100644
--- a/src/test/test-dns-domain.c
+++ b/src/test/test-dns-domain.c
@@ -48,6 +48,7 @@ static void test_dns_label_unescape(void) {
test_dns_label_unescape_one("..", "", 20, -EINVAL);
test_dns_label_unescape_one(".foobar", "", 20, -EINVAL);
test_dns_label_unescape_one("foobar.", "foobar", 20, 6);
+ test_dns_label_unescape_one("foobar..", "foobar", 20, -EINVAL);
}
static void test_dns_name_to_wire_format_one(const char *what, const char *expect, size_t buffer_sz, int ret) {
@@ -359,6 +360,7 @@ static void test_dns_name_is_valid_one(const char *s, int ret) {
static void test_dns_name_is_valid(void) {
test_dns_name_is_valid_one("foo", 1);
test_dns_name_is_valid_one("foo.", 1);
+ test_dns_name_is_valid_one("foo..", 0);
test_dns_name_is_valid_one("Foo", 1);
test_dns_name_is_valid_one("foo.bar", 1);
test_dns_name_is_valid_one("foo.bar.baz", 1);
@@ -366,6 +368,7 @@ static void test_dns_name_is_valid(void) {
test_dns_name_is_valid_one("foo..bar", 0);
test_dns_name_is_valid_one(".foo.bar", 0);
test_dns_name_is_valid_one("foo.bar.", 1);
+ test_dns_name_is_valid_one("foo.bar..", 0);
test_dns_name_is_valid_one("\\zbar", 0);
test_dns_name_is_valid_one("ä", 1);
test_dns_name_is_valid_one("\n", 0);
diff --git a/src/test/test-engine.c b/src/test/test-engine.c
index 23da10fa1a..a651f6b683 100644
--- a/src/test/test-engine.c
+++ b/src/test/test-engine.c
@@ -43,7 +43,7 @@ int main(int argc, char *argv[]) {
assert_se(set_unit_path(TEST_DIR) >= 0);
r = manager_new(UNIT_FILE_USER, true, &m);
if (MANAGER_SKIP_TEST(r)) {
- printf("Skipping test: manager_new: %s\n", strerror(-r));
+ log_notice_errno(r, "Skipping test: manager_new: %m");
return EXIT_TEST_SKIP;
}
assert_se(r >= 0);
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index 05ec1d2eb1..8b4ff22495 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -133,6 +133,28 @@ static void test_exec_privatedevices(Manager *m) {
test(m, "exec-privatedevices-no.service", 0, CLD_EXITED);
}
+static void test_exec_privatedevices_capabilities(Manager *m) {
+ if (detect_container() > 0) {
+ log_notice("testing in container, skipping private device tests");
+ return;
+ }
+ test(m, "exec-privatedevices-yes-capability-mknod.service", 0, CLD_EXITED);
+ test(m, "exec-privatedevices-no-capability-mknod.service", 0, CLD_EXITED);
+}
+
+static void test_exec_readonlypaths(Manager *m) {
+ test(m, "exec-readonlypaths.service", 0, CLD_EXITED);
+ test(m, "exec-readonlypaths-mount-propagation.service", 0, CLD_EXITED);
+}
+
+static void test_exec_readwritepaths(Manager *m) {
+ test(m, "exec-readwritepaths-mount-propagation.service", 0, CLD_EXITED);
+}
+
+static void test_exec_inaccessiblepaths(Manager *m) {
+ test(m, "exec-inaccessiblepaths-mount-propagation.service", 0, CLD_EXITED);
+}
+
static void test_exec_systemcallfilter(Manager *m) {
#ifdef HAVE_SECCOMP
if (!is_seccomp_available())
@@ -324,7 +346,7 @@ static int run_tests(UnitFileScope scope, test_function_t *tests) {
r = manager_new(scope, true, &m);
if (MANAGER_SKIP_TEST(r)) {
- printf("Skipping test: manager_new: %s\n", strerror(-r));
+ log_notice_errno(r, "Skipping test: manager_new: %m");
return EXIT_TEST_SKIP;
}
assert_se(r >= 0);
@@ -345,6 +367,10 @@ int main(int argc, char *argv[]) {
test_exec_ignoresigpipe,
test_exec_privatetmp,
test_exec_privatedevices,
+ test_exec_privatedevices_capabilities,
+ test_exec_readonlypaths,
+ test_exec_readwritepaths,
+ test_exec_inaccessiblepaths,
test_exec_privatenetwork,
test_exec_systemcallfilter,
test_exec_systemcallerrornumber,
diff --git a/src/test/test-fd-util.c b/src/test/test-fd-util.c
index 421d3bdeb3..f555bb976c 100644
--- a/src/test/test-fd-util.c
+++ b/src/test/test-fd-util.c
@@ -31,9 +31,9 @@ static void test_close_many(void) {
char name1[] = "/tmp/test-close-many.XXXXXX";
char name2[] = "/tmp/test-close-many.XXXXXX";
- fds[0] = mkostemp_safe(name0, O_RDWR|O_CLOEXEC);
- fds[1] = mkostemp_safe(name1, O_RDWR|O_CLOEXEC);
- fds[2] = mkostemp_safe(name2, O_RDWR|O_CLOEXEC);
+ fds[0] = mkostemp_safe(name0);
+ fds[1] = mkostemp_safe(name1);
+ fds[2] = mkostemp_safe(name2);
close_many(fds, 2);
@@ -52,7 +52,7 @@ static void test_close_nointr(void) {
char name[] = "/tmp/test-test-close_nointr.XXXXXX";
int fd;
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se(close_nointr(fd) >= 0);
assert_se(close_nointr(fd) < 0);
diff --git a/src/test/test-fdset.c b/src/test/test-fdset.c
index 282aab1246..adbf99a7ec 100644
--- a/src/test/test-fdset.c
+++ b/src/test/test-fdset.c
@@ -31,7 +31,7 @@ static void test_fdset_new_fill(void) {
_cleanup_fdset_free_ FDSet *fdset = NULL;
char name[] = "/tmp/test-fdset_new_fill.XXXXXX";
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se(fdset_new_fill(&fdset) >= 0);
assert_se(fdset_contains(fdset, fd));
@@ -45,7 +45,7 @@ static void test_fdset_put_dup(void) {
_cleanup_fdset_free_ FDSet *fdset = NULL;
char name[] = "/tmp/test-fdset_put_dup.XXXXXX";
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
fdset = fdset_new();
@@ -64,7 +64,7 @@ static void test_fdset_cloexec(void) {
int flags = -1;
char name[] = "/tmp/test-fdset_cloexec.XXXXXX";
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
fdset = fdset_new();
@@ -91,7 +91,7 @@ static void test_fdset_close_others(void) {
int flags = -1;
char name[] = "/tmp/test-fdset_close_others.XXXXXX";
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
fdset = fdset_new();
@@ -113,7 +113,7 @@ static void test_fdset_remove(void) {
FDSet *fdset = NULL;
char name[] = "/tmp/test-fdset_remove.XXXXXX";
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
fdset = fdset_new();
@@ -136,7 +136,7 @@ static void test_fdset_iterate(void) {
int c = 0;
int a;
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
fdset = fdset_new();
@@ -161,7 +161,7 @@ static void test_fdset_isempty(void) {
_cleanup_fdset_free_ FDSet *fdset = NULL;
char name[] = "/tmp/test-fdset_isempty.XXXXXX";
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
fdset = fdset_new();
@@ -179,7 +179,7 @@ static void test_fdset_steal_first(void) {
_cleanup_fdset_free_ FDSet *fdset = NULL;
char name[] = "/tmp/test-fdset_steal_first.XXXXXX";
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
fdset = fdset_new();
diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c
index 79609765e0..92663ef66f 100644
--- a/src/test/test-fileio.c
+++ b/src/test/test-fileio.c
@@ -45,11 +45,11 @@ static void test_parse_env_file(void) {
char **i;
unsigned k;
- fd = mkostemp_safe(p, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(p);
assert_se(fd >= 0);
close(fd);
- fd = mkostemp_safe(t, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(t);
assert_se(fd >= 0);
f = fdopen(fd, "w");
@@ -158,11 +158,11 @@ static void test_parse_multiline_env_file(void) {
_cleanup_strv_free_ char **a = NULL, **b = NULL;
char **i;
- fd = mkostemp_safe(p, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(p);
assert_se(fd >= 0);
close(fd);
- fd = mkostemp_safe(t, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(t);
assert_se(fd >= 0);
f = fdopen(fd, "w");
@@ -211,7 +211,7 @@ static void test_executable_is_script(void) {
FILE *f;
char *command;
- fd = mkostemp_safe(t, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(t);
assert_se(fd >= 0);
f = fdopen(fd, "w");
@@ -300,7 +300,7 @@ static void test_write_string_stream(void) {
int fd;
char buf[64];
- fd = mkostemp_safe(fn, O_RDWR);
+ fd = mkostemp_safe(fn);
assert_se(fd >= 0);
f = fdopen(fd, "r");
@@ -334,7 +334,7 @@ static void test_write_string_file(void) {
char buf[64] = {};
_cleanup_close_ int fd;
- fd = mkostemp_safe(fn, O_RDWR);
+ fd = mkostemp_safe(fn);
assert_se(fd >= 0);
assert_se(write_string_file(fn, "boohoo", WRITE_STRING_FILE_CREATE) == 0);
@@ -350,7 +350,7 @@ static void test_write_string_file_no_create(void) {
_cleanup_close_ int fd;
char buf[64] = {0};
- fd = mkostemp_safe(fn, O_RDWR);
+ fd = mkostemp_safe(fn);
assert_se(fd >= 0);
assert_se(write_string_file("/a/file/which/does/not/exists/i/guess", "boohoo", 0) < 0);
@@ -390,7 +390,7 @@ static void test_load_env_file_pairs(void) {
_cleanup_strv_free_ char **l = NULL;
char **k, **v;
- fd = mkostemp_safe(fn, O_RDWR);
+ fd = mkostemp_safe(fn);
assert_se(fd >= 0);
r = write_string_file(fn,
@@ -433,7 +433,7 @@ static void test_search_and_fopen(void) {
int r;
FILE *f;
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
close(fd);
@@ -469,7 +469,7 @@ static void test_search_and_fopen_nulstr(void) {
int r;
FILE *f;
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
close(fd);
@@ -504,7 +504,7 @@ static void test_writing_tmpfile(void) {
IOVEC_SET_STRING(iov[1], ALPHANUMERICAL "\n");
IOVEC_SET_STRING(iov[2], "");
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
printf("tmpfile: %s", name);
r = writev(fd, iov, 3);
diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c
index 93eec3ef9c..53a3cdc663 100644
--- a/src/test/test-fs-util.c
+++ b/src/test/test-fs-util.c
@@ -20,21 +20,114 @@
#include <unistd.h>
#include "alloc-util.h"
-#include "fileio.h"
#include "fd-util.h"
+#include "fileio.h"
#include "fs-util.h"
#include "macro.h"
#include "mkdir.h"
+#include "path-util.h"
#include "rm-rf.h"
#include "string-util.h"
#include "strv.h"
#include "util.h"
+static void test_chase_symlinks(void) {
+ _cleanup_free_ char *result = NULL;
+ char temp[] = "/tmp/test-chase.XXXXXX";
+ const char *top, *p, *q;
+ int r;
+
+ assert_se(mkdtemp(temp));
+
+ top = strjoina(temp, "/top");
+ assert_se(mkdir(top, 0700) >= 0);
+
+ p = strjoina(top, "/dot");
+ assert_se(symlink(".", p) >= 0);
+
+ p = strjoina(top, "/dotdot");
+ assert_se(symlink("..", p) >= 0);
+
+ p = strjoina(top, "/dotdota");
+ assert_se(symlink("../a", p) >= 0);
+
+ p = strjoina(temp, "/a");
+ assert_se(symlink("b", p) >= 0);
+
+ p = strjoina(temp, "/b");
+ assert_se(symlink("/usr", p) >= 0);
+
+ p = strjoina(temp, "/start");
+ assert_se(symlink("top/dot/dotdota", p) >= 0);
+
+ r = chase_symlinks(p, NULL, &result);
+ assert_se(r >= 0);
+ assert_se(path_equal(result, "/usr"));
+
+ result = mfree(result);
+ r = chase_symlinks(p, temp, &result);
+ assert_se(r == -ENOENT);
+
+ q = strjoina(temp, "/usr");
+ assert_se(mkdir(q, 0700) >= 0);
+
+ r = chase_symlinks(p, temp, &result);
+ assert_se(r >= 0);
+ assert_se(path_equal(result, q));
+
+ p = strjoina(temp, "/slash");
+ assert_se(symlink("/", p) >= 0);
+
+ result = mfree(result);
+ r = chase_symlinks(p, NULL, &result);
+ assert_se(r >= 0);
+ assert_se(path_equal(result, "/"));
+
+ result = mfree(result);
+ r = chase_symlinks(p, temp, &result);
+ assert_se(r >= 0);
+ assert_se(path_equal(result, temp));
+
+ p = strjoina(temp, "/slashslash");
+ assert_se(symlink("///usr///", p) >= 0);
+
+ result = mfree(result);
+ r = chase_symlinks(p, NULL, &result);
+ assert_se(r >= 0);
+ assert_se(path_equal(result, "/usr"));
+
+ result = mfree(result);
+ r = chase_symlinks(p, temp, &result);
+ assert_se(r >= 0);
+ assert_se(path_equal(result, q));
+
+ result = mfree(result);
+ r = chase_symlinks("/etc/./.././", NULL, &result);
+ assert_se(r >= 0);
+ assert_se(path_equal(result, "/"));
+
+ result = mfree(result);
+ r = chase_symlinks("/etc/./.././", "/etc", &result);
+ assert_se(r == -EINVAL);
+
+ result = mfree(result);
+ r = chase_symlinks("/etc/machine-id/foo", NULL, &result);
+ assert_se(r == -ENOTDIR);
+
+ result = mfree(result);
+ p = strjoina(temp, "/recursive-symlink");
+ assert_se(symlink("recursive-symlink", p) >= 0);
+ r = chase_symlinks(p, NULL, &result);
+ assert_se(r == -ELOOP);
+
+ assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+}
+
static void test_unlink_noerrno(void) {
char name[] = "/tmp/test-close_nointr.XXXXXX";
int fd;
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se(close_nointr(fd) >= 0);
@@ -83,7 +176,7 @@ static void test_get_files_in_directory(void) {
}
static void test_var_tmp(void) {
- _cleanup_free_ char *tmpdir_backup = NULL;
+ _cleanup_free_ char *tmpdir_backup = NULL, *temp_backup = NULL, *tmp_backup = NULL;
const char *tmp_dir = NULL, *t;
t = getenv("TMPDIR");
@@ -92,7 +185,21 @@ static void test_var_tmp(void) {
assert_se(tmpdir_backup);
}
+ t = getenv("TEMP");
+ if (t) {
+ temp_backup = strdup(t);
+ assert_se(temp_backup);
+ }
+
+ t = getenv("TMP");
+ if (t) {
+ tmp_backup = strdup(t);
+ assert_se(tmp_backup);
+ }
+
assert(unsetenv("TMPDIR") >= 0);
+ assert(unsetenv("TEMP") >= 0);
+ assert(unsetenv("TMP") >= 0);
assert_se(var_tmp_dir(&tmp_dir) >= 0);
assert_se(streq(tmp_dir, "/var/tmp"));
@@ -113,6 +220,16 @@ static void test_var_tmp(void) {
assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0);
assert_se(streq(getenv("TMPDIR"), tmpdir_backup));
}
+
+ if (temp_backup) {
+ assert_se(setenv("TEMP", temp_backup, true) >= 0);
+ assert_se(streq(getenv("TEMP"), temp_backup));
+ }
+
+ if (tmp_backup) {
+ assert_se(setenv("TMP", tmp_backup, true) >= 0);
+ assert_se(streq(getenv("TMP"), tmp_backup));
+ }
}
int main(int argc, char *argv[]) {
@@ -120,6 +237,7 @@ int main(int argc, char *argv[]) {
test_readlink_and_make_absolute();
test_get_files_in_directory();
test_var_tmp();
+ test_chase_symlinks();
return 0;
}
diff --git a/src/test/test-glob-util.c b/src/test/test-glob-util.c
index 227d4290f0..9eea3eb608 100644
--- a/src/test/test-glob-util.c
+++ b/src/test/test-glob-util.c
@@ -30,7 +30,7 @@ static void test_glob_exists(void) {
int fd = -1;
int r;
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
close(fd);
diff --git a/src/test/test-hostname-util.c b/src/test/test-hostname-util.c
index 1c3d13ed1d..d2c3ea5e0d 100644
--- a/src/test/test-hostname-util.c
+++ b/src/test/test-hostname-util.c
@@ -104,7 +104,7 @@ static void test_read_hostname_config(void) {
char *hostname;
int fd;
- fd = mkostemp_safe(path, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(path);
assert(fd > 0);
close(fd);
diff --git a/src/test/test-list.c b/src/test/test-list.c
index 160064d06a..0ccd745cc9 100644
--- a/src/test/test-list.c
+++ b/src/test/test-list.c
@@ -132,6 +132,29 @@ int main(int argc, const char *argv[]) {
assert_se(items[1].item_prev == &items[3]);
assert_se(items[3].item_prev == NULL);
+ LIST_INSERT_BEFORE(item, head, &items[3], &items[0]);
+ assert_se(items[2].item_next == NULL);
+ assert_se(items[1].item_next == &items[2]);
+ assert_se(items[3].item_next == &items[1]);
+ assert_se(items[0].item_next == &items[3]);
+
+ assert_se(items[2].item_prev == &items[1]);
+ assert_se(items[1].item_prev == &items[3]);
+ assert_se(items[3].item_prev == &items[0]);
+ assert_se(items[0].item_prev == NULL);
+ assert_se(head == &items[0]);
+
+ LIST_REMOVE(item, head, &items[0]);
+ assert_se(LIST_JUST_US(item, &items[0]));
+
+ assert_se(items[2].item_next == NULL);
+ assert_se(items[1].item_next == &items[2]);
+ assert_se(items[3].item_next == &items[1]);
+
+ assert_se(items[2].item_prev == &items[1]);
+ assert_se(items[1].item_prev == &items[3]);
+ assert_se(items[3].item_prev == NULL);
+
LIST_INSERT_BEFORE(item, head, NULL, &items[0]);
assert_se(items[0].item_next == NULL);
assert_se(items[2].item_next == &items[0]);
diff --git a/src/test/test-ns.c b/src/test/test-ns.c
index 9248f2987c..c4d4da6d05 100644
--- a/src/test/test-ns.c
+++ b/src/test/test-ns.c
@@ -26,13 +26,18 @@
int main(int argc, char *argv[]) {
const char * const writable[] = {
"/home",
+ "-/home/lennart/projects/foobar", /* this should be masked automatically */
NULL
};
const char * const readonly[] = {
- "/",
- "/usr",
+ /* "/", */
+ /* "/usr", */
"/boot",
+ "/lib",
+ "/usr/lib",
+ "-/lib64",
+ "-/usr/lib64",
NULL
};
@@ -42,11 +47,12 @@ int main(int argc, char *argv[]) {
};
char *root_directory;
char *projects_directory;
-
int r;
char tmp_dir[] = "/tmp/systemd-private-XXXXXX",
var_tmp_dir[] = "/var/tmp/systemd-private-XXXXXX";
+ log_set_max_level(LOG_DEBUG);
+
assert_se(mkdtemp(tmp_dir));
assert_se(mkdtemp(var_tmp_dir));
@@ -69,6 +75,8 @@ int main(int argc, char *argv[]) {
tmp_dir,
var_tmp_dir,
true,
+ true,
+ true,
PROTECT_HOME_NO,
PROTECT_SYSTEM_NO,
0);
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
index 164a10d8a8..0b10d8e25e 100644
--- a/src/test/test-path-util.c
+++ b/src/test/test-path-util.c
@@ -511,7 +511,24 @@ static void test_hidden_or_backup_file(void) {
assert_se(!hidden_or_backup_file("test.dpkg-old.foo"));
}
+static void test_systemd_installation_has_version(const char *path) {
+ int r;
+ const unsigned versions[] = {0, 231, atoi(PACKAGE_VERSION), 999};
+ unsigned i;
+
+ for (i = 0; i < ELEMENTSOF(versions); i++) {
+ r = systemd_installation_has_version(path, versions[i]);
+ assert_se(r >= 0);
+ log_info("%s has systemd >= %u: %s",
+ path ?: "Current installation", versions[i], yes_no(r));
+ }
+}
+
int main(int argc, char **argv) {
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
test_path();
test_find_binary(argv[0]);
test_prefixes();
@@ -526,5 +543,7 @@ int main(int argc, char **argv) {
test_filename_is_valid();
test_hidden_or_backup_file();
+ test_systemd_installation_has_version(argv[1]); /* NULL is OK */
+
return 0;
}
diff --git a/src/test/test-path.c b/src/test/test-path.c
index 62181e22a0..4d3f0e9948 100644
--- a/src/test/test-path.c
+++ b/src/test/test-path.c
@@ -47,7 +47,7 @@ static int setup_test(Manager **m) {
r = manager_new(UNIT_FILE_USER, true, &tmp);
if (MANAGER_SKIP_TEST(r)) {
- printf("Skipping test: manager_new: %s\n", strerror(-r));
+ log_notice_errno(r, "Skipping test: manager_new: %m");
return -EXIT_TEST_SKIP;
}
assert_se(r >= 0);
diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c
index c068f5c39e..7b37910c33 100644
--- a/src/test/test-sched-prio.c
+++ b/src/test/test-sched-prio.c
@@ -40,7 +40,7 @@ int main(int argc, char *argv[]) {
assert_se(set_unit_path(TEST_DIR) >= 0);
r = manager_new(UNIT_FILE_USER, true, &m);
if (MANAGER_SKIP_TEST(r)) {
- printf("Skipping test: manager_new: %s\n", strerror(-r));
+ log_notice_errno(r, "Skipping test: manager_new: %m");
return EXIT_TEST_SKIP;
}
assert_se(r >= 0);
diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c
index a10227f823..6c34250a01 100644
--- a/src/test/test-stat-util.c
+++ b/src/test/test-stat-util.c
@@ -31,7 +31,7 @@ static void test_files_same(void) {
char name[] = "/tmp/test-files_same.XXXXXX";
char name_alias[] = "/tmp/test-files_same.alias";
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se(symlink(name, name_alias) >= 0);
@@ -47,7 +47,7 @@ static void test_is_symlink(void) {
char name_link[] = "/tmp/test-is_symlink.link";
_cleanup_close_ int fd = -1;
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se(symlink(name, name_link) >= 0);
diff --git a/src/test/test-strv.c b/src/test/test-strv.c
index 841a36782f..ce20f2dd5b 100644
--- a/src/test/test-strv.c
+++ b/src/test/test-strv.c
@@ -54,6 +54,25 @@ static void test_specifier_printf(void) {
puts(w);
}
+static void test_str_in_set(void) {
+ assert_se(STR_IN_SET("x", "x", "y", "z"));
+ assert_se(!STR_IN_SET("X", "x", "y", "z"));
+ assert_se(!STR_IN_SET("", "x", "y", "z"));
+ assert_se(STR_IN_SET("x", "w", "x"));
+}
+
+static void test_strptr_in_set(void) {
+ assert_se(STRPTR_IN_SET("x", "x", "y", "z"));
+ assert_se(!STRPTR_IN_SET("X", "x", "y", "z"));
+ assert_se(!STRPTR_IN_SET("", "x", "y", "z"));
+ assert_se(STRPTR_IN_SET("x", "w", "x"));
+
+ assert_se(!STRPTR_IN_SET(NULL, "x", "y", "z"));
+ assert_se(!STRPTR_IN_SET(NULL, ""));
+ /* strv cannot contain a null, hence the result below */
+ assert_se(!STRPTR_IN_SET(NULL, NULL));
+}
+
static const char* const input_table_multiple[] = {
"one",
"two",
@@ -703,6 +722,8 @@ static void test_strv_fnmatch(void) {
int main(int argc, char *argv[]) {
test_specifier_printf();
+ test_str_in_set();
+ test_strptr_in_set();
test_strv_foreach();
test_strv_foreach_backwards();
test_strv_foreach_pair();
diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c
index 84b448a095..373a1b70ba 100644
--- a/src/test/test-terminal-util.c
+++ b/src/test/test-terminal-util.c
@@ -50,7 +50,7 @@ static void test_read_one_char(void) {
char name[] = "/tmp/test-read_one_char.XXXXXX";
int fd;
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
file = fdopen(fd, "r+");
assert_se(file);
diff --git a/src/test/test-tmpfiles.c b/src/test/test-tmpfiles.c
index b34ebeefb2..f35e6793b7 100644
--- a/src/test/test-tmpfiles.c
+++ b/src/test/test-tmpfiles.c
@@ -51,7 +51,7 @@ int main(int argc, char** argv) {
log_debug("link1: %s", ans);
assert_se(endswith(ans, " (deleted)"));
- fd2 = mkostemp_safe(pattern, O_RDWR|O_CLOEXEC);
+ fd2 = mkostemp_safe(pattern);
assert_se(fd >= 0);
assert_se(unlink(pattern) == 0);
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
index ade0ff2a63..7ef087a2e3 100644
--- a/src/test/test-unit-file.c
+++ b/src/test/test-unit-file.c
@@ -56,12 +56,12 @@ static int test_unit_file_get_set(void) {
r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h, NULL, NULL);
if (r == -EPERM || r == -EACCES) {
- printf("Skipping test: unit_file_get_list: %s", strerror(-r));
+ log_notice_errno(r, "Skipping test: unit_file_get_list: %m");
return EXIT_TEST_SKIP;
}
- log_full(r == 0 ? LOG_INFO : LOG_ERR,
- "unit_file_get_list: %s", strerror(-r));
+ log_full_errno(r == 0 ? LOG_INFO : LOG_ERR, r,
+ "unit_file_get_list: %m");
if (r < 0)
return EXIT_FAILURE;
@@ -117,7 +117,7 @@ static void test_config_parse_exec(void) {
r = manager_new(UNIT_FILE_USER, true, &m);
if (MANAGER_SKIP_TEST(r)) {
- printf("Skipping test: manager_new: %s\n", strerror(-r));
+ log_notice_errno(r, "Skipping test: manager_new: %m");
return;
}
@@ -485,7 +485,7 @@ static void test_load_env_file_1(void) {
char name[] = "/tmp/test-load-env-file.XXXXXX";
_cleanup_close_ int fd;
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se(write(fd, env_file_1, sizeof(env_file_1)) == sizeof(env_file_1));
@@ -508,7 +508,7 @@ static void test_load_env_file_2(void) {
char name[] = "/tmp/test-load-env-file.XXXXXX";
_cleanup_close_ int fd;
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se(write(fd, env_file_2, sizeof(env_file_2)) == sizeof(env_file_2));
@@ -526,7 +526,7 @@ static void test_load_env_file_3(void) {
char name[] = "/tmp/test-load-env-file.XXXXXX";
_cleanup_close_ int fd;
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se(write(fd, env_file_3, sizeof(env_file_3)) == sizeof(env_file_3));
@@ -542,7 +542,7 @@ static void test_load_env_file_4(void) {
_cleanup_close_ int fd;
int r;
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se(write(fd, env_file_4, sizeof(env_file_4)) == sizeof(env_file_4));
@@ -562,7 +562,7 @@ static void test_load_env_file_5(void) {
char name[] = "/tmp/test-load-env-file.XXXXXX";
_cleanup_close_ int fd;
- fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se(write(fd, env_file_5, sizeof(env_file_5)) == sizeof(env_file_5));
diff --git a/src/timesync/timesyncd-conf.c b/src/timesync/timesyncd-conf.c
index 20c64a3354..bf25b112e1 100644
--- a/src/timesync/timesyncd-conf.c
+++ b/src/timesync/timesyncd-conf.c
@@ -98,7 +98,7 @@ int config_parse_servers(
int manager_parse_config_file(Manager *m) {
assert(m);
- return config_parse_many(PKGSYSCONFDIR "/timesyncd.conf",
+ return config_parse_many_nulstr(PKGSYSCONFDIR "/timesyncd.conf",
CONF_PATHS_NULSTR("systemd/timesyncd.conf.d"),
"Time\0",
config_item_perf_lookup, timesyncd_gperf_lookup,
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
index 8851af449d..b45490be1a 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -827,7 +827,7 @@ static int ask_on_consoles(int argc, char *argv[]) {
break;
}
- if (!is_clean_exit(status.si_code, status.si_status, NULL))
+ if (!is_clean_exit(status.si_code, status.si_status, EXIT_CLEAN_DAEMON, NULL))
log_error("Password agent failed with: %d", status.si_status);
terminate_agents(pids);
diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c
index b1aa0223fd..708a665576 100644
--- a/src/udev/net/ethtool-util.c
+++ b/src/udev/net/ethtool-util.c
@@ -25,6 +25,7 @@
#include "conf-parser.h"
#include "ethtool-util.h"
#include "log.h"
+#include "socket-util.h"
#include "string-table.h"
#include "strxcpyx.h"
#include "util.h"
@@ -59,10 +60,9 @@ int ethtool_connect(int *ret) {
assert_return(ret, -EINVAL);
- fd = socket(PF_INET, SOCK_DGRAM, 0);
+ fd = socket_ioctl_fd();
if (fd < 0)
- return -errno;
-
+ return fd;
*ret = fd;
return 0;
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index eedd94e777..ece9248c2a 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -191,20 +191,12 @@ static int load_link(link_config_ctx *ctx, const char *filename) {
}
static bool enable_name_policy(void) {
- _cleanup_free_ char *line = NULL;
- const char *word, *state;
+ _cleanup_free_ char *value = NULL;
int r;
- size_t l;
- r = proc_cmdline(&line);
- if (r < 0) {
- log_warning_errno(r, "Failed to read /proc/cmdline, ignoring: %m");
- return true;
- }
-
- FOREACH_WORD_QUOTED(word, l, line, state)
- if (strneq(word, "net.ifnames=0", l))
- return false;
+ r = get_proc_cmdline_key("net.ifnames=", &value);
+ if (r > 0 && streq(value, "0"))
+ return false;
return true;
}
diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c
index 6e9adc6e96..1825ee75a7 100644
--- a/src/udev/udev-builtin-path_id.c
+++ b/src/udev/udev-builtin-path_id.c
@@ -693,6 +693,15 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
parent = skip_subsystem(parent, "iucv");
supported_transport = true;
supported_parent = true;
+ } else if (streq(subsys, "nvme")) {
+ const char *nsid = udev_device_get_sysattr_value(dev, "nsid");
+
+ if (nsid) {
+ path_prepend(&path, "nvme-%s", nsid);
+ parent = skip_subsystem(parent, "nvme");
+ supported_parent = true;
+ supported_transport = true;
+ }
}
if (parent)
diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c
index 989decbe95..6f8e96a123 100644
--- a/src/udev/udevadm-control.c
+++ b/src/udev/udevadm-control.c
@@ -20,6 +20,7 @@
#include <string.h>
#include <unistd.h>
+#include "time-util.h"
#include "udev-util.h"
#include "udev.h"
@@ -60,7 +61,7 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) {
};
if (getuid() != 0) {
- fprintf(stderr, "root privileges required\n");
+ log_error("root privileges required");
return 1;
}
@@ -81,7 +82,7 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) {
i = util_log_priority(optarg);
if (i < 0) {
- fprintf(stderr, "invalid number '%s'\n", optarg);
+ log_error("invalid number '%s'", optarg);
return rc;
}
if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg), timeout) < 0)
@@ -110,7 +111,7 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) {
break;
case 'p':
if (strchr(optarg, '=') == NULL) {
- fprintf(stderr, "expect <KEY>=<value> instead of '%s'\n", optarg);
+ log_error("expect <KEY>=<value> instead of '%s'", optarg);
return rc;
}
if (udev_ctrl_send_set_env(uctrl, optarg, timeout) < 0)
@@ -124,7 +125,7 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) {
i = strtoul(optarg, &endp, 0);
if (endp[0] != '\0' || i < 1) {
- fprintf(stderr, "invalid number '%s'\n", optarg);
+ log_error("invalid number '%s'", optarg);
return rc;
}
if (udev_ctrl_send_set_children_max(uctrl, i, timeout) < 0)
@@ -134,13 +135,21 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) {
break;
}
case 't': {
+ usec_t s;
int seconds;
+ int r;
- seconds = atoi(optarg);
- if (seconds >= 0)
+ r = parse_sec(optarg, &s);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse timeout value '%s'.", optarg);
+
+ if (((s + USEC_PER_SEC - 1) / USEC_PER_SEC) > INT_MAX)
+ log_error("Timeout value is out of range.");
+ else {
+ seconds = s != USEC_INFINITY ? (int) ((s + USEC_PER_SEC - 1) / USEC_PER_SEC) : INT_MAX;
timeout = seconds;
- else
- fprintf(stderr, "invalid timeout value\n");
+ rc = 0;
+ }
break;
}
case 'h':
@@ -150,9 +159,9 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) {
}
if (optind < argc)
- fprintf(stderr, "Extraneous argument: %s\n", argv[optind]);
+ log_error("Extraneous argument: %s", argv[optind]);
else if (optind == 1)
- fprintf(stderr, "Option missing\n");
+ log_error("Option missing");
return rc;
}
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index 19f1c29198..535d317c27 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -1738,8 +1738,13 @@ int main(int argc, char *argv[]) {
log_info("starting version " VERSION);
/* connect /dev/null to stdin, stdout, stderr */
- if (log_get_max_level() < LOG_DEBUG)
- (void) make_null_stdio();
+ if (log_get_max_level() < LOG_DEBUG) {
+ r = make_null_stdio();
+ if (r < 0)
+ log_warning_errno(r, "Failed to redirect standard streams to /dev/null: %m");
+ }
+
+
pid = fork();
switch (pid) {
diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c
index da306a4444..5cc5abfddf 100644
--- a/src/update-done/update-done.c
+++ b/src/update-done/update-done.c
@@ -23,67 +23,57 @@
#include "util.h"
#define MESSAGE \
- "This file was created by systemd-update-done. Its only \n" \
- "purpose is to hold a timestamp of the time this directory\n" \
- "was updated. See systemd-update-done.service(8).\n"
+ "# This file was created by systemd-update-done. Its only \n" \
+ "# purpose is to hold a timestamp of the time this directory\n" \
+ "# was updated. See systemd-update-done.service(8).\n"
static int apply_timestamp(const char *path, struct timespec *ts) {
struct timespec twice[2] = {
*ts,
*ts
};
- struct stat st;
+ int fd = -1;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
assert(path);
assert(ts);
- if (stat(path, &st) >= 0) {
- /* Is the timestamp file already newer than the OS? If
- * so, there's nothing to do. We ignore the nanosecond
- * component of the timestamp, since some file systems
- * do not support any better accuracy than 1s and we
- * have no way to identify the accuracy
- * available. Most notably ext4 on small disks (where
- * 128 byte inodes are used) does not support better
- * accuracy than 1s. */
- if (st.st_mtim.tv_sec > ts->tv_sec)
- return 0;
-
- /* It is older? Then let's update it */
- if (utimensat(AT_FDCWD, path, twice, AT_SYMLINK_NOFOLLOW) < 0) {
-
- if (errno == EROFS)
- return log_debug("Can't update timestamp file %s, file system is read-only.", path);
+ /*
+ * We store the timestamp both as mtime of the file and in the file itself,
+ * to support filesystems which cannot store nanosecond-precision timestamps.
+ * Hence, don't bother updating the file, let's just rewrite it.
+ */
- return log_error_errno(errno, "Failed to update timestamp on %s: %m", path);
- }
+ r = mac_selinux_create_file_prepare(path, S_IFREG);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set SELinux context for %s: %m", path);
- } else if (errno == ENOENT) {
- _cleanup_close_ int fd = -1;
- int r;
+ fd = open(path, O_CREAT|O_WRONLY|O_TRUNC|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644);
+ mac_selinux_create_file_clear();
- /* The timestamp file doesn't exist yet? Then let's create it. */
+ if (fd < 0) {
+ if (errno == EROFS)
+ return log_debug("Can't create timestamp file %s, file system is read-only.", path);
- r = mac_selinux_create_file_prepare(path, S_IFREG);
- if (r < 0)
- return log_error_errno(r, "Failed to set SELinux context for %s: %m", path);
-
- fd = open(path, O_CREAT|O_EXCL|O_WRONLY|O_TRUNC|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644);
- mac_selinux_create_file_clear();
+ return log_error_errno(errno, "Failed to create/open timestamp file %s: %m", path);
+ }
- if (fd < 0) {
- if (errno == EROFS)
- return log_debug("Can't create timestamp file %s, file system is read-only.", path);
+ f = fdopen(fd, "w");
+ if (!f) {
+ safe_close(fd);
+ return log_error_errno(errno, "Failed to fdopen() timestamp file %s: %m", path);
+ }
- return log_error_errno(errno, "Failed to create timestamp file %s: %m", path);
- }
+ (void) fprintf(f,
+ "%s"
+ "TimestampNSec=" NSEC_FMT "\n",
+ MESSAGE, timespec_load_nsec(ts));
- (void) loop_write(fd, MESSAGE, strlen(MESSAGE), false);
+ fflush(f);
- if (futimens(fd, twice) < 0)
- return log_error_errno(errno, "Failed to update timestamp on %s: %m", path);
- } else
- log_error_errno(errno, "Failed to stat() timestamp file %s: %m", path);
+ if (futimens(fd, twice) < 0)
+ return log_error_errno(errno, "Failed to update timestamp on %s: %m", path);
return 0;
}
diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c
index c0d76f9685..ac4ceb1486 100644
--- a/src/vconsole/vconsole-setup.c
+++ b/src/vconsole/vconsole-setup.c
@@ -75,7 +75,7 @@ static bool is_settable(int fd) {
r = ioctl(fd, KDGKBMODE, &curr_mode);
/*
* Make sure we only adjust consoles in K_XLATE or K_UNICODE mode.
- * Oterwise we would (likely) interfere with X11's processing of the
+ * Otherwise we would (likely) interfere with X11's processing of the
* key events.
*
* http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html