summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/basic/af-list.c2
-rw-r--r--src/basic/arphrd-list.c2
-rw-r--r--src/basic/cap-list.c2
-rw-r--r--src/basic/cgroup-util.c39
-rw-r--r--src/basic/errno-list.c2
-rw-r--r--src/basic/fs-util.c6
-rw-r--r--src/basic/hexdecoct.c13
-rw-r--r--src/basic/log.c25
-rw-r--r--src/basic/missing.h22
-rw-r--r--src/basic/mount-util.c3
-rw-r--r--src/basic/proc-cmdline.c147
-rw-r--r--src/basic/proc-cmdline.h33
-rw-r--r--src/basic/process-util.c58
-rw-r--r--src/basic/socket-util.c77
-rw-r--r--src/basic/socket-util.h4
-rw-r--r--src/basic/sparse-endian.h47
-rw-r--r--src/basic/special.h1
-rw-r--r--src/basic/stat-util.c40
-rw-r--r--src/basic/stat-util.h1
-rw-r--r--src/basic/user-util.c12
-rw-r--r--src/core/dbus-manager.c34
-rw-r--r--src/core/device.c2
-rw-r--r--src/core/execute.c81
-rw-r--r--src/core/job.c2
-rw-r--r--src/core/load-fragment.h2
-rw-r--r--src/core/main.c125
-rw-r--r--src/core/mount-setup.c24
-rw-r--r--src/core/mount.c234
-rw-r--r--src/core/service.c10
-rw-r--r--src/core/socket.c31
-rw-r--r--src/core/triggers.systemd.in22
-rw-r--r--src/core/unit.c12
-rw-r--r--src/cryptsetup/cryptsetup-generator.c102
-rw-r--r--src/cryptsetup/cryptsetup.c67
-rw-r--r--src/debug-generator/debug-generator.c61
-rw-r--r--src/dissect/dissect.c7
-rw-r--r--src/firstboot/firstboot.c12
-rw-r--r--src/fsck/fsck.c12
-rw-r--r--src/fstab-generator/fstab-generator.c150
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c466
-rw-r--r--src/hibernate-resume/hibernate-resume-generator.c25
-rw-r--r--src/import/pull-common.c121
-rw-r--r--src/import/pull-common.h4
-rw-r--r--src/import/pull-raw.c271
-rw-r--r--src/import/pull-raw.h2
-rw-r--r--src/import/pull-tar.c86
-rw-r--r--src/import/pull.c14
-rw-r--r--src/journal-remote/journal-gatewayd.c2
-rw-r--r--src/journal/journalctl.c11
-rw-r--r--src/journal/journald-server.c77
-rw-r--r--src/journal/journald-server.h4
-rw-r--r--src/journal/journald.c2
-rw-r--r--src/kernel-install/kernel-install8
-rw-r--r--src/libudev/libudev-util.c2
-rw-r--r--src/login/logind.h2
-rw-r--r--src/machine/image-dbus.c3
-rw-r--r--src/machine/machinectl.c20
-rw-r--r--src/modules-load/modules-load.c8
-rw-r--r--src/network/netdev/netdev.h2
-rw-r--r--src/network/networkd-conf.h2
-rw-r--r--src/network/networkd-ndisc.c8
-rw-r--r--src/network/networkd-network-gperf.gperf7
-rw-r--r--src/network/networkd-network.h2
-rw-r--r--src/nspawn/nspawn-mount.c18
-rw-r--r--src/nspawn/nspawn-mount.h11
-rw-r--r--src/nspawn/nspawn-settings.h2
-rw-r--r--src/nspawn/nspawn.c88
-rw-r--r--src/nss-mymachines/nss-mymachines.c12
-rw-r--r--src/nss-systemd/nss-systemd.c26
-rw-r--r--src/quotacheck/quotacheck.c15
-rw-r--r--src/remount-fs/remount-fs.c2
-rw-r--r--src/resolve/dns-type.c2
-rw-r--r--src/resolve/resolved-conf.h2
-rw-r--r--src/shared/base-filesystem.c2
-rw-r--r--src/shared/dissect-image.c116
-rw-r--r--src/shared/dissect-image.h5
-rw-r--r--src/shared/fstab-util.c2
-rw-r--r--src/shared/install-printf.c6
-rw-r--r--src/shared/install.c24
-rw-r--r--src/shared/machine-image.c49
-rw-r--r--src/shared/path-lookup.c2
-rw-r--r--src/shared/switch-root.c139
-rw-r--r--src/shared/volatile-util.c68
-rw-r--r--src/shared/volatile-util.h32
-rw-r--r--src/socket-proxy/socket-proxyd.c2
-rw-r--r--src/systemctl/systemctl.c7
-rw-r--r--src/test/test-af-list.c2
-rw-r--r--src/test/test-arphrd-list.c2
-rw-r--r--src/test/test-dissect-image.c2
-rw-r--r--src/test/test-execute.c13
-rw-r--r--src/test/test-hexdecoct.c18
-rw-r--r--src/test/test-proc-cmdline.c96
-rw-r--r--src/test/test-socket-util.c26
-rw-r--r--src/test/test-stat-util.c26
-rw-r--r--src/timesync/timesyncd-conf.h2
-rw-r--r--src/tmpfiles/tmpfiles.c4
-rw-r--r--src/udev/net/link-config.c9
-rw-r--r--src/udev/net/link-config.h2
-rw-r--r--src/udev/udev-builtin-input_id.c3
-rw-r--r--src/udev/udev-builtin-keyboard.c2
-rw-r--r--src/udev/udev-event.c39
-rw-r--r--src/udev/udev-rules.c40
-rw-r--r--src/udev/udev.h4
-rw-r--r--src/udev/udevadm-test.c2
-rw-r--r--src/udev/udevd.c41
l---------src/veritysetup/Makefile1
-rw-r--r--src/veritysetup/veritysetup-generator.c253
-rw-r--r--src/veritysetup/veritysetup.c154
l---------src/volatile-root/Makefile1
-rw-r--r--src/volatile-root/volatile-root.c157
110 files changed, 2834 insertions, 1372 deletions
diff --git a/src/basic/af-list.c b/src/basic/af-list.c
index 3fac9c508b..4b291d177b 100644
--- a/src/basic/af-list.c
+++ b/src/basic/af-list.c
@@ -23,7 +23,7 @@
#include "af-list.h"
#include "macro.h"
-static const struct af_name* lookup_af(register const char *str, register unsigned int len);
+static const struct af_name* lookup_af(register const char *str, register GPERF_LEN_TYPE len);
#include "af-from-name.h"
#include "af-to-name.h"
diff --git a/src/basic/arphrd-list.c b/src/basic/arphrd-list.c
index 6792d1ee3f..2d598dc66f 100644
--- a/src/basic/arphrd-list.c
+++ b/src/basic/arphrd-list.c
@@ -23,7 +23,7 @@
#include "arphrd-list.h"
#include "macro.h"
-static const struct arphrd_name* lookup_arphrd(register const char *str, register unsigned int len);
+static const struct arphrd_name* lookup_arphrd(register const char *str, register GPERF_LEN_TYPE len);
#include "arphrd-from-name.h"
#include "arphrd-to-name.h"
diff --git a/src/basic/cap-list.c b/src/basic/cap-list.c
index 3e773a06f5..d68cc78d05 100644
--- a/src/basic/cap-list.c
+++ b/src/basic/cap-list.c
@@ -26,7 +26,7 @@
#include "parse-util.h"
#include "util.h"
-static const struct capability_name* lookup_capability(register const char *str, register unsigned int len);
+static const struct capability_name* lookup_capability(register const char *str, register GPERF_LEN_TYPE len);
#include "cap-from-name.h"
#include "cap-to-name.h"
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index dc13025115..d2d18f13f0 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -2361,6 +2361,7 @@ int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
bool cg_is_unified_wanted(void) {
static thread_local int wanted = -1;
int r, unified;
+ bool b;
/* If the hierarchy is already mounted, then follow whatever
* was chosen for it. */
@@ -2374,20 +2375,11 @@ bool cg_is_unified_wanted(void) {
if (wanted >= 0)
return wanted;
- r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy", NULL);
- if (r > 0)
- return (wanted = true);
- else {
- _cleanup_free_ char *value = NULL;
-
- r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy=", &value);
- if (r < 0)
- return false;
- if (r == 0)
- return (wanted = false);
+ r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
+ if (r < 0)
+ return false;
- return (wanted = parse_boolean(value) > 0);
- }
+ return (wanted = r > 0 ? b : false);
}
bool cg_is_legacy_wanted(void) {
@@ -2397,6 +2389,7 @@ bool cg_is_legacy_wanted(void) {
bool cg_is_unified_systemd_controller_wanted(void) {
static thread_local int wanted = -1;
int r, unified;
+ bool b;
/* If the unified hierarchy is requested in full, no need to
* bother with this. */
@@ -2415,23 +2408,11 @@ bool cg_is_unified_systemd_controller_wanted(void) {
if (wanted >= 0)
return wanted;
- r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller", NULL);
- if (r > 0)
- wanted = false;
- else {
- _cleanup_free_ char *value = NULL;
-
- r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller=", &value);
- if (r < 0)
- return false;
-
- if (r == 0)
- wanted = false;
- else
- wanted = parse_boolean(value) <= 0;
- }
+ r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
+ if (r < 0)
+ return false;
- return wanted;
+ return (wanted = r > 0 ? b : false);
}
bool cg_is_legacy_systemd_controller_wanted(void) {
diff --git a/src/basic/errno-list.c b/src/basic/errno-list.c
index 31b66bad5e..c6a01eec8b 100644
--- a/src/basic/errno-list.c
+++ b/src/basic/errno-list.c
@@ -23,7 +23,7 @@
#include "macro.h"
static const struct errno_name* lookup_errno(register const char *str,
- register unsigned int len);
+ register GPERF_LEN_TYPE len);
#include "errno-from-name.h"
#include "errno-to-name.h"
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 5b23269109..e31fa2711a 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -799,8 +799,10 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
return -ENOMEM;
}
- *ret = done;
- done = NULL;
+ if (ret) {
+ *ret = done;
+ done = NULL;
+ }
return exists;
}
diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c
index c5bda6c4d6..6843aedd0a 100644
--- a/src/basic/hexdecoct.c
+++ b/src/basic/hexdecoct.c
@@ -97,6 +97,9 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
assert(len);
assert(p);
+ if (l % 2 != 0)
+ return -EINVAL;
+
z = r = malloc((l + 1) / 2 + 1);
if (!r)
return -ENOMEM;
@@ -107,12 +110,10 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
a = unhexchar(x[0]);
if (a < 0)
return a;
- else if (x+1 < p + l) {
- b = unhexchar(x[1]);
- if (b < 0)
- return b;
- } else
- b = 0;
+
+ b = unhexchar(x[1]);
+ if (b < 0)
+ return b;
*(z++) = (uint8_t) a << 4 | (uint8_t) b;
}
diff --git a/src/basic/log.c b/src/basic/log.c
index 557212c022..1362b1c086 100644
--- a/src/basic/log.c
+++ b/src/basic/log.c
@@ -981,24 +981,30 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (streq(key, "debug") && !value)
log_set_max_level(LOG_DEBUG);
- else if (streq(key, "systemd.log_target") && value) {
+ else if (proc_cmdline_key_streq(key, "systemd.log_target")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
if (log_set_target_from_string(value) < 0)
log_warning("Failed to parse log target '%s'. Ignoring.", value);
- } else if (streq(key, "systemd.log_level") && value) {
+ } else if (proc_cmdline_key_streq(key, "systemd.log_level")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
if (log_set_max_level_from_string(value) < 0)
log_warning("Failed to parse log level '%s'. Ignoring.", value);
- } else if (streq(key, "systemd.log_color") && value) {
+ } else if (proc_cmdline_key_streq(key, "systemd.log_color")) {
- if (log_show_color_from_string(value) < 0)
+ if (log_show_color_from_string(value ?: "1") < 0)
log_warning("Failed to parse log color setting '%s'. Ignoring.", value);
- } else if (streq(key, "systemd.log_location") && value) {
+ } else if (proc_cmdline_key_streq(key, "systemd.log_location")) {
- if (log_show_location_from_string(value) < 0)
+ if (log_show_location_from_string(value ?: "1") < 0)
log_warning("Failed to parse log location setting '%s'. Ignoring.", value);
}
@@ -1009,10 +1015,9 @@ void log_parse_environment(void) {
const char *e;
if (get_ctty_devnr(0, NULL) < 0)
- /* Only try to read the command line in daemons.
- We assume that anything that has a controlling
- tty is user stuff. */
- (void) parse_proc_cmdline(parse_proc_cmdline_item, NULL, true);
+ /* Only try to read the command line in daemons. We assume that anything that has a controlling tty is
+ user stuff. */
+ (void) proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
e = secure_getenv("SYSTEMD_LOG_TARGET");
if (e && log_set_target_from_string(e) < 0)
diff --git a/src/basic/missing.h b/src/basic/missing.h
index dd4425697f..480462357d 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -34,6 +34,7 @@
#include <net/ethernet.h>
#include <stdlib.h>
#include <sys/resource.h>
+#include <sys/socket.h>
#include <sys/syscall.h>
#include <uchar.h>
#include <unistd.h>
@@ -50,6 +51,23 @@
#include <linux/btrfs.h>
#endif
+#ifdef HAVE_LINUX_VM_SOCKETS_H
+#include <linux/vm_sockets.h>
+#else
+#define VMADDR_CID_ANY -1U
+struct sockaddr_vm {
+ unsigned short svm_family;
+ unsigned short svm_reserved1;
+ unsigned int svm_port;
+ unsigned int svm_cid;
+ unsigned char svm_zero[sizeof(struct sockaddr) -
+ sizeof(unsigned short) -
+ sizeof(unsigned short) -
+ sizeof(unsigned int) -
+ sizeof(unsigned int)];
+};
+#endif /* !HAVE_LINUX_VM_SOCKETS_H */
+
#include "macro.h"
#ifndef RLIMIT_RTTIME
@@ -1163,4 +1181,8 @@ struct ethtool_link_settings {
#define SOL_ALG 279
#endif
+#ifndef AF_VSOCK
+#define AF_VSOCK 40
+#endif
+
#include "missing_syscall.h"
diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c
index 840e94a553..f0bc9cac18 100644
--- a/src/basic/mount-util.c
+++ b/src/basic/mount-util.c
@@ -673,6 +673,9 @@ int mount_verbose(
else if ((flags & MS_BIND) && !type)
log_debug("Bind-mounting %s on %s (%s \"%s\")...",
what, where, strnull(fl), strempty(options));
+ else if (flags & MS_MOVE)
+ log_debug("Moving mount %s → %s (%s \"%s\")...",
+ what, where, strnull(fl), strempty(options));
else
log_debug("Mounting %s on %s (%s \"%s\")...",
strna(type), where, strnull(fl), strempty(options));
diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c
index 8297a222b7..6ecb6c3f0d 100644
--- a/src/basic/proc-cmdline.c
+++ b/src/basic/proc-cmdline.c
@@ -34,17 +34,30 @@
#include "virt.h"
int proc_cmdline(char **ret) {
+ const char *e;
assert(ret);
+ /* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */
+ e = secure_getenv("SYSTEMD_PROC_CMDLINE");
+ if (e) {
+ char *m;
+
+ m = strdup(e);
+ if (!m)
+ return -ENOMEM;
+
+ *ret = m;
+ return 0;
+ }
+
if (detect_container() > 0)
return get_process_cmdline(1, 0, false, ret);
else
return read_one_line_file("/proc/cmdline", ret);
}
-int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, void *data),
- void *data,
- bool strip_prefix) {
+int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned flags) {
+
_cleanup_free_ char *line = NULL;
const char *p;
int r;
@@ -58,7 +71,7 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, voi
p = line;
for (;;) {
_cleanup_free_ char *word = NULL;
- char *value = NULL, *unprefixed;
+ char *value, *key, *q;
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
if (r < 0)
@@ -66,17 +79,23 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, voi
if (r == 0)
break;
- /* Filter out arguments that are intended only for the
- * initrd */
- unprefixed = startswith(word, "rd.");
- if (unprefixed && !in_initrd())
- continue;
+ key = word;
+
+ /* Filter out arguments that are intended only for the initrd */
+ q = startswith(word, "rd.");
+ if (q) {
+ if (!in_initrd())
+ continue;
+
+ if (flags & PROC_CMDLINE_STRIP_RD_PREFIX)
+ key = q;
+ }
- value = strchr(word, '=');
+ value = strchr(key, '=');
if (value)
*(value++) = 0;
- r = parse_item(strip_prefix && unprefixed ? unprefixed : word, value, data);
+ r = parse_item(key, value, data);
if (r < 0)
return r;
}
@@ -84,13 +103,64 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, voi
return 0;
}
-int get_proc_cmdline_key(const char *key, char **value) {
+static bool relaxed_equal_char(char a, char b) {
+
+ return a == b ||
+ (a == '_' && b == '-') ||
+ (a == '-' && b == '_');
+}
+
+char *proc_cmdline_key_startswith(const char *s, const char *prefix) {
+
+ assert(s);
+ assert(prefix);
+
+ /* Much like startswith(), but considers "-" and "_" the same */
+
+ for (; *prefix != 0; s++, prefix++)
+ if (!relaxed_equal_char(*s, *prefix))
+ return NULL;
+
+ return (char*) s;
+}
+
+bool proc_cmdline_key_streq(const char *x, const char *y) {
+ assert(x);
+ assert(y);
+
+ /* Much like streq(), but considers "-" and "_" the same */
+
+ for (; *x != 0 || *y != 0; x++, y++)
+ if (!relaxed_equal_char(*x, *y))
+ return false;
+
+ return true;
+}
+
+int proc_cmdline_get_key(const char *key, unsigned flags, char **value) {
_cleanup_free_ char *line = NULL, *ret = NULL;
bool found = false;
const char *p;
int r;
- assert(key);
+ /* Looks for a specific key on the kernel command line. Supports two modes:
+ *
+ * a) The "value" parameter is used. In this case a parameter beginning with the "key" string followed by "="
+ * is searched, and the value following this is returned in "value".
+ *
+ * b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if the the key is found as a
+ * separate word (i.e. not followed by "=" but instead by whitespace or the end of the command line), then
+ * this is also accepted, and "value" is returned as NULL.
+ *
+ * c) The "value" parameter is NULL. In this case a search for the exact "key" parameter is performed.
+ *
+ * In all three cases, > 0 is returned if the key is found, 0 if not.*/
+
+ if (isempty(key))
+ return -EINVAL;
+
+ if ((flags & PROC_CMDLINE_VALUE_OPTIONAL) && !value)
+ return -EINVAL;
r = proc_cmdline(&line);
if (r < 0)
@@ -107,21 +177,26 @@ int get_proc_cmdline_key(const char *key, char **value) {
if (r == 0)
break;
- /* Filter out arguments that are intended only for the
- * initrd */
+ /* Automatically filter out arguments that are intended only for the initrd, if we are not in the
+ * initrd. */
if (!in_initrd() && startswith(word, "rd."))
continue;
if (value) {
- e = startswith(word, key);
+ e = proc_cmdline_key_startswith(word, key);
if (!e)
continue;
- r = free_and_strdup(&ret, e);
- if (r < 0)
- return r;
+ if (*e == '=') {
+ r = free_and_strdup(&ret, e+1);
+ if (r < 0)
+ return r;
+
+ found = true;
+
+ } else if (*e == 0 && (flags & PROC_CMDLINE_VALUE_OPTIONAL))
+ found = true;
- found = true;
} else {
if (streq(word, key))
found = true;
@@ -134,20 +209,42 @@ int get_proc_cmdline_key(const char *key, char **value) {
}
return found;
+}
+
+int proc_cmdline_get_bool(const char *key, bool *ret) {
+ _cleanup_free_ char *v = NULL;
+ int r;
+
+ assert(ret);
+
+ r = proc_cmdline_get_key(key, PROC_CMDLINE_VALUE_OPTIONAL, &v);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ *ret = false;
+ return 0;
+ }
+
+ if (v) { /* parameter passed */
+ r = parse_boolean(v);
+ if (r < 0)
+ return r;
+ *ret = r;
+ } else /* no parameter passed */
+ *ret = true;
+ return 1;
}
int shall_restore_state(void) {
- _cleanup_free_ char *value = NULL;
+ bool ret;
int r;
- r = get_proc_cmdline_key("systemd.restore_state=", &value);
+ r = proc_cmdline_get_bool("systemd.restore_state", &ret);
if (r < 0)
return r;
- if (r == 0)
- return true;
- return parse_boolean(value);
+ return r > 0 ? ret : true;
}
static const char * const rlmap[] = {
diff --git a/src/basic/proc-cmdline.h b/src/basic/proc-cmdline.h
index 6d6ee95c11..ebfed355e9 100644
--- a/src/basic/proc-cmdline.h
+++ b/src/basic/proc-cmdline.h
@@ -19,11 +19,36 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdbool.h>
+
+#include "log.h"
+
+enum {
+ PROC_CMDLINE_STRIP_RD_PREFIX = 1,
+ PROC_CMDLINE_VALUE_OPTIONAL = 2,
+};
+
+typedef int (*proc_cmdline_parse_t)(const char *key, const char *value, void *data);
+
int proc_cmdline(char **ret);
-int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, void *data),
- void *data,
- bool strip_prefix);
-int get_proc_cmdline_key(const char *parameter, char **value);
+
+int proc_cmdline_parse(const proc_cmdline_parse_t parse, void *userdata, unsigned flags);
+
+int proc_cmdline_get_key(const char *parameter, unsigned flags, char **value);
+int proc_cmdline_get_bool(const char *key, bool *ret);
+
+char *proc_cmdline_key_startswith(const char *s, const char *prefix);
+bool proc_cmdline_key_streq(const char *x, const char *y);
int shall_restore_state(void);
const char* runlevel_to_target(const char *rl);
+
+/* A little helper call, to be used in proc_cmdline_parse_t callbacks */
+static inline bool proc_cmdline_value_missing(const char *key, const char *value) {
+ if (!value) {
+ log_warning("Missing argument for %s= kernel command line switch, ignoring.", key);
+ return true;
+ }
+
+ return false;
+}
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index d5e7edb589..eead8b00da 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -104,7 +104,7 @@ int get_process_comm(pid_t pid, char **name) {
int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
_cleanup_fclose_ FILE *f = NULL;
bool space = false;
- char *r = NULL, *k;
+ char *k, *ans = NULL;
const char *p;
int c;
@@ -118,7 +118,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
* command line that resolves to the empty string will return the "comm" name of the process instead.
*
* Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
- * comm_fallback is false). */
+ * comm_fallback is false). Returns 0 and sets *line otherwise. */
p = procfs_file_alloca(pid, "cmdline");
@@ -132,11 +132,11 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
if (max_length == 1) {
/* If there's only room for one byte, return the empty string */
- r = new0(char, 1);
- if (!r)
+ ans = new0(char, 1);
+ if (!ans)
return -ENOMEM;
- *line = r;
+ *line = ans;
return 0;
} else if (max_length == 0) {
@@ -144,36 +144,36 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
while ((c = getc(f)) != EOF) {
- if (!GREEDY_REALLOC(r, allocated, len+3)) {
- free(r);
+ if (!GREEDY_REALLOC(ans, allocated, len+3)) {
+ free(ans);
return -ENOMEM;
}
if (isprint(c)) {
if (space) {
- r[len++] = ' ';
+ ans[len++] = ' ';
space = false;
}
- r[len++] = c;
+ ans[len++] = c;
} else if (len > 0)
space = true;
}
if (len > 0)
- r[len] = 0;
+ ans[len] = '\0';
else
- r = mfree(r);
+ ans = mfree(ans);
} else {
bool dotdotdot = false;
size_t left;
- r = new(char, max_length);
- if (!r)
+ ans = new(char, max_length);
+ if (!ans)
return -ENOMEM;
- k = r;
+ k = ans;
left = max_length;
while ((c = getc(f)) != EOF) {
@@ -197,20 +197,20 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
*(k++) = (char) c;
left--;
- } else if (k > r)
+ } else if (k > ans)
space = true;
}
if (dotdotdot) {
if (max_length <= 4) {
- k = r;
+ k = ans;
left = max_length;
} else {
- k = r + max_length - 4;
+ k = ans + max_length - 4;
left = 4;
/* Eat up final spaces */
- while (k > r && isspace(k[-1])) {
+ while (k > ans && isspace(k[-1])) {
k--;
left++;
}
@@ -223,11 +223,11 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
}
/* Kernel threads have no argv[] */
- if (isempty(r)) {
+ if (isempty(ans)) {
_cleanup_free_ char *t = NULL;
int h;
- free(r);
+ free(ans);
if (!comm_fallback)
return -ENOENT;
@@ -237,22 +237,22 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
return h;
if (max_length == 0)
- r = strjoin("[", t, "]");
+ ans = strjoin("[", t, "]");
else {
size_t l;
l = strlen(t);
if (l + 3 <= max_length)
- r = strjoin("[", t, "]");
+ ans = strjoin("[", t, "]");
else if (max_length <= 6) {
- r = new(char, max_length);
- if (!r)
+ ans = new(char, max_length);
+ if (!ans)
return -ENOMEM;
- memcpy(r, "[...]", max_length-1);
- r[max_length-1] = 0;
+ memcpy(ans, "[...]", max_length-1);
+ ans[max_length-1] = 0;
} else {
char *e;
@@ -264,14 +264,14 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
e--;
*e = 0;
- r = strjoin("[", t, "...]");
+ ans = strjoin("[", t, "...]");
}
}
- if (!r)
+ if (!ans)
return -ENOMEM;
}
- *line = r;
+ *line = ans;
return 0;
}
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index 4ebf106109..77f81a60ba 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -113,6 +113,30 @@ int socket_address_parse(SocketAddress *a, const char *s) {
memcpy(a->sockaddr.un.sun_path+1, s+1, l);
a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
+ } else if (startswith(s, "vsock:")) {
+ /* AF_VSOCK socket in vsock:cid:port notation */
+ const char *cid_start = s + strlen("vsock:");
+
+ e = strchr(cid_start, ':');
+ if (!e)
+ return -EINVAL;
+
+ r = safe_atou(e+1, &u);
+ if (r < 0)
+ return r;
+
+ n = strndupa(cid_start, e - cid_start);
+ if (!isempty(n)) {
+ r = safe_atou(n, &a->sockaddr.vm.svm_cid);
+ if (r < 0)
+ return r;
+ } else
+ a->sockaddr.vm.svm_cid = VMADDR_CID_ANY;
+
+ a->sockaddr.vm.svm_family = AF_VSOCK;
+ a->sockaddr.vm.svm_port = u;
+ a->size = sizeof(struct sockaddr_vm);
+
} else {
e = strchr(s, ':');
if (e) {
@@ -289,6 +313,15 @@ int socket_address_verify(const SocketAddress *a) {
return 0;
+ case AF_VSOCK:
+ if (a->size != sizeof(struct sockaddr_vm))
+ return -EINVAL;
+
+ if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
+ return -EINVAL;
+
+ return 0;
+
default:
return -EAFNOSUPPORT;
}
@@ -394,6 +427,15 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
break;
+ case AF_VSOCK:
+ if (a->sockaddr.vm.svm_cid != b->sockaddr.vm.svm_cid)
+ return false;
+
+ if (a->sockaddr.vm.svm_port != b->sockaddr.vm.svm_port)
+ return false;
+
+ break;
+
default:
/* Cannot compare, so we assume the addresses are different */
return false;
@@ -480,15 +522,27 @@ bool socket_address_matches_fd(const SocketAddress *a, int fd) {
return socket_address_equal(a, &b);
}
-int sockaddr_port(const struct sockaddr *_sa) {
+int sockaddr_port(const struct sockaddr *_sa, unsigned *port) {
union sockaddr_union *sa = (union sockaddr_union*) _sa;
assert(sa);
- if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6))
- return -EAFNOSUPPORT;
+ switch (sa->sa.sa_family) {
+ case AF_INET:
+ *port = be16toh(sa->in.sin_port);
+ return 0;
+
+ case AF_INET6:
+ *port = be16toh(sa->in6.sin6_port);
+ return 0;
+
+ case AF_VSOCK:
+ *port = sa->vm.svm_port;
+ return 0;
- return be16toh(sa->sa.sa_family == AF_INET6 ? sa->in6.sin6_port : sa->in.sin_port);
+ default:
+ return -EAFNOSUPPORT;
+ }
}
int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
@@ -591,6 +645,18 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
break;
+ case AF_VSOCK:
+ if (include_port)
+ r = asprintf(&p,
+ "vsock:%u:%u",
+ sa->vm.svm_cid,
+ sa->vm.svm_port);
+ else
+ r = asprintf(&p, "vsock:%u", sa->vm.svm_cid);
+ if (r < 0)
+ return -ENOMEM;
+ break;
+
default:
return -EOPNOTSUPP;
}
@@ -748,6 +814,9 @@ bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b
if (a->sa.sa_family == AF_INET6)
return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0;
+ if (a->sa.sa_family == AF_VSOCK)
+ return a->vm.svm_cid == b->vm.svm_cid;
+
return false;
}
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index 2ef572badb..3c42e220e5 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -30,6 +30,7 @@
#include <linux/if_packet.h>
#include "macro.h"
+#include "missing.h"
#include "util.h"
union sockaddr_union {
@@ -40,6 +41,7 @@ union sockaddr_union {
struct sockaddr_nl nl;
struct sockaddr_storage storage;
struct sockaddr_ll ll;
+ struct sockaddr_vm vm;
};
typedef struct SocketAddress {
@@ -100,7 +102,7 @@ const char* socket_address_get_path(const SocketAddress *a);
bool socket_ipv6_is_supported(void);
-int sockaddr_port(const struct sockaddr *_sa) _pure_;
+int sockaddr_port(const struct sockaddr *_sa, unsigned *port);
int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret);
int getpeername_pretty(int fd, bool include_port, char **ret);
diff --git a/src/basic/sparse-endian.h b/src/basic/sparse-endian.h
index c913fda8c5..a3573b84a9 100644
--- a/src/basic/sparse-endian.h
+++ b/src/basic/sparse-endian.h
@@ -26,19 +26,19 @@
#include <stdint.h>
#ifdef __CHECKER__
-#define __bitwise __attribute__((bitwise))
-#define __force __attribute__((force))
+#define __sd_bitwise __attribute__((bitwise))
+#define __sd_force __attribute__((force))
#else
-#define __bitwise
-#define __force
+#define __sd_bitwise
+#define __sd_force
#endif
-typedef uint16_t __bitwise le16_t;
-typedef uint16_t __bitwise be16_t;
-typedef uint32_t __bitwise le32_t;
-typedef uint32_t __bitwise be32_t;
-typedef uint64_t __bitwise le64_t;
-typedef uint64_t __bitwise be64_t;
+typedef uint16_t __sd_bitwise le16_t;
+typedef uint16_t __sd_bitwise be16_t;
+typedef uint32_t __sd_bitwise le32_t;
+typedef uint32_t __sd_bitwise be32_t;
+typedef uint64_t __sd_bitwise le64_t;
+typedef uint64_t __sd_bitwise be64_t;
#undef htobe16
#undef htole16
@@ -69,20 +69,23 @@ typedef uint64_t __bitwise be64_t;
#define bswap_64_on_be(x) __bswap_64(x)
#endif
-static inline le16_t htole16(uint16_t value) { return (le16_t __force) bswap_16_on_be(value); }
-static inline le32_t htole32(uint32_t value) { return (le32_t __force) bswap_32_on_be(value); }
-static inline le64_t htole64(uint64_t value) { return (le64_t __force) bswap_64_on_be(value); }
+static inline le16_t htole16(uint16_t value) { return (le16_t __sd_force) bswap_16_on_be(value); }
+static inline le32_t htole32(uint32_t value) { return (le32_t __sd_force) bswap_32_on_be(value); }
+static inline le64_t htole64(uint64_t value) { return (le64_t __sd_force) bswap_64_on_be(value); }
-static inline be16_t htobe16(uint16_t value) { return (be16_t __force) bswap_16_on_le(value); }
-static inline be32_t htobe32(uint32_t value) { return (be32_t __force) bswap_32_on_le(value); }
-static inline be64_t htobe64(uint64_t value) { return (be64_t __force) bswap_64_on_le(value); }
+static inline be16_t htobe16(uint16_t value) { return (be16_t __sd_force) bswap_16_on_le(value); }
+static inline be32_t htobe32(uint32_t value) { return (be32_t __sd_force) bswap_32_on_le(value); }
+static inline be64_t htobe64(uint64_t value) { return (be64_t __sd_force) bswap_64_on_le(value); }
-static inline uint16_t le16toh(le16_t value) { return bswap_16_on_be((uint16_t __force)value); }
-static inline uint32_t le32toh(le32_t value) { return bswap_32_on_be((uint32_t __force)value); }
-static inline uint64_t le64toh(le64_t value) { return bswap_64_on_be((uint64_t __force)value); }
+static inline uint16_t le16toh(le16_t value) { return bswap_16_on_be((uint16_t __sd_force)value); }
+static inline uint32_t le32toh(le32_t value) { return bswap_32_on_be((uint32_t __sd_force)value); }
+static inline uint64_t le64toh(le64_t value) { return bswap_64_on_be((uint64_t __sd_force)value); }
-static inline uint16_t be16toh(be16_t value) { return bswap_16_on_le((uint16_t __force)value); }
-static inline uint32_t be32toh(be32_t value) { return bswap_32_on_le((uint32_t __force)value); }
-static inline uint64_t be64toh(be64_t value) { return bswap_64_on_le((uint64_t __force)value); }
+static inline uint16_t be16toh(be16_t value) { return bswap_16_on_le((uint16_t __sd_force)value); }
+static inline uint32_t be32toh(be32_t value) { return bswap_32_on_le((uint32_t __sd_force)value); }
+static inline uint64_t be64toh(be64_t value) { return bswap_64_on_le((uint64_t __sd_force)value); }
+
+#undef __sd_bitwise
+#undef __sd_force
#endif /* SPARSE_ENDIAN_H */
diff --git a/src/basic/special.h b/src/basic/special.h
index 5276bcf598..feb8e5fe21 100644
--- a/src/basic/special.h
+++ b/src/basic/special.h
@@ -103,6 +103,7 @@
#define SPECIAL_DBUS_SOCKET "dbus.socket"
#define SPECIAL_JOURNALD_SOCKET "systemd-journald.socket"
#define SPECIAL_JOURNALD_SERVICE "systemd-journald.service"
+#define SPECIAL_TMPFILES_SETUP_SERVICE "systemd-tmpfiles-setup.service"
/* Magic init signals */
#define SPECIAL_KBREQUEST_TARGET "kbrequest.target"
diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c
index 309e84b93d..7e1914aa14 100644
--- a/src/basic/stat-util.c
+++ b/src/basic/stat-util.c
@@ -28,6 +28,7 @@
#include "dirent-util.h"
#include "fd-util.h"
+#include "fs-util.h"
#include "macro.h"
#include "missing.h"
#include "stat-util.h"
@@ -143,22 +144,29 @@ int path_is_read_only_fs(const char *path) {
}
int path_is_os_tree(const char *path) {
- char *p;
int r;
assert(path);
- /* We use /usr/lib/os-release as flag file if something is an OS */
- p = strjoina(path, "/usr/lib/os-release");
- r = access(p, F_OK);
- if (r >= 0)
- return 1;
+ /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir
+ * always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from
+ * the case where just the os-release file is missing. */
+ if (laccess(path, F_OK) < 0)
+ return -errno;
- /* Also check for the old location in /etc, just in case. */
- p = strjoina(path, "/etc/os-release");
- r = access(p, F_OK);
+ /* We use /usr/lib/os-release as flag file if something is an OS */
+ r = chase_symlinks("/usr/lib/os-release", path, CHASE_PREFIX_ROOT, NULL);
+ if (r == -ENOENT) {
+
+ /* Also check for the old location in /etc, just in case. */
+ r = chase_symlinks("/etc/os-release", path, CHASE_PREFIX_ROOT, NULL);
+ if (r == -ENOENT)
+ return 0; /* We got nothing */
+ }
+ if (r < 0)
+ return r;
- return r >= 0;
+ return 1;
}
int files_same(const char *filea, const char *fileb) {
@@ -196,7 +204,7 @@ int fd_check_fstype(int fd, statfs_f_type_t magic_value) {
int path_check_fstype(const char *path, statfs_f_type_t magic_value) {
_cleanup_close_ int fd = -1;
- fd = open(path, O_RDONLY);
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
if (fd < 0)
return -errno;
@@ -216,3 +224,13 @@ int fd_is_temporary_fs(int fd) {
return is_temporary_fs(&s);
}
+
+int path_is_temporary_fs(const char *path) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
+ if (fd < 0)
+ return -errno;
+
+ return fd_is_temporary_fs(fd);
+}
diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h
index 56d28f791e..5d571efe18 100644
--- a/src/basic/stat-util.h
+++ b/src/basic/stat-util.h
@@ -61,6 +61,7 @@ int path_check_fstype(const char *path, statfs_f_type_t magic_value);
bool is_temporary_fs(const struct statfs *s) _pure_;
int fd_is_temporary_fs(int fd);
+int path_is_temporary_fs(const char *path);
/* Because statfs.t_type can be int on some architectures, we have to cast
* the const magic to the type, otherwise the compiler warns about
diff --git a/src/basic/user-util.c b/src/basic/user-util.c
index 938533d2e7..c619dad527 100644
--- a/src/basic/user-util.c
+++ b/src/basic/user-util.c
@@ -46,6 +46,8 @@
bool uid_is_valid(uid_t uid) {
+ /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.436. */
+
/* Some libc APIs use UID_INVALID as special placeholder */
if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
return false;
@@ -519,7 +521,15 @@ bool valid_user_group_name(const char *u) {
const char *i;
long sz;
- /* Checks if the specified name is a valid user/group name. */
+ /* Checks if the specified name is a valid user/group name. Also see POSIX IEEE Std 1003.1-2008, 2016 Edition,
+ * 3.437. We are a bit stricter here however. Specifically we deviate from POSIX rules:
+ *
+ * - We don't allow any dots (this would break chown syntax which permits dots as user/group name separator)
+ * - We require that names fit into the appropriate utmp field
+ * - We don't allow empty user names
+ *
+ * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters.
+ */
if (isempty(u))
return false;
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 9af49dd1bc..9876251438 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -35,6 +35,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
+#include "fs-util.h"
#include "install.h"
#include "log.h"
#include "path-util.h"
@@ -1484,25 +1485,36 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er
if (r < 0)
return r;
- if (path_equal(root, "/") || !path_is_absolute(root))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid switch root path %s", root);
+ if (isempty(root))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New root directory may not be the empty string.");
+ if (!path_is_absolute(root))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New root path '%s' is not absolute.", root);
+ if (path_equal(root, "/"))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New root directory cannot be the old root directory.");
/* Safety check */
if (isempty(init)) {
- if (!path_is_os_tree(root))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified switch root path %s does not seem to be an OS tree. os-release file is missing.", root);
+ r = path_is_os_tree(root);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to determine whether root path '%s' contains an OS tree: %m", root);
+ if (r == 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified switch root path '%s' does not seem to be an OS tree. os-release file is missing.", root);
} else {
- _cleanup_free_ char *p = NULL;
+ _cleanup_free_ char *chased = NULL;
if (!path_is_absolute(init))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid init path %s", init);
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path to init binary '%s' not absolute.", init);
- p = strappend(root, init);
- if (!p)
- return -ENOMEM;
+ r = chase_symlinks(init, root, CHASE_PREFIX_ROOT, &chased);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Could not resolve init executable %s: %m", init);
+
+ if (laccess(chased, X_OK) < 0) {
+ if (errno == EACCES)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Init binary %s is not executable.", init);
- if (access(p, X_OK) < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified init binary %s does not exist.", p);
+ return sd_bus_error_set_errnof(error, r, "Could not check whether init binary %s is executable: %m", init);
+ }
}
rt = strdup(root);
diff --git a/src/core/device.c b/src/core/device.c
index bd481c8050..0e67c96552 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -385,7 +385,7 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
* on its radar. In this case the device unit is partially initialized
* and includes the deps on the mount unit but at that time the "bind
* mounts" flag wasn't not present. Fix this up now. */
- if (device_is_bound_by_mounts(u, dev))
+ if (dev && device_is_bound_by_mounts(u, dev))
device_upgrade_mount_deps(u);
/* Note that this won't dispatch the load queue, the caller
diff --git a/src/core/execute.c b/src/core/execute.c
index a77edbb162..06a291fd39 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -2173,7 +2173,8 @@ static int exec_child(
int *fds, unsigned n_fds,
char **files_env,
int user_lookup_fd,
- int *exit_status) {
+ int *exit_status,
+ char **error_message) {
_cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL;
_cleanup_free_ char *mac_selinux_context_net = NULL;
@@ -2192,6 +2193,9 @@ static int exec_child(
assert(context);
assert(params);
assert(exit_status);
+ assert(error_message);
+ /* We don't always set error_message, hence it must be initialized */
+ assert(*error_message == NULL);
rename_process_from_path(command->path);
@@ -2209,6 +2213,8 @@ static int exec_child(
r = reset_signal_mask();
if (r < 0) {
*exit_status = EXIT_SIGNAL_MASK;
+ *error_message = strdup("Failed to reset signal mask");
+ /* If strdup fails, here and below, we will just print the generic error message. */
return r;
}
@@ -2224,6 +2230,7 @@ static int exec_child(
r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, fds, n_fds);
if (r < 0) {
*exit_status = EXIT_FDS;
+ *error_message = strdup("Failed to close remaining fds");
return r;
}
@@ -2252,6 +2259,7 @@ static int exec_child(
return 0;
}
*exit_status = EXIT_CONFIRM;
+ *error_message = strdup("Execution cancelled");
return -ECANCELED;
}
}
@@ -2261,17 +2269,27 @@ static int exec_child(
/* Make sure we bypass our own NSS module for any NSS checks */
if (putenv((char*) "SYSTEMD_NSS_DYNAMIC_BYPASS=1") != 0) {
*exit_status = EXIT_USER;
+ *error_message = strdup("Failed to update environment");
return -errno;
}
r = dynamic_creds_realize(dcreds, &uid, &gid);
if (r < 0) {
*exit_status = EXIT_USER;
+ *error_message = strdup("Failed to update dynamic user credentials");
return r;
}
- if (!uid_is_valid(uid) || !gid_is_valid(gid)) {
+ if (!uid_is_valid(uid)) {
*exit_status = EXIT_USER;
+ (void) asprintf(error_message, "UID validation failed for \""UID_FMT"\"", uid);
+ /* If asprintf fails, here and below, we will just print the generic error message. */
+ return -ESRCH;
+ }
+
+ if (!gid_is_valid(gid)) {
+ *exit_status = EXIT_USER;
+ (void) asprintf(error_message, "GID validation failed for \""GID_FMT"\"", gid);
return -ESRCH;
}
@@ -2282,12 +2300,14 @@ static int exec_child(
r = get_fixed_user(context, &username, &uid, &gid, &home, &shell);
if (r < 0) {
*exit_status = EXIT_USER;
+ *error_message = strdup("Failed to determine user credentials");
return r;
}
r = get_fixed_group(context, &groupname, &gid);
if (r < 0) {
*exit_status = EXIT_GROUP;
+ *error_message = strdup("Failed to determine group credentials");
return r;
}
}
@@ -2297,12 +2317,14 @@ static int exec_child(
&supplementary_gids, &ngids);
if (r < 0) {
*exit_status = EXIT_GROUP;
+ *error_message = strdup("Failed to determine supplementary groups");
return r;
}
r = send_user_lookup(unit, user_lookup_fd, uid, gid);
if (r < 0) {
*exit_status = EXIT_USER;
+ *error_message = strdup("Failed to send user credentials to PID1");
return r;
}
@@ -2316,18 +2338,21 @@ static int exec_child(
r = setup_input(context, params, socket_fd, named_iofds);
if (r < 0) {
*exit_status = EXIT_STDIN;
+ *error_message = strdup("Failed to set up stdin");
return r;
}
r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, named_iofds, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
if (r < 0) {
*exit_status = EXIT_STDOUT;
+ *error_message = strdup("Failed to set up stdout");
return r;
}
r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, named_iofds, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
if (r < 0) {
*exit_status = EXIT_STDERR;
+ *error_message = strdup("Failed to set up stderr");
return r;
}
@@ -2335,6 +2360,7 @@ static int exec_child(
r = cg_attach_everywhere(params->cgroup_supported, params->cgroup_path, 0, NULL, NULL);
if (r < 0) {
*exit_status = EXIT_CGROUP;
+ (void) asprintf(error_message, "Failed to attach to cgroup %s", params->cgroup_path);
return r;
}
}
@@ -2355,6 +2381,7 @@ static int exec_child(
log_close();
} else if (r < 0) {
*exit_status = EXIT_OOM_ADJUST;
+ *error_message = strdup("Failed to write /proc/self/oom_score_adj");
return -errno;
}
}
@@ -2599,6 +2626,7 @@ static int exec_child(
r = capability_bounding_set_drop(context->capability_bounding_set, false);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
+ *error_message = strdup("Failed to drop capabilities");
return r;
}
}
@@ -2609,6 +2637,7 @@ static int exec_child(
r = capability_ambient_set_apply(context->capability_ambient_set, true);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
+ *error_message = strdup("Failed to apply ambient capabilities (before UID change)");
return r;
}
}
@@ -2617,6 +2646,7 @@ static int exec_child(
r = enforce_user(context, uid);
if (r < 0) {
*exit_status = EXIT_USER;
+ (void) asprintf(error_message, "Failed to change UID to "UID_FMT, uid);
return r;
}
if (context->capability_ambient_set != 0) {
@@ -2625,6 +2655,7 @@ static int exec_child(
r = capability_ambient_set_apply(context->capability_ambient_set, false);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
+ *error_message = strdup("Failed to apply ambient capabilities (after UID change)");
return r;
}
@@ -2652,6 +2683,7 @@ static int exec_child(
r = setexeccon(exec_context);
if (r < 0) {
*exit_status = EXIT_SELINUX_CONTEXT;
+ (void) asprintf(error_message, "Failed to set SELinux context to %s", exec_context);
return r;
}
}
@@ -2661,6 +2693,7 @@ static int exec_child(
r = setup_smack(context, command);
if (r < 0) {
*exit_status = EXIT_SMACK_PROCESS_LABEL;
+ *error_message = strdup("Failed to set SMACK process label");
return r;
}
@@ -2669,6 +2702,9 @@ static int exec_child(
r = aa_change_onexec(context->apparmor_profile);
if (r < 0 && !context->apparmor_profile_ignore) {
*exit_status = EXIT_APPARMOR_PROFILE;
+ (void) asprintf(error_message,
+ "Failed to prepare AppArmor profile change to %s",
+ context->apparmor_profile);
return -errno;
}
}
@@ -2681,12 +2717,14 @@ static int exec_child(
if (prctl(PR_GET_SECUREBITS) != secure_bits)
if (prctl(PR_SET_SECUREBITS, secure_bits) < 0) {
*exit_status = EXIT_SECUREBITS;
+ *error_message = strdup("Failed to set secure bits");
return -errno;
}
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;
+ *error_message = strdup("Failed to disable new privileges");
return -errno;
}
@@ -2694,48 +2732,56 @@ static int exec_child(
r = apply_address_families(unit, context);
if (r < 0) {
*exit_status = EXIT_ADDRESS_FAMILIES;
+ *error_message = strdup("Failed to restrict address families");
return r;
}
r = apply_memory_deny_write_execute(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
+ *error_message = strdup("Failed to disable writing to executable memory");
return r;
}
r = apply_restrict_realtime(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
+ *error_message = strdup("Failed to apply realtime restrictions");
return r;
}
r = apply_restrict_namespaces(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
+ *error_message = strdup("Failed to apply namespace restrictions");
return r;
}
r = apply_protect_sysctl(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
+ *error_message = strdup("Failed to apply sysctl restrictions");
return r;
}
r = apply_protect_kernel_modules(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
+ *error_message = strdup("Failed to apply module loading restrictions");
return r;
}
r = apply_private_devices(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
+ *error_message = strdup("Failed to set up private devices");
return r;
}
r = apply_syscall_archs(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
+ *error_message = strdup("Failed to apply syscall architecture restrictions");
return r;
}
@@ -2744,6 +2790,7 @@ static int exec_child(
r = apply_syscall_filter(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
+ *error_message = strdup("Failed to apply syscall filters");
return r;
}
#endif
@@ -2752,6 +2799,7 @@ static int exec_child(
final_argv = replace_env_argv(argv, accum_env);
if (!final_argv) {
*exit_status = EXIT_MEMORY;
+ *error_message = strdup("Failed to prepare process arguments");
return -ENOMEM;
}
@@ -2838,6 +2886,7 @@ int exec_spawn(Unit *unit,
if (pid == 0) {
int exit_status;
+ _cleanup_free_ char *error_message = NULL;
r = exec_child(unit,
command,
@@ -2851,17 +2900,27 @@ int exec_spawn(Unit *unit,
fds, n_fds,
files_env,
unit->manager->user_lookup_fds[1],
- &exit_status);
+ &exit_status,
+ &error_message);
if (r < 0) {
log_open();
- log_struct_errno(LOG_ERR, r,
- LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED),
- LOG_UNIT_ID(unit),
- LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m",
- exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD),
- command->path),
- "EXECUTABLE=%s", command->path,
- NULL);
+ if (error_message)
+ log_struct_errno(LOG_ERR, r,
+ LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED),
+ LOG_UNIT_ID(unit),
+ LOG_UNIT_MESSAGE(unit, "%s: %m",
+ error_message),
+ "EXECUTABLE=%s", command->path,
+ NULL);
+ else
+ log_struct_errno(LOG_ERR, r,
+ LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED),
+ LOG_UNIT_ID(unit),
+ LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m",
+ exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD),
+ command->path),
+ "EXECUTABLE=%s", command->path,
+ NULL);
}
_exit(exit_status);
diff --git a/src/core/job.c b/src/core/job.c
index 2ba4c78096..f7c4c59c32 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -645,7 +645,7 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR
[JOB_DEPENDENCY] = "Dependency failed for %s.",
[JOB_ASSERT] = "Assertion failed for %s.",
[JOB_UNSUPPORTED] = "Starting of %s not supported.",
- [JOB_COLLECTED] = "Unecessary job for %s was removed.",
+ [JOB_COLLECTED] = "Unnecessary job for %s was removed.",
};
static const char *const generic_finished_stop_job[_JOB_RESULT_MAX] = {
[JOB_DONE] = "Stopped %s.",
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index bbac2d84b5..fc27a07955 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -120,7 +120,7 @@ int config_parse_restrict_namespaces(const char *unit, const char *filename, uns
int config_parse_bind_paths(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* gperf prototypes */
-const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
+const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
extern const char load_fragment_gperf_nulstr[];
typedef enum Disabled {
diff --git a/src/core/main.c b/src/core/main.c
index c2c1167ab3..ad2ce1330e 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -49,6 +49,7 @@
#include "cpu-set-util.h"
#include "dbus-manager.h"
#include "def.h"
+#include "emergency-action.h"
#include "env-util.h"
#include "fd-util.h"
#include "fdset.h"
@@ -90,7 +91,6 @@
#include "user-util.h"
#include "virt.h"
#include "watchdog.h"
-#include "emergency-action.h"
static enum {
ACTION_RUN,
@@ -337,60 +337,73 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
assert(key);
- if (streq(key, "systemd.unit") && value) {
-
- if (!in_initrd())
- return free_and_strdup(&arg_default_unit, value);
+ if (STR_IN_SET(key, "systemd.unit", "rd.systemd.unit")) {
- } else if (streq(key, "rd.systemd.unit") && value) {
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
- if (in_initrd())
- return free_and_strdup(&arg_default_unit, value);
+ if (!unit_name_is_valid(value, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
+ log_warning("Unit name specified on %s= is not valid, ignoring: %s", key, value);
+ else if (in_initrd() == !!startswith(key, "rd.")) {
+ if (free_and_strdup(&arg_default_unit, value) < 0)
+ return log_oom();
+ }
- } else if (streq(key, "systemd.dump_core") && value) {
+ } else if (proc_cmdline_key_streq(key, "systemd.dump_core")) {
- r = parse_boolean(value);
+ r = value ? parse_boolean(value) : true;
if (r < 0)
log_warning("Failed to parse dump core switch %s. Ignoring.", value);
else
arg_dump_core = r;
- } else if (streq(key, "systemd.crash_chvt") && value) {
+ } else if (proc_cmdline_key_streq(key, "systemd.crash_chvt")) {
- if (parse_crash_chvt(value) < 0)
+ if (!value)
+ arg_crash_chvt = 0; /* turn on */
+ else if (parse_crash_chvt(value) < 0)
log_warning("Failed to parse crash chvt switch %s. Ignoring.", value);
- } else if (streq(key, "systemd.crash_shell") && value) {
+ } else if (proc_cmdline_key_streq(key, "systemd.crash_shell")) {
- r = parse_boolean(value);
+ r = value ? parse_boolean(value) : true;
if (r < 0)
log_warning("Failed to parse crash shell switch %s. Ignoring.", value);
else
arg_crash_shell = r;
- } else if (streq(key, "systemd.crash_reboot") && value) {
+ } else if (proc_cmdline_key_streq(key, "systemd.crash_reboot")) {
- r = parse_boolean(value);
+ r = value ? parse_boolean(value) : true;
if (r < 0)
log_warning("Failed to parse crash reboot switch %s. Ignoring.", value);
else
arg_crash_reboot = r;
- } else if (streq(key, "systemd.confirm_spawn") && value) {
-
- arg_confirm_spawn = mfree(arg_confirm_spawn);
+ } else if (proc_cmdline_key_streq(key, "systemd.confirm_spawn")) {
+ char *s;
- r = parse_confirm_spawn(value, &arg_confirm_spawn);
+ r = parse_confirm_spawn(value, &s);
if (r < 0)
log_warning_errno(r, "Failed to parse confirm_spawn switch %s. Ignoring.", value);
+ else {
+ free(arg_confirm_spawn);
+ arg_confirm_spawn = s;
+ }
- } else if (streq(key, "systemd.show_status") && value) {
+ } else if (proc_cmdline_key_streq(key, "systemd.show_status")) {
- r = parse_show_status(value, &arg_show_status);
- if (r < 0)
- log_warning("Failed to parse show status switch %s. Ignoring.", value);
+ if (value) {
+ r = parse_show_status(value, &arg_show_status);
+ if (r < 0)
+ log_warning("Failed to parse show status switch %s. Ignoring.", value);
+ } else
+ arg_show_status = SHOW_STATUS_YES;
+
+ } else if (proc_cmdline_key_streq(key, "systemd.default_standard_output")) {
- } else if (streq(key, "systemd.default_standard_output") && value) {
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
r = exec_output_from_string(value);
if (r < 0)
@@ -398,7 +411,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
else
arg_default_std_output = r;
- } else if (streq(key, "systemd.default_standard_error") && value) {
+ } else if (proc_cmdline_key_streq(key, "systemd.default_standard_error")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
r = exec_output_from_string(value);
if (r < 0)
@@ -406,24 +422,42 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
else
arg_default_std_error = r;
- } else if (streq(key, "systemd.setenv") && value) {
+ } else if (streq(key, "systemd.setenv")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
if (env_assignment_is_valid(value)) {
char **env;
env = strv_env_set(arg_default_environment, value);
- if (env)
- arg_default_environment = env;
- else
- log_warning_errno(ENOMEM, "Setting environment variable '%s' failed, ignoring: %m", value);
+ if (!env)
+ return log_oom();
+
+ arg_default_environment = env;
} else
log_warning("Environment variable name '%s' is not valid. Ignoring.", value);
- } else if (streq(key, "systemd.machine_id") && value) {
+ } else if (proc_cmdline_key_streq(key, "systemd.machine_id")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ r = set_machine_id(value);
+ if (r < 0)
+ log_warning("MachineID '%s' is not valid. Ignoring.", value);
+
+ } else if (proc_cmdline_key_streq(key, "systemd.default_timeout_start_sec")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ r = parse_sec(value, &arg_default_timeout_start_usec);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse default start timeout: %s, ignoring.", value);
- r = set_machine_id(value);
- if (r < 0)
- log_warning("MachineID '%s' is not valid. Ignoring.", value);
+ if (arg_default_timeout_start_usec <= 0)
+ arg_default_timeout_start_usec = USEC_INFINITY;
} else if (streq(key, "quiet") && !value) {
@@ -445,15 +479,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
target = runlevel_to_target(key);
if (target)
return free_and_strdup(&arg_default_unit, target);
-
- } else if (streq(key, "systemd.default_timeout_start_sec") && value) {
-
- r = parse_sec(value, &arg_default_timeout_start_usec);
- if (r < 0)
- log_warning_errno(r, "Failed to parse default start timeout: %s, ignoring.", value);
-
- if (arg_default_timeout_start_usec <= 0)
- arg_default_timeout_start_usec = USEC_INFINITY;
}
return 0;
@@ -1313,10 +1338,9 @@ static int fixup_environment(void) {
* However if TERM was configured through the kernel
* command line then leave it alone. */
- r = get_proc_cmdline_key("TERM=", &term);
+ r = proc_cmdline_get_key("TERM", 0, &term);
if (r < 0)
return r;
-
if (r == 0) {
term = strdup(default_term_for_tty("/dev/console"));
if (!term)
@@ -1383,7 +1407,7 @@ int main(int argc, char *argv[]) {
called 'systemd'. That is confusing, hence let's call us
systemd right-away. */
program_invocation_short_name = systemd;
- prctl(PR_SET_NAME, systemd);
+ (void) prctl(PR_SET_NAME, systemd);
saved_argv = argv;
saved_argc = argc;
@@ -1407,9 +1431,10 @@ int main(int argc, char *argv[]) {
if (!skip_setup) {
r = mount_setup_early();
if (r < 0) {
- error_message = "Failed to early mount API filesystems";
+ error_message = "Failed to mount early API filesystems";
goto finish;
}
+
dual_timestamp_get(&security_start_timestamp);
if (mac_selinux_setup(&loaded_policy) < 0) {
error_message = "Failed to load SELinux policy";
@@ -1485,7 +1510,7 @@ int main(int argc, char *argv[]) {
log_close_console(); /* force reopen of /dev/console */
log_open();
- /* For the later on, see above... */
+ /* For later on, see above... */
log_set_target(LOG_TARGET_JOURNAL);
/* clear the kernel timestamp,
@@ -1562,7 +1587,7 @@ int main(int argc, char *argv[]) {
}
if (arg_system) {
- r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false);
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
}
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index 6338067d7e..9c2bf3a0ef 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -360,7 +360,6 @@ int mount_setup(bool loaded_policy) {
int r = 0;
r = mount_points_setup(ELEMENTSOF(mount_table), loaded_policy);
-
if (r < 0)
return r;
@@ -391,25 +390,24 @@ int mount_setup(bool loaded_policy) {
* udevd. */
dev_setup(NULL, UID_INVALID, GID_INVALID);
- /* Mark the root directory as shared in regards to mount
- * propagation. The kernel defaults to "private", but we think
- * it makes more sense to have a default of "shared" so that
- * nspawn and the container tools work out of the box. If
- * specific setups need other settings they can reset the
- * propagation mode to private if needed. */
+ /* Mark the root directory as shared in regards to mount propagation. The kernel defaults to "private", but we
+ * think it makes more sense to have a default of "shared" so that nspawn and the container tools work out of
+ * the box. If specific setups need other settings they can reset the propagation mode to private if
+ * needed. Note that we set this only when we are invoked directly by the kernel. If we are invoked by a
+ * container manager we assume the container manager knows what it is doing (for example, because it set up
+ * some directories with different propagation modes). */
if (detect_container() <= 0)
if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0)
log_warning_errno(errno, "Failed to set up the root directory for shared mount propagation: %m");
- /* Create a few directories we always want around, Note that
- * sd_booted() checks for /run/systemd/system, so this mkdir
- * really needs to stay for good, otherwise software that
- * copied sd-daemon.c into their sources will misdetect
- * systemd. */
+ /* Create a few directories we always want around, Note that sd_booted() checks for /run/systemd/system, so
+ * this mkdir really needs to stay for good, otherwise software that copied sd-daemon.c into their sources will
+ * misdetect systemd. */
(void) mkdir_label("/run/systemd", 0755);
(void) mkdir_label("/run/systemd/system", 0755);
- (void) mkdir_label("/run/systemd/inaccessible", 0000);
+
/* Set up inaccessible items */
+ (void) mkdir_label("/run/systemd/inaccessible", 0000);
(void) mknod("/run/systemd/inaccessible/reg", S_IFREG | 0000, 0);
(void) mkdir_label("/run/systemd/inaccessible/dir", 0000);
(void) mknod("/run/systemd/inaccessible/chr", S_IFCHR | 0000, makedev(0, 0));
diff --git a/src/core/mount.c b/src/core/mount.c
index daf7f5697b..8192a3616f 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -1387,6 +1387,128 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
return 0;
}
+typedef struct {
+ bool is_mounted;
+ bool just_mounted;
+ bool just_changed;
+} MountSetupFlags;
+
+static int mount_setup_new_unit(
+ Unit *u,
+ const char *what,
+ const char *where,
+ const char *options,
+ const char *fstype,
+ MountSetupFlags *flags) {
+
+ MountParameters *p;
+
+ assert(u);
+ assert(flags);
+
+ u->source_path = strdup("/proc/self/mountinfo");
+ MOUNT(u)->where = strdup(where);
+ if (!u->source_path && !MOUNT(u)->where)
+ return -ENOMEM;
+
+ /* Make sure to initialize those fields before mount_is_extrinsic(). */
+ MOUNT(u)->from_proc_self_mountinfo = true;
+ p = &MOUNT(u)->parameters_proc_self_mountinfo;
+
+ p->what = strdup(what);
+ p->options = strdup(options);
+ p->fstype = strdup(fstype);
+ if (!p->what || !p->options || !p->fstype)
+ return -ENOMEM;
+
+ if (!mount_is_extrinsic(MOUNT(u))) {
+ const char *target;
+ int r;
+
+ target = mount_is_network(p) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET;
+ r = unit_add_dependency_by_name(u, UNIT_BEFORE, target, NULL, true);
+ if (r < 0)
+ return r;
+
+ r = unit_add_dependency_by_name(u, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
+ if (r < 0)
+ return r;
+ }
+
+ flags->is_mounted = true;
+ flags->just_mounted = true;
+ flags->just_changed = true;
+
+ return 0;
+}
+
+static int mount_setup_existing_unit(
+ Unit *u,
+ const char *what,
+ const char *where,
+ const char *options,
+ const char *fstype,
+ MountSetupFlags *flags) {
+
+ MountParameters *p;
+ bool load_extras = false;
+ int r1, r2, r3;
+
+ assert(u);
+ assert(flags);
+
+ if (!MOUNT(u)->where) {
+ MOUNT(u)->where = strdup(where);
+ if (!MOUNT(u)->where)
+ return -ENOMEM;
+ }
+
+ /* Make sure to initialize those fields before mount_is_extrinsic(). */
+ p = &MOUNT(u)->parameters_proc_self_mountinfo;
+
+ r1 = free_and_strdup(&p->what, what);
+ r2 = free_and_strdup(&p->options, options);
+ r3 = free_and_strdup(&p->fstype, fstype);
+ if (r1 < 0 || r2 < 0 || r3 < 0)
+ return -ENOMEM;
+
+ flags->just_changed = r1 > 0 || r2 > 0 || r3 > 0;
+ flags->is_mounted = true;
+ flags->just_mounted = !MOUNT(u)->from_proc_self_mountinfo;
+
+ MOUNT(u)->from_proc_self_mountinfo = true;
+
+ if (!mount_is_extrinsic(MOUNT(u)) && mount_is_network(p)) {
+ /* _netdev option may have shown up late, or on a
+ * remount. Add remote-fs dependencies, even though
+ * local-fs ones may already be there.
+ *
+ * Note: due to a current limitation (we don't track
+ * in the dependency "Set*" objects who created a
+ * dependency), we can only add deps, never lose them,
+ * until the next full daemon-reload. */
+ unit_add_dependency_by_name(u, UNIT_BEFORE, SPECIAL_REMOTE_FS_TARGET, NULL, true);
+ load_extras = true;
+ }
+
+ if (u->load_state == UNIT_NOT_FOUND) {
+ u->load_state = UNIT_LOADED;
+ u->load_error = 0;
+
+ /* Load in the extras later on, after we
+ * finished initialization of the unit */
+
+ /* FIXME: since we're going to load the unit later on, why setting load_extras=true ? */
+ load_extras = true;
+ flags->just_changed = true;
+ }
+
+ if (load_extras)
+ return mount_add_extras(MOUNT(u));
+
+ return 0;
+}
+
static int mount_setup_unit(
Manager *m,
const char *what,
@@ -1395,10 +1517,8 @@ static int mount_setup_unit(
const char *fstype,
bool set_flags) {
- _cleanup_free_ char *e = NULL, *w = NULL, *o = NULL, *f = NULL;
- bool load_extras = false;
- MountParameters *p;
- bool delete, changed = false;
+ _cleanup_free_ char *e = NULL;
+ MountSetupFlags flags;
Unit *u;
int r;
@@ -1426,114 +1546,34 @@ static int mount_setup_unit(
u = manager_get_unit(m, e);
if (!u) {
- delete = true;
-
+ /* First time we see this mount point meaning that it's
+ * not been initiated by a mount unit but rather by the
+ * sysadmin having called mount(8) directly. */
r = unit_new_for_name(m, sizeof(Mount), e, &u);
if (r < 0)
goto fail;
- MOUNT(u)->where = strdup(where);
- if (!MOUNT(u)->where) {
- r = -ENOMEM;
- goto fail;
- }
-
- u->source_path = strdup("/proc/self/mountinfo");
- if (!u->source_path) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (!mount_is_extrinsic(MOUNT(u))) {
- const char* target;
-
- target = mount_needs_network(options, fstype) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET;
- r = unit_add_dependency_by_name(u, UNIT_BEFORE, target, NULL, true);
- if (r < 0)
- goto fail;
-
- r = unit_add_dependency_by_name(u, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
- if (r < 0)
- goto fail;
- }
-
- unit_add_to_load_queue(u);
- changed = true;
- } else {
- delete = false;
-
- if (!MOUNT(u)->where) {
- MOUNT(u)->where = strdup(where);
- if (!MOUNT(u)->where) {
- r = -ENOMEM;
- goto fail;
- }
- }
-
- if (!mount_is_extrinsic(MOUNT(u)) &&
- mount_needs_network(options, fstype)) {
- /* _netdev option may have shown up late, or on a
- * remount. Add remote-fs dependencies, even though
- * local-fs ones may already be there. */
- unit_add_dependency_by_name(u, UNIT_BEFORE, SPECIAL_REMOTE_FS_TARGET, NULL, true);
- load_extras = true;
- }
-
- if (u->load_state == UNIT_NOT_FOUND) {
- u->load_state = UNIT_LOADED;
- u->load_error = 0;
-
- /* Load in the extras later on, after we
- * finished initialization of the unit */
- load_extras = true;
- changed = true;
- }
- }
+ r = mount_setup_new_unit(u, what, where, options, fstype, &flags);
+ if (r < 0)
+ unit_free(u);
+ } else
+ r = mount_setup_existing_unit(u, what, where, options, fstype, &flags);
- w = strdup(what);
- o = strdup(options);
- f = strdup(fstype);
- if (!w || !o || !f) {
- r = -ENOMEM;
+ if (r < 0)
goto fail;
- }
-
- p = &MOUNT(u)->parameters_proc_self_mountinfo;
-
- changed = changed ||
- !streq_ptr(p->options, options) ||
- !streq_ptr(p->what, what) ||
- !streq_ptr(p->fstype, fstype);
if (set_flags) {
- MOUNT(u)->is_mounted = true;
- MOUNT(u)->just_mounted = !MOUNT(u)->from_proc_self_mountinfo;
- MOUNT(u)->just_changed = changed;
- }
-
- MOUNT(u)->from_proc_self_mountinfo = true;
-
- free_and_replace(p->what, w);
- free_and_replace(p->options, o);
- free_and_replace(p->fstype, f);
-
- if (load_extras) {
- r = mount_add_extras(MOUNT(u));
- if (r < 0)
- goto fail;
+ MOUNT(u)->is_mounted = flags.is_mounted;
+ MOUNT(u)->just_mounted = flags.just_mounted;
+ MOUNT(u)->just_changed = flags.just_changed;
}
- if (changed)
+ if (flags.just_changed)
unit_add_to_dbus_queue(u);
return 0;
-
fail:
log_warning_errno(r, "Failed to set up mount unit: %m");
-
- if (delete)
- unit_free(u);
-
return r;
}
diff --git a/src/core/service.c b/src/core/service.c
index 73a8104d17..54074ff7bc 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -1292,10 +1292,10 @@ static int service_spawn(
return r;
}
- if (r == 0 && IN_SET(sa.sa.sa_family, AF_INET, AF_INET6)) {
+ if (r == 0 && IN_SET(sa.sa.sa_family, AF_INET, AF_INET6, AF_VSOCK)) {
_cleanup_free_ char *addr = NULL;
char *t;
- int port;
+ unsigned port;
r = sockaddr_pretty(&sa.sa, salen, true, false, &addr);
if (r < 0)
@@ -1306,9 +1306,9 @@ static int service_spawn(
return -ENOMEM;
our_env[n_env++] = t;
- port = sockaddr_port(&sa.sa);
- if (port < 0)
- return port;
+ r = sockaddr_port(&sa.sa, &port);
+ if (r < 0)
+ return r;
if (asprintf(&t, "REMOTE_PORT=%u", port) < 0)
return -ENOMEM;
diff --git a/src/core/socket.c b/src/core/socket.c
index 0960a30039..3cae6b31bb 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -63,6 +63,7 @@ struct SocketPeer {
Socket *socket;
union sockaddr_union peer;
+ socklen_t peer_salen;
};
static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
@@ -448,7 +449,7 @@ static int socket_verify(Socket *s) {
return 0;
if (!s->ports) {
- log_unit_error(UNIT(s), "Unit lacks Listen setting. Refusing.");
+ log_unit_error(UNIT(s), "Unit has no Listen setting (ListenStream=, ListenDatagram=, ListenFIFO=, ...). Refusing.");
return -EINVAL;
}
@@ -484,12 +485,15 @@ static void peer_address_hash_func(const void *p, struct siphash *state) {
const SocketPeer *s = p;
assert(s);
- assert(IN_SET(s->peer.sa.sa_family, AF_INET, AF_INET6));
if (s->peer.sa.sa_family == AF_INET)
siphash24_compress(&s->peer.in.sin_addr, sizeof(s->peer.in.sin_addr), state);
- else
+ else if (s->peer.sa.sa_family == AF_INET6)
siphash24_compress(&s->peer.in6.sin6_addr, sizeof(s->peer.in6.sin6_addr), state);
+ else if (s->peer.sa.sa_family == AF_VSOCK)
+ siphash24_compress(&s->peer.vm.svm_cid, sizeof(s->peer.vm.svm_cid), state);
+ else
+ assert_not_reached("Unknown address family.");
}
static int peer_address_compare_func(const void *a, const void *b) {
@@ -505,6 +509,12 @@ static int peer_address_compare_func(const void *a, const void *b) {
return memcmp(&x->peer.in.sin_addr, &y->peer.in.sin_addr, sizeof(x->peer.in.sin_addr));
case AF_INET6:
return memcmp(&x->peer.in6.sin6_addr, &y->peer.in6.sin6_addr, sizeof(x->peer.in6.sin6_addr));
+ case AF_VSOCK:
+ if (x->peer.vm.svm_cid < y->peer.vm.svm_cid)
+ return -1;
+ if (x->peer.vm.svm_cid > y->peer.vm.svm_cid)
+ return 1;
+ return 0;
}
assert_not_reached("Black sheep in the family!");
}
@@ -591,7 +601,7 @@ int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) {
if (r < 0)
return log_error_errno(errno, "getpeername failed: %m");
- if (!IN_SET(sa.peer.sa.sa_family, AF_INET, AF_INET6)) {
+ if (!IN_SET(sa.peer.sa.sa_family, AF_INET, AF_INET6, AF_VSOCK)) {
*p = NULL;
return 0;
}
@@ -607,6 +617,7 @@ int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) {
return log_oom();
remote->peer = sa.peer;
+ remote->peer_salen = salen;
r = set_put(s->peers_by_address, remote);
if (r < 0)
@@ -937,6 +948,16 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
break;
}
+ case AF_VSOCK:
+ if (asprintf(&r,
+ "%u-%u:%u-%u:%u",
+ nr,
+ local.vm.svm_cid, local.vm.svm_port,
+ remote.vm.svm_cid, remote.vm.svm_port) < 0)
+ return -ENOMEM;
+
+ break;
+
default:
assert_not_reached("Unhandled socket type.");
}
@@ -2189,7 +2210,7 @@ static void socket_enter_running(Socket *s, int cfd) {
} else if (r > 0 && p->n_ref > s->max_connections_per_source) {
_cleanup_free_ char *t = NULL;
- sockaddr_pretty(&p->peer.sa, FAMILY_ADDRESS_SIZE(p->peer.sa.sa_family), true, false, &t);
+ (void) sockaddr_pretty(&p->peer.sa, p->peer_salen, true, false, &t);
log_unit_warning(UNIT(s),
"Too many incoming connections (%u) from source %s, dropping connection.",
diff --git a/src/core/triggers.systemd.in b/src/core/triggers.systemd.in
index 0d8c303136..f8c8cbc5f9 100644
--- a/src/core/triggers.systemd.in
+++ b/src/core/triggers.systemd.in
@@ -27,11 +27,13 @@
-- installed, because other cases are covered by the *un scriptlets,
-- so sometimes we will reload needlessly.
-pid = posix.fork()
-if pid == 0 then
- assert(posix.exec("%{_bindir}/systemctl", "daemon-reload"))
-elseif pid > 0 then
- posix.wait(pid)
+if posix.access("/run/systemd/system") then
+ pid = posix.fork()
+ if pid == 0 then
+ assert(posix.exec("%{_bindir}/systemctl", "daemon-reload"))
+ elseif pid > 0 then
+ posix.wait(pid)
+ end
end
%transfiletriggerun -p <lua> -- @systemunitdir@ /etc/systemd/system
@@ -48,10 +50,12 @@ end
-- file in %transfiletriggerun and execute the daemon-reload in
-- the first %filetriggerpostun.
-posix.mkdir("%{_localstatedir}/lib")
-posix.mkdir("%{_localstatedir}/lib/rpm-state")
-posix.mkdir("%{_localstatedir}/lib/rpm-state/systemd")
-io.open("%{_localstatedir}/lib/rpm-state/systemd/needs-reload", "w")
+if posix.access("/run/systemd/system") then
+ posix.mkdir("%{_localstatedir}/lib")
+ posix.mkdir("%{_localstatedir}/lib/rpm-state")
+ posix.mkdir("%{_localstatedir}/lib/rpm-state/systemd")
+ io.open("%{_localstatedir}/lib/rpm-state/systemd/needs-reload", "w")
+end
%filetriggerpostun -P 1000100 -p <lua> -- @systemunitdir@ /etc/systemd/system
if posix.access("%{_localstatedir}/lib/rpm-state/systemd/needs-reload") then
diff --git a/src/core/unit.c b/src/core/unit.c
index 5d0b17425b..409668f6d2 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -866,11 +866,15 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
return 0;
if (c->private_tmp) {
- r = unit_require_mounts_for(u, "/tmp");
- if (r < 0)
- return r;
+ const char *p;
+
+ FOREACH_STRING(p, "/tmp", "/var/tmp") {
+ r = unit_require_mounts_for(u, p);
+ if (r < 0)
+ return r;
+ }
- r = unit_require_mounts_for(u, "/var/tmp");
+ r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_TMPFILES_SETUP_SERVICE, NULL, true);
if (r < 0)
return r;
}
diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c
index 68029865a0..23bf014929 100644
--- a/src/cryptsetup/cryptsetup-generator.c
+++ b/src/cryptsetup/cryptsetup-generator.c
@@ -102,18 +102,17 @@ static int create_disk(
if (!f)
return log_error_errno(errno, "Failed to create unit file %s: %m", p);
- fputs(
- "# Automatically generated by systemd-cryptsetup-generator\n\n"
- "[Unit]\n"
- "Description=Cryptography Setup for %I\n"
- "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
- "SourcePath=/etc/crypttab\n"
- "DefaultDependencies=no\n"
- "Conflicts=umount.target\n"
- "BindsTo=dev-mapper-%i.device\n"
- "IgnoreOnIsolate=true\n"
- "After=cryptsetup-pre.target\n",
- f);
+ fputs("# Automatically generated by systemd-cryptsetup-generator\n\n"
+ "[Unit]\n"
+ "Description=Cryptography Setup for %I\n"
+ "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
+ "SourcePath=/etc/crypttab\n"
+ "DefaultDependencies=no\n"
+ "Conflicts=umount.target\n"
+ "BindsTo=dev-mapper-%i.device\n"
+ "IgnoreOnIsolate=true\n"
+ "After=cryptsetup-pre.target\n",
+ f);
if (!nofail)
fprintf(f,
@@ -278,27 +277,30 @@ static crypto_device *get_crypto_device(const char *uuid) {
}
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
- int r;
- crypto_device *d;
_cleanup_free_ char *uuid = NULL, *uuid_value = NULL;
+ crypto_device *d;
+ int r;
- if (streq(key, "luks") && value) {
+ if (streq(key, "luks")) {
- r = parse_boolean(value);
+ r = value ? parse_boolean(value) : 1;
if (r < 0)
- log_warning("Failed to parse luks switch %s. Ignoring.", value);
+ log_warning("Failed to parse luks= kernel command line switch %s. Ignoring.", value);
else
arg_enabled = r;
- } else if (streq(key, "luks.crypttab") && value) {
+ } else if (streq(key, "luks.crypttab")) {
- r = parse_boolean(value);
+ r = value ? parse_boolean(value) : 1;
if (r < 0)
- log_warning("Failed to parse luks crypttab switch %s. Ignoring.", value);
+ log_warning("Failed to parse luks.crypttab= kernel command line switch %s. Ignoring.", value);
else
arg_read_crypttab = r;
- } else if (streq(key, "luks.uuid") && value) {
+ } else if (streq(key, "luks.uuid")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
d = get_crypto_device(startswith(value, "luks-") ? value+5 : value);
if (!d)
@@ -306,7 +308,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
d->create = arg_whitelist = true;
- } else if (streq(key, "luks.options") && value) {
+ } else if (streq(key, "luks.options")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
if (r == 2) {
@@ -314,13 +319,14 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (!d)
return log_oom();
- free(d->options);
- d->options = uuid_value;
- uuid_value = NULL;
+ free_and_replace(d->options, uuid_value);
} else if (free_and_strdup(&arg_default_options, value) < 0)
return log_oom();
- } else if (streq(key, "luks.key") && value) {
+ } else if (streq(key, "luks.key")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
if (r == 2) {
@@ -328,13 +334,14 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (!d)
return log_oom();
- free(d->keyfile);
- d->keyfile = uuid_value;
- uuid_value = NULL;
+ free_and_replace(d->keyfile, uuid_value);
} else if (free_and_strdup(&arg_default_keyfile, value) < 0)
return log_oom();
- } else if (streq(key, "luks.name") && value) {
+ } else if (streq(key, "luks.name")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
if (r == 2) {
@@ -349,7 +356,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
uuid_value = NULL;
} else
log_warning("Failed to parse luks name switch %s. Ignoring.", value);
-
}
return 0;
@@ -458,7 +464,7 @@ static int add_proc_cmdline_devices(void) {
}
int main(int argc, char *argv[]) {
- int r = EXIT_FAILURE;
+ int r;
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
@@ -475,32 +481,36 @@ int main(int argc, char *argv[]) {
umask(0022);
arg_disks = hashmap_new(&string_hash_ops);
- if (!arg_disks)
- goto cleanup;
+ if (!arg_disks) {
+ r = log_oom();
+ goto finish;
+ }
- r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, true);
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
if (r < 0) {
- log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
- r = EXIT_FAILURE;
+ log_warning_errno(r, "Failed to parse kernel command line: %m");
+ goto finish;
}
if (!arg_enabled) {
- r = EXIT_SUCCESS;
- goto cleanup;
+ r = 0;
+ goto finish;
}
- if (add_crypttab_devices() < 0)
- goto cleanup;
+ r = add_crypttab_devices();
+ if (r < 0)
+ goto finish;
- if (add_proc_cmdline_devices() < 0)
- goto cleanup;
+ r = add_proc_cmdline_devices();
+ if (r < 0)
+ goto finish;
- r = EXIT_SUCCESS;
+ r = 0;
-cleanup:
+finish:
free_arg_disks();
free(arg_default_options);
free(arg_default_keyfile);
- return r;
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index c7fec609df..cefd1b85df 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -310,7 +310,7 @@ static char *disk_mount_point(const char *label) {
if (asprintf(&device, "/dev/mapper/%s", label) < 0)
return NULL;
- f = setmntent("/etc/fstab", "r");
+ f = setmntent("/etc/fstab", "re");
if (!f)
return NULL;
@@ -593,17 +593,18 @@ static int help(void) {
}
int main(int argc, char *argv[]) {
- int r = EXIT_FAILURE;
struct crypt_device *cd = NULL;
+ int r;
if (argc <= 1) {
- help();
- return EXIT_SUCCESS;
+ r = help();
+ goto finish;
}
if (argc < 3) {
log_error("This program requires at least two arguments.");
- return EXIT_FAILURE;
+ r = -EINVAL;
+ goto finish;
}
log_set_target(LOG_TARGET_AUTO);
@@ -614,7 +615,6 @@ int main(int argc, char *argv[]) {
if (streq(argv[1], "attach")) {
uint32_t flags = 0;
- int k;
unsigned tries;
usec_t until;
crypt_status_info status;
@@ -648,11 +648,11 @@ int main(int argc, char *argv[]) {
if (arg_header) {
log_debug("LUKS header: %s", arg_header);
- k = crypt_init(&cd, arg_header);
+ r = crypt_init(&cd, arg_header);
} else
- k = crypt_init(&cd, argv[3]);
- if (k != 0) {
- log_error_errno(k, "crypt_init() failed: %m");
+ r = crypt_init(&cd, argv[3]);
+ if (r < 0) {
+ log_error_errno(r, "crypt_init() failed: %m");
goto finish;
}
@@ -661,7 +661,7 @@ int main(int argc, char *argv[]) {
status = crypt_status(cd, argv[2]);
if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
log_info("Volume %s already active.", argv[2]);
- r = EXIT_SUCCESS;
+ r = 0;
goto finish;
}
@@ -691,29 +691,30 @@ int main(int argc, char *argv[]) {
_cleanup_strv_free_erase_ char **passwords = NULL;
if (!key_file) {
- k = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords);
- if (k == -EAGAIN)
+ r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords);
+ if (r == -EAGAIN)
continue;
- else if (k < 0)
+ if (r < 0)
goto finish;
}
if (streq_ptr(arg_type, CRYPT_TCRYPT))
- k = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
+ r = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
else
- k = attach_luks_or_plain(cd,
+ r = attach_luks_or_plain(cd,
argv[2],
key_file,
arg_header ? argv[3] : NULL,
passwords,
flags);
- if (k >= 0)
+ if (r >= 0)
break;
- else if (k == -EAGAIN) {
+ if (r == -EAGAIN) {
key_file = NULL;
continue;
- } else if (k != -EPERM) {
- log_error_errno(k, "Failed to activate: %m");
+ }
+ if (r != -EPERM) {
+ log_error_errno(r, "Failed to activate: %m");
goto finish;
}
@@ -722,40 +723,40 @@ int main(int argc, char *argv[]) {
if (arg_tries != 0 && tries >= arg_tries) {
log_error("Too many attempts; giving up.");
- r = EXIT_FAILURE;
+ r = -EPERM;
goto finish;
}
} else if (streq(argv[1], "detach")) {
- int k;
- k = crypt_init_by_name(&cd, argv[2]);
- if (k == -ENODEV) {
+ r = crypt_init_by_name(&cd, argv[2]);
+ if (r == -ENODEV) {
log_info("Volume %s already inactive.", argv[2]);
- r = EXIT_SUCCESS;
+ r = 0;
goto finish;
- } else if (k) {
- log_error_errno(k, "crypt_init_by_name() failed: %m");
+ }
+ if (r < 0) {
+ log_error_errno(r, "crypt_init_by_name() failed: %m");
goto finish;
}
crypt_set_log_callback(cd, log_glue, NULL);
- k = crypt_deactivate(cd, argv[2]);
- if (k < 0) {
- log_error_errno(k, "Failed to deactivate: %m");
+ r = crypt_deactivate(cd, argv[2]);
+ if (r < 0) {
+ log_error_errno(r, "Failed to deactivate: %m");
goto finish;
}
} else {
log_error("Unknown verb %s.", argv[1]);
+ r = -EINVAL;
goto finish;
}
- r = EXIT_SUCCESS;
+ r = 0;
finish:
-
if (cd)
crypt_free(cd);
@@ -764,5 +765,5 @@ finish:
free(arg_header);
strv_free(arg_tcrypt_keyfiles);
- return r;
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c
index 28ebe36b38..1d8bc71e57 100644
--- a/src/debug-generator/debug-generator.c
+++ b/src/debug-generator/debug-generator.c
@@ -39,56 +39,53 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
assert(key);
if (streq(key, "systemd.mask")) {
+ char *n;
- if (!value)
- log_error("Missing argument for systemd.mask= kernel command line parameter.");
- else {
- char *n;
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
- r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n);
- if (r < 0)
- return log_error_errno(r, "Failed to glob unit name: %m");
+ r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to glob unit name: %m");
- r = strv_consume(&arg_mask, n);
- if (r < 0)
- return log_oom();
- }
+ r = strv_consume(&arg_mask, n);
+ if (r < 0)
+ return log_oom();
} else if (streq(key, "systemd.wants")) {
+ char *n;
- if (!value)
- log_error("Missing argument for systemd.want= kernel command line parameter.");
- else {
- char *n;
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
- r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n);
- if (r < 0)
- return log_error_errno(r, "Failed to glob unit name: %m");
+ r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to glob unit name: %m");
- r = strv_consume(&arg_wants, n);
- if (r < 0)
- return log_oom();
- }
+ r = strv_consume(&arg_wants, n);
+ if (r < 0)
+ return log_oom();
- } else if (streq(key, "systemd.debug-shell")) {
+ } else if (proc_cmdline_key_streq(key, "systemd.debug_shell")) {
if (value) {
r = parse_boolean(value);
if (r < 0)
- log_error("Failed to parse systemd.debug-shell= argument '%s', ignoring.", value);
+ log_error("Failed to parse systemd.debug_shell= argument '%s', ignoring.", value);
else
arg_debug_shell = r;
} else
arg_debug_shell = true;
+
} else if (streq(key, "systemd.unit")) {
- if (!value)
- log_error("Missing argument for systemd.unit= kernel command line parameter.");
- else {
- r = free_and_strdup(&arg_default_unit, value);
- if (r < 0)
- return log_error_errno(r, "Failed to set default unit %s: %m", value);
- }
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ r = free_and_strdup(&arg_default_unit, value);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set default unit %s: %m", value);
+
} else if (!value) {
const char *target;
@@ -173,7 +170,7 @@ int main(int argc, char *argv[]) {
umask(0022);
- r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false);
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c
index f2f1e135ec..fd9db5ba87 100644
--- a/src/dissect/dissect.c
+++ b/src/dissect/dissect.c
@@ -35,7 +35,7 @@ static enum {
} arg_action = ACTION_DISSECT;
static const char *arg_image = NULL;
static const char *arg_path = NULL;
-static DissectImageFlags arg_flags = DISSECT_IMAGE_DISCARD_ON_LOOP;
+static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP;
static void *arg_root_hash = NULL;
static size_t arg_root_hash_size = 0;
@@ -191,7 +191,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- r = dissect_image(d->fd, arg_root_hash, arg_root_hash_size, &m);
+ r = dissect_image(d->fd, arg_root_hash, arg_root_hash_size, arg_flags, &m);
if (r == -ENOPKG) {
log_error_errno(r, "Couldn't identify a suitable partition table or file system in %s.", arg_image);
goto finish;
@@ -221,6 +221,9 @@ int main(int argc, char *argv[]) {
p->rw ? "writable" : "read-only",
partition_designator_to_string(i));
+ if (!sd_id128_is_null(p->uuid))
+ printf(" (UUID " SD_ID128_FORMAT_STR ")", SD_ID128_FORMAT_VAL(p->uuid));
+
if (p->fstype)
printf(" of type %s", p->fstype);
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index c9e8e54ee3..fd7051f21e 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -33,6 +33,7 @@
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
+#include "proc-cmdline.h"
#include "random-util.h"
#include "string-util.h"
#include "strv.h"
@@ -825,6 +826,7 @@ static int parse_argv(int argc, char *argv[]) {
}
int main(int argc, char *argv[]) {
+ bool enabled;
int r;
r = parse_argv(argc, argv);
@@ -837,6 +839,16 @@ int main(int argc, char *argv[]) {
umask(0022);
+ r = proc_cmdline_get_bool("systemd.firstboot", &enabled);
+ if (r < 0) {
+ log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring.");
+ goto finish;
+ }
+ if (r > 0 && !enabled) {
+ r = 0; /* disabled */
+ goto finish;
+ }
+
r = process_locale();
if (r < 0)
goto finish;
diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c
index be25c6a2b2..2100681e17 100644
--- a/src/fsck/fsck.c
+++ b/src/fsck/fsck.c
@@ -99,7 +99,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
assert(key);
- if (streq(key, "fsck.mode") && value) {
+ if (streq(key, "fsck.mode")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
if (streq(value, "auto"))
arg_force = arg_skip = false;
@@ -110,7 +113,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
else
log_warning("Invalid fsck.mode= parameter '%s'. Ignoring.", value);
- } else if (streq(key, "fsck.repair") && value) {
+ } else if (streq(key, "fsck.repair")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
if (streq(value, "preen"))
arg_repair = "-a";
@@ -293,7 +299,7 @@ int main(int argc, char *argv[]) {
umask(0022);
- r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, true);
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index f6a912ae06..3c601a63e2 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -42,16 +42,20 @@
#include "unit-name.h"
#include "util.h"
#include "virt.h"
+#include "volatile-util.h"
static const char *arg_dest = "/tmp";
+static const char *arg_dest_late = "/tmp";
static bool arg_fstab_enabled = true;
static char *arg_root_what = NULL;
static char *arg_root_fstype = NULL;
static char *arg_root_options = NULL;
+static char *arg_root_hash = NULL;
static int arg_root_rw = -1;
static char *arg_usr_what = NULL;
static char *arg_usr_fstype = NULL;
static char *arg_usr_options = NULL;
+static VolatileMode arg_volatile_mode = _VOLATILE_MODE_INVALID;
static int add_swap(
const char *what,
@@ -235,6 +239,7 @@ static int write_requires_mounts_for(FILE *f, const char *opts) {
}
static int add_mount(
+ const char *dest,
const char *what,
const char *where,
const char *fstype,
@@ -286,7 +291,7 @@ static int add_mount(
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
- unit = strjoin(arg_dest, "/", name);
+ unit = strjoin(dest, "/", name);
if (!unit)
return log_oom();
@@ -318,7 +323,7 @@ static int add_mount(
}
if (passno != 0) {
- r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
+ r = generator_write_fsck_deps(f, dest, what, where, fstype);
if (r < 0)
return r;
}
@@ -334,7 +339,7 @@ static int add_mount(
if (!isempty(fstype) && !streq(fstype, "auto"))
fprintf(f, "Type=%s\n", fstype);
- r = generator_write_timeouts(arg_dest, what, where, opts, &filtered);
+ r = generator_write_timeouts(dest, what, where, opts, &filtered);
if (r < 0)
return r;
@@ -350,7 +355,7 @@ static int add_mount(
return log_error_errno(r, "Failed to write unit file %s: %m", unit);
if (!noauto && !automount) {
- lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", name);
+ lnk = strjoin(dest, "/", post, nofail ? ".wants/" : ".requires/", name);
if (!lnk)
return log_oom();
@@ -364,7 +369,7 @@ static int add_mount(
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
- automount_unit = strjoin(arg_dest, "/", automount_name);
+ automount_unit = strjoin(dest, "/", automount_name);
if (!automount_unit)
return log_oom();
@@ -406,7 +411,7 @@ static int add_mount(
return log_error_errno(r, "Failed to write unit file %s: %m", automount_unit);
free(lnk);
- lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name);
+ lnk = strjoin(dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name);
if (!lnk)
return log_oom();
@@ -479,7 +484,8 @@ static int parse_fstab(bool initrd) {
else
post = SPECIAL_LOCAL_FS_TARGET;
- k = add_mount(what,
+ k = add_mount(arg_dest,
+ what,
where,
me->mnt_type,
me->mnt_opts,
@@ -540,7 +546,8 @@ static int add_sysroot_mount(void) {
return r;
}
- return add_mount(what,
+ return add_mount(arg_dest,
+ what,
"/sysroot",
arg_root_fstype,
opts,
@@ -593,7 +600,8 @@ static int add_sysroot_usr_mount(void) {
opts = arg_usr_options;
log_debug("Found entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype));
- return add_mount(what,
+ return add_mount(arg_dest,
+ what,
"/sysroot/usr",
arg_usr_fstype,
opts,
@@ -605,6 +613,46 @@ static int add_sysroot_usr_mount(void) {
"/proc/cmdline");
}
+static int add_volatile_root(void) {
+ const char *from, *to;
+
+ if (arg_volatile_mode != VOLATILE_YES)
+ return 0;
+
+ /* Let's add in systemd-remount-volatile.service which will remount the root device to tmpfs if this is
+ * requested, leaving only /usr from the root mount inside. */
+
+ from = strjoina(SYSTEM_DATA_UNIT_PATH "/systemd-volatile-root.service");
+ to = strjoina(arg_dest, "/" SPECIAL_INITRD_ROOT_FS_TARGET, ".requires/systemd-volatile-root.service");
+
+ (void) mkdir_parents(to, 0755);
+
+ if (symlink(from, to) < 0)
+ return log_error_errno(errno, "Failed to hook in volatile remount service: %m");
+
+ return 0;
+}
+
+static int add_volatile_var(void) {
+
+ if (arg_volatile_mode != VOLATILE_STATE)
+ return 0;
+
+ /* If requested, mount /var as tmpfs, but do so only if there's nothing else defined for this. */
+
+ return add_mount(arg_dest_late,
+ "tmpfs",
+ "/var",
+ "tmpfs",
+ "mode=0755",
+ 0,
+ false,
+ false,
+ false,
+ SPECIAL_LOCAL_FS_TARGET,
+ "/proc/cmdline");
+}
+
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r;
@@ -612,27 +660,36 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
* instance should take precedence. In the case of multiple rootflags=
* or usrflags= the arguments should be concatenated */
- if (STR_IN_SET(key, "fstab", "rd.fstab") && value) {
+ if (STR_IN_SET(key, "fstab", "rd.fstab")) {
- r = parse_boolean(value);
+ r = value ? parse_boolean(value) : 1;
if (r < 0)
log_warning("Failed to parse fstab switch %s. Ignoring.", value);
else
arg_fstab_enabled = r;
- } else if (streq(key, "root") && value) {
+ } else if (streq(key, "root")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
if (free_and_strdup(&arg_root_what, value) < 0)
return log_oom();
- } else if (streq(key, "rootfstype") && value) {
+ } else if (streq(key, "rootfstype")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
if (free_and_strdup(&arg_root_fstype, value) < 0)
return log_oom();
- } else if (streq(key, "rootflags") && value) {
+ } else if (streq(key, "rootflags")) {
char *o;
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
o = arg_root_options ?
strjoin(arg_root_options, ",", value) :
strdup(value);
@@ -641,20 +698,36 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
free(arg_root_options);
arg_root_options = o;
+ } else if (streq(key, "roothash")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
- } else if (streq(key, "mount.usr") && value) {
+ if (free_and_strdup(&arg_root_hash, value) < 0)
+ return log_oom();
+
+ } else if (streq(key, "mount.usr")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
if (free_and_strdup(&arg_usr_what, value) < 0)
return log_oom();
- } else if (streq(key, "mount.usrfstype") && value) {
+ } else if (streq(key, "mount.usrfstype")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
if (free_and_strdup(&arg_usr_fstype, value) < 0)
return log_oom();
- } else if (streq(key, "mount.usrflags") && value) {
+ } else if (streq(key, "mount.usrflags")) {
char *o;
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
o = arg_usr_options ?
strjoin(arg_usr_options, ",", value) :
strdup(value);
@@ -668,10 +741,40 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
arg_root_rw = true;
else if (streq(key, "ro") && !value)
arg_root_rw = false;
+ else if (streq(key, "systemd.volatile")) {
+ VolatileMode m;
+
+ if (value) {
+ m = volatile_mode_from_string(value);
+ if (m < 0)
+ log_warning("Failed to parse systemd.volatile= argument: %s", value);
+ else
+ arg_volatile_mode = m;
+ } else
+ arg_volatile_mode = VOLATILE_YES;
+ }
return 0;
}
+static int determine_root(void) {
+ /* If we have a root hash but no root device then Verity is used, and we use the "root" DM device as root. */
+
+ if (arg_root_what)
+ return 0;
+
+ if (!arg_root_hash)
+ return 0;
+
+ arg_root_what = strdup("/dev/mapper/root");
+ if (!arg_root_what)
+ return log_oom();
+
+ log_info("Using verity root device %s.", arg_root_what);
+
+ return 1;
+}
+
int main(int argc, char *argv[]) {
int r = 0;
@@ -682,6 +785,8 @@ int main(int argc, char *argv[]) {
if (argc > 1)
arg_dest = argv[1];
+ if (argc > 3)
+ arg_dest_late = argv[3];
log_set_target(LOG_TARGET_SAFE);
log_parse_environment();
@@ -689,10 +794,12 @@ int main(int argc, char *argv[]) {
umask(0022);
- r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false);
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
+ (void) determine_root();
+
/* Always honour root= and usr= in the kernel command line if we are in an initrd */
if (in_initrd()) {
int k;
@@ -702,8 +809,12 @@ int main(int argc, char *argv[]) {
k = add_sysroot_usr_mount();
if (k < 0)
r = k;
+
+ k = add_volatile_root();
+ if (k < 0)
+ r = k;
} else
- r = 0;
+ r = add_volatile_var();
/* Honour /etc/fstab only when that's enabled */
if (arg_fstab_enabled) {
@@ -729,6 +840,7 @@ int main(int argc, char *argv[]) {
free(arg_root_what);
free(arg_root_fstype);
free(arg_root_options);
+ free(arg_root_hash);
free(arg_usr_what);
free(arg_usr_fstype);
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index 0f95f0d813..e61ef8f249 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -29,6 +29,7 @@
#include "blkid-util.h"
#include "btrfs-util.h"
#include "dirent-util.h"
+#include "dissect-image.h"
#include "efivars.h"
#include "fd-util.h"
#include "fileio.h"
@@ -54,7 +55,7 @@ static bool arg_enabled = true;
static bool arg_root_enabled = true;
static bool arg_root_rw = false;
-static int add_cryptsetup(const char *id, const char *what, bool rw, char **device) {
+static int add_cryptsetup(const char *id, const char *what, bool rw, bool require, char **device) {
_cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL;
_cleanup_fclose_ FILE *f = NULL;
char *from, *ret;
@@ -62,7 +63,6 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi
assert(id);
assert(what);
- assert(device);
r = unit_name_from_path(what, ".device", &d);
if (r < 0)
@@ -119,23 +119,26 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi
if (symlink(from, to) < 0)
return log_error_errno(errno, "Failed to create symlink %s: %m", to);
- free(to);
- to = strjoin(arg_dest, "/cryptsetup.target.requires/", n);
- if (!to)
- return log_oom();
+ if (require) {
+ free(to);
- mkdir_parents_label(to, 0755);
- if (symlink(from, to) < 0)
- return log_error_errno(errno, "Failed to create symlink %s: %m", to);
+ to = strjoin(arg_dest, "/cryptsetup.target.requires/", n);
+ if (!to)
+ return log_oom();
- free(to);
- to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n);
- if (!to)
- return log_oom();
+ mkdir_parents_label(to, 0755);
+ if (symlink(from, to) < 0)
+ return log_error_errno(errno, "Failed to create symlink %s: %m", to);
- mkdir_parents_label(to, 0755);
- if (symlink(from, to) < 0)
- return log_error_errno(errno, "Failed to create symlink %s: %m", to);
+ free(to);
+ to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n);
+ if (!to)
+ return log_oom();
+
+ mkdir_parents_label(to, 0755);
+ if (symlink(from, to) < 0)
+ return log_error_errno(errno, "Failed to create symlink %s: %m", to);
+ }
free(p);
p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf");
@@ -155,7 +158,8 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi
if (!ret)
return log_oom();
- *device = ret;
+ if (device)
+ *device = ret;
return 0;
}
@@ -182,7 +186,7 @@ static int add_mount(
if (streq_ptr(fstype, "crypto_LUKS")) {
- r = add_cryptsetup(id, what, rw, &crypto_what);
+ r = add_cryptsetup(id, what, rw, true, &crypto_what);
if (r < 0)
return r;
@@ -270,61 +274,28 @@ static bool path_is_busy(const char *where) {
return false;
}
-static int probe_and_add_mount(
+static int add_partition_mount(
+ DissectedPartition *p,
const char *id,
- const char *what,
const char *where,
- bool rw,
- const char *description,
- const char *post) {
+ const char *description) {
- _cleanup_blkid_free_probe_ blkid_probe b = NULL;
- const char *fstype = NULL;
- int r;
-
- assert(id);
- assert(what);
- assert(where);
- assert(description);
+ assert(p);
if (path_is_busy(where)) {
log_debug("%s already populated, ignoring.", where);
return 0;
}
- /* Let's check the partition type here, so that we know
- * whether to do LUKS magic. */
-
- errno = 0;
- b = blkid_new_probe_from_filename(what);
- if (!b) {
- if (errno == 0)
- return log_oom();
- return log_error_errno(errno, "Failed to allocate prober: %m");
- }
-
- blkid_probe_enable_superblocks(b, 1);
- blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
-
- errno = 0;
- r = blkid_do_safeprobe(b);
- if (r == -2 || r == 1) /* no result or uncertain */
- return 0;
- else if (r != 0)
- return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what);
-
- /* add_mount is OK with fstype being NULL. */
- (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
-
return add_mount(
id,
- what,
+ p->node,
where,
- fstype,
- rw,
+ p->fstype,
+ p->rw,
NULL,
description,
- post);
+ SPECIAL_LOCAL_FS_TARGET);
}
static int add_swap(const char *path) {
@@ -449,22 +420,17 @@ static int add_automount(
return 0;
}
-static int add_boot(const char *what) {
+static int add_esp(DissectedPartition *p) {
const char *esp;
int r;
- assert(what);
+ assert(p);
if (in_initrd()) {
log_debug("In initrd, ignoring the ESP.");
return 0;
}
- if (detect_container() > 0) {
- log_debug("In a container, ignoring the ESP.");
- return 0;
- }
-
/* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice */
esp = access("/efi/", F_OK) >= 0 ? "/efi" : "/boot";
@@ -480,9 +446,7 @@ static int add_boot(const char *what) {
}
if (is_efi_boot()) {
- _cleanup_blkid_free_probe_ blkid_probe b = NULL;
- const char *fstype = NULL, *uuid_string = NULL;
- sd_id128_t loader_uuid, part_uuid;
+ sd_id128_t loader_uuid;
/* If this is an EFI boot, be extra careful, and only mount the ESP if it was the ESP used for booting. */
@@ -494,43 +458,7 @@ static int add_boot(const char *what) {
if (r < 0)
return log_error_errno(r, "Failed to read ESP partition UUID: %m");
- errno = 0;
- b = blkid_new_probe_from_filename(what);
- if (!b) {
- if (errno == 0)
- return log_oom();
- return log_error_errno(errno, "Failed to allocate prober: %m");
- }
-
- blkid_probe_enable_partitions(b, 1);
- blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
-
- errno = 0;
- r = blkid_do_safeprobe(b);
- if (r == -2 || r == 1) /* no result or uncertain */
- return 0;
- else if (r != 0)
- return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what);
-
- (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
- if (!streq_ptr(fstype, "vfat")) {
- log_debug("Partition for %s is not a FAT filesystem, ignoring.", esp);
- return 0;
- }
-
- errno = 0;
- r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid_string, NULL);
- if (r != 0) {
- log_debug_errno(errno, "Partition for %s does not have a UUID, ignoring.", esp);
- return 0;
- }
-
- if (sd_id128_from_string(uuid_string, &part_uuid) < 0) {
- log_debug("Partition for %s does not have a valid UUID, ignoring.", esp);
- return 0;
- }
-
- if (!sd_id128_equal(part_uuid, loader_uuid)) {
+ if (!sd_id128_equal(p->uuid, loader_uuid)) {
log_debug("Partition for %s does not appear to be the partition we are booted from.", esp);
return 0;
}
@@ -538,35 +466,29 @@ static int add_boot(const char *what) {
log_debug("Not an EFI boot, skipping ESP check.");
return add_automount("boot",
- what,
- esp,
- "vfat",
- true,
- "umask=0077",
- "EFI System Partition Automount",
- 120 * USEC_PER_SEC);
+ p->node,
+ esp,
+ p->fstype,
+ true,
+ "umask=0077",
+ "EFI System Partition Automount",
+ 120 * USEC_PER_SEC);
}
#else
-static int add_boot(const char *what) {
+static int add_esp(const char *what) {
return 0;
}
#endif
-static int enumerate_partitions(dev_t devnum) {
-
- _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
+static int open_parent(dev_t devnum, int *ret) {
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
- _cleanup_blkid_free_probe_ blkid_probe b = NULL;
_cleanup_udev_unref_ struct udev *udev = NULL;
- _cleanup_free_ char *boot = NULL, *home = NULL, *srv = NULL;
- struct udev_list_entry *first, *item;
- struct udev_device *parent = NULL;
- const char *name, *node, *pttype, *devtype;
- int boot_nr = -1, home_nr = -1, srv_nr = -1;
- bool home_rw = true, srv_rw = true;
- blkid_partlist pl;
- int r, k;
+ const char *name, *devtype, *node;
+ struct udev_device *parent;
dev_t pn;
+ int fd;
+
+ assert(ret);
udev = udev_new();
if (!udev)
@@ -580,228 +502,94 @@ static int enumerate_partitions(dev_t devnum) {
if (!name)
name = udev_device_get_syspath(d);
if (!name) {
- log_debug("Device %u:%u does not have a name, ignoring.",
- major(devnum), minor(devnum));
- return 0;
+ log_debug("Device %u:%u does not have a name, ignoring.", major(devnum), minor(devnum));
+ goto not_found;
}
parent = udev_device_get_parent(d);
if (!parent) {
log_debug("%s: not a partitioned device, ignoring.", name);
- return 0;
+ goto not_found;
}
/* Does it have a devtype? */
devtype = udev_device_get_devtype(parent);
if (!devtype) {
log_debug("%s: parent doesn't have a device type, ignoring.", name);
- return 0;
+ goto not_found;
}
/* Is this a disk or a partition? We only care for disks... */
if (!streq(devtype, "disk")) {
log_debug("%s: parent isn't a raw disk, ignoring.", name);
- return 0;
+ goto not_found;
}
/* Does it have a device node? */
node = udev_device_get_devnode(parent);
if (!node) {
log_debug("%s: parent device does not have device node, ignoring.", name);
- return 0;
+ goto not_found;
}
log_debug("%s: root device %s.", name, node);
pn = udev_device_get_devnum(parent);
- if (major(pn) == 0)
- return 0;
-
- errno = 0;
- b = blkid_new_probe_from_filename(node);
- if (!b) {
- if (errno == 0)
- return log_oom();
-
- return log_error_errno(errno, "%s: failed to allocate prober: %m", node);
+ if (major(pn) == 0) {
+ log_debug("%s: parent device is not a proper block device, ignoring.", name);
+ goto not_found;
}
- blkid_probe_enable_partitions(b, 1);
- blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
+ fd = open(node, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open %s: %m", node);
- errno = 0;
- r = blkid_do_safeprobe(b);
- if (r == 1)
- return 0; /* no results */
- else if (r == -2) {
- log_warning("%s: probe gave ambiguous results, ignoring.", node);
- return 0;
- } else if (r != 0)
- return log_error_errno(errno ?: EIO, "%s: failed to probe: %m", node);
+ *ret = fd;
+ return 1;
- errno = 0;
- r = blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
- if (r != 0) {
- if (errno == 0)
- return 0; /* No partition table found. */
+not_found:
+ *ret = -1;
+ return 0;
+}
- return log_error_errno(errno, "%s: failed to determine partition table type: %m", node);
- }
+static int enumerate_partitions(dev_t devnum) {
- /* We only do this all for GPT... */
- if (!streq_ptr(pttype, "gpt")) {
- log_debug("%s: not a GPT partition table, ignoring.", node);
- return 0;
- }
+ _cleanup_close_ int fd = -1;
+ _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
+ int r, k;
- errno = 0;
- pl = blkid_probe_get_partitions(b);
- if (!pl) {
- if (errno == 0)
- return log_oom();
+ r = open_parent(devnum, &fd);
+ if (r <= 0)
+ return r;
- return log_error_errno(errno, "%s: failed to list partitions: %m", node);
+ r = dissect_image(fd, NULL, 0, DISSECT_IMAGE_GPT_ONLY, &m);
+ if (r == -ENOPKG) {
+ log_debug_errno(r, "No suitable partition table found, ignoring.");
+ return 0;
}
-
- e = udev_enumerate_new(udev);
- if (!e)
- return log_oom();
-
- r = udev_enumerate_add_match_parent(e, parent);
- if (r < 0)
- return log_oom();
-
- r = udev_enumerate_add_match_subsystem(e, "block");
- if (r < 0)
- return log_oom();
-
- r = udev_enumerate_scan_devices(e);
if (r < 0)
- return log_error_errno(r, "%s: failed to enumerate partitions: %m", node);
-
- first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first) {
- _cleanup_udev_device_unref_ struct udev_device *q;
- unsigned long long flags;
- const char *stype, *subnode;
- sd_id128_t type_id;
- blkid_partition pp;
- dev_t qn;
- int nr;
-
- q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
- if (!q)
- continue;
-
- qn = udev_device_get_devnum(q);
- if (major(qn) == 0)
- continue;
-
- if (qn == devnum)
- continue;
-
- if (qn == pn)
- continue;
-
- subnode = udev_device_get_devnode(q);
- if (!subnode)
- continue;
-
- pp = blkid_partlist_devno_to_partition(pl, qn);
- if (!pp)
- continue;
-
- nr = blkid_partition_get_partno(pp);
- if (nr < 0)
- continue;
-
- stype = blkid_partition_get_type_string(pp);
- if (!stype)
- continue;
-
- if (sd_id128_from_string(stype, &type_id) < 0)
- continue;
-
- flags = blkid_partition_get_flags(pp);
-
- if (sd_id128_equal(type_id, GPT_SWAP)) {
-
- if (flags & GPT_FLAG_NO_AUTO)
- continue;
-
- if (flags & GPT_FLAG_READ_ONLY) {
- log_debug("%s marked as read-only swap partition, which is bogus. Ignoring.", subnode);
- continue;
- }
-
- k = add_swap(subnode);
- if (k < 0)
- r = k;
-
- } else if (sd_id128_equal(type_id, GPT_ESP)) {
-
- /* We only care for the first /boot partition */
- if (boot && nr >= boot_nr)
- continue;
-
- /* Note that we do not honour the "no-auto"
- * flag for the ESP, as it is often unset, to
- * hide it from Windows. */
-
- boot_nr = nr;
-
- r = free_and_strdup(&boot, subnode);
- if (r < 0)
- return log_oom();
-
- } else if (sd_id128_equal(type_id, GPT_HOME)) {
+ return log_error_errno(r, "Failed to dissect: %m");
- if (flags & GPT_FLAG_NO_AUTO)
- continue;
-
- /* We only care for the first /home partition */
- if (home && nr >= home_nr)
- continue;
-
- home_nr = nr;
- home_rw = !(flags & GPT_FLAG_READ_ONLY),
-
- r = free_and_strdup(&home, subnode);
- if (r < 0)
- return log_oom();
-
- } else if (sd_id128_equal(type_id, GPT_SRV)) {
-
- if (flags & GPT_FLAG_NO_AUTO)
- continue;
-
- /* We only care for the first /srv partition */
- if (srv && nr >= srv_nr)
- continue;
-
- srv_nr = nr;
- srv_rw = !(flags & GPT_FLAG_READ_ONLY),
-
- r = free_and_strdup(&srv, subnode);
- if (r < 0)
- return log_oom();
- }
+ if (m->partitions[PARTITION_SWAP].found) {
+ k = add_swap(m->partitions[PARTITION_SWAP].node);
+ if (k < 0)
+ r = k;
}
- if (boot) {
- k = add_boot(boot);
+ if (m->partitions[PARTITION_ESP].found) {
+ k = add_esp(m->partitions + PARTITION_ESP);
if (k < 0)
r = k;
}
- if (home) {
- k = probe_and_add_mount("home", home, "/home", home_rw, "Home Partition", SPECIAL_LOCAL_FS_TARGET);
+ if (m->partitions[PARTITION_HOME].found) {
+ k = add_partition_mount(m->partitions + PARTITION_HOME, "home", "/home", "Home Partition");
if (k < 0)
r = k;
}
- if (srv) {
- k = probe_and_add_mount("srv", srv, "/srv", srv_rw, "Server Data Partition", SPECIAL_LOCAL_FS_TARGET);
+ if (m->partitions[PARTITION_SRV].found) {
+ k = add_partition_mount(m->partitions + PARTITION_SRV, "srv", "/srv", "Server Data Partition");
if (k < 0)
r = k;
}
@@ -876,8 +664,40 @@ static int get_block_device_harder(const char *path, dev_t *dev) {
if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
continue;
- if (found) /* Don't try to support multiple backing block devices */
- goto fallback;
+ if (found) {
+ _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL;
+
+ /* We found a device backed by multiple other devices. We don't really support automatic
+ * discovery on such setups, with the exception of dm-verity partitions. In this case there are
+ * two backing devices: the data partition and the hash partition. We are fine with such
+ * setups, however, only if both partitions are on the same physical device. Hence, let's
+ * verify this. */
+
+ u = strjoin(p, "/", de->d_name, "/../dev");
+ if (!u)
+ return -ENOMEM;
+
+ v = strjoin(p, "/", found->d_name, "/../dev");
+ if (!v)
+ return -ENOMEM;
+
+ r = read_one_line_file(u, &a);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to read %s: %m", u);
+ goto fallback;
+ }
+
+ r = read_one_line_file(v, &b);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to read %s: %m", v);
+ goto fallback;
+ }
+
+ /* Check if the parent device is the same. If not, then the two backing devices are on
+ * different physical devices, and we don't support that. */
+ if (!streq(a, b))
+ goto fallback;
+ }
found = de;
}
@@ -912,21 +732,33 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
assert(key);
- if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto") && value) {
+ if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto")) {
- r = parse_boolean(value);
+ r = value ? parse_boolean(value) : 1;
if (r < 0)
log_warning("Failed to parse gpt-auto switch \"%s\". Ignoring.", value);
else
arg_enabled = r;
- } else if (streq(key, "root") && value) {
+ } else if (streq(key, "root")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
/* Disable root disk logic if there's a root= value
* specified (unless it happens to be "gpt-auto") */
arg_root_enabled = streq(value, "gpt-auto");
+ } else if (streq(key, "roothash")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ /* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */
+
+ arg_root_enabled = false;
+
} else if (streq(key, "rw") && !value)
arg_root_rw = true;
else if (streq(key, "ro") && !value)
@@ -935,6 +767,16 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
return 0;
}
+#ifdef ENABLE_EFI
+static int add_root_cryptsetup(void) {
+
+ /* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which
+ * sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */
+
+ return add_cryptsetup("root", "/dev/gpt-auto-root-luks", true, false, NULL);
+}
+#endif
+
static int add_root_mount(void) {
#ifdef ENABLE_EFI
@@ -960,6 +802,10 @@ static int add_root_mount(void) {
r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root");
if (r < 0)
return 0;
+
+ r = add_root_cryptsetup();
+ if (r < 0)
+ return r;
}
return add_mount(
@@ -983,11 +829,11 @@ static int add_mounts(void) {
r = get_block_device_harder("/", &devno);
if (r < 0)
return log_error_errno(r, "Failed to determine block device of root file system: %m");
- else if (r == 0) {
+ if (r == 0) {
r = get_block_device_harder("/usr", &devno);
if (r < 0)
return log_error_errno(r, "Failed to determine block device of /usr file system: %m");
- else if (r == 0) {
+ if (r == 0) {
log_debug("Neither root nor /usr file system are on a (single) block device.");
return 0;
}
@@ -997,7 +843,7 @@ static int add_mounts(void) {
}
int main(int argc, char *argv[]) {
- int r = 0;
+ int r = 0, k;
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
@@ -1018,7 +864,7 @@ int main(int argc, char *argv[]) {
return EXIT_SUCCESS;
}
- r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false);
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
@@ -1031,8 +877,6 @@ int main(int argc, char *argv[]) {
r = add_root_mount();
if (!in_initrd()) {
- int k;
-
k = add_mounts();
if (k < 0)
r = k;
diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c
index 9f59c04b26..a97fe668d5 100644
--- a/src/hibernate-resume/hibernate-resume-generator.c
+++ b/src/hibernate-resume/hibernate-resume-generator.c
@@ -31,15 +31,22 @@
#include "util.h"
static const char *arg_dest = "/tmp";
-static char *arg_resume_dev = NULL;
+static char *arg_resume_device = NULL;
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
- if (streq(key, "resume") && value) {
- free(arg_resume_dev);
- arg_resume_dev = fstab_node_to_udev_node(value);
- if (!arg_resume_dev)
+ if (streq(key, "resume")) {
+ char *s;
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ s = fstab_node_to_udev_node(value);
+ if (!s)
return log_oom();
+
+ free(arg_resume_device);
+ arg_resume_device = s;
}
return 0;
@@ -49,10 +56,10 @@ static int process_resume(void) {
_cleanup_free_ char *name = NULL, *lnk = NULL;
int r;
- if (!arg_resume_dev)
+ if (!arg_resume_device)
return 0;
- r = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_dev, ".service", &name);
+ r = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_device, ".service", &name);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
@@ -88,12 +95,12 @@ int main(int argc, char *argv[]) {
if (!in_initrd())
return EXIT_SUCCESS;
- r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false);
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
r = process_resume();
- free(arg_resume_dev);
+ free(arg_resume_device);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/import/pull-common.c b/src/import/pull-common.c
index 5ddc0c56f4..62a9195cc4 100644
--- a/src/import/pull-common.c
+++ b/src/import/pull-common.c
@@ -218,37 +218,40 @@ int pull_make_path(const char *url, const char *etag, const char *image_root, co
return 0;
}
-int pull_make_settings_job(
+int pull_make_auxiliary_job(
PullJob **ret,
const char *url,
+ int (*strip_suffixes)(const char *name, char **ret),
+ const char *suffix,
CurlGlue *glue,
PullJobFinished on_finished,
void *userdata) {
- _cleanup_free_ char *last_component = NULL, *ll = NULL, *settings_url = NULL;
+ _cleanup_free_ char *last_component = NULL, *ll = NULL, *auxiliary_url = NULL;
_cleanup_(pull_job_unrefp) PullJob *job = NULL;
const char *q;
int r;
assert(ret);
assert(url);
+ assert(strip_suffixes);
assert(glue);
r = import_url_last_component(url, &last_component);
if (r < 0)
return r;
- r = tar_strip_suffixes(last_component, &ll);
+ r = strip_suffixes(last_component, &ll);
if (r < 0)
return r;
- q = strjoina(ll, ".nspawn");
+ q = strjoina(ll, suffix);
- r = import_url_change_last_component(url, q, &settings_url);
+ r = import_url_change_last_component(url, q, &auxiliary_url);
if (r < 0)
return r;
- r = pull_job_new(&job, settings_url, glue, userdata);
+ r = pull_job_new(&job, auxiliary_url, glue, userdata);
if (r < 0)
return r;
@@ -320,45 +323,39 @@ int pull_make_verification_jobs(
return 0;
}
-int pull_verify(PullJob *main_job,
- PullJob *settings_job,
- PullJob *checksum_job,
- PullJob *signature_job) {
-
- _cleanup_close_pair_ int gpg_pipe[2] = { -1, -1 };
+static int verify_one(PullJob *checksum_job, PullJob *job) {
_cleanup_free_ char *fn = NULL;
- _cleanup_close_ int sig_file = -1;
- const char *p, *line;
- char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX";
- _cleanup_(sigkill_waitp) pid_t pid = 0;
- bool gpg_home_created = false;
+ const char *line, *p;
int r;
- assert(main_job);
- assert(main_job->state == PULL_JOB_DONE);
+ assert(checksum_job);
- if (!checksum_job)
+ if (!job)
return 0;
- assert(main_job->calc_checksum);
- assert(main_job->checksum);
- assert(checksum_job->state == PULL_JOB_DONE);
+ assert(IN_SET(job->state, PULL_JOB_DONE, PULL_JOB_FAILED));
- if (!checksum_job->payload || checksum_job->payload_size <= 0) {
- log_error("Checksum is empty, cannot verify.");
- return -EBADMSG;
- }
+ /* Don't verify the checksum if we didn't actually successfully download something new */
+ if (job->state != PULL_JOB_DONE)
+ return 0;
+ if (job->error != 0)
+ return 0;
+ if (job->etag_exists)
+ return 0;
- r = import_url_last_component(main_job->url, &fn);
+ assert(job->calc_checksum);
+ assert(job->checksum);
+
+ r = import_url_last_component(job->url, &fn);
if (r < 0)
return log_oom();
if (!filename_is_valid(fn)) {
- log_error("Cannot verify checksum, could not determine valid server-side file name.");
+ log_error("Cannot verify checksum, could not determine server-side file name.");
return -EBADMSG;
}
- line = strjoina(main_job->checksum, " *", fn, "\n");
+ line = strjoina(job->checksum, " *", fn, "\n");
p = memmem(checksum_job->payload,
checksum_job->payload_size,
@@ -366,47 +363,55 @@ int pull_verify(PullJob *main_job,
strlen(line));
if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
- log_error("DOWNLOAD INVALID: Checksum did not check out, payload has been tampered with.");
+ log_error("DOWNLOAD INVALID: Checksum of %s file did not checkout, file has been tampered with.", fn);
return -EBADMSG;
}
- log_info("SHA256 checksum of %s is valid.", main_job->url);
+ log_info("SHA256 checksum of %s is valid.", job->url);
+ return 1;
+}
- assert(!settings_job || IN_SET(settings_job->state, PULL_JOB_DONE, PULL_JOB_FAILED));
+int pull_verify(PullJob *main_job,
+ PullJob *roothash_job,
+ PullJob *settings_job,
+ PullJob *checksum_job,
+ PullJob *signature_job) {
- if (settings_job &&
- settings_job->state == PULL_JOB_DONE &&
- settings_job->error == 0 &&
- !settings_job->etag_exists) {
+ _cleanup_close_pair_ int gpg_pipe[2] = { -1, -1 };
+ _cleanup_free_ char *fn = NULL;
+ _cleanup_close_ int sig_file = -1;
+ char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX";
+ _cleanup_(sigkill_waitp) pid_t pid = 0;
+ bool gpg_home_created = false;
+ int r;
- _cleanup_free_ char *settings_fn = NULL;
+ assert(main_job);
+ assert(main_job->state == PULL_JOB_DONE);
- assert(settings_job->calc_checksum);
- assert(settings_job->checksum);
+ if (!checksum_job)
+ return 0;
- r = import_url_last_component(settings_job->url, &settings_fn);
- if (r < 0)
- return log_oom();
+ assert(main_job->calc_checksum);
+ assert(main_job->checksum);
- if (!filename_is_valid(settings_fn)) {
- log_error("Cannot verify checksum, could not determine server-side settings file name.");
- return -EBADMSG;
- }
+ assert(checksum_job->state == PULL_JOB_DONE);
- line = strjoina(settings_job->checksum, " *", settings_fn, "\n");
+ if (!checksum_job->payload || checksum_job->payload_size <= 0) {
+ log_error("Checksum is empty, cannot verify.");
+ return -EBADMSG;
+ }
- p = memmem(checksum_job->payload,
- checksum_job->payload_size,
- line,
- strlen(line));
+ r = verify_one(checksum_job, main_job);
+ if (r < 0)
+ return r;
- if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
- log_error("DOWNLOAD INVALID: Checksum of settings file did not checkout, settings file has been tampered with.");
- return -EBADMSG;
- }
+ r = verify_one(checksum_job, roothash_job);
+ if (r < 0)
+ return r;
- log_info("SHA256 checksum of %s is valid.", settings_job->url);
- }
+ r = verify_one(checksum_job, settings_job);
+ if (r < 0)
+ return r;
if (!signature_job)
return 0;
diff --git a/src/import/pull-common.h b/src/import/pull-common.h
index 929a131c88..f1f1a17fa9 100644
--- a/src/import/pull-common.h
+++ b/src/import/pull-common.h
@@ -30,7 +30,7 @@ int pull_find_old_etags(const char *url, const char *root, int dt, const char *p
int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret);
-int pull_make_settings_job(PullJob **ret, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata);
+int pull_make_auxiliary_job(PullJob **ret, const char *url, int (*strip_suffixes)(const char *name, char **ret), const char *suffix, CurlGlue *glue, PullJobFinished on_finished, void *userdata);
int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata);
-int pull_verify(PullJob *main_job, PullJob *settings_job, PullJob *checksum_job, PullJob *signature_job);
+int pull_verify(PullJob *main_job, PullJob *roothash_job, PullJob *settings_job, PullJob *checksum_job, PullJob *signature_job);
diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c
index 0cf410a5d9..ef7fb6ac42 100644
--- a/src/import/pull-raw.c
+++ b/src/import/pull-raw.c
@@ -63,6 +63,7 @@ struct RawPull {
char *image_root;
PullJob *raw_job;
+ PullJob *roothash_job;
PullJob *settings_job;
PullJob *checksum_job;
PullJob *signature_job;
@@ -74,6 +75,7 @@ struct RawPull {
bool force_local;
bool grow_machine_directory;
bool settings;
+ bool roothash;
char *final_path;
char *temp_path;
@@ -81,6 +83,9 @@ struct RawPull {
char *settings_path;
char *settings_temp_path;
+ char *roothash_path;
+ char *roothash_temp_path;
+
ImportVerify verify;
};
@@ -90,6 +95,7 @@ RawPull* raw_pull_unref(RawPull *i) {
pull_job_unref(i->raw_job);
pull_job_unref(i->settings_job);
+ pull_job_unref(i->roothash_job);
pull_job_unref(i->checksum_job);
pull_job_unref(i->signature_job);
@@ -101,12 +107,18 @@ RawPull* raw_pull_unref(RawPull *i) {
free(i->temp_path);
}
+ if (i->roothash_temp_path) {
+ (void) unlink(i->roothash_temp_path);
+ free(i->roothash_temp_path);
+ }
+
if (i->settings_temp_path) {
(void) unlink(i->settings_temp_path);
free(i->settings_temp_path);
}
free(i->final_path);
+ free(i->roothash_path);
free(i->settings_path);
free(i->image_root);
free(i->local);
@@ -176,6 +188,11 @@ static void raw_pull_report_progress(RawPull *i, RawProgress p) {
remain -= 5;
}
+ if (i->roothash_job) {
+ percent += i->roothash_job->progress_percent * 5 / 100;
+ remain -= 5;
+ }
+
if (i->checksum_job) {
percent += i->checksum_job->progress_percent * 5 / 100;
remain -= 5;
@@ -262,6 +279,55 @@ static int raw_pull_maybe_convert_qcow2(RawPull *i) {
return 1;
}
+static int raw_pull_determine_path(RawPull *i, const char *suffix, char **field) {
+ int r;
+
+ assert(i);
+ assert(field);
+
+ if (*field)
+ return 0;
+
+ assert(i->raw_job);
+
+ r = pull_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", suffix, field);
+ if (r < 0)
+ return log_oom();
+
+ return 1;
+}
+
+static int raw_pull_copy_auxiliary_file(
+ RawPull *i,
+ const char *suffix,
+ char **path) {
+
+ const char *local;
+ int r;
+
+ assert(i);
+ assert(suffix);
+ assert(path);
+
+ r = raw_pull_determine_path(i, suffix, path);
+ if (r < 0)
+ return r;
+
+ local = strjoina(i->image_root, "/", i->local, suffix);
+
+ r = copy_file_atomic(*path, local, 0644, i->force_local, 0);
+ if (r == -EEXIST)
+ log_warning_errno(r, "File %s already exists, not replacing.", local);
+ else if (r == -ENOENT)
+ log_debug_errno(r, "Skipping creation of auxiliary file, since none was found.");
+ else if (r < 0)
+ log_warning_errno(r, "Failed to copy file %s, ignoring: %m", local);
+ else
+ log_info("Created new file %s.", local);
+
+ return 0;
+}
+
static int raw_pull_make_local_copy(RawPull *i) {
_cleanup_free_ char *tp = NULL;
_cleanup_close_ int dfd = -1;
@@ -274,12 +340,6 @@ static int raw_pull_make_local_copy(RawPull *i) {
if (!i->local)
return 0;
- if (!i->final_path) {
- r = pull_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", ".raw", &i->final_path);
- if (r < 0)
- return log_oom();
- }
-
if (i->raw_job->etag_exists) {
/* We have downloaded this one previously, reopen it */
@@ -338,27 +398,16 @@ static int raw_pull_make_local_copy(RawPull *i) {
log_info("Created new local image '%s'.", i->local);
- if (i->settings) {
- const char *local_settings;
- assert(i->settings_job);
-
- if (!i->settings_path) {
- r = pull_make_path(i->settings_job->url, i->settings_job->etag, i->image_root, ".settings-", NULL, &i->settings_path);
- if (r < 0)
- return log_oom();
- }
-
- local_settings = strjoina(i->image_root, "/", i->local, ".nspawn");
+ if (i->roothash) {
+ r = raw_pull_copy_auxiliary_file(i, ".roothash", &i->roothash_path);
+ if (r < 0)
+ return r;
+ }
- r = copy_file_atomic(i->settings_path, local_settings, 0644, i->force_local, 0);
- if (r == -EEXIST)
- log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings);
- else if (r == -ENOENT)
- log_debug_errno(r, "Skipping creation of settings file, since none was found.");
- else if (r < 0)
- log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings);
- else
- log_info("Created new settings file %s.", local_settings);
+ if (i->settings) {
+ r = raw_pull_copy_auxiliary_file(i, ".nspawn", &i->settings_path);
+ if (r < 0)
+ return r;
}
return 0;
@@ -370,6 +419,8 @@ static bool raw_pull_is_done(RawPull *i) {
if (!PULL_JOB_IS_COMPLETE(i->raw_job))
return false;
+ if (i->roothash_job && !PULL_JOB_IS_COMPLETE(i->roothash_job))
+ return false;
if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job))
return false;
if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job))
@@ -380,6 +431,39 @@ static bool raw_pull_is_done(RawPull *i) {
return true;
}
+static int raw_pull_rename_auxiliary_file(
+ RawPull *i,
+ const char *suffix,
+ char **temp_path,
+ char **path) {
+
+ int r;
+
+ assert(i);
+ assert(temp_path);
+ assert(suffix);
+ assert(path);
+
+ /* Regenerate final name for this auxiliary file, we might know the etag of the raw file now, and we shoud
+ * incorporate it in the file name if we can */
+ *path = mfree(*path);
+ r = raw_pull_determine_path(i, suffix, path);
+ if (r < 0)
+ return r;
+
+ r = import_make_read_only(*temp_path);
+ if (r < 0)
+ return r;
+
+ r = rename_noreplace(AT_FDCWD, *temp_path, AT_FDCWD, *path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to rename file %s to %s: %m", *temp_path, *path);
+
+ *temp_path = mfree(*temp_path);
+
+ return 1;
+}
+
static void raw_pull_job_on_finished(PullJob *j) {
RawPull *i;
int r;
@@ -388,7 +472,10 @@ static void raw_pull_job_on_finished(PullJob *j) {
assert(j->userdata);
i = j->userdata;
- if (j == i->settings_job) {
+ if (j == i->roothash_job) {
+ if (j->error != 0)
+ log_info_errno(j->error, "Root hash file could not be retrieved, proceeding without.");
+ } else if (j == i->settings_job) {
if (j->error != 0)
log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
} else if (j->error != 0) {
@@ -413,16 +500,22 @@ static void raw_pull_job_on_finished(PullJob *j) {
if (!raw_pull_is_done(i))
return;
+ if (i->roothash_job)
+ i->roothash_job->disk_fd = safe_close(i->roothash_job->disk_fd);
if (i->settings_job)
i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd);
+ r = raw_pull_determine_path(i, ".raw", &i->final_path);
+ if (r < 0)
+ goto finish;
+
if (!i->raw_job->etag_exists) {
/* This is a new download, verify it, and move it into place */
assert(i->raw_job->disk_fd >= 0);
raw_pull_report_progress(i, RAW_VERIFYING);
- r = pull_verify(i->raw_job, i->settings_job, i->checksum_job, i->signature_job);
+ r = pull_verify(i->raw_job, i->roothash_job, i->settings_job, i->checksum_job, i->signature_job);
if (r < 0)
goto finish;
@@ -446,24 +539,18 @@ static void raw_pull_job_on_finished(PullJob *j) {
i->temp_path = mfree(i->temp_path);
- if (i->settings_job &&
- i->settings_job->error == 0 &&
- !i->settings_job->etag_exists) {
-
- assert(i->settings_temp_path);
- assert(i->settings_path);
-
- r = import_make_read_only(i->settings_temp_path);
+ if (i->roothash_job &&
+ i->roothash_job->error == 0) {
+ r = raw_pull_rename_auxiliary_file(i, ".roothash", &i->roothash_temp_path, &i->roothash_path);
if (r < 0)
goto finish;
+ }
- r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path);
- if (r < 0) {
- log_error_errno(r, "Failed to rename settings file: %m");
+ if (i->settings_job &&
+ i->settings_job->error == 0) {
+ r = raw_pull_rename_auxiliary_file(i, ".nspawn", &i->settings_temp_path, &i->settings_path);
+ if (r < 0)
goto finish;
- }
-
- i->settings_temp_path = mfree(i->settings_temp_path);
}
}
@@ -482,6 +569,35 @@ finish:
sd_event_exit(i->event, r);
}
+static int raw_pull_job_on_open_disk_generic(
+ RawPull *i,
+ PullJob *j,
+ const char *extra,
+ char **temp_path) {
+
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ assert(i);
+ assert(j);
+ assert(extra);
+ assert(temp_path);
+
+ if (!*temp_path) {
+ r = tempfn_random_child(i->image_root, extra, temp_path);
+ if (r < 0)
+ return log_oom();
+ }
+
+ (void) mkdir_parents_label(*temp_path, 0700);
+
+ j->disk_fd = open(*temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
+ if (j->disk_fd < 0)
+ return log_error_errno(errno, "Failed to create %s: %m", *temp_path);
+
+ return 0;
+}
+
static int raw_pull_job_on_open_disk_raw(PullJob *j) {
RawPull *i;
int r;
@@ -491,57 +607,40 @@ static int raw_pull_job_on_open_disk_raw(PullJob *j) {
i = j->userdata;
assert(i->raw_job == j);
- assert(!i->final_path);
- assert(!i->temp_path);
-
- r = pull_make_path(j->url, j->etag, i->image_root, ".raw-", ".raw", &i->final_path);
- if (r < 0)
- return log_oom();
- r = tempfn_random(i->final_path, NULL, &i->temp_path);
+ r = raw_pull_job_on_open_disk_generic(i, j, "raw", &i->temp_path);
if (r < 0)
- return log_oom();
-
- (void) mkdir_parents_label(i->temp_path, 0700);
-
- j->disk_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
- if (j->disk_fd < 0)
- return log_error_errno(errno, "Failed to create %s: %m", i->temp_path);
+ return r;
r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL);
if (r < 0)
- log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path);
+ log_warning_errno(r, "Failed to set file attributes on %s, ignoring: %m", i->temp_path);
return 0;
}
-static int raw_pull_job_on_open_disk_settings(PullJob *j) {
+static int raw_pull_job_on_open_disk_roothash(PullJob *j) {
RawPull *i;
- int r;
assert(j);
assert(j->userdata);
i = j->userdata;
- assert(i->settings_job == j);
- assert(!i->settings_path);
- assert(!i->settings_temp_path);
+ assert(i->roothash_job == j);
- r = pull_make_path(j->url, j->etag, i->image_root, ".settings-", NULL, &i->settings_path);
- if (r < 0)
- return log_oom();
+ return raw_pull_job_on_open_disk_generic(i, j, "roothash", &i->roothash_temp_path);
+}
- r = tempfn_random(i->settings_path, NULL, &i->settings_temp_path);
- if (r < 0)
- return log_oom();
+static int raw_pull_job_on_open_disk_settings(PullJob *j) {
+ RawPull *i;
- mkdir_parents_label(i->settings_temp_path, 0700);
+ assert(j);
+ assert(j->userdata);
- j->disk_fd = open(i->settings_temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
- if (j->disk_fd < 0)
- return log_error_errno(errno, "Failed to create %s: %m", i->settings_temp_path);
+ i = j->userdata;
+ assert(i->settings_job == j);
- return 0;
+ return raw_pull_job_on_open_disk_generic(i, j, "settings", &i->settings_temp_path);
}
static void raw_pull_job_on_progress(PullJob *j) {
@@ -561,7 +660,8 @@ int raw_pull_start(
const char *local,
bool force_local,
ImportVerify verify,
- bool settings) {
+ bool settings,
+ bool roothash) {
int r;
@@ -585,6 +685,7 @@ int raw_pull_start(
i->force_local = force_local;
i->verify = verify;
i->settings = settings;
+ i->roothash = roothash;
/* Queue job for the image itself */
r = pull_job_new(&i->raw_job, url, i->glue, i);
@@ -601,18 +702,24 @@ int raw_pull_start(
if (r < 0)
return r;
+ if (roothash) {
+ r = pull_make_auxiliary_job(&i->roothash_job, url, raw_strip_suffixes, ".roothash", i->glue, raw_pull_job_on_finished, i);
+ if (r < 0)
+ return r;
+
+ i->roothash_job->on_open_disk = raw_pull_job_on_open_disk_roothash;
+ i->roothash_job->on_progress = raw_pull_job_on_progress;
+ i->roothash_job->calc_checksum = verify != IMPORT_VERIFY_NO;
+ }
+
if (settings) {
- r = pull_make_settings_job(&i->settings_job, url, i->glue, raw_pull_job_on_finished, i);
+ r = pull_make_auxiliary_job(&i->settings_job, url, raw_strip_suffixes, ".nspawn", i->glue, raw_pull_job_on_finished, i);
if (r < 0)
return r;
i->settings_job->on_open_disk = raw_pull_job_on_open_disk_settings;
i->settings_job->on_progress = raw_pull_job_on_progress;
i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO;
-
- r = pull_find_old_etags(i->settings_job->url, i->image_root, DT_REG, ".settings-", NULL, &i->settings_job->old_etags);
- if (r < 0)
- return r;
}
r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, raw_pull_job_on_finished, i);
@@ -623,6 +730,12 @@ int raw_pull_start(
if (r < 0)
return r;
+ if (i->roothash_job) {
+ r = pull_job_begin(i->roothash_job);
+ if (r < 0)
+ return r;
+ }
+
if (i->settings_job) {
r = pull_job_begin(i->settings_job);
if (r < 0)
diff --git a/src/import/pull-raw.h b/src/import/pull-raw.h
index 8f6d16eb3a..6954d98994 100644
--- a/src/import/pull-raw.h
+++ b/src/import/pull-raw.h
@@ -33,4 +33,4 @@ RawPull* raw_pull_unref(RawPull *pull);
DEFINE_TRIVIAL_CLEANUP_FUNC(RawPull*, raw_pull_unref);
-int raw_pull_start(RawPull *pull, const char *url, const char *local, bool force_local, ImportVerify verify, bool settings);
+int raw_pull_start(RawPull *pull, const char *url, const char *local, bool force_local, ImportVerify verify, bool settings, bool roothash);
diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c
index 68e2397b02..375ee778e2 100644
--- a/src/import/pull-tar.c
+++ b/src/import/pull-tar.c
@@ -215,6 +215,24 @@ static void tar_pull_report_progress(TarPull *i, TarProgress p) {
log_debug("Combined progress %u%%", percent);
}
+static int tar_pull_determine_path(TarPull *i, const char *suffix, char **field) {
+ int r;
+
+ assert(i);
+ assert(field);
+
+ if (*field)
+ return 0;
+
+ assert(i->tar_job);
+
+ r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", suffix, field);
+ if (r < 0)
+ return log_oom();
+
+ return 1;
+}
+
static int tar_pull_make_local_copy(TarPull *i) {
int r;
@@ -224,12 +242,6 @@ static int tar_pull_make_local_copy(TarPull *i) {
if (!i->local)
return 0;
- if (!i->final_path) {
- r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path);
- if (r < 0)
- return log_oom();
- }
-
r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
if (r < 0)
return r;
@@ -238,11 +250,9 @@ static int tar_pull_make_local_copy(TarPull *i) {
const char *local_settings;
assert(i->settings_job);
- if (!i->settings_path) {
- r = pull_make_path(i->settings_job->url, i->settings_job->etag, i->image_root, ".settings-", NULL, &i->settings_path);
- if (r < 0)
- return log_oom();
- }
+ r = tar_pull_determine_path(i, ".nspawn", &i->settings_path);
+ if (r < 0)
+ return r;
local_settings = strjoina(i->image_root, "/", i->local, ".nspawn");
@@ -311,6 +321,10 @@ static void tar_pull_job_on_finished(PullJob *j) {
if (i->settings_job)
i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd);
+ r = tar_pull_determine_path(i, NULL, &i->final_path);
+ if (r < 0)
+ goto finish;
+
if (i->tar_pid > 0) {
r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
i->tar_pid = 0;
@@ -327,7 +341,7 @@ static void tar_pull_job_on_finished(PullJob *j) {
tar_pull_report_progress(i, TAR_VERIFYING);
- r = pull_verify(i->tar_job, i->settings_job, i->checksum_job, i->signature_job);
+ r = pull_verify(i->tar_job, NULL, i->settings_job, i->checksum_job, i->signature_job);
if (r < 0)
goto finish;
@@ -346,16 +360,18 @@ static void tar_pull_job_on_finished(PullJob *j) {
i->temp_path = mfree(i->temp_path);
if (i->settings_job &&
- i->settings_job->error == 0 &&
- !i->settings_job->etag_exists) {
+ i->settings_job->error == 0) {
assert(i->settings_temp_path);
assert(i->settings_path);
- /* Also move the settings file into place, if
- * it exist. Note that we do so only if we
- * also moved the tar file in place, to keep
- * things strictly in sync. */
+ /* Also move the settings file into place, if it exist. Note that we do so only if we also
+ * moved the tar file in place, to keep things strictly in sync. */
+
+ i->settings_path = mfree(i->settings_path);
+ r = tar_pull_determine_path(i, ".nspawn", &i->settings_path);
+ if (r < 0)
+ goto finish;
r = import_make_read_only(i->settings_temp_path);
if (r < 0)
@@ -395,17 +411,13 @@ static int tar_pull_job_on_open_disk_tar(PullJob *j) {
i = j->userdata;
assert(i->tar_job == j);
- assert(!i->final_path);
- assert(!i->temp_path);
assert(i->tar_pid <= 0);
- r = pull_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
- if (r < 0)
- return log_oom();
-
- r = tempfn_random(i->final_path, NULL, &i->temp_path);
- if (r < 0)
- return log_oom();
+ if (!i->temp_path) {
+ r = tempfn_random_child(i->image_root, "tar", &i->temp_path);
+ if (r < 0)
+ return log_oom();
+ }
mkdir_parents_label(i->temp_path, 0700);
@@ -434,16 +446,12 @@ static int tar_pull_job_on_open_disk_settings(PullJob *j) {
i = j->userdata;
assert(i->settings_job == j);
- assert(!i->settings_path);
- assert(!i->settings_temp_path);
- r = pull_make_path(j->url, j->etag, i->image_root, ".settings-", NULL, &i->settings_path);
- if (r < 0)
- return log_oom();
-
- r = tempfn_random(i->settings_path, NULL, &i->settings_temp_path);
- if (r < 0)
- return log_oom();
+ if (!i->settings_temp_path) {
+ r = tempfn_random_child(i->image_root, "settings", &i->settings_temp_path);
+ if (r < 0)
+ return log_oom();
+ }
mkdir_parents_label(i->settings_temp_path, 0700);
@@ -513,17 +521,13 @@ int tar_pull_start(
/* Set up download job for the settings file (.nspawn) */
if (settings) {
- r = pull_make_settings_job(&i->settings_job, url, i->glue, tar_pull_job_on_finished, i);
+ r = pull_make_auxiliary_job(&i->settings_job, url, tar_strip_suffixes, ".nspawn", i->glue, tar_pull_job_on_finished, i);
if (r < 0)
return r;
i->settings_job->on_open_disk = tar_pull_job_on_open_disk_settings;
i->settings_job->on_progress = tar_pull_job_on_progress;
i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO;
-
- r = pull_find_old_etags(i->settings_job->url, i->image_root, DT_REG, ".settings-", NULL, &i->settings_job->old_etags);
- if (r < 0)
- return r;
}
/* Set up download of checksum/signature files */
diff --git a/src/import/pull.c b/src/import/pull.c
index 53b1211965..4af5d9c853 100644
--- a/src/import/pull.c
+++ b/src/import/pull.c
@@ -37,6 +37,7 @@ static bool arg_force = false;
static const char *arg_image_root = "/var/lib/machines";
static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
static bool arg_settings = true;
+static bool arg_roothash = true;
static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
log_notice("Transfer aborted.");
@@ -204,7 +205,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to allocate puller: %m");
- r = raw_pull_start(pull, url, local, arg_force, arg_verify, arg_settings);
+ r = raw_pull_start(pull, url, local, arg_force, arg_verify, arg_settings, arg_roothash);
if (r < 0)
return log_error_errno(r, "Failed to pull image: %m");
@@ -226,6 +227,7 @@ static int help(int argc, char *argv[], void *userdata) {
" --verify=MODE Verify downloaded image, one of: 'no',\n"
" 'checksum', 'signature'\n"
" --settings=BOOL Download settings file with image\n"
+ " --roothash=BOOL Download root hash file with image\n"
" --image-root=PATH Image root directory\n\n"
"Commands:\n"
" tar URL [NAME] Download a TAR image\n"
@@ -243,6 +245,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_IMAGE_ROOT,
ARG_VERIFY,
ARG_SETTINGS,
+ ARG_ROOTHASH,
};
static const struct option options[] = {
@@ -252,6 +255,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
{ "verify", required_argument, NULL, ARG_VERIFY },
{ "settings", required_argument, NULL, ARG_SETTINGS },
+ { "roothash", required_argument, NULL, ARG_ROOTHASH },
{}
};
@@ -295,6 +299,14 @@ static int parse_argv(int argc, char *argv[]) {
arg_settings = r;
break;
+ case ARG_ROOTHASH:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --roothash= parameter '%s'", optarg);
+
+ arg_roothash = r;
+ break;
+
case '?':
return -EINVAL;
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index f75a6f06d2..8ad9738edf 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -905,7 +905,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "key", required_argument, NULL, ARG_KEY },
{ "cert", required_argument, NULL, ARG_CERT },
{ "trust", required_argument, NULL, ARG_TRUST },
- { "directory", required_argument, NULL, 'D' },
+ { "directory", required_argument, NULL, 'D' },
{}
};
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index ecd1e94a33..2a5f2b37e8 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -103,7 +103,7 @@ static const char *arg_directory = NULL;
static char **arg_file = NULL;
static bool arg_file_stdin = false;
static int arg_priorities = 0xFF;
-static const char *arg_verify_key = NULL;
+static char *arg_verify_key = NULL;
#ifdef HAVE_GCRYPT
static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
static bool arg_force = false;
@@ -683,7 +683,11 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_VERIFY_KEY:
arg_action = ACTION_VERIFY;
- arg_verify_key = optarg;
+ r = free_and_strdup(&arg_verify_key, optarg);
+ if (r < 0)
+ return r;
+ string_erase(optarg);
+
arg_merge = false;
break;
@@ -885,7 +889,7 @@ static int parse_argv(int argc, char *argv[]) {
* to users, and automatically turn --unit= into --user-unit= if combined with --user. */
r = strv_extend_strv(&arg_user_units, arg_system_units, true);
if (r < 0)
- return -ENOMEM;
+ return r;
arg_system_units = strv_free(arg_system_units);
}
@@ -2621,6 +2625,7 @@ finish:
strv_free(arg_user_units);
free(arg_root);
+ free(arg_verify_key);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 5c6941ebd6..8b92ea3def 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -283,17 +283,16 @@ static int open_journal(
}
static bool flushed_flag_is_set(void) {
- return (access("/run/systemd/journal/flushed", F_OK) >= 0);
+ return access("/run/systemd/journal/flushed", F_OK) >= 0;
}
static int system_journal_open(Server *s, bool flush_requested) {
- bool flushed = false;
const char *fn;
int r = 0;
if (!s->system_journal &&
- (s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) &&
- (flush_requested || (flushed = flushed_flag_is_set()))) {
+ IN_SET(s->storage, STORAGE_PERSISTENT, STORAGE_AUTO) &&
+ (flush_requested || flushed_flag_is_set())) {
/* If in auto mode: first try to create the machine
* path, but not the prefix.
@@ -326,8 +325,8 @@ static int system_journal_open(Server *s, bool flush_requested) {
* Perform an implicit flush to var, leaving the runtime
* journal closed, now that the system journal is back.
*/
- if (s->runtime_journal && flushed)
- (void) server_flush_to_var(s);
+ if (!flush_requested)
+ (void) server_flush_to_var(s, true);
}
if (!s->runtime_journal &&
@@ -1183,7 +1182,7 @@ finish:
dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id, priority, object_pid);
}
-int server_flush_to_var(Server *s) {
+int server_flush_to_var(Server *s, bool require_flag_file) {
sd_id128_t machine;
sd_journal *j = NULL;
char ts[FORMAT_TIMESPAN_MAX];
@@ -1193,13 +1192,15 @@ int server_flush_to_var(Server *s) {
assert(s);
- if (s->storage != STORAGE_AUTO &&
- s->storage != STORAGE_PERSISTENT)
+ if (!IN_SET(s->storage, STORAGE_AUTO, STORAGE_PERSISTENT))
return 0;
if (!s->runtime_journal)
return 0;
+ if (require_flag_file && !flushed_flag_is_set())
+ return 0;
+
(void) system_journal_open(s, true);
if (!s->system_journal)
@@ -1411,7 +1412,7 @@ static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *
log_info("Received request to flush runtime journal from PID " PID_FMT, si->ssi_pid);
- (void) server_flush_to_var(s);
+ (void) server_flush_to_var(s, false);
server_sync(s);
server_vacuum(s, false);
@@ -1532,60 +1533,93 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
assert(s);
- if (streq(key, "systemd.journald.forward_to_syslog")) {
+ if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_syslog")) {
+
r = value ? parse_boolean(value) : true;
if (r < 0)
log_warning("Failed to parse forward to syslog switch \"%s\". Ignoring.", value);
else
s->forward_to_syslog = r;
- } else if (streq(key, "systemd.journald.forward_to_kmsg")) {
+
+ } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_kmsg")) {
+
r = value ? parse_boolean(value) : true;
if (r < 0)
log_warning("Failed to parse forward to kmsg switch \"%s\". Ignoring.", value);
else
s->forward_to_kmsg = r;
- } else if (streq(key, "systemd.journald.forward_to_console")) {
+
+ } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_console")) {
+
r = value ? parse_boolean(value) : true;
if (r < 0)
log_warning("Failed to parse forward to console switch \"%s\". Ignoring.", value);
else
s->forward_to_console = r;
- } else if (streq(key, "systemd.journald.forward_to_wall")) {
+
+ } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_wall")) {
+
r = value ? parse_boolean(value) : true;
if (r < 0)
log_warning("Failed to parse forward to wall switch \"%s\". Ignoring.", value);
else
s->forward_to_wall = r;
- } else if (streq(key, "systemd.journald.max_level_console") && value) {
+
+ } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_console")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
r = log_level_from_string(value);
if (r < 0)
log_warning("Failed to parse max level console value \"%s\". Ignoring.", value);
else
s->max_level_console = r;
- } else if (streq(key, "systemd.journald.max_level_store") && value) {
+
+ } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_store")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
r = log_level_from_string(value);
if (r < 0)
log_warning("Failed to parse max level store value \"%s\". Ignoring.", value);
else
s->max_level_store = r;
- } else if (streq(key, "systemd.journald.max_level_syslog") && value) {
+
+ } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_syslog")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
r = log_level_from_string(value);
if (r < 0)
log_warning("Failed to parse max level syslog value \"%s\". Ignoring.", value);
else
s->max_level_syslog = r;
- } else if (streq(key, "systemd.journald.max_level_kmsg") && value) {
+
+ } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_kmsg")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
r = log_level_from_string(value);
if (r < 0)
log_warning("Failed to parse max level kmsg value \"%s\". Ignoring.", value);
else
s->max_level_kmsg = r;
- } else if (streq(key, "systemd.journald.max_level_wall") && value) {
+
+ } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_wall")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
r = log_level_from_string(value);
if (r < 0)
log_warning("Failed to parse max level wall value \"%s\". Ignoring.", value);
else
s->max_level_wall = r;
+
} else if (startswith(key, "systemd.journald"))
log_warning("Unknown journald kernel command line option \"%s\". Ignoring.", key);
@@ -1898,7 +1932,10 @@ int server_init(Server *s) {
journal_reset_metrics(&s->runtime_storage.metrics);
server_parse_config_file(s);
- parse_proc_cmdline(parse_proc_cmdline_item, s, true);
+
+ r = proc_cmdline_parse(parse_proc_cmdline_item, s, PROC_CMDLINE_STRIP_RD_PREFIX);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
if (!!s->rate_limit_interval ^ !!s->rate_limit_burst) {
log_debug("Setting both rate limit interval and burst from "USEC_FMT",%u to 0,0",
diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h
index 99d91496be..716e758b7c 100644
--- a/src/journal/journald-server.h
+++ b/src/journal/journald-server.h
@@ -179,7 +179,7 @@ void server_dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigne
void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) _printf_(3,0) _sentinel_;
/* gperf lookup function */
-const struct ConfigPerfItem* journald_gperf_lookup(const char *key, unsigned length);
+const struct ConfigPerfItem* journald_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
int config_parse_storage(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);
@@ -197,7 +197,7 @@ void server_sync(Server *s);
int server_vacuum(Server *s, bool verbose);
void server_rotate(Server *s);
int server_schedule_sync(Server *s, int priority);
-int server_flush_to_var(Server *s);
+int server_flush_to_var(Server *s, bool require_flag_file);
void server_maybe_append_tags(Server *s);
int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata);
void server_space_usage_message(Server *s, JournalStorage *storage);
diff --git a/src/journal/journald.c b/src/journal/journald.c
index fc26ef1785..54fd1f999d 100644
--- a/src/journal/journald.c
+++ b/src/journal/journald.c
@@ -52,7 +52,7 @@ int main(int argc, char *argv[]) {
goto finish;
server_vacuum(&server, false);
- server_flush_to_var(&server);
+ server_flush_to_var(&server, true);
server_flush_dev_kmsg(&server);
log_debug("systemd-journald running as pid "PID_FMT, getpid());
diff --git a/src/kernel-install/kernel-install b/src/kernel-install/kernel-install
index a95b9717f0..c7d9f4eea9 100644
--- a/src/kernel-install/kernel-install
+++ b/src/kernel-install/kernel-install
@@ -34,7 +34,7 @@ dropindirs_sort()
local -a files
local f d i
- readarray -t files < <(
+ readarray -t files <<<"$(
for d in "$@"; do
for i in "$d/"*"$suffix"; do
if [[ -e "$i" ]]; then
@@ -42,7 +42,7 @@ dropindirs_sort()
fi
done
done | sort -Vu
- )
+ )"
for f in "${files[@]}"; do
for d in "$@"; do
@@ -104,11 +104,11 @@ fi
ret=0
-readarray -t PLUGINS < <(
+readarray -t PLUGINS <<<"$(
dropindirs_sort ".install" \
"/etc/kernel/install.d" \
"/usr/lib/kernel/install.d"
-)
+)"
case $COMMAND in
add)
diff --git a/src/libudev/libudev-util.c b/src/libudev/libudev-util.c
index 574cfeac85..a9819b9db3 100644
--- a/src/libudev/libudev-util.c
+++ b/src/libudev/libudev-util.c
@@ -186,7 +186,7 @@ int util_replace_whitespace(const char *str, char *to, size_t len)
to[j++] = str[i++];
}
to[j] = '\0';
- return 0;
+ return j;
}
/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
diff --git a/src/login/logind.h b/src/login/logind.h
index 086fa1eeb5..7556ee2e48 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -182,7 +182,7 @@ int manager_unit_is_active(Manager *manager, const char *unit);
int manager_job_is_active(Manager *manager, const char *path);
/* gperf lookup function */
-const struct ConfigPerfItem* logind_gperf_lookup(const char *key, unsigned length);
+const struct ConfigPerfItem* logind_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
int manager_set_lid_switch_ignore(Manager *m, usec_t until);
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index e2fb882393..d5051007fc 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -293,7 +293,6 @@ int bus_image_method_set_limit(
static int directory_image_get_os_release(Image *image, char ***ret, sd_bus_error *error) {
_cleanup_free_ char *path = NULL;
- _cleanup_close_ int fd = -1;
int r;
assert(image);
@@ -336,7 +335,7 @@ static int raw_image_get_os_release(Image *image, char ***ret, sd_bus_error *err
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to set up loop block device for %s: %m", image->path);
- r = dissect_image(d->fd, NULL, 0, &m);
+ r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT, &m);
if (r == -ENOPKG)
return sd_bus_error_set_errnof(error, r, "Disk image %s not understood: %m", image->path);
if (r < 0)
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index f85ef5b6ec..31a40d47c3 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -243,14 +243,16 @@ static int list_machines(int argc, char *argv[], void *userdata) {
if (name[0] == '.' && !arg_all)
continue;
- if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1)) {
+ if (!GREEDY_REALLOC0(machines, n_allocated, n_machines + 1)) {
r = log_oom();
goto out;
}
- machines[n_machines].os = NULL;
- machines[n_machines].version_id = NULL;
- r = call_get_os_release(
+ machines[n_machines].name = name;
+ machines[n_machines].class = class;
+ machines[n_machines].service = service;
+
+ (void) call_get_os_release(
bus,
"GetMachineOSRelease",
name,
@@ -258,12 +260,6 @@ static int list_machines(int argc, char *argv[], void *userdata) {
"VERSION_ID\0",
&machines[n_machines].os,
&machines[n_machines].version_id);
- if (r < 0)
- goto out;
-
- machines[n_machines].name = name;
- machines[n_machines].class = class;
- machines[n_machines].service = service;
l = strlen(name);
if (l > max_name)
@@ -2670,9 +2666,9 @@ static int help(int argc, char *argv[], void *userdata) {
" -o --output=STRING Change journal output mode (short,\n"
" short-monotonic, verbose, export, json,\n"
" json-pretty, json-sse, cat)\n"
- " --verify=MODE Verification mode for downloaded images (no,\n"
+ " --verify=MODE Verification mode for downloaded images (no,\n"
" checksum, signature)\n"
- " --force Download image even if already exists\n\n"
+ " --force Download image even if already exists\n\n"
"Machine Commands:\n"
" list List running VMs and containers\n"
" status NAME... Show VM/container details\n"
diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c
index 0901fea8dc..615998a6f6 100644
--- a/src/modules-load/modules-load.c
+++ b/src/modules-load/modules-load.c
@@ -62,7 +62,11 @@ static int add_modules(const char *p) {
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r;
- if (streq(key, "modules-load") && value) {
+ if (proc_cmdline_key_streq(key, "modules_load")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
r = add_modules(value);
if (r < 0)
return r;
@@ -226,7 +230,7 @@ int main(int argc, char *argv[]) {
umask(0022);
- r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, true);
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h
index 70ff947b99..37c7431213 100644
--- a/src/network/netdev/netdev.h
+++ b/src/network/netdev/netdev.h
@@ -175,7 +175,7 @@ NetDevKind netdev_kind_from_string(const char *d) _pure_;
int config_parse_netdev_kind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* gperf */
-const struct ConfigPerfItem* network_netdev_gperf_lookup(const char *key, unsigned length);
+const struct ConfigPerfItem* network_netdev_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
/* Macros which append INTERFACE= to the message */
diff --git a/src/network/networkd-conf.h b/src/network/networkd-conf.h
index 93819626ba..1136975a5e 100644
--- a/src/network/networkd-conf.h
+++ b/src/network/networkd-conf.h
@@ -23,7 +23,7 @@ typedef struct Manager Manager;
int manager_parse_config_file(Manager *m);
-const struct ConfigPerfItem* networkd_gperf_lookup(const char *key, unsigned length);
+const struct ConfigPerfItem* networkd_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
int config_parse_duid_type(
const char *unit,
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
index 5097ab9d72..4fd5d8ae70 100644
--- a/src/network/networkd-ndisc.c
+++ b/src/network/networkd-ndisc.c
@@ -133,6 +133,7 @@ static void ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
route->family = AF_INET6;
route->table = link->network->ipv6_accept_ra_route_table;
+ route->priority = link->network->dhcp_route_metric;
route->protocol = RTPROT_RA;
route->pref = preference;
route->gw.in6 = gateway;
@@ -254,6 +255,7 @@ static void ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt)
route->family = AF_INET6;
route->table = link->network->ipv6_accept_ra_route_table;
+ route->priority = link->network->dhcp_route_metric;
route->protocol = RTPROT_RA;
route->flags = RTM_F_PREFIX;
route->dst_prefixlen = prefixlen;
@@ -585,11 +587,13 @@ static void ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
break;
case SD_NDISC_OPTION_RDNSS:
- ndisc_router_process_rdnss(link, rt);
+ if (link->network->ipv6_accept_ra_use_dns)
+ ndisc_router_process_rdnss(link, rt);
break;
case SD_NDISC_OPTION_DNSSL:
- ndisc_router_process_dnssl(link, rt);
+ if (link->network->ipv6_accept_ra_use_dns)
+ ndisc_router_process_dnssl(link, rt);
break;
}
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 862cfad1ff..7b54e81fb8 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -60,14 +60,13 @@ Network.IPForward, config_parse_address_family_boolean_with
Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade)
Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Network, ipv6_privacy_extensions)
Network.IPv6AcceptRA, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra)
-Network.ActiveSlave, config_parse_bool, 0, offsetof(Network, active_slave)
-Network.PrimarySlave, config_parse_bool, 0, offsetof(Network, primary_slave)
-/* legacy alias for the above */
Network.IPv6AcceptRouterAdvertisements, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra)
Network.IPv6DuplicateAddressDetection, config_parse_int, 0, offsetof(Network, ipv6_dad_transmits)
Network.IPv6HopLimit, config_parse_int, 0, offsetof(Network, ipv6_hop_limit)
-Network.ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp)
+Network.ActiveSlave, config_parse_bool, 0, offsetof(Network, active_slave)
+Network.PrimarySlave, config_parse_bool, 0, offsetof(Network, primary_slave)
Network.IPv4ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp)
+Network.ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp)
Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier)
Address.Address, config_parse_address, 0, 0
Address.Peer, config_parse_address, 0, 0
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 4cf784f67c..b7da9d22d4 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -244,7 +244,7 @@ int config_parse_ntp(const char *unit, const char *filename, unsigned line, cons
/* 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);
-const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, unsigned length);
+const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
extern const sd_bus_vtable network_vtable[];
diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c
index aaa64a7ba8..72c007f204 100644
--- a/src/nspawn/nspawn-mount.c
+++ b/src/nspawn/nspawn-mount.c
@@ -1349,21 +1349,3 @@ fail:
(void) rmdir(template);
return r;
}
-
-VolatileMode volatile_mode_from_string(const char *s) {
- int b;
-
- if (isempty(s))
- return _VOLATILE_MODE_INVALID;
-
- b = parse_boolean(s);
- if (b > 0)
- return VOLATILE_YES;
- if (b == 0)
- return VOLATILE_NO;
-
- if (streq(s, "state"))
- return VOLATILE_STATE;
-
- return _VOLATILE_MODE_INVALID;
-}
diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h
index 467082a737..6b33fbff57 100644
--- a/src/nspawn/nspawn-mount.h
+++ b/src/nspawn/nspawn-mount.h
@@ -22,6 +22,7 @@
#include <stdbool.h>
#include "cgroup-util.h"
+#include "volatile-util.h"
typedef enum MountSettingsMask {
MOUNT_FATAL = 1 << 0, /* if set, a mount error is considered fatal */
@@ -32,14 +33,6 @@ typedef enum MountSettingsMask {
Works only if MOUNT_APPLY_APIVFS_RO is also set. */
} MountSettingsMask;
-typedef enum VolatileMode {
- VOLATILE_NO,
- VOLATILE_YES,
- VOLATILE_STATE,
- _VOLATILE_MODE_MAX,
- _VOLATILE_MODE_INVALID = -1
-} VolatileMode;
-
typedef enum CustomMountType {
CUSTOM_MOUNT_BIND,
CUSTOM_MOUNT_TMPFS,
@@ -77,5 +70,3 @@ int mount_custom(const char *dest, CustomMount *mounts, unsigned n, bool userns,
int setup_volatile(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
int setup_volatile_state(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
-
-VolatileMode volatile_mode_from_string(const char *s);
diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h
index 4bd0c642df..e9ea087191 100644
--- a/src/nspawn/nspawn-settings.h
+++ b/src/nspawn/nspawn-settings.h
@@ -103,7 +103,7 @@ bool settings_private_network(Settings *s);
DEFINE_TRIVIAL_CLEANUP_FUNC(Settings*, settings_free);
-const struct ConfigPerfItem* nspawn_gperf_lookup(const char *key, unsigned length);
+const struct ConfigPerfItem* nspawn_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
int config_parse_capability(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_id128(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/nspawn/nspawn.c b/src/nspawn/nspawn.c
index faf1ecc5df..78ae2f4a0f 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -1288,15 +1288,18 @@ static int setup_timezone(const char *dest) {
return 0;
}
- r = unlink(where);
- if (r < 0 && errno != ENOENT) {
- log_error_errno(errno, "Failed to remove existing timezone info %s in container: %m", where);
+ if (unlink(where) < 0 && errno != ENOENT) {
+ log_full_errno(IN_SET(errno, EROFS, EACCES, EPERM) ? LOG_DEBUG : LOG_WARNING, /* Don't complain on read-only images */
+ errno,
+ "Failed to remove existing timezone info %s in container, ignoring: %m", where);
return 0;
}
what = strjoina("../usr/share/zoneinfo/", z);
if (symlink(what, where) < 0) {
- log_error_errno(errno, "Failed to correct timezone of container: %m");
+ log_full_errno(IN_SET(errno, EROFS, EACCES, EPERM) ? LOG_DEBUG : LOG_WARNING,
+ errno,
+ "Failed to correct timezone of container, ignoring: %m");
return 0;
}
@@ -1308,31 +1311,43 @@ static int setup_timezone(const char *dest) {
}
static int setup_resolv_conf(const char *dest) {
- const char *where = NULL;
- int r;
+ _cleanup_free_ char *resolved = NULL, *etc = NULL;
+ const char *where;
+ int r, found;
assert(dest);
if (arg_private_network)
return 0;
- /* Fix resolv.conf, if possible */
- where = prefix_roota(dest, "/etc/resolv.conf");
+ r = chase_symlinks("/etc", dest, CHASE_PREFIX_ROOT, &etc);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to resolve /etc path in container, ignoring: %m");
+ return 0;
+ }
+
+ where = strjoina(etc, "/resolv.conf");
+ found = chase_symlinks(where, dest, CHASE_NONEXISTENT, &resolved);
+ if (found < 0) {
+ log_warning_errno(found, "Failed to resolve /etc/resolv.conf path in container, ignoring: %m");
+ return 0;
+ }
if (access("/run/systemd/resolve/resolv.conf", F_OK) >= 0 &&
- access("/usr/lib/systemd/resolv.conf", F_OK) >= 0) {
+ access("/usr/lib/systemd/resolv.conf", F_OK) >= 0) {
+
/* resolved is enabled on the host. In this, case bind mount its static resolv.conf file into the
* container, so that the container can use the host's resolver. Given that network namespacing is
* disabled it's only natural of the container also uses the host's resolver. It also has the big
* advantage that the container will be able to follow the host's DNS server configuration changes
* transparently. */
- (void) touch(where);
+ if (found == 0) /* missing? */
+ (void) touch(resolved);
- r = mount_verbose(LOG_WARNING, "/usr/lib/systemd/resolv.conf", where, NULL, MS_BIND, NULL);
+ r = mount_verbose(LOG_DEBUG, "/usr/lib/systemd/resolv.conf", resolved, NULL, MS_BIND, NULL);
if (r >= 0)
- return mount_verbose(LOG_ERR, NULL, where, NULL,
- MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL);
+ return mount_verbose(LOG_ERR, NULL, resolved, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL);
}
/* If that didn't work, let's copy the file */
@@ -1343,7 +1358,7 @@ static int setup_resolv_conf(const char *dest) {
*
* If the disk image is read-only, there's also no point in complaining.
*/
- log_full_errno(IN_SET(r, -ELOOP, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
+ log_full_errno(IN_SET(r, -ELOOP, -EROFS, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to copy /etc/resolv.conf to %s, ignoring: %m", where);
return 0;
}
@@ -1425,12 +1440,9 @@ 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/
- */
+ /* Explicitly warn the user when /dev is already populated. */
if (errno == EEXIST)
- log_notice("%s/dev/ should be an empty directory", dest);
+ log_notice("%s/dev is pre-mounted and pre-populated. If a pre-mounted /dev is provided it needs to be an unpopulated file system.", dest);
if (errno != EPERM)
return log_error_errno(errno, "mknod(%s) failed: %m", to);
@@ -2470,20 +2482,6 @@ static int outer_child(
if (r < 0)
return r;
- /* 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.*/
- r = mount_verbose(LOG_ERR, NULL, directory, NULL, MS_SHARED|MS_REC, NULL);
- if (r < 0)
- return r;
-
- r = recursive_chown(directory, arg_uid_shift, arg_uid_range);
- if (r < 0)
- return r;
-
r = setup_volatile(
directory,
arg_volatile_mode,
@@ -2504,6 +2502,20 @@ static int outer_child(
if (r < 0)
return r;
+ /* 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.*/
+ r = mount_verbose(LOG_ERR, NULL, directory, NULL, MS_SHARED|MS_REC, NULL);
+ if (r < 0)
+ return r;
+
+ r = recursive_chown(directory, arg_uid_shift, arg_uid_range);
+ if (r < 0)
+ return r;
+
r = base_filesystem_create(directory, arg_uid_shift, (gid_t) arg_uid_shift);
if (r < 0)
return r;
@@ -3458,8 +3470,8 @@ static int run(int master,
}
static int load_root_hash(const char *image) {
- _cleanup_free_ char *text = NULL;
- char *fn, *n, *e;
+ _cleanup_free_ char *text = NULL, *fn = NULL;
+ char *n, *e;
void *k;
size_t l;
int r;
@@ -3743,7 +3755,11 @@ int main(int argc, char *argv[]) {
goto finish;
}
- r = dissect_image(loop->fd, arg_root_hash, arg_root_hash_size, &dissected_image);
+ r = dissect_image(
+ loop->fd,
+ arg_root_hash, arg_root_hash_size,
+ DISSECT_IMAGE_REQUIRE_ROOT,
+ &dissected_image);
if (r == -ENOPKG) {
log_error_errno(r, "Could not find a suitable file system or partition table in image: %s", arg_image);
diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c
index 895f61c462..fac37faea5 100644
--- a/src/nss-mymachines/nss-mymachines.c
+++ b/src/nss-mymachines/nss-mymachines.c
@@ -512,10 +512,8 @@ enum nss_status _nss_mymachines_getpwuid_r(
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
- if (!uid_is_valid(uid)) {
- r = -EINVAL;
- goto fail;
- }
+ if (!uid_is_valid(uid))
+ goto not_found;
/* We consider all uids < 65536 host uids */
if (uid < HOST_UID_LIMIT)
@@ -686,10 +684,8 @@ enum nss_status _nss_mymachines_getgrgid_r(
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
- if (!gid_is_valid(gid)) {
- r = -EINVAL;
- goto fail;
- }
+ if (!gid_is_valid(gid))
+ goto not_found;
/* We consider all gids < 65536 host gids */
if (gid < HOST_GID_LIMIT)
diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c
index c80972742b..fd5064c937 100644
--- a/src/nss-systemd/nss-systemd.c
+++ b/src/nss-systemd/nss-systemd.c
@@ -123,10 +123,10 @@ enum nss_status _nss_systemd_getpwnam_r(
assert(name);
assert(pwd);
- if (!valid_user_group_name(name)) {
- r = -EINVAL;
- goto fail;
- }
+ /* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't
+ * generate EINVAL here, because it isn't really out business to complain about invalid user names. */
+ if (!valid_user_group_name(name))
+ goto not_found;
/* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
if (streq(name, root_passwd.pw_name)) {
@@ -227,10 +227,8 @@ enum nss_status _nss_systemd_getpwuid_r(
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
- if (!uid_is_valid(uid)) {
- r = -EINVAL;
- goto fail;
- }
+ if (!uid_is_valid(uid))
+ goto not_found;
/* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
if (uid == root_passwd.pw_uid) {
@@ -329,10 +327,8 @@ enum nss_status _nss_systemd_getgrnam_r(
assert(name);
assert(gr);
- if (!valid_user_group_name(name)) {
- r = -EINVAL;
- goto fail;
- }
+ if (!valid_user_group_name(name))
+ goto not_found;
/* Synthesize records for root and nobody, in case they are missing form /etc/group */
if (streq(name, root_group.gr_name)) {
@@ -430,10 +426,8 @@ enum nss_status _nss_systemd_getgrgid_r(
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
- if (!gid_is_valid(gid)) {
- r = -EINVAL;
- goto fail;
- }
+ if (!gid_is_valid(gid))
+ goto not_found;
/* Synthesize records for root and nobody, in case they are missing from /etc/group */
if (gid == root_group.gr_gid) {
diff --git a/src/quotacheck/quotacheck.c b/src/quotacheck/quotacheck.c
index 2714cde5c7..a42fce377e 100644
--- a/src/quotacheck/quotacheck.c
+++ b/src/quotacheck/quotacheck.c
@@ -34,7 +34,10 @@ static bool arg_force = false;
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
- if (streq(key, "quotacheck.mode") && value) {
+ if (streq(key, "quotacheck.mode")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
if (streq(value, "auto"))
arg_force = arg_skip = false;
@@ -88,7 +91,7 @@ int main(int argc, char *argv[]) {
umask(0022);
- r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false);
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
@@ -104,9 +107,10 @@ int main(int argc, char *argv[]) {
pid = fork();
if (pid < 0) {
- log_error_errno(errno, "fork(): %m");
- return EXIT_FAILURE;
- } else if (pid == 0) {
+ r = log_error_errno(errno, "fork(): %m");
+ goto finish;
+ }
+ if (pid == 0) {
/* Child */
@@ -120,5 +124,6 @@ int main(int argc, char *argv[]) {
r = wait_for_terminate_and_warn("quotacheck", pid, true);
+finish:
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c
index c3bdcaf1da..0cb9bd9261 100644
--- a/src/remount-fs/remount-fs.c
+++ b/src/remount-fs/remount-fs.c
@@ -56,7 +56,7 @@ int main(int argc, char *argv[]) {
umask(0022);
- f = setmntent("/etc/fstab", "r");
+ f = setmntent("/etc/fstab", "re");
if (!f) {
if (errno == ENOENT) {
r = 0;
diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c
index aaf5ed62c1..d89ae28dcd 100644
--- a/src/resolve/dns-type.c
+++ b/src/resolve/dns-type.c
@@ -29,7 +29,7 @@ typedef const struct {
} dns_type;
static const struct dns_type_name *
-lookup_dns_type (register const char *str, register unsigned int len);
+lookup_dns_type (register const char *str, register GPERF_LEN_TYPE len);
#include "dns_type-from-name.h"
#include "dns_type-to-name.h"
diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h
index fc425a36b2..8184d6cadf 100644
--- a/src/resolve/resolved-conf.h
+++ b/src/resolve/resolved-conf.h
@@ -41,7 +41,7 @@ int manager_parse_search_domains_and_warn(Manager *m, const char *string);
int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word);
int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string);
-const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned length);
+const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
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);
diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c
index f1fbce9dca..127cbe44e3 100644
--- a/src/shared/base-filesystem.c
+++ b/src/shared/base-filesystem.c
@@ -101,7 +101,7 @@ int base_filesystem_create(const char *root, uid_t uid, gid_t gid) {
if (r < 0 && errno != EEXIST)
return log_error_errno(errno, "Failed to create symlink at %s/%s: %m", root, table[i].dir);
- if (uid != UID_INVALID || gid != UID_INVALID) {
+ if (uid_is_valid(uid) || gid_is_valid(gid)) {
if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
return log_error_errno(errno, "Failed to chown symlink at %s/%s: %m", root, table[i].dir);
}
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index d3ba9b9dde..878cb008aa 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -84,7 +84,7 @@ not_found:
#endif
}
-int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectedImage **ret) {
+int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret) {
#ifdef HAVE_BLKID
sd_id128_t root_uuid = SD_ID128_NULL, verity_uuid = SD_ID128_NULL;
@@ -95,7 +95,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
_cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_free_ char *generic_node = NULL;
- const char *pttype = NULL, *usage = NULL;
+ sd_id128_t generic_uuid = SD_ID128_NULL;
+ const char *pttype = NULL;
struct udev_list_entry *first, *item;
blkid_partlist pl;
int r, generic_nr;
@@ -147,8 +148,12 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
return -errno;
}
- blkid_probe_enable_superblocks(b, 1);
- blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE);
+ if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
+ /* Look for file system superblocks, unless we only shall look for GPT partition tables */
+ blkid_probe_enable_superblocks(b, 1);
+ blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE);
+ }
+
blkid_probe_enable_partitions(b, 1);
blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
@@ -169,40 +174,45 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
if (!m)
return -ENOMEM;
- (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
- if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
- _cleanup_free_ char *t = NULL, *n = NULL;
- const char *fstype = NULL;
+ if (!(flags & DISSECT_IMAGE_GPT_ONLY) &&
+ (flags & DISSECT_IMAGE_REQUIRE_ROOT)) {
+ const char *usage = NULL;
- /* OK, we have found a file system, that's our root partition then. */
- (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
+ (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
+ if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
+ _cleanup_free_ char *t = NULL, *n = NULL;
+ const char *fstype = NULL;
- if (fstype) {
- t = strdup(fstype);
- if (!t)
- return -ENOMEM;
- }
+ /* OK, we have found a file system, that's our root partition then. */
+ (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
- if (asprintf(&n, "/dev/block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
- return -ENOMEM;
+ if (fstype) {
+ t = strdup(fstype);
+ if (!t)
+ return -ENOMEM;
+ }
+
+ if (asprintf(&n, "/dev/block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
+ return -ENOMEM;
- m->partitions[PARTITION_ROOT] = (DissectedPartition) {
- .found = true,
- .rw = true,
- .partno = -1,
- .architecture = _ARCHITECTURE_INVALID,
- .fstype = t,
- .node = n,
- };
+ m->partitions[PARTITION_ROOT] = (DissectedPartition) {
+ .found = true,
+ .rw = true,
+ .partno = -1,
+ .architecture = _ARCHITECTURE_INVALID,
+ .fstype = t,
+ .node = n,
+ };
- t = n = NULL;
+ t = n = NULL;
- m->encrypted = streq(fstype, "crypto_LUKS");
+ m->encrypted = streq(fstype, "crypto_LUKS");
- *ret = m;
- m = NULL;
+ *ret = m;
+ m = NULL;
- return 0;
+ return 0;
+ }
}
(void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
@@ -212,7 +222,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
is_gpt = streq_ptr(pttype, "gpt");
is_mbr = streq_ptr(pttype, "dos");
- if (!is_gpt && !is_mbr)
+ if (!is_gpt && ((flags & DISSECT_IMAGE_GPT_ONLY) || !is_mbr))
return -ENOPKG;
errno = 0;
@@ -300,7 +310,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
first = udev_enumerate_get_list_entry(e);
udev_list_entry_foreach(item, first) {
_cleanup_udev_device_unref_ struct udev_device *q;
- unsigned long long flags;
+ unsigned long long pflags;
blkid_partition pp;
const char *node;
dev_t qn;
@@ -325,7 +335,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
if (!pp)
continue;
- flags = blkid_partition_get_flags(pp);
+ pflags = blkid_partition_get_flags(pp);
nr = blkid_partition_get_partno(pp);
if (nr < 0)
@@ -337,7 +347,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
sd_id128_t type_id, id;
bool rw = true;
- if (flags & GPT_FLAG_NO_AUTO)
+ if (pflags & GPT_FLAG_NO_AUTO)
continue;
sid = blkid_partition_get_uuid(pp);
@@ -354,10 +364,10 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
if (sd_id128_equal(type_id, GPT_HOME)) {
designator = PARTITION_HOME;
- rw = !(flags & GPT_FLAG_READ_ONLY);
+ rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_SRV)) {
designator = PARTITION_SRV;
- rw = !(flags & GPT_FLAG_READ_ONLY);
+ rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_ESP)) {
designator = PARTITION_ESP;
fstype = "vfat";
@@ -371,7 +381,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
designator = PARTITION_ROOT;
architecture = native_architecture();
- rw = !(flags & GPT_FLAG_READ_ONLY);
+ rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) {
m->can_verity = true;
@@ -395,9 +405,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
designator = PARTITION_ROOT_SECONDARY;
architecture = SECONDARY_ARCHITECTURE;
- rw = !(flags & GPT_FLAG_READ_ONLY);
+ rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) {
-
m->can_verity = true;
/* Ignore verity unless root has is specified */
@@ -419,7 +428,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
multiple_generic = true;
else {
generic_nr = nr;
- generic_rw = !(flags & GPT_FLAG_READ_ONLY);
+ generic_rw = !(pflags & GPT_FLAG_READ_ONLY);
+ generic_uuid = id;
generic_node = strdup(node);
if (!generic_node)
return -ENOMEM;
@@ -450,6 +460,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
.architecture = architecture,
.node = n,
.fstype = t,
+ .uuid = id,
};
n = t = NULL;
@@ -457,7 +468,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
} else if (is_mbr) {
- if (flags != 0x80) /* Bootable flag */
+ if (pflags != 0x80) /* Bootable flag */
continue;
if (blkid_partition_get_type(pp) != 0x83) /* Linux partition */
@@ -480,7 +491,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
* either, then check if there's a single generic one, and use that. */
if (m->partitions[PARTITION_ROOT_VERITY].found)
- return -ENXIO;
+ return -EADDRNOTAVAIL;
if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
@@ -489,8 +500,19 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
- } else if (generic_node && !root_hash) {
+ } else if (flags & DISSECT_IMAGE_REQUIRE_ROOT) {
+
+ /* If the root has was set, then we won't fallback to a generic node, because the root hash
+ * decides */
+ if (root_hash)
+ return -EADDRNOTAVAIL;
+
+ /* If we didn't find a generic node, then we can't fix this up either */
+ if (!generic_node)
+ return -ENXIO;
+ /* If we didn't find a properly marked root partition, but we did find a single suitable
+ * generic Linux partition, then use this as root partition, if the caller asked for it. */
if (multiple_generic)
return -ENOTUNIQ;
@@ -500,17 +522,15 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
.partno = generic_nr,
.architecture = _ARCHITECTURE_INVALID,
.node = generic_node,
+ .uuid = generic_uuid,
};
generic_node = NULL;
- } else
- return -ENXIO;
+ }
}
- assert(m->partitions[PARTITION_ROOT].found);
-
if (root_hash) {
- if (!m->partitions[PARTITION_ROOT_VERITY].found)
+ if (!m->partitions[PARTITION_ROOT_VERITY].found || !m->partitions[PARTITION_ROOT].found)
return -EADDRNOTAVAIL;
/* If we found the primary root with the hash, then we definitely want to suppress any secondary root
diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h
index 175ddd8ea0..26319bd8e7 100644
--- a/src/shared/dissect-image.h
+++ b/src/shared/dissect-image.h
@@ -32,6 +32,7 @@ struct DissectedPartition {
bool rw:1;
int partno; /* -1 if there was no partition and the images contains a file system directly */
int architecture; /* Intended architecture: either native, secondary or unset (-1). */
+ sd_id128_t uuid; /* Partition entry UUID as reported by the GPT */
char *fstype;
char *node;
char *decrypted_node;
@@ -67,6 +68,8 @@ typedef enum DissectImageFlags {
DISSECT_IMAGE_DISCARD_ANY = DISSECT_IMAGE_DISCARD_ON_LOOP |
DISSECT_IMAGE_DISCARD |
DISSECT_IMAGE_DISCARD_ON_CRYPTO,
+ DISSECT_IMAGE_GPT_ONLY = 16, /* Only recognize images with GPT partition tables */
+ DISSECT_IMAGE_REQUIRE_ROOT = 32, /* Don't accept disks without root partition */
} DissectImageFlags;
struct DissectedImage {
@@ -76,7 +79,7 @@ struct DissectedImage {
DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX];
};
-int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectedImage **ret);
+int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret);
DissectedImage* dissected_image_unref(DissectedImage *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c
index f0bfb30bb5..87b520b540 100644
--- a/src/shared/fstab-util.c
+++ b/src/shared/fstab-util.c
@@ -38,7 +38,7 @@ bool fstab_is_mount_point(const char *mount) {
_cleanup_endmntent_ FILE *f = NULL;
struct mntent *m;
- f = setmntent("/etc/fstab", "r");
+ f = setmntent("/etc/fstab", "re");
if (!f)
return false;
diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c
index 823fb61cda..c10ed3d311 100644
--- a/src/shared/install-printf.c
+++ b/src/shared/install-printf.c
@@ -93,9 +93,9 @@ static int specifier_instance(char specifier, void *data, void *userdata, char *
return r;
if (isempty(instance)) {
- instance = strdup(i->default_instance ?: "");
- if (!instance)
- return -ENOMEM;
+ r = free_and_strdup(&instance, i->default_instance ?: "");
+ if (r < 0)
+ return r;
}
*ret = instance;
diff --git a/src/shared/install.c b/src/shared/install.c
index 474426d927..478abac8ab 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -1567,18 +1567,12 @@ static int install_info_symlink_wants(
if (strv_isempty(list))
return 0;
- if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
+ if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE) && i->default_instance) {
UnitFileInstallInfo instance = {
.type = _UNIT_FILE_TYPE_INVALID,
};
_cleanup_free_ char *path = NULL;
- /* Don't install any symlink if there's no default
- * instance configured */
-
- if (!i->default_instance)
- return 0;
-
r = unit_name_replace_instance(i->name, i->default_instance, &buf);
if (r < 0)
return r;
@@ -1861,7 +1855,7 @@ int unit_file_unmask(
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
- _cleanup_free_ char **todo = NULL;
+ _cleanup_strv_free_ char **todo = NULL;
size_t n_todo = 0, n_allocated = 0;
const char *config_path;
char **i;
@@ -1899,7 +1893,11 @@ int unit_file_unmask(
if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
return -ENOMEM;
- todo[n_todo++] = *i;
+ todo[n_todo] = strdup(*i);
+ if (!todo[n_todo])
+ return -ENOMEM;
+
+ n_todo++;
}
strv_uniq(todo);
@@ -1947,7 +1945,7 @@ int unit_file_link(
unsigned *n_changes) {
_cleanup_lookup_paths_free_ LookupPaths paths = {};
- _cleanup_free_ char **todo = NULL;
+ _cleanup_strv_free_ char **todo = NULL;
size_t n_todo = 0, n_allocated = 0;
const char *config_path;
char **i;
@@ -1996,7 +1994,11 @@ int unit_file_link(
if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
return -ENOMEM;
- todo[n_todo++] = *i;
+ todo[n_todo] = strdup(*i);
+ if (!todo[n_todo])
+ return -ENOMEM;
+
+ n_todo++;
}
strv_uniq(todo);
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index 712aff65b9..7bc5c0a128 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -99,6 +99,16 @@ static char **image_settings_path(Image *image) {
return ret;
}
+static char *image_roothash_path(Image *image) {
+ const char *fn;
+
+ assert(image);
+
+ fn = strjoina(image->name, ".roothash");
+
+ return file_in_same_dir(image->path, fn);
+}
+
static int image_new(
ImageType t,
const char *pretty,
@@ -397,6 +407,7 @@ void image_hashmap_free(Hashmap *map) {
int image_remove(Image *i) {
_cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
_cleanup_strv_free_ char **settings = NULL;
+ _cleanup_free_ char *roothash = NULL;
char **j;
int r;
@@ -409,6 +420,10 @@ int image_remove(Image *i) {
if (!settings)
return -ENOMEM;
+ roothash = image_roothash_path(i);
+ if (!roothash)
+ return -ENOMEM;
+
/* Make sure we don't interfere with a running nspawn */
r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
if (r < 0)
@@ -445,14 +460,17 @@ int image_remove(Image *i) {
log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", *j);
}
+ if (unlink(roothash) < 0 && errno != ENOENT)
+ log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", roothash);
+
return 0;
}
-static int rename_settings_file(const char *path, const char *new_name) {
+static int rename_auxiliary_file(const char *path, const char *new_name, const char *suffix) {
_cleanup_free_ char *rs = NULL;
const char *fn;
- fn = strjoina(new_name, ".nspawn");
+ fn = strjoina(new_name, suffix);
rs = file_in_same_dir(path, fn);
if (!rs)
@@ -463,7 +481,7 @@ static int rename_settings_file(const char *path, const char *new_name) {
int image_rename(Image *i, const char *new_name) {
_cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT, name_lock = LOCK_FILE_INIT;
- _cleanup_free_ char *new_path = NULL, *nn = NULL;
+ _cleanup_free_ char *new_path = NULL, *nn = NULL, *roothash = NULL;
_cleanup_strv_free_ char **settings = NULL;
unsigned file_attr = 0;
char **j;
@@ -481,6 +499,10 @@ int image_rename(Image *i, const char *new_name) {
if (!settings)
return -ENOMEM;
+ roothash = image_roothash_path(i);
+ if (!roothash)
+ return -ENOMEM;
+
/* Make sure we don't interfere with a running nspawn */
r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
if (r < 0)
@@ -550,19 +572,23 @@ int image_rename(Image *i, const char *new_name) {
nn = NULL;
STRV_FOREACH(j, settings) {
- r = rename_settings_file(*j, new_name);
+ r = rename_auxiliary_file(*j, new_name, ".nspawn");
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to rename settings file %s, ignoring: %m", *j);
}
+ r = rename_auxiliary_file(roothash, new_name, ".roothash");
+ if (r < 0 && r != -ENOENT)
+ log_debug_errno(r, "Failed to rename roothash file %s, ignoring: %m", roothash);
+
return 0;
}
-static int clone_settings_file(const char *path, const char *new_name) {
+static int clone_auxiliary_file(const char *path, const char *new_name, const char *suffix) {
_cleanup_free_ char *rs = NULL;
const char *fn;
- fn = strjoina(new_name, ".nspawn");
+ fn = strjoina(new_name, suffix);
rs = file_in_same_dir(path, fn);
if (!rs)
@@ -574,6 +600,7 @@ static int clone_settings_file(const char *path, const char *new_name) {
int image_clone(Image *i, const char *new_name, bool read_only) {
_cleanup_release_lock_file_ LockFile name_lock = LOCK_FILE_INIT;
_cleanup_strv_free_ char **settings = NULL;
+ _cleanup_free_ char *roothash = NULL;
const char *new_path;
char **j;
int r;
@@ -587,6 +614,10 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
if (!settings)
return -ENOMEM;
+ roothash = image_roothash_path(i);
+ if (!roothash)
+ return -ENOMEM;
+
/* Make sure nobody takes the new name, between the time we
* checked it is currently unused in all search paths, and the
* time we take possession of it */
@@ -636,11 +667,15 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
return r;
STRV_FOREACH(j, settings) {
- r = clone_settings_file(*j, new_name);
+ r = clone_auxiliary_file(*j, new_name, ".nspawn");
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to clone settings %s, ignoring: %m", *j);
}
+ r = clone_auxiliary_file(roothash, new_name, ".roothash");
+ if (r < 0 && r != -ENOENT)
+ log_debug_errno(r, "Failed to clone root hash file %s, ignoring: %m", roothash);
+
return 0;
}
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index a23d09967e..586ef64e72 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -139,7 +139,7 @@ static char** user_dirs(
const char *e;
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
_cleanup_free_ char *data_home = NULL;
- _cleanup_free_ char **res = NULL;
+ _cleanup_strv_free_ char **res = NULL;
char **tmp;
int r;
diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c
index 4eff4f692e..afdf1ab5ad 100644
--- a/src/shared/switch-root.c
+++ b/src/shared/switch-root.c
@@ -28,123 +28,102 @@
#include "base-filesystem.h"
#include "fd-util.h"
+#include "fs-util.h"
#include "log.h"
#include "missing.h"
#include "mkdir.h"
+#include "mount-util.h"
#include "path-util.h"
#include "rm-rf.h"
#include "stdio-util.h"
#include "string-util.h"
+#include "strv.h"
#include "switch-root.h"
#include "user-util.h"
#include "util.h"
-int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, unsigned long mountflags) {
-
- /* Don't try to unmount/move the old "/", there's no way to do it. */
- static const char move_mounts[] =
- "/dev\0"
- "/proc\0"
- "/sys\0"
- "/run\0";
+int switch_root(const char *new_root,
+ const char *old_root_after, /* path below the new root, where to place the old root after the transition */
+ bool unmount_old_root,
+ unsigned long mount_flags) { /* MS_MOVE or MS_BIND */
+ _cleanup_free_ char *resolved_old_root_after = NULL;
_cleanup_close_ int old_root_fd = -1;
- struct stat new_root_stat;
bool old_root_remove;
- const char *i, *temporary_old_root;
+ const char *i;
+ int r;
+
+ assert(new_root);
+ assert(old_root_after);
if (path_equal(new_root, "/"))
return 0;
- temporary_old_root = strjoina(new_root, oldroot);
- mkdir_p_label(temporary_old_root, 0755);
-
+ /* Check if we shall remove the contents of the old root */
old_root_remove = in_initrd();
+ if (old_root_remove) {
+ old_root_fd = open("/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
+ if (old_root_fd < 0)
+ return log_error_errno(errno, "Failed to open root directory: %m");
+ }
- if (stat(new_root, &new_root_stat) < 0)
- return log_error_errno(errno, "Failed to stat directory %s: %m", new_root);
+ /* Determine where we shall place the old root after the transition */
+ r = chase_symlinks(old_root_after, new_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved_old_root_after);
+ if (r < 0)
+ return log_error_errno(r, "Failed to resolve %s/%s: %m", new_root, old_root_after);
+ if (r == 0) /* Doesn't exist yet. Let's create it */
+ (void) mkdir_p_label(resolved_old_root_after, 0755);
- /* Work-around for kernel design: the kernel refuses switching
- * root if any file systems are mounted MS_SHARED. Hence
+ /* Work-around for kernel design: the kernel refuses MS_MOVE if any file systems are mounted MS_SHARED. Hence
* remount them MS_PRIVATE here as a work-around.
*
* https://bugzilla.redhat.com/show_bug.cgi?id=847418 */
if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
- log_warning_errno(errno, "Failed to make \"/\" private mount: %m");
-
- NULSTR_FOREACH(i, move_mounts) {
- char new_mount[PATH_MAX];
- struct stat sb;
- size_t n;
-
- n = snprintf(new_mount, sizeof new_mount, "%s%s", new_root, i);
- if (n >= sizeof new_mount) {
- bool move = mountflags & MS_MOVE;
-
- log_warning("New path is too long, %s: %s%s",
- move ? "forcing unmount instead" : "ignoring",
- new_root, i);
-
- if (move)
- if (umount2(i, MNT_FORCE) < 0)
- log_warning_errno(errno, "Failed to unmount %s: %m", i);
- continue;
- }
-
- mkdir_p_label(new_mount, 0755);
-
- if (stat(new_mount, &sb) < 0 ||
- sb.st_dev != new_root_stat.st_dev) {
-
- /* Mount point seems to be mounted already or
- * stat failed. Unmount the old mount point. */
- if (umount2(i, MNT_DETACH) < 0)
- log_warning_errno(errno, "Failed to unmount %s: %m", i);
- continue;
- }
-
- if (mount(i, new_mount, NULL, mountflags, NULL) < 0) {
- if (mountflags & MS_MOVE) {
- log_error_errno(errno, "Failed to move mount %s to %s, forcing unmount: %m", i, new_mount);
-
- if (umount2(i, MNT_FORCE) < 0)
- log_warning_errno(errno, "Failed to unmount %s: %m", i);
-
- } else if (mountflags & MS_BIND)
- log_error_errno(errno, "Failed to bind mount %s to %s: %m", i, new_mount);
- }
+ return log_error_errno(errno, "Failed to set \"/\" mount propagation to private: %m");
+
+ FOREACH_STRING(i, "/sys", "/dev", "/run", "/proc") {
+ _cleanup_free_ char *chased = NULL;
+
+ r = chase_symlinks(i, new_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &chased);
+ if (r < 0)
+ return log_error_errno(r, "Failed to resolve %s/%s: %m", new_root, i);
+ if (r > 0) {
+ /* Already exists. Let's see if it is a mount point already. */
+ r = path_is_mount_point(chased, NULL, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", chased);
+ if (r > 0) /* If it is already mounted, then do nothing */
+ continue;
+ } else
+ /* Doesn't exist yet? */
+ (void) mkdir_p_label(chased, 0755);
+
+ if (mount(i, chased, NULL, mount_flags, NULL) < 0)
+ return log_error_errno(r, "Failed to mount %s to %s: %m", i, chased);
}
- /* Do not fail, if base_filesystem_create() fails. Not all
- * switch roots are like base_filesystem_create() wants them
- * to look like. They might even boot, if they are RO and
- * don't have the FS layout. Just ignore the error and
- * switch_root() nevertheless. */
+ /* Do not fail if base_filesystem_create() fails. Not all switch roots are like base_filesystem_create() wants
+ * them to look like. They might even boot, if they are RO and don't have the FS layout. Just ignore the error
+ * and switch_root() nevertheless. */
(void) base_filesystem_create(new_root, UID_INVALID, GID_INVALID);
if (chdir(new_root) < 0)
return log_error_errno(errno, "Failed to change directory to %s: %m", new_root);
- if (old_root_remove) {
- old_root_fd = open("/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
- if (old_root_fd < 0)
- log_warning_errno(errno, "Failed to open root directory: %m");
- }
-
- /* We first try a pivot_root() so that we can umount the old
- * root dir. In many cases (i.e. where rootfs is /), that's
- * not possible however, and hence we simply overmount root */
- if (pivot_root(new_root, temporary_old_root) >= 0) {
+ /* We first try a pivot_root() so that we can umount the old root dir. In many cases (i.e. where rootfs is /),
+ * that's not possible however, and hence we simply overmount root */
+ if (pivot_root(new_root, resolved_old_root_after) >= 0) {
/* Immediately get rid of the old root, if detach_oldroot is set.
* Since we are running off it we need to do this lazily. */
- if (detach_oldroot && umount2(oldroot, MNT_DETACH) < 0)
- log_error_errno(errno, "Failed to lazily umount old root dir %s, %s: %m",
- oldroot,
- errno == ENOENT ? "ignoring" : "leaving it around");
+ if (unmount_old_root) {
+ r = umount_recursive(old_root_after, MNT_DETACH);
+ if (r < 0)
+ log_warning_errno(r, "Failed to unmount old root directory tree, ignoring: %m");
+ }
} else if (mount(new_root, "/", NULL, MS_MOVE, NULL) < 0)
- return log_error_errno(errno, "Failed to mount moving %s to /: %m", new_root);
+ return log_error_errno(errno, "Failed to move %s to /: %m", new_root);
if (chroot(".") < 0)
return log_error_errno(errno, "Failed to change root: %m");
diff --git a/src/shared/volatile-util.c b/src/shared/volatile-util.c
new file mode 100644
index 0000000000..e7e9721411
--- /dev/null
+++ b/src/shared/volatile-util.c
@@ -0,0 +1,68 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "proc-cmdline.h"
+#include "string-util.h"
+#include "volatile-util.h"
+
+VolatileMode volatile_mode_from_string(const char *s) {
+ int b;
+
+ if (isempty(s))
+ return _VOLATILE_MODE_INVALID;
+
+ b = parse_boolean(s);
+ if (b > 0)
+ return VOLATILE_YES;
+ if (b == 0)
+ return VOLATILE_NO;
+
+ if (streq(s, "state"))
+ return VOLATILE_STATE;
+
+ return _VOLATILE_MODE_INVALID;
+}
+
+int query_volatile_mode(VolatileMode *ret) {
+ _cleanup_free_ char *mode = NULL;
+ VolatileMode m = VOLATILE_NO;
+ int r;
+
+ r = proc_cmdline_get_key("systemd.volatile", PROC_CMDLINE_VALUE_OPTIONAL, &mode);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ goto finish;
+
+ if (mode) {
+ m = volatile_mode_from_string(mode);
+ if (m < 0)
+ return -EINVAL;
+ } else
+ m = VOLATILE_YES;
+
+ r = 1;
+
+finish:
+ *ret = m;
+ return r;
+}
diff --git a/src/shared/volatile-util.h b/src/shared/volatile-util.h
new file mode 100644
index 0000000000..17930ba6ae
--- /dev/null
+++ b/src/shared/volatile-util.h
@@ -0,0 +1,32 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef enum VolatileMode {
+ VOLATILE_NO,
+ VOLATILE_YES,
+ VOLATILE_STATE,
+ _VOLATILE_MODE_MAX,
+ _VOLATILE_MODE_INVALID = -1
+} VolatileMode;
+
+VolatileMode volatile_mode_from_string(const char *s);
+
+int query_volatile_mode(VolatileMode *ret);
diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c
index b810891d7d..1b99b7bc82 100644
--- a/src/socket-proxy/socket-proxyd.c
+++ b/src/socket-proxy/socket-proxyd.c
@@ -564,7 +564,7 @@ static void help(void) {
printf("%1$s [HOST:PORT]\n"
"%1$s [SOCKET]\n\n"
"Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
- " -c --max-connections= Set the maximum number of connections to be accepted\n"
+ " -c --connections-max= Set the maximum number of connections to be accepted\n"
" -h --help Show this help\n"
" --version Show package version\n",
program_invocation_short_name);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index f7e85c1ade..41e8d6075a 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -450,7 +450,7 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
unsigned basic_len;
id_len = MIN(max_id_len, 25u); /* as much as it needs, but at most 25 for now */
- basic_len = circle_len + 5 + id_len + 5 + active_len + sub_len;
+ basic_len = circle_len + 1 + id_len + 1 + load_len + 1 + active_len + 1 + sub_len + 1;
if (job_count)
basic_len += job_len + 1;
@@ -472,7 +472,8 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
id_len += incr;
desc_len += MIN(extra_len - incr, max_desc_len - desc_len);
}
- }
+ } else
+ desc_len = 0;
} else {
id_len = max_id_len;
desc_len = max_desc_len;
@@ -1734,7 +1735,7 @@ static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, cha
if (r < 0)
return bus_log_parse_error(r);
- *deps = ret;
+ *deps = strv_uniq(ret);
ret = NULL;
return 0;
diff --git a/src/test/test-af-list.c b/src/test/test-af-list.c
index aeaa0929b1..e2479133de 100644
--- a/src/test/test-af-list.c
+++ b/src/test/test-af-list.c
@@ -24,7 +24,7 @@
#include "string-util.h"
#include "util.h"
-static const struct af_name* lookup_af(register const char *str, register unsigned int len);
+static const struct af_name* lookup_af(register const char *str, register GPERF_LEN_TYPE len);
#include "af-from-name.h"
#include "af-list.h"
diff --git a/src/test/test-arphrd-list.c b/src/test/test-arphrd-list.c
index f3989ad201..8f4f342faa 100644
--- a/src/test/test-arphrd-list.c
+++ b/src/test/test-arphrd-list.c
@@ -24,7 +24,7 @@
#include "string-util.h"
#include "util.h"
-static const struct arphrd_name* lookup_arphrd(register const char *str, register unsigned int len);
+static const struct arphrd_name* lookup_arphrd(register const char *str, register GPERF_LEN_TYPE len);
#include "arphrd-from-name.h"
#include "arphrd-list.h"
diff --git a/src/test/test-dissect-image.c b/src/test/test-dissect-image.c
index 0512a15e88..2bb68be0db 100644
--- a/src/test/test-dissect-image.c
+++ b/src/test/test-dissect-image.c
@@ -43,7 +43,7 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
- r = dissect_image(d->fd, NULL, 0, &m);
+ r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT, &m);
if (r < 0) {
log_error_errno(r, "Failed to dissect image: %m");
return EXIT_FAILURE;
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index c56aa62667..bc9a2021f9 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -33,6 +33,7 @@
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
#endif
+#include "stat-util.h"
#include "test-helper.h"
#include "unit.h"
#include "util.h"
@@ -188,15 +189,27 @@ static void test_exec_protectkernelmodules(Manager *m) {
}
static void test_exec_readonlypaths(Manager *m) {
+
+ if (path_is_read_only_fs("/var") > 0)
+ return;
+
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) {
+
+ if (path_is_read_only_fs("/") > 0)
+ return;
+
test(m, "exec-readwritepaths-mount-propagation.service", 0, CLD_EXITED);
}
static void test_exec_inaccessiblepaths(Manager *m) {
+
+ if (path_is_read_only_fs("/") > 0)
+ return;
+
test(m, "exec-inaccessiblepaths-mount-propagation.service", 0, CLD_EXITED);
}
diff --git a/src/test/test-hexdecoct.c b/src/test/test-hexdecoct.c
index 276f25d091..fcae427e74 100644
--- a/src/test/test-hexdecoct.c
+++ b/src/test/test-hexdecoct.c
@@ -87,27 +87,19 @@ static void test_undecchar(void) {
}
static void test_unhexmem(void) {
- const char *hex = "efa214921";
+ const char *hex = "efa2149213";
const char *hex_invalid = "efa214921o";
_cleanup_free_ char *hex2 = NULL;
_cleanup_free_ void *mem = NULL;
size_t len;
- assert_se(unhexmem(hex, strlen(hex), &mem, &len) == 0);
- assert_se(unhexmem(hex, strlen(hex) + 1, &mem, &len) == -EINVAL);
assert_se(unhexmem(hex_invalid, strlen(hex_invalid), &mem, &len) == -EINVAL);
+ assert_se(unhexmem(hex, strlen(hex) + 1, &mem, &len) == -EINVAL);
+ assert_se(unhexmem(hex, strlen(hex) - 1, &mem, &len) == -EINVAL);
+ assert_se(unhexmem(hex, strlen(hex), &mem, &len) == 0);
assert_se((hex2 = hexmem(mem, len)));
-
- free(mem);
-
- assert_se(memcmp(hex, hex2, strlen(hex)) == 0);
-
- free(hex2);
-
- assert_se(unhexmem(hex, strlen(hex) - 1, &mem, &len) == 0);
- assert_se((hex2 = hexmem(mem, len)));
- assert_se(memcmp(hex, hex2, strlen(hex) - 1) == 0);
+ assert_se(streq(hex, hex2));
}
/* https://tools.ietf.org/html/rfc4648#section-10 */
diff --git a/src/test/test-proc-cmdline.c b/src/test/test-proc-cmdline.c
index 4101678f19..12dac8585b 100644
--- a/src/test/test-proc-cmdline.c
+++ b/src/test/test-proc-cmdline.c
@@ -35,8 +35,8 @@ static int parse_item(const char *key, const char *value, void *data) {
return 0;
}
-static void test_parse_proc_cmdline(void) {
- assert_se(parse_proc_cmdline(parse_item, &obj, true) >= 0);
+static void test_proc_cmdline_parse(void) {
+ assert_se(proc_cmdline_parse(parse_item, &obj, true) >= 0);
}
static void test_runlevel_to_target(void) {
@@ -55,11 +55,101 @@ static void test_runlevel_to_target(void) {
assert_se(streq_ptr(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET));
}
+static void test_proc_cmdline_get_key(void) {
+ _cleanup_free_ char *value = NULL;
+
+ putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm");
+
+ assert_se(proc_cmdline_get_key("", 0, &value) == -EINVAL);
+ assert_se(proc_cmdline_get_key("abc", 0, NULL) == 0);
+ assert_se(proc_cmdline_get_key("abc", 0, &value) == 0 && value == NULL);
+ assert_se(proc_cmdline_get_key("abc", PROC_CMDLINE_VALUE_OPTIONAL, &value) == 0 && value == NULL);
+
+ assert_se(proc_cmdline_get_key("foo_bar", 0, &value) > 0 && streq_ptr(value, "quux"));
+ value = mfree(value);
+ assert_se(proc_cmdline_get_key("foo_bar", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "quux"));
+ value = mfree(value);
+ assert_se(proc_cmdline_get_key("foo-bar", 0, &value) > 0 && streq_ptr(value, "quux"));
+ value = mfree(value);
+ assert_se(proc_cmdline_get_key("foo-bar", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "quux"));
+ value = mfree(value);
+ assert_se(proc_cmdline_get_key("foo-bar", 0, NULL) == 0);
+ assert_se(proc_cmdline_get_key("foo-bar", PROC_CMDLINE_VALUE_OPTIONAL, NULL) == -EINVAL);
+
+ assert_se(proc_cmdline_get_key("wuff-piep", 0, &value) > 0 && streq_ptr(value, "tuet"));
+ value = mfree(value);
+ assert_se(proc_cmdline_get_key("wuff-piep", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "tuet"));
+ value = mfree(value);
+ assert_se(proc_cmdline_get_key("wuff_piep", 0, &value) > 0 && streq_ptr(value, "tuet"));
+ value = mfree(value);
+ assert_se(proc_cmdline_get_key("wuff_piep", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "tuet"));
+ value = mfree(value);
+ assert_se(proc_cmdline_get_key("wuff_piep", 0, NULL) == 0);
+ assert_se(proc_cmdline_get_key("wuff_piep", PROC_CMDLINE_VALUE_OPTIONAL, NULL) == -EINVAL);
+
+ assert_se(proc_cmdline_get_key("zumm", 0, &value) == 0 && value == NULL);
+ assert_se(proc_cmdline_get_key("zumm", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && value == NULL);
+ assert_se(proc_cmdline_get_key("zumm", 0, NULL) > 0);
+}
+
+static void test_proc_cmdline_get_bool(void) {
+ bool value = false;
+
+ putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar bar-waldo=1 x_y-z=0 quux=miep");
+
+ assert_se(proc_cmdline_get_bool("", &value) == -EINVAL);
+ assert_se(proc_cmdline_get_bool("abc", &value) == 0 && value == false);
+ assert_se(proc_cmdline_get_bool("foo_bar", &value) > 0 && value == true);
+ assert_se(proc_cmdline_get_bool("foo-bar", &value) > 0 && value == true);
+ assert_se(proc_cmdline_get_bool("bar-waldo", &value) > 0 && value == true);
+ assert_se(proc_cmdline_get_bool("bar_waldo", &value) > 0 && value == true);
+ assert_se(proc_cmdline_get_bool("x_y-z", &value) > 0 && value == false);
+ assert_se(proc_cmdline_get_bool("x-y-z", &value) > 0 && value == false);
+ assert_se(proc_cmdline_get_bool("x-y_z", &value) > 0 && value == false);
+ assert_se(proc_cmdline_get_bool("x_y_z", &value) > 0 && value == false);
+ assert_se(proc_cmdline_get_bool("quux", &value) == -EINVAL && value == false);
+}
+
+static void test_proc_cmdline_key_streq(void) {
+
+ assert_se(proc_cmdline_key_streq("", ""));
+ assert_se(proc_cmdline_key_streq("a", "a"));
+ assert_se(!proc_cmdline_key_streq("", "a"));
+ assert_se(!proc_cmdline_key_streq("a", ""));
+ assert_se(proc_cmdline_key_streq("a", "a"));
+ assert_se(!proc_cmdline_key_streq("a", "b"));
+ assert_se(proc_cmdline_key_streq("x-y-z", "x-y-z"));
+ assert_se(proc_cmdline_key_streq("x-y-z", "x_y_z"));
+ assert_se(proc_cmdline_key_streq("x-y-z", "x-y_z"));
+ assert_se(proc_cmdline_key_streq("x-y-z", "x_y-z"));
+ assert_se(proc_cmdline_key_streq("x_y-z", "x-y_z"));
+ assert_se(!proc_cmdline_key_streq("x_y-z", "x-z_z"));
+}
+
+static void test_proc_cmdline_key_startswith(void) {
+
+ assert_se(proc_cmdline_key_startswith("", ""));
+ assert_se(proc_cmdline_key_startswith("x", ""));
+ assert_se(!proc_cmdline_key_startswith("", "x"));
+ assert_se(proc_cmdline_key_startswith("x", "x"));
+ assert_se(!proc_cmdline_key_startswith("x", "y"));
+ assert_se(!proc_cmdline_key_startswith("foo-bar", "quux"));
+ assert_se(proc_cmdline_key_startswith("foo-bar", "foo"));
+ assert_se(proc_cmdline_key_startswith("foo-bar", "foo-bar"));
+ assert_se(proc_cmdline_key_startswith("foo-bar", "foo_bar"));
+ assert_se(proc_cmdline_key_startswith("foo-bar", "foo_"));
+ assert_se(!proc_cmdline_key_startswith("foo-bar", "foo_xx"));
+}
+
int main(void) {
log_parse_environment();
log_open();
- test_parse_proc_cmdline();
+ test_proc_cmdline_parse();
+ test_proc_cmdline_key_streq();
+ test_proc_cmdline_key_startswith();
+ test_proc_cmdline_get_key();
+ test_proc_cmdline_get_bool();
test_runlevel_to_target();
return 0;
diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c
index 1f853a7f16..d80613dc84 100644
--- a/src/test/test-socket-util.c
+++ b/src/test/test-socket-util.c
@@ -92,6 +92,14 @@ static void test_socket_address_parse(void) {
assert_se(socket_address_parse(&a, "@abstract") >= 0);
assert_se(a.sockaddr.sa.sa_family == AF_UNIX);
+
+ assert_se(socket_address_parse(&a, "vsock::1234") >= 0);
+ assert_se(a.sockaddr.sa.sa_family == AF_VSOCK);
+ assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
+ assert_se(a.sockaddr.sa.sa_family == AF_VSOCK);
+ assert_se(socket_address_parse(&a, "vsock:2:1234x") < 0);
+ assert_se(socket_address_parse(&a, "vsock:2x:1234") < 0);
+ assert_se(socket_address_parse(&a, "vsock:2") < 0);
}
static void test_socket_address_parse_netlink(void) {
@@ -145,6 +153,14 @@ static void test_socket_address_equal(void) {
assert_se(socket_address_parse_netlink(&a, "firewall") >= 0);
assert_se(socket_address_parse_netlink(&b, "firewall") >= 0);
assert_se(socket_address_equal(&a, &b));
+
+ assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
+ assert_se(socket_address_parse(&b, "vsock:2:1234") >= 0);
+ assert_se(socket_address_equal(&a, &b));
+ assert_se(socket_address_parse(&b, "vsock:2:1235") >= 0);
+ assert_se(!socket_address_equal(&a, &b));
+ assert_se(socket_address_parse(&b, "vsock:3:1234") >= 0);
+ assert_se(!socket_address_equal(&a, &b));
}
static void test_socket_address_get_path(void) {
@@ -161,6 +177,9 @@ static void test_socket_address_get_path(void) {
assert_se(socket_address_parse(&a, "/foo/bar") >= 0);
assert_se(streq(socket_address_get_path(&a), "/foo/bar"));
+
+ assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
+ assert_se(!socket_address_get_path(&a));
}
static void test_socket_address_is(void) {
@@ -408,11 +427,18 @@ static void test_sockaddr_equal(void) {
.in6.sin6_port = 0,
.in6.sin6_addr = IN6ADDR_ANY_INIT,
};
+ union sockaddr_union e = {
+ .vm.svm_family = AF_VSOCK,
+ .vm.svm_port = 0,
+ .vm.svm_cid = VMADDR_CID_ANY,
+ };
assert_se(sockaddr_equal(&a, &a));
assert_se(sockaddr_equal(&a, &b));
assert_se(sockaddr_equal(&d, &d));
+ assert_se(sockaddr_equal(&e, &e));
assert_se(!sockaddr_equal(&a, &c));
assert_se(!sockaddr_equal(&b, &c));
+ assert_se(!sockaddr_equal(&a, &e));
}
static void test_sockaddr_un_len(void) {
diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c
index 6c34250a01..a48dca99e1 100644
--- a/src/test/test-stat-util.c
+++ b/src/test/test-stat-util.c
@@ -18,12 +18,14 @@
***/
#include <fcntl.h>
+#include <linux/magic.h>
#include <unistd.h>
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "macro.h"
+#include "missing.h"
#include "stat-util.h"
static void test_files_same(void) {
@@ -60,9 +62,33 @@ static void test_is_symlink(void) {
unlink(name_link);
}
+static void test_path_is_os_tree(void) {
+ assert_se(path_is_os_tree("/") > 0);
+ assert_se(path_is_os_tree("/etc") == 0);
+ assert_se(path_is_os_tree("/idontexist") == -ENOENT);
+}
+
+static void test_path_check_fstype(void) {
+ assert_se(path_check_fstype("/run", TMPFS_MAGIC) > 0);
+ assert_se(path_check_fstype("/run", BTRFS_SUPER_MAGIC) == 0);
+ assert_se(path_check_fstype("/proc", PROC_SUPER_MAGIC) > 0);
+ assert_se(path_check_fstype("/proc", BTRFS_SUPER_MAGIC) == 0);
+ assert_se(path_check_fstype("/proc", BTRFS_SUPER_MAGIC) == 0);
+ assert_se(path_check_fstype("/i-dont-exist", BTRFS_SUPER_MAGIC) == -ENOENT);
+}
+
+static void test_path_is_temporary_fs(void) {
+ assert_se(path_is_temporary_fs("/run") > 0);
+ assert_se(path_is_temporary_fs("/proc") == 0);
+ assert_se(path_is_temporary_fs("/i-dont-exist") == -ENOENT);
+}
+
int main(int argc, char *argv[]) {
test_files_same();
test_is_symlink();
+ test_path_is_os_tree();
+ test_path_check_fstype();
+ test_path_is_temporary_fs();
return 0;
}
diff --git a/src/timesync/timesyncd-conf.h b/src/timesync/timesyncd-conf.h
index cba0724b1b..0280697e9c 100644
--- a/src/timesync/timesyncd-conf.h
+++ b/src/timesync/timesyncd-conf.h
@@ -22,7 +22,7 @@
#include "conf-parser.h"
#include "timesyncd-manager.h"
-const struct ConfigPerfItem* timesyncd_gperf_lookup(const char *key, unsigned length);
+const struct ConfigPerfItem* timesyncd_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
int manager_parse_server_string(Manager *m, ServerType type, const char *string);
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 79f75e165b..f4ce9791fb 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -649,7 +649,7 @@ static int path_set_perms(Item *i, const char *path) {
else {
log_debug("chmod \"%s\" to mode %o", path, m);
if (chmod(fn, m) < 0)
- return log_error_errno(errno, "chmod(%s) failed: %m", path);
+ return log_error_errno(errno, "chmod() of %s via %s failed: %m", path, fn);
}
}
@@ -662,7 +662,7 @@ static int path_set_perms(Item *i, const char *path) {
if (chown(fn,
i->uid_set ? i->uid : UID_INVALID,
i->gid_set ? i->gid : GID_INVALID) < 0)
- return log_error_errno(errno, "chown(%s) failed: %m", path);
+ return log_error_errno(errno, "chown() of %s via %s failed: %m", path, fn);
}
}
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index 1dca375279..3af87f1388 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -192,14 +192,9 @@ static int load_link(link_config_ctx *ctx, const char *filename) {
}
static bool enable_name_policy(void) {
- _cleanup_free_ char *value = NULL;
- int r;
-
- r = get_proc_cmdline_key("net.ifnames=", &value);
- if (r > 0 && streq(value, "0"))
- return false;
+ bool b;
- return true;
+ return proc_cmdline_get_bool("net.ifnames", &b) <= 0 || b;
}
int link_config_load(link_config_ctx *ctx) {
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
index a99060d943..5a25cec6fd 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -94,7 +94,7 @@ const char *mac_policy_to_string(MACPolicy p) _const_;
MACPolicy mac_policy_from_string(const char *p) _pure_;
/* gperf lookup function */
-const struct ConfigPerfItem* link_config_gperf_lookup(const char *key, unsigned length);
+const struct ConfigPerfItem* link_config_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
int config_parse_mac_policy(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_name_policy(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/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c
index 59b9804dc4..51f364bf94 100644
--- a/src/udev/udev-builtin-input_id.c
+++ b/src/udev/udev-builtin-input_id.c
@@ -323,6 +323,9 @@ static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], boo
if (!is_pointer && !is_key && test_bit(EV_REL, bitmask_ev) &&
(test_bit(REL_WHEEL, bitmask_rel) || test_bit(REL_HWHEEL, bitmask_rel)))
udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1");
+ if (test_bit(EV_SW, bitmask_ev))
+ udev_builtin_add_property(dev, test, "ID_INPUT_SWITCH", "1");
+
}
devnode = udev_device_get_devnode(dev);
diff --git a/src/udev/udev-builtin-keyboard.c b/src/udev/udev-builtin-keyboard.c
index aa10beafb0..09024116f2 100644
--- a/src/udev/udev-builtin-keyboard.c
+++ b/src/udev/udev-builtin-keyboard.c
@@ -29,7 +29,7 @@
#include "string-util.h"
#include "udev.h"
-static const struct key *keyboard_lookup_key(const char *str, unsigned len);
+static const struct key *keyboard_lookup_key(const char *str, GPERF_LEN_TYPE len);
#include "keyboard-keys-from-name.h"
static int install_force_release(struct udev_device *dev, const unsigned *release, unsigned release_count) {
diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index 304a28777b..deffefd60b 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -73,7 +73,9 @@ void udev_event_unref(struct udev_event *event) {
free(event);
}
-size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size) {
+size_t udev_event_apply_format(struct udev_event *event,
+ const char *src, char *dest, size_t size,
+ bool replace_whitespace) {
struct udev_device *dev = event->dev;
enum subst_type {
SUBST_UNKNOWN,
@@ -130,8 +132,10 @@ size_t udev_event_apply_format(struct udev_event *event, const char *src, char *
for (;;) {
enum subst_type type = SUBST_UNKNOWN;
- char attrbuf[UTIL_PATH_SIZE];
- char *attr = NULL;
+ char attrbuf[UTIL_PATH_SIZE], sbuf[UTIL_PATH_SIZE];
+ char *attr = NULL, *_s;
+ size_t _l;
+ bool replws = replace_whitespace;
while (from[0] != '\0') {
if (from[0] == '$') {
@@ -200,6 +204,19 @@ subst:
attr = NULL;
}
+ /* result subst handles space as field separator */
+ if (type == SUBST_RESULT)
+ replws = false;
+
+ if (replws) {
+ /* store dest string ptr and remaining len */
+ _s = s;
+ _l = l;
+ /* temporarily use sbuf */
+ s = sbuf;
+ l = UTIL_PATH_SIZE;
+ }
+
switch (type) {
case SUBST_DEVPATH:
l = strpcpy(&s, l, udev_device_get_devpath(dev));
@@ -380,6 +397,20 @@ subst:
log_error("unknown substitution type=%i", type);
break;
}
+
+ /* replace whitespace in sbuf and copy to dest */
+ if (replws) {
+ size_t tmplen = UTIL_PATH_SIZE - l;
+
+ /* restore s and l to dest string values */
+ s = _s;
+ l = _l;
+
+ /* copy ws-replaced value to s */
+ tmplen = util_replace_whitespace(sbuf, s, MIN(tmplen, l));
+ l -= tmplen;
+ s += tmplen;
+ }
}
out:
@@ -927,7 +958,7 @@ void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_
const char *cmd = udev_list_entry_get_name(list_entry);
enum udev_builtin_cmd builtin_cmd = udev_list_entry_get_num(list_entry);
- udev_event_apply_format(event, cmd, command, sizeof(command));
+ udev_event_apply_format(event, cmd, command, sizeof(command), false);
if (builtin_cmd < UDEV_BUILTIN_MAX)
udev_builtin_run(event->dev, builtin_cmd, command, false);
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index b0238220e4..4d07b8fce0 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -1676,7 +1676,7 @@ static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct
name = rules_str(rules, cur->key.attr_off);
switch (cur->key.attrsubst) {
case SB_FORMAT:
- udev_event_apply_format(event, name, nbuf, sizeof(nbuf));
+ udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false);
name = nbuf;
/* fall through */
case SB_NONE:
@@ -1838,7 +1838,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
_cleanup_free_ char *value = NULL;
size_t len;
- udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename));
+ udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename), false);
sysctl_normalize(filename);
if (sysctl_read(filename, &value) < 0)
goto nomatch;
@@ -1916,7 +1916,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
struct stat statbuf;
int match;
- udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename));
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename), false);
if (util_resolve_subsys_kernel(event->udev, filename, filename, sizeof(filename), 0) != 0) {
if (filename[0] != '/') {
char tmp[UTIL_PATH_SIZE];
@@ -1942,7 +1942,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
char result[UTIL_LINE_SIZE];
event->program_result = mfree(event->program_result);
- udev_event_apply_format(event, rules_str(rules, cur->key.value_off), program, sizeof(program));
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), program, sizeof(program), false);
log_debug("PROGRAM '%s' %s:%u",
program,
rules_str(rules, rule->rule.filename_off),
@@ -1969,7 +1969,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
case TK_M_IMPORT_FILE: {
char import[UTIL_PATH_SIZE];
- udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import));
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
if (import_file_into_properties(event->dev, import) != 0)
if (cur->key.op != OP_NOMATCH)
goto nomatch;
@@ -1978,7 +1978,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
case TK_M_IMPORT_PROG: {
char import[UTIL_PATH_SIZE];
- udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import));
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
log_debug("IMPORT '%s' %s:%u",
import,
rules_str(rules, rule->rule.filename_off),
@@ -2009,7 +2009,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
event->builtin_run |= (1 << cur->key.builtin_cmd);
}
- udev_event_apply_format(event, rules_str(rules, cur->key.value_off), command, sizeof(command));
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), command, sizeof(command), false);
log_debug("IMPORT builtin '%s' %s:%u",
udev_builtin_name(cur->key.builtin_cmd),
rules_str(rules, rule->rule.filename_off),
@@ -2077,7 +2077,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
case TK_M_IMPORT_PARENT: {
char import[UTIL_PATH_SIZE];
- udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import));
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
if (import_parent_into_properties(event->dev, import) != 0)
if (cur->key.op != OP_NOMATCH)
goto nomatch;
@@ -2115,7 +2115,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
break;
if (cur->key.op == OP_ASSIGN_FINAL)
event->owner_final = true;
- udev_event_apply_format(event, rules_str(rules, cur->key.value_off), owner, sizeof(owner));
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), owner, sizeof(owner), false);
event->owner_set = true;
r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL);
if (r < 0) {
@@ -2141,7 +2141,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
break;
if (cur->key.op == OP_ASSIGN_FINAL)
event->group_final = true;
- udev_event_apply_format(event, rules_str(rules, cur->key.value_off), group, sizeof(group));
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), group, sizeof(group), false);
event->group_set = true;
r = get_group_creds(&gr, &event->gid);
if (r < 0) {
@@ -2165,7 +2165,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
if (event->mode_final)
break;
- udev_event_apply_format(event, rules_str(rules, cur->key.value_off), mode_str, sizeof(mode_str));
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), mode_str, sizeof(mode_str), false);
mode = strtol(mode_str, &endptr, 8);
if (endptr[0] != '\0') {
log_error("ignoring invalid mode '%s'", mode_str);
@@ -2222,7 +2222,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
const char *name, *label;
name = rules_str(rules, cur->key.attr_off);
- udev_event_apply_format(event, rules_str(rules, cur->key.value_off), label_str, sizeof(label_str));
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), label_str, sizeof(label_str), false);
if (label_str[0] != '\0')
label = label_str;
else
@@ -2256,10 +2256,10 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
char temp[UTIL_NAME_SIZE];
/* append value separated by space */
- udev_event_apply_format(event, value, temp, sizeof(temp));
+ udev_event_apply_format(event, value, temp, sizeof(temp), false);
strscpyl(value_new, sizeof(value_new), value_old, " ", temp, NULL);
} else
- udev_event_apply_format(event, value, value_new, sizeof(value_new));
+ udev_event_apply_format(event, value, value_new, sizeof(value_new), false);
udev_device_add_property(event->dev, name, value_new);
break;
@@ -2268,7 +2268,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
char tag[UTIL_PATH_SIZE];
const char *p;
- udev_event_apply_format(event, rules_str(rules, cur->key.value_off), tag, sizeof(tag));
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), tag, sizeof(tag), false);
if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL)
udev_device_cleanup_tags_list(event->dev);
for (p = tag; *p != '\0'; p++) {
@@ -2296,7 +2296,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
break;
if (cur->key.op == OP_ASSIGN_FINAL)
event->name_final = true;
- udev_event_apply_format(event, name, name_str, sizeof(name_str));
+ udev_event_apply_format(event, name, name_str, sizeof(name_str), false);
if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) {
count = util_replace_chars(name_str, "/");
if (count > 0)
@@ -2336,7 +2336,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
udev_device_cleanup_devlinks_list(event->dev);
/* allow multiple symlinks separated by spaces */
- udev_event_apply_format(event, rules_str(rules, cur->key.value_off), temp, sizeof(temp));
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), temp, sizeof(temp), esc != ESCAPE_NONE);
if (esc == ESCAPE_UNSET)
count = util_replace_chars(temp, "/ ");
else if (esc == ESCAPE_REPLACE)
@@ -2376,7 +2376,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL);
attr_subst_subdir(attr, sizeof(attr));
- udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value));
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value), false);
log_debug("ATTR '%s' writing '%s' %s:%u", attr, value,
rules_str(rules, rule->rule.filename_off),
rule->rule.filename_line);
@@ -2392,9 +2392,9 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
char value[UTIL_NAME_SIZE];
int r;
- udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename));
+ udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename), false);
sysctl_normalize(filename);
- udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value));
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value), false);
log_debug("SYSCTL '%s' writing '%s' %s:%u", filename, value,
rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
r = sysctl_write(filename, value);
diff --git a/src/udev/udev.h b/src/udev/udev.h
index 8433e8d9f2..c0cb7eae84 100644
--- a/src/udev/udev.h
+++ b/src/udev/udev.h
@@ -80,7 +80,9 @@ int udev_rules_apply_static_dev_perms(struct udev_rules *rules);
/* udev-event.c */
struct udev_event *udev_event_new(struct udev_device *dev);
void udev_event_unref(struct udev_event *event);
-size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size);
+size_t udev_event_apply_format(struct udev_event *event,
+ const char *src, char *dest, size_t size,
+ bool replace_whitespace);
int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string,
char *result, size_t maxsize, int read_value);
int udev_event_spawn(struct udev_event *event,
diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c
index 702dbe5282..07b667f131 100644
--- a/src/udev/udevadm-test.c
+++ b/src/udev/udevadm-test.c
@@ -144,7 +144,7 @@ static int adm_test(struct udev *udev, int argc, char *argv[]) {
udev_list_entry_foreach(entry, udev_list_get_entry(&event->run_list)) {
char program[UTIL_PATH_SIZE];
- udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program));
+ udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program), false);
printf("run: '%s'\n", program);
}
out:
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index 895c6f271b..dd23054b0d 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -1357,10 +1357,10 @@ static int listen_fds(int *rctrl, int *rnetlink) {
/*
* read the kernel command line, in case we need to get into debug mode
- * udev.log-priority=<level> syslog priority
- * udev.children-max=<number of workers> events are fully serialized if set to 1
- * udev.exec-delay=<number of seconds> delay execution of every executed program
- * udev.event-timeout=<number of seconds> seconds to wait before terminating an event
+ * udev.log_priority=<level> syslog priority
+ * udev.children_max=<number of workers> events are fully serialized if set to 1
+ * udev.exec_delay=<number of seconds> delay execution of every executed program
+ * udev.event_timeout=<number of seconds> seconds to wait before terminating an event
*/
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r = 0;
@@ -1370,25 +1370,46 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (!value)
return 0;
- if (streq(key, "udev.log-priority") && value) {
+ if (proc_cmdline_key_streq(key, "udev.log_priority")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
r = util_log_priority(value);
if (r >= 0)
log_set_max_level(r);
- } else if (streq(key, "udev.event-timeout") && value) {
+
+ } else if (proc_cmdline_key_streq(key, "udev.event_timeout")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
r = safe_atou64(value, &arg_event_timeout_usec);
if (r >= 0) {
arg_event_timeout_usec *= USEC_PER_SEC;
arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1;
}
- } else if (streq(key, "udev.children-max") && value)
+
+ } else if (proc_cmdline_key_streq(key, "udev.children_max")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
r = safe_atou(value, &arg_children_max);
- else if (streq(key, "udev.exec-delay") && value)
+
+ } else if (proc_cmdline_key_streq(key, "udev.exec_delay")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
r = safe_atoi(value, &arg_exec_delay);
- else if (startswith(key, "udev."))
+
+ } else if (startswith(key, "udev."))
log_warning("Unknown udev kernel command line option \"%s\"", key);
if (r < 0)
log_warning_errno(r, "Failed to parse \"%s=%s\", ignoring: %m", key, value);
+
return 0;
}
@@ -1649,7 +1670,7 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto exit;
- r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, true);
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
if (r < 0)
log_warning_errno(r, "failed to parse kernel command line, ignoring: %m");
diff --git a/src/veritysetup/Makefile b/src/veritysetup/Makefile
new file mode 120000
index 0000000000..d0b0e8e008
--- /dev/null
+++ b/src/veritysetup/Makefile
@@ -0,0 +1 @@
+../Makefile \ No newline at end of file
diff --git a/src/veritysetup/veritysetup-generator.c b/src/veritysetup/veritysetup-generator.c
new file mode 100644
index 0000000000..519ac050f9
--- /dev/null
+++ b/src/veritysetup/veritysetup-generator.c
@@ -0,0 +1,253 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fstab-util.h"
+#include "hexdecoct.h"
+#include "id128-util.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "proc-cmdline.h"
+#include "string-util.h"
+#include "unit-name.h"
+
+static char *arg_dest = NULL;
+static bool arg_enabled = true;
+static char *arg_root_hash = NULL;
+static char *arg_data_what = NULL;
+static char *arg_hash_what = NULL;
+
+static int create_device(void) {
+ _cleanup_free_ char *u = NULL, *v = NULL, *d = NULL, *e = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ const char *p, *to;
+ int r;
+
+ /* If all three pieces of information are missing, then verity is turned off */
+ if (!arg_root_hash && !arg_data_what && !arg_hash_what)
+ return 0;
+
+ /* if one of them is missing however, the data is simply incomplete and this is an error */
+ if (!arg_root_hash)
+ log_error("Verity information incomplete, root hash unspecified.");
+ if (!arg_data_what)
+ log_error("Verity information incomplete, root data device unspecified.");
+ if (!arg_hash_what)
+ log_error("Verity information incomplete, root hash device unspecified.");
+
+ if (!arg_root_hash || !arg_data_what || !arg_hash_what)
+ return -EINVAL;
+
+ log_debug("Using root verity data device %s,\n"
+ " hash device %s,\n"
+ " and root hash %s.", arg_data_what, arg_hash_what, arg_root_hash);
+
+ p = strjoina(arg_dest, "/systemd-veritysetup@root.service");
+
+ u = fstab_node_to_udev_node(arg_data_what);
+ if (!u)
+ return log_oom();
+ v = fstab_node_to_udev_node(arg_hash_what);
+ if (!v)
+ return log_oom();
+
+ r = unit_name_from_path(u, ".device", &d);
+ if (r < 0)
+ return log_error_errno(r, "Failed to to generate unit name: %m");
+ r = unit_name_from_path(v, ".device", &e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to to generate unit name: %m");
+
+ f = fopen(p, "wxe");
+ if (!f)
+ return log_error_errno(errno, "Failed to create unit file %s: %m", p);
+
+ fprintf(f,
+ "# Automatically generated by systemd-veritysetup-generator\n\n"
+ "[Unit]\n"
+ "Description=Integrity Protection Setup for %%I\n"
+ "Documentation=man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n"
+ "SourcePath=/proc/cmdline\n"
+ "DefaultDependencies=no\n"
+ "Conflicts=umount.target\n"
+ "BindsTo=%s %s\n"
+ "IgnoreOnIsolate=true\n"
+ "After=cryptsetup-pre.target %s %s\n"
+ "Before=cryptsetup.target umount.target\n"
+ "\n[Service]\n"
+ "Type=oneshot\n"
+ "RemainAfterExit=yes\n"
+ "ExecStart=" ROOTLIBEXECDIR "/systemd-veritysetup attach root '%s' '%s' '%s'\n"
+ "ExecStop=" ROOTLIBEXECDIR "/systemd-veritysetup detach root\n",
+ d, e,
+ d, e,
+ u, v, arg_root_hash);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write file %s: %m", p);
+
+ to = strjoina(arg_dest, "/cryptsetup.target.requires/systemd-veritysetup@root.service");
+
+ (void) mkdir_parents(to, 0755);
+ if (symlink("../systemd-veritysetup@root.service", to) < 0)
+ return log_error_errno(errno, "Failed to create symlink %s: %m", to);
+
+ return 0;
+}
+
+static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
+ int r;
+
+ if (streq(key, "systemd.verity")) {
+
+ r = value ? parse_boolean(value) : 1;
+ if (r < 0)
+ log_warning("Failed to parse verity= kernel command line switch %s. Ignoring.", value);
+ else
+ arg_enabled = r;
+
+ } else if (streq(key, "roothash")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ r = free_and_strdup(&arg_root_hash, value);
+ if (r < 0)
+ return log_oom();
+
+ } else if (streq(key, "systemd.verity_root_data")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ r = free_and_strdup(&arg_data_what, value);
+ if (r < 0)
+ return log_oom();
+
+ } else if (streq(key, "systemd.verity_root_hash")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ r = free_and_strdup(&arg_hash_what, value);
+ if (r < 0)
+ return log_oom();
+ }
+
+ return 0;
+}
+
+static int determine_devices(void) {
+ _cleanup_free_ void *m = NULL;
+ sd_id128_t root_uuid, verity_uuid;
+ char ids[37];
+ size_t l;
+ int r;
+
+ /* Try to automatically derive the root data and hash device paths from the root hash */
+
+ if (!arg_root_hash)
+ return 0;
+
+ if (arg_data_what && arg_hash_what)
+ return 0;
+
+ r = unhexmem(arg_root_hash, strlen(arg_root_hash), &m, &l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse root hash: %s", arg_root_hash);
+ if (l < sizeof(sd_id128_t)) {
+ log_debug("Root hash is shorter than 128 bits (32 characters), ignoring for discovering verity partition.");
+ return 0;
+ }
+
+ if (!arg_data_what) {
+ memcpy(&root_uuid, m, sizeof(root_uuid));
+
+ arg_data_what = strjoin("/dev/disk/by-partuuid/", id128_to_uuid_string(root_uuid, ids));
+ if (!arg_data_what)
+ return log_oom();
+ }
+
+ if (!arg_hash_what) {
+ memcpy(&verity_uuid, (uint8_t*) m + l - sizeof(verity_uuid), sizeof(verity_uuid));
+
+ arg_hash_what = strjoin("/dev/disk/by-partuuid/", id128_to_uuid_string(verity_uuid, ids));
+ if (!arg_hash_what)
+ return log_oom();
+ }
+
+ return 1;
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+
+ if (argc > 1 && argc != 4) {
+ log_error("This program takes three or no arguments.");
+ return EXIT_FAILURE;
+ }
+
+ if (argc > 1)
+ arg_dest = argv[1];
+
+ log_set_target(LOG_TARGET_SAFE);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to parse kernel command line: %m");
+ goto finish;
+ }
+
+ /* For now we only support the root device on verity. Later on we might want to add support for /etc/veritytab
+ * or similar to define additional mappings */
+
+ if (!arg_enabled) {
+ r = 0;
+ goto finish;
+ }
+
+ r = determine_devices();
+ if (r < 0)
+ goto finish;
+
+ r = create_device();
+ if (r < 0)
+ goto finish;
+
+ r = 0;
+
+finish:
+ free(arg_root_hash);
+ free(arg_data_what);
+ free(arg_hash_what);
+
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/veritysetup/veritysetup.c b/src/veritysetup/veritysetup.c
new file mode 100644
index 0000000000..f809d51638
--- /dev/null
+++ b/src/veritysetup/veritysetup.c
@@ -0,0 +1,154 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <libcryptsetup.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include "log.h"
+#include "hexdecoct.h"
+#include "string-util.h"
+#include "alloc-util.h"
+
+static char *arg_root_hash = NULL;
+static char *arg_data_what = NULL;
+static char *arg_hash_what = NULL;
+
+static int help(void) {
+ printf("%s attach VOLUME DATADEVICE HASHDEVICE ROOTHASH\n"
+ "%s detach VOLUME\n\n"
+ "Attaches or detaches an integrity protected block device.\n",
+ program_invocation_short_name,
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static void log_glue(int level, const char *msg, void *usrptr) {
+ log_debug("%s", msg);
+}
+
+int main(int argc, char *argv[]) {
+ struct crypt_device *cd = NULL;
+ int r;
+
+ if (argc <= 1) {
+ r = help();
+ goto finish;
+ }
+
+ if (argc < 3) {
+ log_error("This program requires at least two arguments.");
+ r = -EINVAL;
+ goto finish;
+ }
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+ if (streq(argv[1], "attach")) {
+ _cleanup_free_ void *m = NULL;
+ crypt_status_info status;
+ size_t l;
+
+ if (argc < 6) {
+ log_error("attach requires at least two arguments.");
+ r = -EINVAL;
+ goto finish;
+ }
+
+ r = unhexmem(argv[5], strlen(argv[5]), &m, &l);
+ if (r < 0) {
+ log_error("Failed to parse root hash.");
+ goto finish;
+ }
+
+ r = crypt_init(&cd, argv[4]);
+ if (r < 0) {
+ log_error_errno(r, "Failed to open verity device %s: %m", argv[4]);
+ goto finish;
+ }
+
+ crypt_set_log_callback(cd, log_glue, NULL);
+
+ status = crypt_status(cd, argv[2]);
+ if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
+ log_info("Volume %s already active.", argv[2]);
+ r = 0;
+ goto finish;
+ }
+
+ r = crypt_load(cd, CRYPT_VERITY, NULL);
+ if (r < 0) {
+ log_error_errno(r, "Failed to load verity superblock: %m");
+ goto finish;
+ }
+
+ r = crypt_set_data_device(cd, argv[3]);
+ if (r < 0) {
+ log_error_errno(r, "Failed to configure data device: %m");
+ goto finish;
+ }
+
+ r = crypt_activate_by_volume_key(cd, argv[2], m, l, CRYPT_ACTIVATE_READONLY);
+ if (r < 0) {
+ log_error_errno(r, "Failed to set up verity device: %m");
+ goto finish;
+ }
+
+ } else if (streq(argv[1], "detach")) {
+
+ r = crypt_init_by_name(&cd, argv[2]);
+ if (r == -ENODEV) {
+ log_info("Volume %s already inactive.", argv[2]);
+ goto finish;
+ } else if (r < 0) {
+ log_error_errno(r, "crypt_init_by_name() failed: %m");
+ goto finish;
+ }
+
+ crypt_set_log_callback(cd, log_glue, NULL);
+
+ r = crypt_deactivate(cd, argv[2]);
+ if (r < 0) {
+ log_error_errno(r, "Failed to deactivate: %m");
+ goto finish;
+ }
+
+ } else {
+ log_error("Unknown verb %s.", argv[1]);
+ r = -EINVAL;
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ if (cd)
+ crypt_free(cd);
+
+ free(arg_root_hash);
+ free(arg_data_what);
+ free(arg_hash_what);
+
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/volatile-root/Makefile b/src/volatile-root/Makefile
new file mode 120000
index 0000000000..d0b0e8e008
--- /dev/null
+++ b/src/volatile-root/Makefile
@@ -0,0 +1 @@
+../Makefile \ No newline at end of file
diff --git a/src/volatile-root/volatile-root.c b/src/volatile-root/volatile-root.c
new file mode 100644
index 0000000000..3c0b6fa1de
--- /dev/null
+++ b/src/volatile-root/volatile-root.c
@@ -0,0 +1,157 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mount.h>
+
+#include "alloc-util.h"
+#include "fs-util.h"
+#include "mkdir.h"
+#include "mount-util.h"
+#include "stat-util.h"
+#include "volatile-util.h"
+#include "string-util.h"
+#include "path-util.h"
+
+static int make_volatile(const char *path) {
+ _cleanup_free_ char *old_usr = NULL;
+ int r;
+
+ r = path_is_mount_point(path, NULL, AT_SYMLINK_FOLLOW);
+ if (r < 0)
+ return log_error_errno(r, "Couldn't determine whether %s is a mount point: %m", path);
+ if (r == 0) {
+ log_error("%s is not a mount point.", path);
+ return -EINVAL;
+ }
+
+ r = path_is_temporary_fs(path);
+ if (r < 0)
+ return log_error_errno(r, "Couldn't determine whether %s is a temporary file system: %m", path);
+ if (r > 0) {
+ log_info("%s already is a temporary file system.", path);
+ return 0;
+ }
+
+ r = chase_symlinks("/usr", path, CHASE_PREFIX_ROOT, &old_usr);
+ if (r < 0)
+ return log_error_errno(r, "/usr not available in old root: %m");
+
+ r = mkdir_p("/run/systemd/volatile-sysroot", 0700);
+ if (r < 0)
+ return log_error_errno(r, "Couldn't generate volatile sysroot directory: %m");
+
+ r = mount_verbose(LOG_ERR, "tmpfs", "/run/systemd/volatile-sysroot", "tmpfs", MS_STRICTATIME, "mode=755");
+ if (r < 0)
+ goto finish_rmdir;
+
+ if (mkdir("/run/systemd/volatile-sysroot/usr", 0755) < 0) {
+ r = -errno;
+ goto finish_umount;
+ }
+
+ r = mount_verbose(LOG_ERR, old_usr, "/run/systemd/volatile-sysroot/usr", NULL, MS_BIND|MS_REC, NULL);
+ if (r < 0)
+ goto finish_umount;
+
+ r = bind_remount_recursive("/run/systemd/volatile-sysroot/usr", true, NULL);
+ if (r < 0)
+ goto finish_umount;
+
+ r = umount_recursive(path, 0);
+ if (r < 0) {
+ log_error_errno(r, "Failed to unmount %s: %m", path);
+ goto finish_umount;
+ }
+
+ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
+ log_warning_errno(errno, "Failed to remount %s MS_SLAVE|MS_REC: %m", path);
+
+ r = mount_verbose(LOG_ERR, "/run/systemd/volatile-sysroot", path, NULL, MS_MOVE, NULL);
+
+finish_umount:
+ (void) umount_recursive("/run/systemd/volatile-sysroot", 0);
+
+finish_rmdir:
+ (void) rmdir("/run/systemd/volatile-sysroot");
+
+ return r;
+}
+
+int main(int argc, char *argv[]) {
+ VolatileMode m = _VOLATILE_MODE_INVALID;
+ const char *path;
+ int r;
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ if (argc > 3) {
+ log_error("Too many arguments. Expected directory and mode.");
+ r = -EINVAL;
+ goto finish;
+ }
+
+ r = query_volatile_mode(&m);
+ if (r < 0) {
+ log_error_errno(r, "Failed to determine volatile mode from kernel command line.");
+ goto finish;
+ }
+ if (r == 0 && argc >= 2) {
+ /* The kernel command line always wins. However if nothing was set there, the argument passed here wins instead. */
+ m = volatile_mode_from_string(argv[1]);
+ if (m < 0) {
+ log_error("Couldn't parse volatile mode: %s", argv[1]);
+ r = -EINVAL;
+ goto finish;
+ }
+ }
+
+ if (argc < 3)
+ path = "/sysroot";
+ else {
+ path = argv[2];
+
+ if (isempty(path)) {
+ log_error("Directory name cannot be empty.");
+ r = -EINVAL;
+ goto finish;
+ }
+ if (!path_is_absolute(path)) {
+ log_error("Directory must be specified as absolute path.");
+ r = -EINVAL;
+ goto finish;
+ }
+ if (path_equal(path, "/")) {
+ log_error("Directory cannot be the root directory.");
+ r = -EINVAL;
+ goto finish;
+ }
+ }
+
+ if (m != VOLATILE_YES) {
+ r = 0;
+ goto finish;
+ }
+
+ r = make_volatile(path);
+
+finish:
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}