summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyze/analyze.c2
-rw-r--r--src/basic/MurmurHash2.c6
-rw-r--r--src/basic/architecture.c3
-rw-r--r--src/basic/architecture.h3
-rw-r--r--src/basic/calendarspec.c14
-rw-r--r--src/basic/cgroup-util.c3
-rw-r--r--src/basic/conf-files.c3
-rw-r--r--src/basic/copy.c2
-rw-r--r--src/basic/dirent-util.c3
-rw-r--r--src/basic/fs-util.c23
-rw-r--r--src/basic/hexdecoct.c4
-rw-r--r--src/basic/khash.c2
-rw-r--r--src/basic/nss-util.h6
-rw-r--r--src/basic/path-util.c25
-rw-r--r--src/basic/path-util.h2
-rw-r--r--src/basic/proc-cmdline.c2
-rw-r--r--src/basic/raw-clone.h4
-rw-r--r--src/basic/rm-rf.c2
-rw-r--r--src/basic/siphash24.c7
-rw-r--r--src/basic/socket-util.c2
-rw-r--r--src/basic/string-util.c11
-rw-r--r--src/basic/string-util.h5
-rw-r--r--src/basic/time-util.c34
-rw-r--r--src/basic/time-util.h11
-rw-r--r--src/basic/virt.c2
-rw-r--r--src/boot/efi/boot.c2
-rw-r--r--src/boot/efi/stub.c2
-rw-r--r--src/cgls/cgls.c162
-rw-r--r--src/cgtop/cgtop.c67
-rw-r--r--src/core/dbus-execute.c26
-rw-r--r--src/core/dbus-manager.c227
-rw-r--r--src/core/dbus-manager.h2
-rw-r--r--src/core/dbus-unit.c6
-rw-r--r--src/core/dbus.c18
-rw-r--r--src/core/execute.c207
-rw-r--r--src/core/execute.h3
-rw-r--r--src/core/job.c2
-rw-r--r--src/core/killall.c29
-rw-r--r--src/core/load-dropin.c114
-rw-r--r--src/core/load-dropin.h9
-rw-r--r--src/core/load-fragment-gperf.gperf.m42
-rw-r--r--src/core/load-fragment.c21
-rw-r--r--src/core/manager.c24
-rw-r--r--src/core/mount.c5
-rw-r--r--src/core/namespace.c187
-rw-r--r--src/core/namespace.h6
-rw-r--r--src/core/socket.c1
-rw-r--r--src/core/timer.c7
-rw-r--r--src/core/transaction.c5
-rw-r--r--src/core/unit.c32
-rw-r--r--src/coredump/coredump.c6
-rw-r--r--src/cryptsetup/cryptsetup.c4
-rw-r--r--src/dissect/dissect.c8
-rw-r--r--src/fstab-generator/fstab-generator.c61
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c2
-rw-r--r--src/hostname/hostnamectl.c23
-rw-r--r--src/hostname/hostnamed.c4
-rw-r--r--src/journal-remote/journal-gatewayd.c12
-rw-r--r--src/journal-remote/journal-remote.c4
-rw-r--r--src/journal-remote/microhttpd-util.h16
-rw-r--r--src/journal/journal-file.c106
-rw-r--r--src/journal/journalctl.c8
-rw-r--r--src/journal/lookup3.c4
-rw-r--r--src/libsystemd-network/dhcp6-option.c2
-rw-r--r--src/libsystemd-network/lldp-network.c21
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c2
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c2
-rw-r--r--src/libsystemd-network/sd-dhcp6-lease.c2
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.c3
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.h1
-rw-r--r--src/libsystemd/sd-device/sd-device.c2
-rw-r--r--src/libsystemd/sd-event/sd-event.c8
-rw-r--r--src/libudev/libudev-util.c14
-rw-r--r--src/locale/localectl.c5
-rw-r--r--src/login/loginctl.c40
-rw-r--r--src/login/logind-dbus.c10
-rw-r--r--src/login/logind-seat.c2
-rw-r--r--src/login/logind-session.c9
-rw-r--r--src/machine/machinectl.c37
-rw-r--r--src/mount/mount-tool.c101
-rw-r--r--src/network/networkd-address.c4
-rw-r--r--src/network/networkd-ipv6-proxy-ndp.c209
-rw-r--r--src/network/networkd-ipv6-proxy-ndp.h44
-rw-r--r--src/network/networkd-link.c5
-rw-r--r--src/network/networkd-network-gperf.gperf1
-rw-r--r--src/network/networkd-network.c6
-rw-r--r--src/network/networkd-network.h3
-rw-r--r--src/nspawn/nspawn-gperf.gperf1
-rw-r--r--src/nspawn/nspawn-mount.c113
-rw-r--r--src/nspawn/nspawn-mount.h3
-rw-r--r--src/nspawn/nspawn-patch-uid.c2
-rw-r--r--src/nspawn/nspawn-settings.c30
-rw-r--r--src/nspawn/nspawn-settings.h6
-rw-r--r--src/nspawn/nspawn-stub-pid1.c2
-rw-r--r--src/nspawn/nspawn.c103
-rw-r--r--src/nss-myhostname/nss-myhostname.c11
-rw-r--r--src/nss-resolve/nss-resolve.c16
-rw-r--r--src/reply-password/reply-password.c2
-rw-r--r--src/resolve/resolve-tool.c8
-rw-r--r--src/resolve/resolved-bus.c8
-rw-r--r--src/resolve/resolved-dns-query.c10
-rw-r--r--src/resolve/resolved-dns-query.h5
-rw-r--r--src/resolve/resolved-dns-server.c19
-rw-r--r--src/resolve/resolved-dns-server.h2
-rw-r--r--src/resolve/resolved-dns-stub.c128
-rw-r--r--src/resolve/resolved-dns-transaction.c11
-rw-r--r--src/resolve/resolved-link-bus.c2
-rw-r--r--src/resolve/resolved-manager.c49
-rw-r--r--src/resolve/resolved-manager.h1
-rw-r--r--src/run/run.c81
-rw-r--r--src/shared/ask-password-api.c10
-rw-r--r--src/shared/bus-unit-util.c14
-rw-r--r--src/shared/bus-util.c15
-rw-r--r--src/shared/bus-util.h6
-rw-r--r--src/shared/cgroup-show.c86
-rw-r--r--src/shared/cgroup-show.h11
-rw-r--r--src/shared/clean-ipc.c4
-rw-r--r--src/shared/dissect-image.c137
-rw-r--r--src/shared/dissect-image.h2
-rw-r--r--src/shared/dropin.c112
-rw-r--r--src/shared/dropin.h37
-rw-r--r--src/shared/gpt.h2
-rw-r--r--src/shared/install.c212
-rw-r--r--src/shared/logs-show.c5
-rw-r--r--src/shared/path-lookup.c65
-rw-r--r--src/shared/ptyfwd.c30
-rw-r--r--src/shared/ptyfwd.h2
-rw-r--r--src/shared/seccomp-util.c236
-rw-r--r--src/shared/seccomp-util.h22
-rw-r--r--src/system-update-generator/system-update-generator.c25
-rw-r--r--src/systemctl/systemctl.c71
-rw-r--r--src/test/test-af-list.c1
-rw-r--r--src/test/test-arphrd-list.c1
-rw-r--r--src/test/test-calendarspec.c1
-rw-r--r--src/test/test-date.c10
-rw-r--r--src/test/test-execute.c5
-rw-r--r--src/test/test-fs-util.c18
-rw-r--r--src/test/test-install-root.c23
-rw-r--r--src/test/test-ns.c2
-rw-r--r--src/test/test-nss.c2
-rw-r--r--src/test/test-seccomp.c31
-rw-r--r--src/test/test-string-util.c35
-rw-r--r--src/test/test-time.c28
-rw-r--r--src/timedate/timedatectl.c5
-rw-r--r--src/tmpfiles/tmpfiles.c6
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c4
-rw-r--r--src/udev/udev-event.c449
-rw-r--r--src/veritysetup/veritysetup-generator.c4
148 files changed, 3166 insertions, 1314 deletions
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index 51d881c5fb..a9402fdb28 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -461,6 +461,7 @@ static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
"org.freedesktop.hostname1",
"/org/freedesktop/hostname1",
hostname_map,
+ &error,
host);
if (r < 0)
log_debug_errno(r, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error, r));
@@ -469,6 +470,7 @@ static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
manager_map,
+ &error,
host);
if (r < 0)
return log_error_errno(r, "Failed to get host information from systemd: %s", bus_error_message(&error, r));
diff --git a/src/basic/MurmurHash2.c b/src/basic/MurmurHash2.c
index 9020793930..a282a21201 100644
--- a/src/basic/MurmurHash2.c
+++ b/src/basic/MurmurHash2.c
@@ -69,9 +69,9 @@ uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed )
switch(len)
{
- case 3: h ^= data[2] << 16;
- case 2: h ^= data[1] << 8;
- case 1: h ^= data[0];
+ case 3: h ^= data[2] << 16; /* fall through */
+ case 2: h ^= data[1] << 8; /* fall through */
+ case 1: h ^= data[0]; /* fall through */
h *= m;
};
diff --git a/src/basic/architecture.c b/src/basic/architecture.c
index b74dc0db78..5a3dc08a4a 100644
--- a/src/basic/architecture.c
+++ b/src/basic/architecture.c
@@ -123,7 +123,8 @@ int uname_architecture(void) {
{ "crisv32", ARCHITECTURE_CRIS },
#elif defined(__nios2__)
{ "nios2", ARCHITECTURE_NIOS2 },
-#elif defined(__riscv__)
+#elif defined(__riscv__) || defined(__riscv)
+ /* __riscv__ is obsolete, remove in 2018 */
{ "riscv32", ARCHITECTURE_RISCV32 },
{ "riscv64", ARCHITECTURE_RISCV64 },
# if __SIZEOF_POINTER__ == 4
diff --git a/src/basic/architecture.h b/src/basic/architecture.h
index b329df2f6d..d6b8603b06 100644
--- a/src/basic/architecture.h
+++ b/src/basic/architecture.h
@@ -194,7 +194,8 @@ int uname_architecture(void);
#elif defined(__nios2__)
# define native_architecture() ARCHITECTURE_NIOS2
# define LIB_ARCH_TUPLE "nios2-linux-gnu"
-#elif defined(__riscv__)
+#elif defined(__riscv__) || defined(__riscv)
+ /* __riscv__ is obsolete, remove in 2018 */
# if __SIZEOF_POINTER__ == 4
# define native_architecture() ARCHITECTURE_RISCV32
# define LIB_ARCH_TUPLE "riscv32-linux-gnu"
diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c
index 2e5622699d..2323eb8555 100644
--- a/src/basic/calendarspec.c
+++ b/src/basic/calendarspec.c
@@ -116,8 +116,7 @@ static void normalize_chain(CalendarComponent **c) {
/* Drop non-unique entries */
for (k = n-1; k > 0; k--) {
- if (b[k-1]->start == next->start &&
- b[k-1]->repeat == next->repeat) {
+ if (component_compare(&b[k-1], &next) == 0) {
free(b[k-1]);
continue;
}
@@ -1020,7 +1019,7 @@ static int find_end_of_month(struct tm *tm, bool utc, int day) {
t.tm_mon++;
t.tm_mday = 1 - day;
- if (mktime_or_timegm(&t, utc) == (time_t) -1 ||
+ if (mktime_or_timegm(&t, utc) < 0 ||
t.tm_mon != tm->tm_mon)
return -1;
@@ -1086,7 +1085,7 @@ static bool tm_out_of_bounds(const struct tm *tm, bool utc) {
t = *tm;
- if (mktime_or_timegm(&t, utc) == (time_t) -1)
+ if (mktime_or_timegm(&t, utc) < 0)
return true;
/*
@@ -1115,7 +1114,7 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
return true;
t = *tm;
- if (mktime_or_timegm(&t, utc) == (time_t) -1)
+ if (mktime_or_timegm(&t, utc) < 0)
return false;
k = t.tm_wday == 0 ? 6 : t.tm_wday - 1;
@@ -1228,6 +1227,9 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
assert(spec);
assert(next);
+ if (usec > USEC_TIMESTAMP_FORMATTABLE_MAX)
+ return -EINVAL;
+
usec++;
t = (time_t) (usec / USEC_PER_SEC);
assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
@@ -1238,7 +1240,7 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
return r;
t = mktime_or_timegm(&tm, spec->utc);
- if (t == (time_t) -1)
+ if (t < 0)
return -EINVAL;
*next = (usec_t) t * USEC_PER_SEC + tm_usec;
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index d2d18f13f0..6948ed3931 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -182,8 +182,7 @@ int cg_read_subgroup(DIR *d, char **fn) {
if (de->d_type != DT_DIR)
continue;
- if (streq(de->d_name, ".") ||
- streq(de->d_name, ".."))
+ if (dot_or_dot_dot(de->d_name))
continue;
b = strdup(de->d_name);
diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c
index c0c22610d7..b5780194df 100644
--- a/src/basic/conf-files.c
+++ b/src/basic/conf-files.c
@@ -43,7 +43,6 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char
int r;
assert(path);
- assert(suffix);
dirpath = prefix_roota(root, path);
@@ -94,7 +93,6 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const
int r;
assert(strv);
- assert(suffix);
/* This alters the dirs string array */
if (!path_strv_resolve_uniq(dirs, root))
@@ -126,7 +124,6 @@ int conf_files_list_strv(char ***strv, const char *suffix, const char *root, con
_cleanup_strv_free_ char **copy = NULL;
assert(strv);
- assert(suffix);
copy = strv_copy((char**) dirs);
if (!copy)
diff --git a/src/basic/copy.c b/src/basic/copy.c
index 9883f5fa31..e9a7efd232 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -331,7 +331,7 @@ static int fd_copy_directory(
struct stat buf;
int q;
- if (STR_IN_SET(de->d_name, ".", ".."))
+ if (dot_or_dot_dot(de->d_name))
continue;
if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
diff --git a/src/basic/dirent-util.c b/src/basic/dirent-util.c
index 59067121b7..6b9d26773e 100644
--- a/src/basic/dirent-util.c
+++ b/src/basic/dirent-util.c
@@ -70,5 +70,8 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
if (de->d_name[0] == '.')
return false;
+ if (!suffix)
+ return true;
+
return endswith(de->d_name, suffix);
}
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index e31fa2711a..8fe19ee4e4 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -723,6 +723,8 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
return -errno;
if (S_ISLNK(st.st_mode)) {
+ char *joined;
+
_cleanup_free_ char *destination = NULL;
/* This is a symlink, in this case read the destination. But let's make sure we don't follow
@@ -746,9 +748,6 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fd < 0)
return -errno;
- free_and_replace(buffer, destination);
-
- todo = buffer;
free(done);
/* Note that we do not revalidate the root, we take it as is. */
@@ -760,19 +759,17 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
return -ENOMEM;
}
- } else {
- char *joined;
+ }
- /* A relative destination. If so, this is what we'll prefix what's left to do with what
- * we just read, and start the loop again, but remain in the current directory. */
+ /* Prefix what's left to do with what we just read, and start the loop again,
+ * but remain in the current directory. */
- joined = strjoin("/", destination, todo);
- if (!joined)
- return -ENOMEM;
+ joined = strjoin("/", destination, todo);
+ if (!joined)
+ return -ENOMEM;
- free(buffer);
- todo = buffer = joined;
- }
+ free(buffer);
+ todo = buffer = joined;
continue;
}
diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c
index 6843aedd0a..2d6e377f0a 100644
--- a/src/basic/hexdecoct.c
+++ b/src/basic/hexdecoct.c
@@ -72,10 +72,10 @@ int unhexchar(char c) {
}
char *hexmem(const void *p, size_t l) {
- char *r, *z;
const uint8_t *x;
+ char *r, *z;
- z = r = malloc(l * 2 + 1);
+ z = r = new(char, l * 2 + 1);
if (!r)
return NULL;
diff --git a/src/basic/khash.c b/src/basic/khash.c
index 9a2a3edb75..84648dc1c9 100644
--- a/src/basic/khash.c
+++ b/src/basic/khash.c
@@ -143,7 +143,7 @@ int khash_dup(khash *h, khash **ret) {
copy->fd = -1;
copy->algorithm = strdup(h->algorithm);
- if (!copy)
+ if (!copy->algorithm)
return -ENOMEM;
copy->fd = accept4(h->fd, NULL, 0, SOCK_CLOEXEC);
diff --git a/src/basic/nss-util.h b/src/basic/nss-util.h
index e7844fff96..9d927a8227 100644
--- a/src/basic/nss-util.h
+++ b/src/basic/nss-util.h
@@ -27,6 +27,10 @@
#define NSS_SIGNALS_BLOCK SIGALRM,SIGVTALRM,SIGPIPE,SIGCHLD,SIGTSTP,SIGIO,SIGHUP,SIGUSR1,SIGUSR2,SIGPROF,SIGURG,SIGWINCH
+#ifndef DEPRECATED_RES_USE_INET6
+# define DEPRECATED_RES_USE_INET6 0x00002000
+#endif
+
#define NSS_GETHOSTBYNAME_PROTOTYPES(module) \
enum nss_status _nss_##module##_gethostbyname4_r( \
const char *name, \
@@ -92,7 +96,7 @@ enum nss_status _nss_##module##_gethostbyname_r( \
int *errnop, int *h_errnop) { \
enum nss_status ret = NSS_STATUS_NOTFOUND; \
\
- if (_res.options & RES_USE_INET6) \
+ if (_res.options & DEPRECATED_RES_USE_INET6) \
ret = _nss_##module##_gethostbyname3_r( \
name, \
AF_INET6, \
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index 9a51e0d8bc..1313a52c9c 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -699,10 +699,7 @@ bool filename_is_valid(const char *p) {
if (isempty(p))
return false;
- if (streq(p, "."))
- return false;
-
- if (streq(p, ".."))
+ if (dot_or_dot_dot(p))
return false;
e = strchrnul(p, '/');
@@ -720,14 +717,17 @@ bool path_is_safe(const char *p) {
if (isempty(p))
return false;
- if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
+ if (dot_or_dot_dot(p))
+ return false;
+
+ if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
return false;
if (strlen(p)+1 > PATH_MAX)
return false;
/* The following two checks are not really dangerous, but hey, they still are confusing */
- if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
+ if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
return false;
if (strstr(p, "//"))
@@ -892,3 +892,16 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version)
return false;
}
+
+bool dot_or_dot_dot(const char *path) {
+ if (!path)
+ return false;
+ if (path[0] != '.')
+ return false;
+ if (path[1] == 0)
+ return true;
+ if (path[1] != '.')
+ return false;
+
+ return path[2] == 0;
+}
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index 349cdac7d6..35aef3adc8 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -141,3 +141,5 @@ bool is_device_path(const char *path);
bool is_deviceallow_pattern(const char *path);
int systemd_installation_has_version(const char *root, unsigned minimal_version);
+
+bool dot_or_dot_dot(const char *path);
diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c
index 6ecb6c3f0d..f703e7f145 100644
--- a/src/basic/proc-cmdline.c
+++ b/src/basic/proc-cmdline.c
@@ -148,7 +148,7 @@ int proc_cmdline_get_key(const char *key, unsigned flags, char **value) {
* 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
+ * b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if 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.
*
diff --git a/src/basic/raw-clone.h b/src/basic/raw-clone.h
index d473828999..c6e531ada4 100644
--- a/src/basic/raw-clone.h
+++ b/src/basic/raw-clone.h
@@ -47,8 +47,8 @@
static inline int raw_clone(unsigned long flags) {
assert((flags & (CLONE_VM|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID|
CLONE_CHILD_CLEARTID|CLONE_SETTLS)) == 0);
-#if defined(__s390__) || defined(__CRIS__)
- /* On s390 and cris the order of the first and second arguments
+#if defined(__s390x__) || defined(__s390__) || defined(__CRIS__)
+ /* On s390/s390x and cris the order of the first and second arguments
* of the raw clone() system call is reversed. */
return (int) syscall(__NR_clone, NULL, flags);
#elif defined(__sparc__) && defined(__arch64__)
diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
index 07d42f78dd..08497af729 100644
--- a/src/basic/rm-rf.c
+++ b/src/basic/rm-rf.c
@@ -83,7 +83,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
bool is_dir;
struct stat st;
- if (streq(de->d_name, ".") || streq(de->d_name, ".."))
+ if (dot_or_dot_dot(de->d_name))
continue;
if (de->d_type == DT_UNKNOWN ||
diff --git a/src/basic/siphash24.c b/src/basic/siphash24.c
index 8c1cdc3db6..4bb41786c8 100644
--- a/src/basic/siphash24.c
+++ b/src/basic/siphash24.c
@@ -127,18 +127,25 @@ void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) {
switch (left) {
case 7:
state->padding |= ((uint64_t) in[6]) << 48;
+ /* fall through */
case 6:
state->padding |= ((uint64_t) in[5]) << 40;
+ /* fall through */
case 5:
state->padding |= ((uint64_t) in[4]) << 32;
+ /* fall through */
case 4:
state->padding |= ((uint64_t) in[3]) << 24;
+ /* fall through */
case 3:
state->padding |= ((uint64_t) in[2]) << 16;
+ /* fall through */
case 2:
state->padding |= ((uint64_t) in[1]) << 8;
+ /* fall through */
case 1:
state->padding |= ((uint64_t) in[0]);
+ /* fall through */
case 0:
break;
}
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index 77f81a60ba..17e90a8994 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -877,7 +877,7 @@ bool ifname_valid(const char *p) {
if (strlen(p) >= IFNAMSIZ)
return false;
- if (STR_IN_SET(p, ".", ".."))
+ if (dot_or_dot_dot(p))
return false;
while (*p) {
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
index 2ba3604ba0..9d2f4bc8f9 100644
--- a/src/basic/string-util.c
+++ b/src/basic/string-util.c
@@ -821,6 +821,7 @@ int free_and_strdup(char **p, const char *s) {
return 1;
}
+#if !HAVE_DECL_EXPLICIT_BZERO
/*
* Pointer to memset is volatile so that compiler must de-reference
* the pointer and can't assume that it points to any function in
@@ -831,19 +832,19 @@ typedef void *(*memset_t)(void *,int,size_t);
static volatile memset_t memset_func = memset;
-void* memory_erase(void *p, size_t l) {
- return memset_func(p, 'x', l);
+void explicit_bzero(void *p, size_t l) {
+ memset_func(p, '\0', l);
}
+#endif
char* string_erase(char *x) {
-
if (!x)
return NULL;
/* A delicious drop of snake-oil! To be called on memory where
* we stored passphrases or so, after we used them. */
-
- return memory_erase(x, strlen(x));
+ explicit_bzero(x, strlen(x));
+ return x;
}
char *string_free_erase(char *s) {
diff --git a/src/basic/string-util.h b/src/basic/string-util.h
index e99f7964be..be44dedff4 100644
--- a/src/basic/string-util.h
+++ b/src/basic/string-util.h
@@ -189,7 +189,10 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const
return memmem(haystack, haystacklen, needle, needlelen);
}
-void* memory_erase(void *p, size_t l);
+#if !HAVE_DECL_EXPLICIT_BZERO
+void explicit_bzero(void *p, size_t l);
+#endif
+
char *string_erase(char *x);
char *string_free_erase(char *s);
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index 7a5b29d77e..a0db97c41a 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -185,7 +185,7 @@ usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) {
usec_t timespec_load(const struct timespec *ts) {
assert(ts);
- if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1)
+ if (ts->tv_sec < 0 || ts->tv_nsec < 0)
return USEC_INFINITY;
if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
@@ -199,7 +199,7 @@ usec_t timespec_load(const struct timespec *ts) {
nsec_t timespec_load_nsec(const struct timespec *ts) {
assert(ts);
- if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1)
+ if (ts->tv_sec < 0 || ts->tv_nsec < 0)
return NSEC_INFINITY;
if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC)
@@ -211,7 +211,8 @@ nsec_t timespec_load_nsec(const struct timespec *ts) {
struct timespec *timespec_store(struct timespec *ts, usec_t u) {
assert(ts);
- if (u == USEC_INFINITY) {
+ if (u == USEC_INFINITY ||
+ u / USEC_PER_SEC >= TIME_T_MAX) {
ts->tv_sec = (time_t) -1;
ts->tv_nsec = (long) -1;
return ts;
@@ -226,8 +227,7 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u) {
usec_t timeval_load(const struct timeval *tv) {
assert(tv);
- if (tv->tv_sec == (time_t) -1 &&
- tv->tv_usec == (suseconds_t) -1)
+ if (tv->tv_sec < 0 || tv->tv_usec < 0)
return USEC_INFINITY;
if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
@@ -241,7 +241,8 @@ usec_t timeval_load(const struct timeval *tv) {
struct timeval *timeval_store(struct timeval *tv, usec_t u) {
assert(tv);
- if (u == USEC_INFINITY) {
+ if (u == USEC_INFINITY||
+ u / USEC_PER_SEC > TIME_T_MAX) {
tv->tv_sec = (time_t) -1;
tv->tv_usec = (suseconds_t) -1;
} else {
@@ -288,9 +289,11 @@ static char *format_timestamp_internal(
if (t <= 0 || t == USEC_INFINITY)
return NULL; /* Timestamp is unset */
+ /* Let's not format times with years > 9999 */
+ if (t > USEC_TIMESTAMP_FORMATTABLE_MAX)
+ return NULL;
+
sec = (time_t) (t / USEC_PER_SEC); /* Round down */
- if ((usec_t) sec != (t / USEC_PER_SEC))
- return NULL; /* overflow? */
if (!localtime_or_gmtime_r(&sec, &tm, utc))
return NULL;
@@ -551,12 +554,12 @@ void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
}
int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
- unsigned long long a, b;
+ uint64_t a, b;
assert(value);
assert(t);
- if (sscanf(value, "%llu %llu", &a, &b) != 2) {
+ if (sscanf(value, "%" PRIu64 "%" PRIu64, &a, &b) != 2) {
log_debug("Failed to parse dual timestamp value \"%s\": %m", value);
return -EINVAL;
}
@@ -830,16 +833,23 @@ parse_usec:
from_tm:
x = mktime_or_timegm(&tm, utc);
- if (x == (time_t) -1)
+ if (x < 0)
return -EINVAL;
if (weekday >= 0 && tm.tm_wday != weekday)
return -EINVAL;
ret = (usec_t) x * USEC_PER_SEC + x_usec;
+ if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
+ return -EINVAL;
finish:
+ if (ret + plus < ret) /* overflow? */
+ return -EINVAL;
ret += plus;
+ if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
+ return -EINVAL;
+
if (ret > minus)
ret -= minus;
else
@@ -1271,7 +1281,7 @@ bool clock_supported(clockid_t clock) {
if (!clock_boottime_supported())
return false;
- /* fall through, after checking the cached value for CLOCK_BOOTTIME. */
+ /* fall through */
default:
/* For everything else, check properly */
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
index f67a4474ed..7463507f51 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -181,3 +181,14 @@ static inline usec_t usec_sub(usec_t timestamp, int64_t delta) {
return timestamp - delta;
}
+
+#if SIZEOF_TIME_T == 8
+/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit year
+ * territory. However, since we want to stay away from this in all timezones we take one day off. */
+#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 253402214399000000)
+#elif SIZEOF_TIME_T == 4
+/* With a 32bit time_t we can't go beyond 2038... */
+#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000)
+#else
+#error "Yuck, time_t is neither 4 not 8 bytes wide?"
+#endif
diff --git a/src/basic/virt.c b/src/basic/virt.c
index 33641e6886..03ce71a728 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -49,7 +49,7 @@ static int detect_vm_cpuid(void) {
{ "KVMKVMKVM", VIRTUALIZATION_KVM },
/* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
{ "VMwareVMware", VIRTUALIZATION_VMWARE },
- /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
+ /* https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs */
{ "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
/* https://wiki.freebsd.org/bhyve */
{ "bhyve bhyve ", VIRTUALIZATION_BHYVE },
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 44ea6215dc..7cc54a8cdd 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -1647,7 +1647,7 @@ static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, con
loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16);
#ifdef SD_BOOT_LOG_TPM
- /* Try to log any options to the TPM, escpecially to catch manually edited options */
+ /* Try to log any options to the TPM, especially to catch manually edited options */
err = tpm_log_event(SD_TPM_PCR,
(EFI_PHYSICAL_ADDRESS) loaded_image->LoadOptions,
loaded_image->LoadOptionsSize, loaded_image->LoadOptions);
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index 1e250f34f4..7c1ffb1bca 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -100,7 +100,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
cmdline = line;
#ifdef SD_BOOT_LOG_TPM
- /* Try to log any options to the TPM, escpecially manually edited options */
+ /* Try to log any options to the TPM, especially manually edited options */
err = tpm_log_event(SD_TPM_PCR,
(EFI_PHYSICAL_ADDRESS) loaded_image->LoadOptions,
loaded_image->LoadOptionsSize, loaded_image->LoadOptions);
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c
index b55aa86a40..5574c14555 100644
--- a/src/cgls/cgls.c
+++ b/src/cgls/cgls.c
@@ -26,7 +26,6 @@
#include "sd-bus.h"
#include "alloc-util.h"
-#include "bus-error.h"
#include "bus-util.h"
#include "cgroup-show.h"
#include "cgroup-util.h"
@@ -35,12 +34,21 @@
#include "output-mode.h"
#include "pager.h"
#include "path-util.h"
+#include "strv.h"
#include "unit-name.h"
#include "util.h"
static bool arg_no_pager = false;
static bool arg_kernel_threads = false;
static bool arg_all = false;
+
+static enum {
+ SHOW_UNIT_NONE,
+ SHOW_UNIT_SYSTEM,
+ SHOW_UNIT_USER,
+} arg_show_unit = SHOW_UNIT_NONE;
+static char **arg_names = NULL;
+
static int arg_full = -1;
static char* arg_machine = NULL;
@@ -51,6 +59,8 @@ static void help(void) {
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
" -a --all Show all groups, including empty\n"
+ " -u --unit Show the subtrees of specifified system units\n"
+ " --user-unit Show the subtrees of specifified user units\n"
" -l --full Do not ellipsize output\n"
" -k Include kernel threads in output\n"
" -M --machine= Show container\n"
@@ -62,15 +72,18 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_NO_PAGER = 0x100,
ARG_VERSION,
+ ARG_USER_UNIT,
};
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "no-pager", no_argument, NULL, ARG_NO_PAGER },
- { "all", no_argument, NULL, 'a' },
- { "full", no_argument, NULL, 'l' },
- { "machine", required_argument, NULL, 'M' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "all", no_argument, NULL, 'a' },
+ { "full", no_argument, NULL, 'l' },
+ { "machine", required_argument, NULL, 'M' },
+ { "unit", optional_argument, NULL, 'u' },
+ { "user-unit", optional_argument, NULL, ARG_USER_UNIT },
{}
};
@@ -79,7 +92,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 1);
assert(argv);
- while ((c = getopt_long(argc, argv, "hkalM:", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "-hkalM:u::", options, NULL)) >= 0)
switch (c) {
@@ -98,6 +111,24 @@ static int parse_argv(int argc, char *argv[]) {
arg_all = true;
break;
+ case 'u':
+ arg_show_unit = SHOW_UNIT_SYSTEM;
+ if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */
+ return log_oom();
+ break;
+
+ case ARG_USER_UNIT:
+ arg_show_unit = SHOW_UNIT_USER;
+ if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */
+ return log_oom();
+ break;
+
+ case 1:
+ /* positional argument */
+ if (strv_push(&arg_names, optarg) < 0)
+ return log_oom();
+ break;
+
case 'l':
arg_full = true;
break;
@@ -117,51 +148,12 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- return 1;
-}
-
-static int get_cgroup_root(char **ret) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_free_ char *unit = NULL, *path = NULL;
- const char *m;
- int r;
-
- if (!arg_machine) {
- r = cg_get_root_path(ret);
- if (r == -ENOMEDIUM)
- return log_error_errno(r, "Failed to get root control group path: No cgroup filesystem mounted on /sys/fs/cgroup");
- else if (r < 0)
- return log_error_errno(r, "Failed to get root control group path: %m");
-
- return 0;
+ if (arg_machine && arg_show_unit != SHOW_UNIT_NONE) {
+ log_error("Cannot combine --unit or --user-unit with --machine=.");
+ return -EINVAL;
}
- m = strjoina("/run/systemd/machines/", arg_machine);
- r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to load machine data: %m");
-
- path = unit_dbus_path_from_name(unit);
- if (!path)
- return log_oom();
-
- r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
- if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
-
- r = sd_bus_get_property_string(
- bus,
- "org.freedesktop.systemd1",
- path,
- unit_dbus_interface_from_name(unit),
- "ControlGroup",
- &error,
- ret);
- if (r < 0)
- return log_error_errno(r, "Failed to query unit control group path: %s", bus_error_message(&error, r));
-
- return 0;
+ return 1;
}
static void show_cg_info(const char *controller, const char *path) {
@@ -194,31 +186,65 @@ int main(int argc, char *argv[]) {
(arg_full > 0) * OUTPUT_FULL_WIDTH |
arg_kernel_threads * OUTPUT_KERNEL_THREADS;
- if (optind < argc) {
+ if (arg_names) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *root = NULL;
- int i;
+ char **name;
- r = get_cgroup_root(&root);
- if (r < 0)
- goto finish;
-
- for (i = optind; i < argc; i++) {
+ STRV_FOREACH(name, arg_names) {
int q;
- if (path_startswith(argv[i], "/sys/fs/cgroup")) {
+ if (arg_show_unit != SHOW_UNIT_NONE) {
+ /* Command line arguments are unit names */
+ _cleanup_free_ char *cgroup = NULL;
+
+ if (!bus) {
+ /* Connect to the bus only if necessary */
+ r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL,
+ arg_show_unit == SHOW_UNIT_USER,
+ &bus);
+ if (r < 0) {
+ log_error_errno(r, "Failed to create bus connection: %m");
+ goto finish;
+ }
+ }
+
+ q = show_cgroup_get_unit_path_and_warn(bus, *name, &cgroup);
+ if (q < 0)
+ goto failed;
+
+ if (isempty(cgroup)) {
+ log_warning("Unit %s not found.", *name);
+ q = -ENOENT;
+ goto failed;
+ }
+
+ printf("Unit %s (%s):\n", *name, cgroup);
+ fflush(stdout);
+
+ q = show_cgroup_by_path(cgroup, NULL, 0, output_flags);
- printf("Directory %s:\n", argv[i]);
+ } else if (path_startswith(*name, "/sys/fs/cgroup")) {
+
+ printf("Directory %s:\n", *name);
fflush(stdout);
- q = show_cgroup_by_path(argv[i], NULL, 0, output_flags);
+ q = show_cgroup_by_path(*name, NULL, 0, output_flags);
} else {
_cleanup_free_ char *c = NULL, *p = NULL, *j = NULL;
const char *controller, *path;
- r = cg_split_spec(argv[i], &c, &p);
- if (r < 0) {
- log_error_errno(r, "Failed to split argument %s: %m", argv[i]);
- goto finish;
+ if (!root) {
+ /* Query root only if needed, treat error as fatal */
+ r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
+ if (r < 0)
+ goto finish;
+ }
+
+ q = cg_split_spec(*name, &c, &p);
+ if (q < 0) {
+ log_error_errno(q, "Failed to split argument %s: %m", *name);
+ goto failed;
}
controller = c ?: SYSTEMD_CGROUP_CONTROLLER;
@@ -239,7 +265,8 @@ int main(int argc, char *argv[]) {
q = show_cgroup(controller, path, NULL, 0, output_flags);
}
- if (q < 0)
+ failed:
+ if (q < 0 && r >= 0)
r = q;
}
@@ -267,7 +294,7 @@ int main(int argc, char *argv[]) {
if (!done) {
_cleanup_free_ char *root = NULL;
- r = get_cgroup_root(&root);
+ r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
if (r < 0)
goto finish;
@@ -283,6 +310,7 @@ int main(int argc, char *argv[]) {
finish:
pager_close();
+ free(arg_names); /* don't free the strings */
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index af5c3d8695..50ac6a58b0 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -31,6 +31,7 @@
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-util.h"
+#include "cgroup-show.h"
#include "cgroup-util.h"
#include "fd-util.h"
#include "fileio.h"
@@ -862,13 +863,9 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (optind == argc-1) {
- if (arg_machine) {
- log_error("Specifying a control group path together with the -M option is not allowed");
- return -EINVAL;
- }
+ if (optind == argc - 1)
arg_root = argv[optind];
- } else if (optind < argc) {
+ else if (optind < argc) {
log_error("Too many arguments.");
return -EINVAL;
}
@@ -890,59 +887,6 @@ static const char* counting_what(void) {
return "userspace processes (excl. kernel)";
}
-static int get_cgroup_root(char **ret) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_free_ char *unit = NULL, *path = NULL;
- const char *m;
- int r;
-
- if (arg_root) {
- char *aux;
-
- aux = strdup(arg_root);
- if (!aux)
- return log_oom();
-
- *ret = aux;
- return 0;
- }
-
- if (!arg_machine) {
- r = cg_get_root_path(ret);
- if (r < 0)
- return log_error_errno(r, "Failed to get root control group path: %m");
-
- return 0;
- }
-
- m = strjoina("/run/systemd/machines/", arg_machine);
- r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to load machine data: %m");
-
- path = unit_dbus_path_from_name(unit);
- if (!path)
- return log_oom();
-
- r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
- if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
-
- r = sd_bus_get_property_string(
- bus,
- "org.freedesktop.systemd1",
- path,
- unit_dbus_interface_from_name(unit),
- "ControlGroup",
- &error,
- ret);
- if (r < 0)
- return log_error_errno(r, "Failed to query unit control group path: %s", bus_error_message(&error, r));
-
- return 0;
-}
-
int main(int argc, char *argv[]) {
int r;
Hashmap *a = NULL, *b = NULL;
@@ -967,11 +911,12 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto finish;
- r = get_cgroup_root(&root);
+ r = show_cgroup_get_path_and_warn(arg_machine, arg_root, &root);
if (r < 0) {
log_error_errno(r, "Failed to get root control group path: %m");
goto finish;
- }
+ } else
+ log_debug("Cgroup path: %s", root);
a = hashmap_new(&string_hash_ops);
b = hashmap_new(&string_hash_ops);
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index 9960b7a811..7df4cab3f6 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -758,6 +758,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("LimitRTTIMESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RootImage", "s", NULL, offsetof(ExecContext, root_image), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Nice", "i", property_get_nice, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IOScheduling", "i", property_get_ioprio, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -828,6 +829,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("RestrictNamespaces", "t", bus_property_get_ulong, offsetof(ExecContext, restrict_namespaces), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("BindPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("BindReadOnlyPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("MountAPIVFS", "b", bus_property_get_bool, offsetof(ExecContext, mount_apivfs), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END
};
@@ -1047,7 +1049,7 @@ int bus_exec_context_set_transient_property(
return 1;
- } else if (STR_IN_SET(name, "TTYPath", "RootDirectory")) {
+ } else if (STR_IN_SET(name, "TTYPath", "RootDirectory", "RootImage")) {
const char *s;
r = sd_bus_message_read(message, "s", &s);
@@ -1060,6 +1062,8 @@ int bus_exec_context_set_transient_property(
if (mode != UNIT_CHECK) {
if (streq(name, "TTYPath"))
r = free_and_strdup(&c->tty_path, s);
+ else if (streq(name, "RootImage"))
+ r = free_and_strdup(&c->root_image, s);
else {
assert(streq(name, "RootDirectory"));
r = free_and_strdup(&c->root_directory, s);
@@ -1207,7 +1211,7 @@ int bus_exec_context_set_transient_property(
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
"NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute",
"RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables",
- "ProtectKernelModules", "ProtectControlGroups")) {
+ "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS")) {
int b;
r = sd_bus_message_read(message, "b", &b);
@@ -1247,6 +1251,8 @@ int bus_exec_context_set_transient_property(
c->protect_kernel_modules = b;
else if (streq(name, "ProtectControlGroups"))
c->protect_control_groups = b;
+ else if (streq(name, "MountAPIVFS"))
+ c->mount_apivfs = b;
unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, yes_no(b));
}
@@ -1382,7 +1388,7 @@ int bus_exec_context_set_transient_property(
_cleanup_free_ char *joined = NULL;
_cleanup_fclose_ FILE *f = NULL;
- _cleanup_free_ char **l = NULL;
+ _cleanup_strv_free_ char **l = NULL;
size_t size = 0;
char **i;
@@ -1495,12 +1501,15 @@ int bus_exec_context_set_transient_property(
return r;
STRV_FOREACH(p, l) {
- int offset;
- if (!utf8_is_valid(*p))
+ const char *i = *p;
+ size_t offset;
+
+ if (!utf8_is_valid(i))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name);
- offset = **p == '-';
- if (!path_is_absolute(*p + offset))
+ offset = i[0] == '-';
+ offset += i[offset] == '+';
+ if (!path_is_absolute(i + offset))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name);
}
@@ -1519,7 +1528,6 @@ int bus_exec_context_set_transient_property(
unit_write_drop_in_private_format(u, mode, name, "%s=", name);
} else {
r = strv_extend_strv(dirs, l, true);
-
if (r < 0)
return -ENOMEM;
@@ -1698,7 +1706,7 @@ int bus_exec_context_set_transient_property(
if (!path_is_absolute(source))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path %s is not absolute.", source);
if (!path_is_absolute(destination))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not absolute.", source);
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not absolute.", destination);
if (!IN_SET(mount_flags, 0, MS_REC))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mount flags.");
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 9876251438..f87b52a266 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -19,6 +19,7 @@
#include <errno.h>
#include <sys/prctl.h>
+#include <sys/statvfs.h>
#include <unistd.h>
#include "alloc-util.h"
@@ -38,6 +39,7 @@
#include "fs-util.h"
#include "install.h"
#include "log.h"
+#include "parse-util.h"
#include "path-util.h"
#include "selinux-access.h"
#include "stat-util.h"
@@ -48,6 +50,10 @@
#include "virt.h"
#include "watchdog.h"
+/* Require 16MiB free in /run/systemd for reloading/reexecing. After all we need to serialize our state there, and if
+ * we can't we'll fail badly. */
+#define RELOAD_DISK_SPACE_MIN (UINT64_C(16) * UINT64_C(1024) * UINT64_C(1024))
+
static UnitFileFlags unit_file_bools_to_flags(bool runtime, bool force) {
return (runtime ? UNIT_FILE_RUNTIME : 0) |
(force ? UNIT_FILE_FORCE : 0);
@@ -842,13 +848,9 @@ static int method_get_unit_processes(sd_bus_message *message, void *userdata, sd
if (r < 0)
return r;
- r = manager_load_unit(m, name, NULL, error, &u);
- if (r < 0)
- return r;
-
- r = bus_unit_check_load_state(u, error);
- if (r < 0)
- return r;
+ u = manager_get_unit(m, name);
+ if (!u)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", name);
return bus_unit_method_get_processes(message, u, error);
}
@@ -1312,6 +1314,40 @@ static int method_refuse_snapshot(sd_bus_message *message, void *userdata, sd_bu
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for snapshots has been removed.");
}
+static int verify_run_space(const char *message, sd_bus_error *error) {
+ struct statvfs svfs;
+ uint64_t available;
+
+ if (statvfs("/run/systemd", &svfs) < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to statvfs(/run/systemd): %m");
+
+ available = (uint64_t) svfs.f_bfree * (uint64_t) svfs.f_bsize;
+
+ if (available < RELOAD_DISK_SPACE_MIN) {
+ char fb_available[FORMAT_BYTES_MAX], fb_need[FORMAT_BYTES_MAX];
+ return sd_bus_error_setf(error,
+ BUS_ERROR_DISK_FULL,
+ "%s, not enough space available on /run/systemd. "
+ "Currently, %s are free, but a safety buffer of %s is enforced.",
+ message,
+ format_bytes(fb_available, sizeof(fb_available), available),
+ format_bytes(fb_need, sizeof(fb_need), RELOAD_DISK_SPACE_MIN));
+ }
+
+ return 0;
+}
+
+int verify_run_space_and_log(const char *message) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ r = verify_run_space(message, &error);
+ if (r < 0)
+ log_error_errno(r, "%s", bus_error_message(&error, r));
+
+ return r;
+}
+
static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
int r;
@@ -1319,6 +1355,10 @@ static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *
assert(message);
assert(m);
+ r = verify_run_space("Refusing to reload", error);
+ if (r < 0)
+ return r;
+
r = mac_selinux_access_check(message, "reload", error);
if (r < 0)
return r;
@@ -1351,6 +1391,10 @@ static int method_reexecute(sd_bus_message *message, void *userdata, sd_bus_erro
assert(message);
assert(m);
+ r = verify_run_space("Refusing to reexecute", error);
+ if (r < 0)
+ return r;
+
r = mac_selinux_access_check(message, "reload", error);
if (r < 0)
return r;
@@ -1469,11 +1513,26 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er
char *ri = NULL, *rt = NULL;
const char *root, *init;
Manager *m = userdata;
+ struct statvfs svfs;
+ uint64_t available;
int r;
assert(message);
assert(m);
+ if (statvfs("/run/systemd", &svfs) < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to statvfs(/run/systemd): %m");
+
+ available = (uint64_t) svfs.f_bfree * (uint64_t) svfs.f_bsize;
+
+ if (available < RELOAD_DISK_SPACE_MIN) {
+ char fb_available[FORMAT_BYTES_MAX], fb_need[FORMAT_BYTES_MAX];
+ log_warning("Dangerously low amount of free space on /run/systemd, root switching operation might not complete successfuly. "
+ "Currently, %s are free, but %s are suggested. Proceeding anyway.",
+ format_bytes(fb_available, sizeof(fb_available), available),
+ format_bytes(fb_need, sizeof(fb_need), RELOAD_DISK_SPACE_MIN));
+ }
+
r = mac_selinux_access_check(message, "reboot", error);
if (r < 0)
return r;
@@ -1853,63 +1912,6 @@ static int send_unit_files_changed(sd_bus *bus, void *userdata) {
return sd_bus_send(bus, message, NULL);
}
-static int reply_unit_file_changes_and_free(
- Manager *m,
- sd_bus_message *message,
- int carries_install_info,
- UnitFileChange *changes,
- unsigned n_changes) {
-
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- unsigned i;
- int r;
-
- if (unit_file_changes_have_modification(changes, n_changes)) {
- r = bus_foreach_bus(m, NULL, send_unit_files_changed, NULL);
- if (r < 0)
- log_debug_errno(r, "Failed to send UnitFilesChanged signal: %m");
- }
-
- r = sd_bus_message_new_method_return(message, &reply);
- if (r < 0)
- goto fail;
-
- if (carries_install_info >= 0) {
- r = sd_bus_message_append(reply, "b", carries_install_info);
- if (r < 0)
- goto fail;
- }
-
- r = sd_bus_message_open_container(reply, 'a', "(sss)");
- if (r < 0)
- goto fail;
-
- for (i = 0; i < n_changes; i++)
- if (changes[i].type >= 0) {
- const char *change = unit_file_change_type_to_string(changes[i].type);
- assert(change != NULL);
-
- r = sd_bus_message_append(
- reply, "(sss)",
- change,
- changes[i].path,
- changes[i].source);
- if (r < 0)
- goto fail;
- }
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- goto fail;
-
- unit_file_changes_free(changes, n_changes);
- return sd_bus_send(NULL, reply, NULL);
-
-fail:
- unit_file_changes_free(changes, n_changes);
- return r;
-}
-
/* Create an error reply, using the error information from changes[]
* if possible, and fall back to generating an error from error code c.
* The error message only describes the first error.
@@ -1923,12 +1925,14 @@ static int install_error(
unsigned n_changes) {
int r;
unsigned i;
- assert(c < 0);
for (i = 0; i < n_changes; i++)
+
switch(changes[i].type) {
+
case 0 ... INT_MAX:
continue;
+
case -EEXIST:
if (changes[i].source)
r = sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS,
@@ -1939,29 +1943,106 @@ static int install_error(
"File %s already exists.",
changes[i].path);
goto found;
+
case -ERFKILL:
r = sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED,
"Unit file %s is masked.", changes[i].path);
goto found;
+
case -EADDRNOTAVAIL:
r = sd_bus_error_setf(error, BUS_ERROR_UNIT_GENERATED,
"Unit %s is transient or generated.", changes[i].path);
goto found;
+
case -ELOOP:
r = sd_bus_error_setf(error, BUS_ERROR_UNIT_LINKED,
"Refusing to operate on linked unit file %s", changes[i].path);
goto found;
+
+ case -ENOENT:
+ r = sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit file %s does not exist.", changes[i].path);
+ goto found;
+
default:
r = sd_bus_error_set_errnof(error, changes[i].type, "File %s: %m", changes[i].path);
goto found;
}
- r = c;
+ r = c < 0 ? c : -EINVAL;
+
found:
unit_file_changes_free(changes, n_changes);
return r;
}
+static int reply_unit_file_changes_and_free(
+ Manager *m,
+ sd_bus_message *message,
+ int carries_install_info,
+ UnitFileChange *changes,
+ unsigned n_changes,
+ sd_bus_error *error) {
+
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ bool bad = false, good = false;
+ unsigned i;
+ int r;
+
+ if (unit_file_changes_have_modification(changes, n_changes)) {
+ r = bus_foreach_bus(m, NULL, send_unit_files_changed, NULL);
+ if (r < 0)
+ log_debug_errno(r, "Failed to send UnitFilesChanged signal: %m");
+ }
+
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ goto fail;
+
+ if (carries_install_info >= 0) {
+ r = sd_bus_message_append(reply, "b", carries_install_info);
+ if (r < 0)
+ goto fail;
+ }
+
+ r = sd_bus_message_open_container(reply, 'a', "(sss)");
+ if (r < 0)
+ goto fail;
+
+ for (i = 0; i < n_changes; i++) {
+
+ if (changes[i].type < 0) {
+ bad = true;
+ continue;
+ }
+
+ r = sd_bus_message_append(
+ reply, "(sss)",
+ unit_file_change_type_to_string(changes[i].type),
+ changes[i].path,
+ changes[i].source);
+ if (r < 0)
+ goto fail;
+
+ good = true;
+ }
+
+ /* If there was a failed change, and no successful change, then return the first failure as proper method call
+ * error. */
+ if (bad && !good)
+ return install_error(error, 0, changes, n_changes);
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ goto fail;
+
+ unit_file_changes_free(changes, n_changes);
+ return sd_bus_send(NULL, reply, NULL);
+
+fail:
+ unit_file_changes_free(changes, n_changes);
+ return r;
+}
+
static int method_enable_unit_files_generic(
sd_bus_message *message,
Manager *m,
@@ -1998,7 +2079,7 @@ static int method_enable_unit_files_generic(
if (r < 0)
return install_error(error, r, changes, n_changes);
- return reply_unit_file_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes);
+ return reply_unit_file_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes, error);
}
static int method_enable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -2067,7 +2148,7 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use
if (r < 0)
return install_error(error, r, changes, n_changes);
- return reply_unit_file_changes_and_free(m, message, r, changes, n_changes);
+ return reply_unit_file_changes_and_free(m, message, r, changes, n_changes, error);
}
static int method_disable_unit_files_generic(
@@ -2102,7 +2183,7 @@ static int method_disable_unit_files_generic(
if (r < 0)
return install_error(error, r, changes, n_changes);
- return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
+ return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes, error);
}
static int method_disable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -2137,7 +2218,7 @@ static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_
if (r < 0)
return install_error(error, r, changes, n_changes);
- return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
+ return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes, error);
}
static int method_set_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -2168,7 +2249,7 @@ static int method_set_default_target(sd_bus_message *message, void *userdata, sd
if (r < 0)
return install_error(error, r, changes, n_changes);
- return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
+ return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes, error);
}
static int method_preset_all_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -2211,7 +2292,7 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata,
if (r < 0)
return install_error(error, r, changes, n_changes);
- return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
+ return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes, error);
}
static int method_add_dependency_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -2251,7 +2332,7 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd
if (r < 0)
return install_error(error, r, changes, n_changes);
- return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
+ return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes, error);
}
static int method_get_unit_file_links(sd_bus_message *message, void *userdata, sd_bus_error *error) {
diff --git a/src/core/dbus-manager.h b/src/core/dbus-manager.h
index 36a2e9481b..9f3222da28 100644
--- a/src/core/dbus-manager.h
+++ b/src/core/dbus-manager.h
@@ -26,3 +26,5 @@ extern const sd_bus_vtable bus_manager_vtable[];
void bus_manager_send_finished(Manager *m, usec_t firmware_usec, usec_t loader_usec, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, usec_t total_usec);
void bus_manager_send_reloading(Manager *m, bool active);
void bus_manager_send_change_signal(Manager *m);
+
+int verify_run_space_and_log(const char *message);
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 2adc1d9288..f1306a023f 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -1006,6 +1006,10 @@ int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bu
assert(message);
+ r = mac_selinux_unit_access_check(u, message, "status", error);
+ if (r < 0)
+ return r;
+
pids = set_new(NULL);
if (!pids)
return -ENOMEM;
@@ -1218,7 +1222,7 @@ int bus_unit_queue_job(
(type == JOB_STOP && u->refuse_manual_stop) ||
((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop)) ||
(type == JOB_RELOAD_OR_START && job_type_collapse(type, u) == JOB_START && u->refuse_manual_start))
- return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only.", u->id);
+ return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only (it is configured to refuse manual start/stop).", u->id);
r = manager_add_job(u->manager, type, u, mode, error, &j);
if (r < 0)
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 07ab21f199..0493e5786c 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -175,7 +175,7 @@ static int signal_activation_request(sd_bus_message *message, void *userdata, sd
goto failed;
if (u->refuse_manual_start) {
- r = sd_bus_error_setf(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, %s may be requested by dependency only.", u->id);
+ r = sd_bus_error_setf(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, %s may be requested by dependency only (it is configured to refuse manual start/stop).", u->id);
goto failed;
}
@@ -477,7 +477,7 @@ static int bus_kill_context_find(sd_bus *bus, const char *path, const char *inte
}
static int bus_job_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
- _cleanup_free_ char **l = NULL;
+ _cleanup_strv_free_ char **l = NULL;
Manager *m = userdata;
unsigned k = 0;
Iterator i;
@@ -504,7 +504,7 @@ static int bus_job_enumerate(sd_bus *bus, const char *path, void *userdata, char
}
static int bus_unit_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
- _cleanup_free_ char **l = NULL;
+ _cleanup_strv_free_ char **l = NULL;
Manager *m = userdata;
unsigned k = 0;
Iterator i;
@@ -1041,6 +1041,7 @@ int bus_init(Manager *m, bool try_bus_connect) {
static void destroy_bus(Manager *m, sd_bus **bus) {
Iterator i;
+ Unit *u;
Job *j;
assert(m);
@@ -1049,6 +1050,17 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
if (!*bus)
return;
+ /* Make sure all bus slots watching names are released. */
+ HASHMAP_FOREACH(u, m->watch_bus, i) {
+ if (!u->match_bus_slot)
+ continue;
+
+ if (sd_bus_slot_get_bus(u->match_bus_slot) != *bus)
+ continue;
+
+ u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
+ }
+
/* Get rid of tracked clients on this bus */
if (m->subscribed && sd_bus_track_get_bus(m->subscribed) == *bus)
m->subscribed = sd_bus_track_unref(m->subscribed);
diff --git a/src/core/execute.c b/src/core/execute.c
index 06a291fd39..4c2968f971 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -1643,6 +1643,9 @@ static bool exec_needs_mount_namespace(
assert(context);
assert(params);
+ if (context->root_image)
+ return true;
+
if (!strv_isempty(context->read_write_paths) ||
!strv_isempty(context->read_only_paths) ||
!strv_isempty(context->inaccessible_paths))
@@ -1665,6 +1668,9 @@ static bool exec_needs_mount_namespace(
context->protect_control_groups)
return true;
+ if (context->mount_apivfs)
+ return true;
+
return false;
}
@@ -1685,25 +1691,31 @@ static int setup_private_users(uid_t uid, gid_t gid) {
* child then writes the UID mapping, under full privileges. The parent waits for the child to finish and
* continues execution normally. */
- if (uid != 0 && uid_is_valid(uid))
- asprintf(&uid_map,
- "0 0 1\n" /* Map root → root */
- UID_FMT " " UID_FMT " 1\n", /* Map $UID → $UID */
- uid, uid);
- else
+ if (uid != 0 && uid_is_valid(uid)) {
+ r = asprintf(&uid_map,
+ "0 0 1\n" /* Map root → root */
+ UID_FMT " " UID_FMT " 1\n", /* Map $UID → $UID */
+ uid, uid);
+ if (r < 0)
+ return -ENOMEM;
+ } else {
uid_map = strdup("0 0 1\n"); /* The case where the above is the same */
- if (!uid_map)
- return -ENOMEM;
+ if (!uid_map)
+ return -ENOMEM;
+ }
- if (gid != 0 && gid_is_valid(gid))
- asprintf(&gid_map,
- "0 0 1\n" /* Map root → root */
- GID_FMT " " GID_FMT " 1\n", /* Map $GID → $GID */
- gid, gid);
- else
+ if (gid != 0 && gid_is_valid(gid)) {
+ r = asprintf(&gid_map,
+ "0 0 1\n" /* Map root → root */
+ GID_FMT " " GID_FMT " 1\n", /* Map $GID → $GID */
+ gid, gid);
+ if (r < 0)
+ return -ENOMEM;
+ } else {
gid_map = strdup("0 0 1\n"); /* The case where the above is the same */
- if (!gid_map)
- return -ENOMEM;
+ if (!gid_map)
+ return -ENOMEM;
+ }
/* Create a communication channel so that the parent can tell the child when it finished creating the user
* namespace. */
@@ -1896,8 +1908,8 @@ static int compile_read_write_paths(
_cleanup_strv_free_ char **l = NULL;
char **rt;
- /* Compile the list of writable paths. This is the combination of the explicitly configured paths, plus all
- * runtime directories. */
+ /* Compile the list of writable paths. This is the combination of
+ * the explicitly configured paths, plus all runtime directories. */
if (strv_isempty(context->read_write_paths) &&
strv_isempty(context->runtime_directory)) {
@@ -1926,20 +1938,26 @@ static int compile_read_write_paths(
return 0;
}
-static int apply_mount_namespace(Unit *u, const ExecContext *context,
- const ExecParameters *params,
- ExecRuntime *runtime) {
- int r;
- _cleanup_free_ char **rw = NULL;
+static int apply_mount_namespace(
+ Unit *u,
+ ExecCommand *command,
+ const ExecContext *context,
+ const ExecParameters *params,
+ ExecRuntime *runtime) {
+
+ _cleanup_strv_free_ char **rw = NULL;
char *tmp = NULL, *var = NULL;
- const char *root_dir = NULL;
+ const char *root_dir = NULL, *root_image = NULL;
NameSpaceInfo ns_info = {
.ignore_protect_paths = false,
.private_dev = context->private_devices,
.protect_control_groups = context->protect_control_groups,
.protect_kernel_tunables = context->protect_kernel_tunables,
.protect_kernel_modules = context->protect_kernel_modules,
+ .mount_apivfs = context->mount_apivfs,
};
+ bool apply_restrictions;
+ int r;
assert(context);
@@ -1958,8 +1976,12 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context,
if (r < 0)
return r;
- if (params->flags & EXEC_APPLY_CHROOT)
- root_dir = context->root_directory;
+ if (params->flags & EXEC_APPLY_CHROOT) {
+ root_image = context->root_image;
+
+ if (!root_image)
+ root_dir = context->root_directory;
+ }
/*
* If DynamicUser=no and RootDirectory= is set then lets pass a relaxed
@@ -1969,16 +1991,20 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context,
if (!context->dynamic_user && root_dir)
ns_info.ignore_protect_paths = true;
- r = setup_namespace(root_dir, &ns_info, rw,
- context->read_only_paths,
- context->inaccessible_paths,
+ apply_restrictions = (params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged;
+
+ r = setup_namespace(root_dir, root_image,
+ &ns_info, rw,
+ apply_restrictions ? context->read_only_paths : NULL,
+ apply_restrictions ? context->inaccessible_paths : NULL,
context->bind_mounts,
context->n_bind_mounts,
tmp,
var,
- context->protect_home,
- context->protect_system,
- context->mount_flags);
+ apply_restrictions ? context->protect_home : PROTECT_HOME_NO,
+ apply_restrictions ? context->protect_system : PROTECT_SYSTEM_NO,
+ context->mount_flags,
+ DISSECT_IMAGE_DISCARD_ON_LOOP);
/* If we couldn't set up the namespace this is probably due to a
* missing capability. In this case, silently proceeed. */
@@ -1992,33 +2018,47 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context,
return r;
}
-static int apply_working_directory(const ExecContext *context,
- const ExecParameters *params,
- const char *home,
- const bool needs_mount_ns) {
- const char *d;
- const char *wd;
+static int apply_working_directory(
+ const ExecContext *context,
+ const ExecParameters *params,
+ const char *home,
+ const bool needs_mount_ns,
+ int *exit_status) {
+
+ const char *d, *wd;
assert(context);
+ assert(exit_status);
+
+ if (context->working_directory_home) {
+
+ if (!home) {
+ *exit_status = EXIT_CHDIR;
+ return -ENXIO;
+ }
- if (context->working_directory_home)
wd = home;
- else if (context->working_directory)
+
+ } else if (context->working_directory)
wd = context->working_directory;
else
wd = "/";
if (params->flags & EXEC_APPLY_CHROOT) {
if (!needs_mount_ns && context->root_directory)
- if (chroot(context->root_directory) < 0)
+ if (chroot(context->root_directory) < 0) {
+ *exit_status = EXIT_CHROOT;
return -errno;
+ }
d = wd;
} else
- d = strjoina(strempty(context->root_directory), "/", strempty(wd));
+ d = prefix_roota(context->root_directory, wd);
- if (chdir(d) < 0 && !context->working_directory_missing_ok)
+ if (chdir(d) < 0 && !context->working_directory_missing_ok) {
+ *exit_status = EXIT_CHDIR;
return -errno;
+ }
return 0;
}
@@ -2160,6 +2200,35 @@ static int send_user_lookup(
return 0;
}
+static int acquire_home(const ExecContext *c, uid_t uid, const char** home, char **buf) {
+ int r;
+
+ assert(c);
+ assert(home);
+ assert(buf);
+
+ /* If WorkingDirectory=~ is set, try to acquire a usable home directory. */
+
+ if (*home)
+ return 0;
+
+ if (!c->working_directory_home)
+ return 0;
+
+ if (uid == 0) {
+ /* Hardcode /root as home directory for UID 0 */
+ *home = "/root";
+ return 1;
+ }
+
+ r = get_home_dir(buf);
+ if (r < 0)
+ return r;
+
+ *home = *buf;
+ return 1;
+}
+
static int exec_child(
Unit *unit,
ExecCommand *command,
@@ -2177,7 +2246,7 @@ static int exec_child(
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;
+ _cleanup_free_ char *mac_selinux_context_net = NULL, *home_buffer = NULL;
_cleanup_free_ gid_t *supplementary_gids = NULL;
const char *username = NULL, *groupname = NULL;
const char *home = NULL, *shell = NULL;
@@ -2330,6 +2399,13 @@ static int exec_child(
user_lookup_fd = safe_close(user_lookup_fd);
+ r = acquire_home(context, uid, &home, &home_buffer);
+ if (r < 0) {
+ *exit_status = EXIT_CHDIR;
+ *error_message = strdup("Failed to determine $HOME for user");
+ return r;
+ }
+
/* If a socket is connected to STDIN/STDOUT/STDERR, we
* must sure to drop O_NONBLOCK */
if (socket_fd >= 0)
@@ -2433,11 +2509,12 @@ static int exec_child(
}
if (context->utmp_id)
- utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path,
+ utmp_put_init_process(context->utmp_id, getpid(), getsid(0),
+ context->tty_path,
context->utmp_mode == EXEC_UTMP_INIT ? INIT_PROCESS :
context->utmp_mode == EXEC_UTMP_LOGIN ? LOGIN_PROCESS :
USER_PROCESS,
- username ? "root" : context->user);
+ username);
if (context->user) {
r = chown_terminal(STDIN_FILENO, uid);
@@ -2536,7 +2613,7 @@ static int exec_child(
needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime);
if (needs_mount_namespace) {
- r = apply_mount_namespace(unit, context, params, runtime);
+ r = apply_mount_namespace(unit, command, context, params, runtime);
if (r < 0) {
*exit_status = EXIT_NAMESPACE;
return r;
@@ -2544,11 +2621,9 @@ static int exec_child(
}
/* Apply just after mount namespace setup */
- r = apply_working_directory(context, params, home, needs_mount_namespace);
- if (r < 0) {
- *exit_status = EXIT_CHROOT;
+ r = apply_working_directory(context, params, home, needs_mount_namespace, exit_status);
+ if (r < 0)
return r;
- }
/* Drop groups as early as possbile */
if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) {
@@ -2975,6 +3050,7 @@ void exec_context_done(ExecContext *c) {
c->working_directory = mfree(c->working_directory);
c->root_directory = mfree(c->root_directory);
+ c->root_image = mfree(c->root_image);
c->tty_path = mfree(c->tty_path);
c->syslog_identifier = mfree(c->syslog_identifier);
c->user = mfree(c->user);
@@ -3096,7 +3172,7 @@ const char* exec_context_fdname(const ExecContext *c, int fd_index) {
int exec_context_named_iofds(Unit *unit, const ExecContext *c, const ExecParameters *p, int named_iofds[3]) {
unsigned i, targets;
- const char *stdio_fdname[3];
+ const char* stdio_fdname[3];
assert(c);
assert(p);
@@ -3109,18 +3185,32 @@ int exec_context_named_iofds(Unit *unit, const ExecContext *c, const ExecParamet
stdio_fdname[i] = exec_context_fdname(c, i);
for (i = 0; i < p->n_fds && targets > 0; i++)
- if (named_iofds[STDIN_FILENO] < 0 && c->std_input == EXEC_INPUT_NAMED_FD && stdio_fdname[STDIN_FILENO] && streq(p->fd_names[i], stdio_fdname[STDIN_FILENO])) {
+ if (named_iofds[STDIN_FILENO] < 0 &&
+ c->std_input == EXEC_INPUT_NAMED_FD &&
+ stdio_fdname[STDIN_FILENO] &&
+ streq(p->fd_names[i], stdio_fdname[STDIN_FILENO])) {
+
named_iofds[STDIN_FILENO] = p->fds[i];
targets--;
- } else if (named_iofds[STDOUT_FILENO] < 0 && c->std_output == EXEC_OUTPUT_NAMED_FD && stdio_fdname[STDOUT_FILENO] && streq(p->fd_names[i], stdio_fdname[STDOUT_FILENO])) {
+
+ } else if (named_iofds[STDOUT_FILENO] < 0 &&
+ c->std_output == EXEC_OUTPUT_NAMED_FD &&
+ stdio_fdname[STDOUT_FILENO] &&
+ streq(p->fd_names[i], stdio_fdname[STDOUT_FILENO])) {
+
named_iofds[STDOUT_FILENO] = p->fds[i];
targets--;
- } else if (named_iofds[STDERR_FILENO] < 0 && c->std_error == EXEC_OUTPUT_NAMED_FD && stdio_fdname[STDERR_FILENO] && streq(p->fd_names[i], stdio_fdname[STDERR_FILENO])) {
+
+ } else if (named_iofds[STDERR_FILENO] < 0 &&
+ c->std_error == EXEC_OUTPUT_NAMED_FD &&
+ stdio_fdname[STDERR_FILENO] &&
+ streq(p->fd_names[i], stdio_fdname[STDERR_FILENO])) {
+
named_iofds[STDERR_FILENO] = p->fds[i];
targets--;
}
- return (targets == 0 ? 0 : -ENOENT);
+ return targets == 0 ? 0 : -ENOENT;
}
int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) {
@@ -3276,6 +3366,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
"%sPrivateUsers: %s\n"
"%sProtectHome: %s\n"
"%sProtectSystem: %s\n"
+ "%sMountAPIVFS: %s\n"
"%sIgnoreSIGPIPE: %s\n"
"%sMemoryDenyWriteExecute: %s\n"
"%sRestrictRealtime: %s\n",
@@ -3292,10 +3383,14 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
prefix, yes_no(c->private_users),
prefix, protect_home_to_string(c->protect_home),
prefix, protect_system_to_string(c->protect_system),
+ prefix, yes_no(c->mount_apivfs),
prefix, yes_no(c->ignore_sigpipe),
prefix, yes_no(c->memory_deny_write_execute),
prefix, yes_no(c->restrict_realtime));
+ if (c->root_image)
+ fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
+
STRV_FOREACH(e, c->environment)
fprintf(f, "%sEnvironment: %s\n", prefix, *e);
diff --git a/src/core/execute.h b/src/core/execute.h
index f8694ef520..9f2b6fd39e 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -106,7 +106,7 @@ struct ExecContext {
char **pass_environment;
struct rlimit *rlimit[_RLIMIT_MAX];
- char *working_directory, *root_directory;
+ char *working_directory, *root_directory, *root_image;
bool working_directory_missing_ok;
bool working_directory_home;
@@ -183,6 +183,7 @@ struct ExecContext {
bool protect_kernel_tunables;
bool protect_kernel_modules;
bool protect_control_groups;
+ bool mount_apivfs;
bool no_new_privileges;
diff --git a/src/core/job.c b/src/core/job.c
index f7c4c59c32..00f7d7998f 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -697,7 +697,7 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR
static void job_print_status_message(Unit *u, JobType t, JobResult result) {
static const struct {
const char *color, *word;
- } const statuses[_JOB_RESULT_MAX] = {
+ } statuses[_JOB_RESULT_MAX] = {
[JOB_DONE] = { ANSI_GREEN, " OK " },
[JOB_TIMEOUT] = { ANSI_HIGHLIGHT_RED, " TIME " },
[JOB_FAILED] = { ANSI_HIGHLIGHT_RED, "FAILED" },
diff --git a/src/core/killall.c b/src/core/killall.c
index b3aa22adc5..7a9df546ee 100644
--- a/src/core/killall.c
+++ b/src/core/killall.c
@@ -66,29 +66,26 @@ static bool ignore_proc(pid_t pid, bool warn_rootfs) {
if (count <= 0)
return true;
- /* Processes with argv[0][0] = '@' we ignore from the killing
- * spree.
+ /* Processes with argv[0][0] = '@' we ignore from the killing spree.
*
* http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */
- if (c == '@' && warn_rootfs) {
- _cleanup_free_ char *comm = NULL;
+ if (c != '@')
+ return false;
- r = pid_from_same_root_fs(pid);
- if (r < 0)
- return true;
+ if (warn_rootfs &&
+ pid_from_same_root_fs(pid) == 0) {
+
+ _cleanup_free_ char *comm = NULL;
get_process_comm(pid, &comm);
- if (r)
- log_notice("Process " PID_FMT " (%s) has been marked to be excluded from killing. It is "
- "running from the root file system, and thus likely to block re-mounting of the "
- "root file system to read-only. Please consider moving it into an initrd file "
- "system instead.", pid, strna(comm));
- return true;
- } else if (c == '@')
- return true;
+ log_notice("Process " PID_FMT " (%s) has been marked to be excluded from killing. It is "
+ "running from the root file system, and thus likely to block re-mounting of the "
+ "root file system to read-only. Please consider moving it into an initrd file "
+ "system instead.", pid, strna(comm));
+ }
- return false;
+ return true;
}
static void wait_for_children(Set *pids, sigset_t *mask) {
diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c
index f83fa09301..ff3636149a 100644
--- a/src/core/load-dropin.c
+++ b/src/core/load-dropin.c
@@ -19,51 +19,121 @@
#include "conf-parser.h"
+#include "fs-util.h"
#include "load-dropin.h"
#include "load-fragment.h"
#include "log.h"
+#include "stat-util.h"
+#include "string-util.h"
#include "strv.h"
#include "unit-name.h"
#include "unit.h"
-static int add_dependency_consumer(
- UnitDependency dependency,
- const char *entry,
- const char* filepath,
- void *arg) {
- Unit *u = arg;
+static bool unit_name_compatible(const char *a, const char *b) {
+ _cleanup_free_ char *prefix = NULL;
int r;
- assert(u);
+ /* the straightforward case: the symlink name matches the target */
+ if (streq(a, b))
+ return true;
+
+ r = unit_name_template(a, &prefix);
+ if (r < 0) {
+ log_oom();
+ return true;
+ }
+
+ /* an instance name points to a target that is just the template name */
+ if (streq(prefix, b))
+ return true;
+
+ return false;
+}
+
+static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suffix) {
+ _cleanup_strv_free_ char **paths = NULL;
+ char **p;
+ int r;
- r = unit_add_dependency_by_name(u, dependency, entry, filepath, true);
+ r = unit_file_find_dropin_paths(NULL,
+ u->manager->lookup_paths.search_path,
+ u->manager->unit_path_cache,
+ dir_suffix,
+ NULL,
+ u->names,
+ &paths);
if (r < 0)
- log_error_errno(r, "Cannot add dependency %s to %s, ignoring: %m", entry, u->id);
+ return r;
+
+ STRV_FOREACH(p, paths) {
+ const char *entry;
+ _cleanup_free_ char *target = NULL;
+
+ entry = basename(*p);
+
+ if (null_or_empty_path(*p) > 0) {
+ /* an error usually means an invalid symlink, which is not a mask */
+ log_unit_debug(u, "%s dependency on %s is masked by %s, ignoring.",
+ unit_dependency_to_string(dependency), entry, *p);
+ continue;
+ }
+
+ r = is_symlink(*p);
+ if (r < 0) {
+ log_unit_warning_errno(u, r, "%s dropin %s unreadable, ignoring: %m",
+ unit_dependency_to_string(dependency), *p);
+ continue;
+ }
+ if (r == 0) {
+ log_unit_warning(u, "%s dependency dropin %s is not a symlink, ignoring.",
+ unit_dependency_to_string(dependency), *p);
+ continue;
+ }
+
+ if (!unit_name_is_valid(entry, UNIT_NAME_ANY)) {
+ log_unit_warning(u, "%s dependency dropin %s is not a valid unit name, ignoring.",
+ unit_dependency_to_string(dependency), *p);
+ continue;
+ }
+
+ r = readlink_malloc(*p, &target);
+ if (r < 0) {
+ log_unit_warning_errno(u, r, "readlink(\"%s\") failed, ignoring: %m", *p);
+ continue;
+ }
+
+ /* We don't treat this as an error, especially because we didn't check this for a
+ * long time. Nevertheless, we warn, because such mismatch can be mighty confusing. */
+ if (!unit_name_compatible(entry, basename(target)))
+ log_unit_warning(u, "%s dependency dropin %s target %s has different name",
+ unit_dependency_to_string(dependency), *p, target);
+
+ r = unit_add_dependency_by_name(u, dependency, entry, *p, true);
+ if (r < 0)
+ log_unit_error_errno(u, r, "cannot add %s dependency on %s, ignoring: %m",
+ unit_dependency_to_string(dependency), entry);
+ }
return 0;
}
int unit_load_dropin(Unit *u) {
_cleanup_strv_free_ char **l = NULL;
- Iterator i;
- char *t, **f;
+ char **f;
int r;
assert(u);
- /* Load dependencies from supplementary drop-in directories */
-
- SET_FOREACH(t, u->names, i) {
- char **p;
+ /* Load dependencies from .wants and .requires directories */
+ r = process_deps(u, UNIT_WANTS, ".wants");
+ if (r < 0)
+ return r;
- STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
- unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".wants", UNIT_WANTS,
- add_dependency_consumer, u, NULL);
- unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".requires", UNIT_REQUIRES,
- add_dependency_consumer, u, NULL);
- }
- }
+ r = process_deps(u, UNIT_REQUIRES, ".requires");
+ if (r < 0)
+ return r;
+ /* Load .conf dropins */
r = unit_find_dropin_paths(u, &l);
if (r <= 0)
return 0;
diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h
index 942d26724e..5828a223ce 100644
--- a/src/core/load-dropin.h
+++ b/src/core/load-dropin.h
@@ -25,10 +25,11 @@
/* Read service data supplementary drop-in directories */
static inline int unit_find_dropin_paths(Unit *u, char ***paths) {
- return unit_file_find_dropin_paths(u->manager->lookup_paths.search_path,
- u->manager->unit_path_cache,
- u->names,
- paths);
+ return unit_file_find_dropin_conf_paths(NULL,
+ u->manager->lookup_paths.search_path,
+ u->manager->unit_path_cache,
+ u->names,
+ paths);
}
int unit_load_dropin(Unit *u);
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 15f22a2681..cb9e6fea27 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -19,6 +19,7 @@ m4_dnl Define the context options only once
m4_define(`EXEC_CONTEXT_CONFIG_ITEMS',
`$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context)
$1.RootDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.root_directory)
+$1.RootImage, config_parse_unit_path_printf, 0, offsetof($1, exec_context.root_image)
$1.User, config_parse_user_group, 0, offsetof($1, exec_context.user)
$1.Group, config_parse_user_group, 0, offsetof($1, exec_context.group)
$1.SupplementaryGroups, config_parse_user_group_strv, 0, offsetof($1, exec_context.supplementary_groups)
@@ -101,6 +102,7 @@ $1.PrivateUsers, config_parse_bool, 0,
$1.ProtectSystem, config_parse_protect_system, 0, offsetof($1, exec_context)
$1.ProtectHome, config_parse_protect_home, 0, offsetof($1, exec_context)
$1.MountFlags, config_parse_exec_mount_flags, 0, offsetof($1, exec_context)
+$1.MountAPIVFS, config_parse_bool, 0, offsetof($1, exec_context.mount_apivfs)
$1.Personality, config_parse_personality, 0, offsetof($1, exec_context.personality)
$1.RuntimeDirectoryMode, config_parse_mode, 0, offsetof($1, exec_context.runtime_directory_mode)
$1.RuntimeDirectory, config_parse_runtime_directory, 0, offsetof($1, exec_context.runtime_directory)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index bc0cf73d39..5b7471c0d0 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -620,7 +620,7 @@ int config_parse_exec(
* If it's prefixed with - and the path doesn't exist,
* we ignore it instead of erroring out;
* if it's prefixed with @, we allow overriding of argv[0];
- * and if it's prefixed with !, it will be run with full privileges */
+ * and if it's prefixed with +, it will be run with full privileges */
if (*f == '-' && !ignore)
ignore = true;
else if (*f == '@' && !separate_argv0)
@@ -3839,7 +3839,8 @@ int config_parse_namespace_path_strv(
cur = rvalue;
for (;;) {
_cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL;
- bool ignore_enoent;
+ const char *w;
+ bool ignore_enoent = false, shall_prefix = false;
r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
if (r == 0)
@@ -3856,9 +3857,17 @@ int config_parse_namespace_path_strv(
continue;
}
- ignore_enoent = word[0] == '-';
+ w = word;
+ if (startswith(w, "-")) {
+ ignore_enoent = true;
+ w++;
+ }
+ if (startswith(w, "+")) {
+ shall_prefix = true;
+ w++;
+ }
- r = unit_full_printf(u, word + ignore_enoent, &resolved);
+ r = unit_full_printf(u, w, &resolved);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers in %s: %m", word);
continue;
@@ -3871,7 +3880,9 @@ int config_parse_namespace_path_strv(
path_kill_slashes(resolved);
- joined = strjoin(ignore_enoent ? "-" : "", resolved);
+ joined = strjoin(ignore_enoent ? "-" : "",
+ shall_prefix ? "+" : "",
+ resolved);
r = strv_push(sv, joined);
if (r < 0)
diff --git a/src/core/manager.c b/src/core/manager.c
index d83c5ef5e2..e4da945777 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -1984,7 +1984,9 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
if (MANAGER_IS_SYSTEM(m)) {
/* This is for compatibility with the
* original sysvinit */
- m->exit_code = MANAGER_REEXECUTE;
+ r = verify_run_space_and_log("Refusing to reexecute");
+ if (r >= 0)
+ m->exit_code = MANAGER_REEXECUTE;
break;
}
@@ -2061,7 +2063,9 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
}
case SIGHUP:
- m->exit_code = MANAGER_RELOAD;
+ r = verify_run_space_and_log("Refusing to reload");
+ if (r >= 0)
+ m->exit_code = MANAGER_RELOAD;
break;
default: {
@@ -2432,18 +2436,22 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
}
int manager_open_serialization(Manager *m, FILE **_f) {
- const char *path;
int fd = -1;
FILE *f;
assert(_f);
- path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp";
- fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
- if (fd < 0)
- return -errno;
+ fd = memfd_create("systemd-serialization", MFD_CLOEXEC);
+ if (fd < 0) {
+ const char *path;
- log_debug("Serializing state to %s", path);
+ path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp";
+ fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+ log_debug("Serializing state to %s.", path);
+ } else
+ log_debug("Serializing state to memfd.");
f = fdopen(fd, "w+");
if (!f) {
diff --git a/src/core/mount.c b/src/core/mount.c
index 8192a3616f..ca0c4b0d5e 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -1241,7 +1241,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
if (f == MOUNT_SUCCESS || m->from_proc_self_mountinfo)
/* If /bin/mount returned success, or if we see the mount point in /proc/self/mountinfo we are
- * happy. If we see the first condition first, we should see the the second condition
+ * happy. If we see the first condition first, we should see the second condition
* immediately after – or /bin/mount lies to us and is broken. */
mount_enter_mounted(m, f);
else
@@ -1408,7 +1408,7 @@ static int mount_setup_new_unit(
u->source_path = strdup("/proc/self/mountinfo");
MOUNT(u)->where = strdup(where);
- if (!u->source_path && !MOUNT(u)->where)
+ if (!u->source_path || !MOUNT(u)->where)
return -ENOMEM;
/* Make sure to initialize those fields before mount_is_extrinsic(). */
@@ -1435,6 +1435,7 @@ static int mount_setup_new_unit(
return r;
}
+ unit_add_to_load_queue(u);
flags->is_mounted = true;
flags->just_mounted = true;
flags->just_changed = true;
diff --git a/src/core/namespace.c b/src/core/namespace.c
index 834883267c..75dca5b791 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -30,6 +30,7 @@
#include "dev-setup.h"
#include "fd-util.h"
#include "fs-util.h"
+#include "loop-util.h"
#include "loopback-setup.h"
#include "missing.h"
#include "mkdir.h"
@@ -52,10 +53,13 @@ typedef enum MountMode {
INACCESSIBLE,
BIND_MOUNT,
BIND_MOUNT_RECURSIVE,
- READONLY,
PRIVATE_TMP,
PRIVATE_VAR_TMP,
PRIVATE_DEV,
+ BIND_DEV,
+ SYSFS,
+ PROCFS,
+ READONLY,
READWRITE,
} MountMode;
@@ -70,13 +74,13 @@ typedef struct MountEntry {
char *source_malloc;
} MountEntry;
-/*
- * The following Protect tables are to protect paths and mark some of them
- * READONLY, in case a path is covered by an option from another table, then
- * it is marked READWRITE in the current one, and the more restrictive mode is
- * applied from that other table. This way all options can be combined in a
- * safe and comprehensible way for users.
- */
+/* If MountAPIVFS= is used, let's mount /sys and /proc into the it, but only as a fallback if the user hasn't mounted
+ * something there already. These mounts are hence overriden by any other explicitly configured mounts. */
+static const MountEntry apivfs_table[] = {
+ { "/proc", PROCFS, false },
+ { "/dev", BIND_DEV, false },
+ { "/sys", SYSFS, false },
+};
/* ProtectKernelTunables= option and the related filesystem APIs */
static const MountEntry protect_kernel_tunables_table[] = {
@@ -176,6 +180,13 @@ static const char *mount_entry_source(const MountEntry *p) {
return p->source_malloc ?: p->source_const;
}
+static void mount_entry_done(MountEntry *p) {
+ assert(p);
+
+ p->path_malloc = mfree(p->path_malloc);
+ p->source_malloc = mfree(p->source_malloc);
+}
+
static int append_access_mounts(MountEntry **p, char **strv, MountMode mode) {
char **i;
@@ -351,7 +362,7 @@ static void drop_duplicates(MountEntry *m, unsigned *n) {
if (previous && path_equal(mount_entry_path(f), mount_entry_path(previous))) {
log_debug("%s is duplicate.", mount_entry_path(f));
previous->read_only = previous->read_only || mount_entry_read_only(f); /* Propagate the read-only flag to the remaining entry */
- f->path_malloc = mfree(f->path_malloc);
+ mount_entry_done(f);
continue;
}
@@ -379,7 +390,7 @@ static void drop_inaccessible(MountEntry *m, unsigned *n) {
* it, as inaccessible paths really should drop the entire subtree. */
if (clear && path_startswith(mount_entry_path(f), clear)) {
log_debug("%s is masked by %s.", mount_entry_path(f), clear);
- f->path_malloc = mfree(f->path_malloc);
+ mount_entry_done(f);
continue;
}
@@ -419,7 +430,7 @@ static void drop_nop(MountEntry *m, unsigned *n) {
/* We found it, let's see if it's the same mode, if so, we can drop this entry */
if (found && p->mode == f->mode) {
log_debug("%s is redundant by %s", mount_entry_path(f), mount_entry_path(p));
- f->path_malloc = mfree(f->path_malloc);
+ mount_entry_done(f);
continue;
}
}
@@ -447,7 +458,7 @@ static void drop_outside_root(const char *root_directory, MountEntry *m, unsigne
if (!path_startswith(mount_entry_path(f), root_directory)) {
log_debug("%s is outside of root directory.", mount_entry_path(f));
- f->path_malloc = mfree(f->path_malloc);
+ mount_entry_done(f);
continue;
}
@@ -458,7 +469,7 @@ static void drop_outside_root(const char *root_directory, MountEntry *m, unsigne
*n = t - m;
}
-static int mount_dev(MountEntry *m) {
+static int mount_private_dev(MountEntry *m) {
static const char devnodes[] =
"/dev/null\0"
"/dev/zero\0"
@@ -597,6 +608,62 @@ fail:
return r;
}
+static int mount_bind_dev(MountEntry *m) {
+ int r;
+
+ assert(m);
+
+ /* Implements the little brother of mount_private_dev(): simply bind mounts the host's /dev into the service's
+ * /dev. This is only used when RootDirectory= is set. */
+
+ r = path_is_mount_point(mount_entry_path(m), NULL, 0);
+ if (r < 0)
+ return log_debug_errno(r, "Unable to determine whether /dev is already mounted: %m");
+ if (r > 0) /* make this a NOP if /dev is already a mount point */
+ return 0;
+
+ if (mount("/dev", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL) < 0)
+ return log_debug_errno(errno, "Failed to bind mount %s: %m", mount_entry_path(m));
+
+ return 1;
+}
+
+static int mount_sysfs(MountEntry *m) {
+ int r;
+
+ assert(m);
+
+ r = path_is_mount_point(mount_entry_path(m), NULL, 0);
+ if (r < 0)
+ return log_debug_errno(r, "Unable to determine whether /sys is already mounted: %m");
+ if (r > 0) /* make this a NOP if /sys is already a mount point */
+ return 0;
+
+ /* Bind mount the host's version so that we get all child mounts of it, too. */
+ if (mount("/sys", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL) < 0)
+ return log_debug_errno(errno, "Failed to mount %s: %m", mount_entry_path(m));
+
+ return 1;
+}
+
+static int mount_procfs(MountEntry *m) {
+ int r;
+
+ assert(m);
+
+ r = path_is_mount_point(mount_entry_path(m), NULL, 0);
+ if (r < 0)
+ return log_debug_errno(r, "Unable to determine whether /proc is already mounted: %m");
+ if (r > 0) /* make this a NOP if /proc is already a mount point */
+ return 0;
+
+ /* Mount a new instance, so that we get the one that matches our user namespace, if we are running in one */
+ if (mount("proc", mount_entry_path(m), "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) < 0)
+ return log_debug_errno(errno, "Failed to mount %s: %m", mount_entry_path(m));
+
+ return 1;
+}
+
static int mount_entry_chase(
const char *root_directory,
MountEntry *m,
@@ -684,6 +751,7 @@ static int apply_mount(
case BIND_MOUNT_RECURSIVE:
/* Also chase the source mount */
+
r = mount_entry_chase(root_directory, m, mount_entry_source(m), &m->source_malloc);
if (r <= 0)
return r;
@@ -700,7 +768,16 @@ static int apply_mount(
break;
case PRIVATE_DEV:
- return mount_dev(m);
+ return mount_private_dev(m);
+
+ case BIND_DEV:
+ return mount_bind_dev(m);
+
+ case SYSFS:
+ return mount_sysfs(m);
+
+ case PROCFS:
+ return mount_procfs(m);
default:
assert_not_reached("Unknown mode");
@@ -722,7 +799,7 @@ static int make_read_only(MountEntry *m, char **blacklist) {
if (mount_entry_read_only(m))
r = bind_remount_recursive(mount_entry_path(m), true, blacklist);
- else if (m->mode == PRIVATE_DEV) { /* Can be readonly but the submounts can't*/
+ else if (m->mode == PRIVATE_DEV) { /* Superblock can be readonly but the submounts can't*/
if (mount(NULL, mount_entry_path(m), NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0)
r = -errno;
} else
@@ -738,6 +815,17 @@ static int make_read_only(MountEntry *m, char **blacklist) {
return r;
}
+static bool namespace_info_mount_apivfs(const NameSpaceInfo *ns_info) {
+ assert(ns_info);
+
+ /* ProtectControlGroups= and ProtectKernelTunables= imply MountAPIVFS=, since to protect the API VFS mounts,
+ * they need to be around in the first place... */
+
+ return ns_info->mount_apivfs ||
+ ns_info->protect_control_groups ||
+ ns_info->protect_kernel_tunables;
+}
+
static unsigned namespace_calculate_mounts(
const NameSpaceInfo *ns_info,
char** read_write_paths,
@@ -774,11 +862,13 @@ static unsigned namespace_calculate_mounts(
(ns_info->protect_kernel_tunables ? ELEMENTSOF(protect_kernel_tunables_table) : 0) +
(ns_info->protect_control_groups ? 1 : 0) +
(ns_info->protect_kernel_modules ? ELEMENTSOF(protect_kernel_modules_table) : 0) +
- protect_home_cnt + protect_system_cnt;
+ protect_home_cnt + protect_system_cnt +
+ (namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0);
}
int setup_namespace(
const char* root_directory,
+ const char* root_image,
const NameSpaceInfo *ns_info,
char** read_write_paths,
char** read_only_paths,
@@ -789,16 +879,57 @@ int setup_namespace(
const char* var_tmp_dir,
ProtectHome protect_home,
ProtectSystem protect_system,
- unsigned long mount_flags) {
+ unsigned long mount_flags,
+ DissectImageFlags dissect_image_flags) {
+ _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
+ _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
+ _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
+ _cleanup_free_ void *root_hash = NULL;
MountEntry *m, *mounts = NULL;
+ size_t root_hash_size = 0;
bool make_slave = false;
unsigned n_mounts;
int r = 0;
+ assert(ns_info);
+
if (mount_flags == 0)
mount_flags = MS_SHARED;
+ if (root_image) {
+ dissect_image_flags |= DISSECT_IMAGE_REQUIRE_ROOT;
+
+ if (protect_system == PROTECT_SYSTEM_STRICT && strv_isempty(read_write_paths))
+ dissect_image_flags |= DISSECT_IMAGE_READ_ONLY;
+
+ r = loop_device_make_by_path(root_image,
+ dissect_image_flags & DISSECT_IMAGE_READ_ONLY ? O_RDONLY : O_RDWR,
+ &loop_device);
+ if (r < 0)
+ return r;
+
+ r = root_hash_load(root_image, &root_hash, &root_hash_size);
+ if (r < 0)
+ return r;
+
+ r = dissect_image(loop_device->fd, root_hash, root_hash_size, dissect_image_flags, &dissected_image);
+ if (r < 0)
+ return r;
+
+ r = dissected_image_decrypt(dissected_image, NULL, root_hash, root_hash_size, dissect_image_flags, &decrypted_image);
+ if (r < 0)
+ return r;
+
+ if (!root_directory) {
+ /* Create a mount point for the image, if it's still missing. We use the same mount point for
+ * all images, which is safe, since they all live in their own namespaces after all, and hence
+ * won't see each other. */
+ root_directory = "/run/systemd/unit-root";
+ (void) mkdir(root_directory, 0700);
+ }
+ }
+
n_mounts = namespace_calculate_mounts(
ns_info,
read_write_paths,
@@ -878,6 +1009,12 @@ int setup_namespace(
if (r < 0)
goto finish;
+ if (namespace_info_mount_apivfs(ns_info)) {
+ r = append_static_mounts(&m, apivfs_table, ELEMENTSOF(apivfs_table), ns_info->ignore_protect_paths);
+ if (r < 0)
+ goto finish;
+ }
+
assert(mounts + n_mounts == m);
/* Prepend the root directory where that's necessary */
@@ -907,7 +1044,19 @@ int setup_namespace(
}
}
- if (root_directory) {
+ if (root_image) {
+ r = dissected_image_mount(dissected_image, root_directory, dissect_image_flags);
+ if (r < 0)
+ goto finish;
+
+ r = decrypted_image_relinquish(decrypted_image);
+ if (r < 0)
+ goto finish;
+
+ loop_device_relinquish(loop_device);
+
+ } else if (root_directory) {
+
/* Turn directory into bind mount, if it isn't one yet */
r = path_is_mount_point(root_directory, NULL, AT_SYMLINK_FOLLOW);
if (r < 0)
@@ -964,7 +1113,7 @@ int setup_namespace(
finish:
for (m = mounts; m < mounts + n_mounts; m++)
- free(m->path_malloc);
+ mount_entry_done(m);
return r;
}
diff --git a/src/core/namespace.h b/src/core/namespace.h
index de3edc419c..f54954bd86 100644
--- a/src/core/namespace.h
+++ b/src/core/namespace.h
@@ -25,6 +25,7 @@ typedef struct BindMount BindMount;
#include <stdbool.h>
+#include "dissect-image.h"
#include "macro.h"
typedef enum ProtectHome {
@@ -50,6 +51,7 @@ struct NameSpaceInfo {
bool protect_control_groups:1;
bool protect_kernel_tunables:1;
bool protect_kernel_modules:1;
+ bool mount_apivfs:1;
};
struct BindMount {
@@ -62,6 +64,7 @@ struct BindMount {
int setup_namespace(
const char *root_directory,
+ const char *root_image,
const NameSpaceInfo *ns_info,
char **read_write_paths,
char **read_only_paths,
@@ -72,7 +75,8 @@ int setup_namespace(
const char *var_tmp_dir,
ProtectHome protect_home,
ProtectSystem protect_system,
- unsigned long mount_flags);
+ unsigned long mount_flags,
+ DissectImageFlags dissected_image_flags);
int setup_tmp_dirs(
const char *id,
diff --git a/src/core/socket.c b/src/core/socket.c
index 3cae6b31bb..a7b9ada65c 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -2668,6 +2668,7 @@ const char* socket_port_type_to_string(SocketPort *p) {
if (socket_address_family(&p->address) == AF_NETLINK)
return "Netlink";
+ /* fall through */
default:
return NULL;
}
diff --git a/src/core/timer.c b/src/core/timer.c
index c6b28dd9c5..af67b7591a 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -232,7 +232,7 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
if (v->base == TIMER_CALENDAR) {
_cleanup_free_ char *p = NULL;
- calendar_spec_to_string(v->calendar_spec, &p);
+ (void) calendar_spec_to_string(v->calendar_spec, &p);
fprintf(f,
"%s%s: %s\n",
@@ -350,7 +350,7 @@ static void add_random(Timer *t, usec_t *v) {
else
*v += add;
- log_unit_info(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0));
+ log_unit_debug(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0));
}
static void timer_enter_waiting(Timer *t, bool initial) {
@@ -422,7 +422,8 @@ static void timer_enter_waiting(Timer *t, bool initial) {
}
/* In a container we don't want to include the time the host
* was already up when the container started, so count from
- * our own startup. Fall through. */
+ * our own startup. */
+ /* fall through */
case TIMER_STARTUP:
base = UNIT(t)->manager->userspace_timestamp.monotonic;
break;
diff --git a/src/core/transaction.c b/src/core/transaction.c
index e22e3b30c2..b6d1062414 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -907,7 +907,10 @@ int transaction_add_job_and_dependencies(
SET_FOREACH(dep, following, i) {
r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, false, false, ignore_order, e);
if (r < 0) {
- log_unit_warning(dep, "Cannot add dependency job for, ignoring: %s", bus_error_message(e, r));
+ log_unit_full(dep,
+ r == -ERFKILL ? LOG_INFO : LOG_WARNING,
+ r, "Cannot add dependency job, ignoring: %s",
+ bus_error_message(e, r));
sd_bus_error_free(e);
}
}
diff --git a/src/core/unit.c b/src/core/unit.c
index 409668f6d2..5e4b1567d8 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -862,6 +862,12 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
return r;
}
+ if (c->root_image) {
+ r = unit_require_mounts_for(u, c->root_image);
+ if (r < 0)
+ return r;
+ }
+
if (!MANAGER_IS_SYSTEM(u->manager))
return 0;
@@ -1083,6 +1089,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
/* Common implementation for multiple backends */
int unit_load_fragment_and_dropin(Unit *u) {
+ Unit *t;
int r;
assert(u);
@@ -1095,16 +1102,18 @@ int unit_load_fragment_and_dropin(Unit *u) {
if (u->load_state == UNIT_STUB)
return -ENOENT;
- /* Load drop-in directory data */
- r = unit_load_dropin(unit_follow_merge(u));
- if (r < 0)
- return r;
+ /* If the unit is an alias and the final unit has already been
+ * loaded, there's no point in reloading the dropins one more time. */
+ t = unit_follow_merge(u);
+ if (t != u && t->load_state != UNIT_STUB)
+ return 0;
- return 0;
+ return unit_load_dropin(t);
}
/* Common implementation for multiple backends */
int unit_load_fragment_and_dropin_optional(Unit *u) {
+ Unit *t;
int r;
assert(u);
@@ -1120,12 +1129,13 @@ int unit_load_fragment_and_dropin_optional(Unit *u) {
if (u->load_state == UNIT_STUB)
u->load_state = UNIT_LOADED;
- /* Load drop-in directory data */
- r = unit_load_dropin(unit_follow_merge(u));
- if (r < 0)
- return r;
+ /* If the unit is an alias and the final unit has already been
+ * loaded, there's no point in reloading the dropins one more time. */
+ t = unit_follow_merge(u);
+ if (t != u && t->load_state != UNIT_STUB)
+ return 0;
- return 0;
+ return unit_load_dropin(t);
}
int unit_add_default_target_dependency(Unit *u, Unit *target) {
@@ -2640,7 +2650,7 @@ void unit_unwatch_bus_name(Unit *u, const char *name) {
assert(u);
assert(name);
- hashmap_remove_value(u->manager->watch_bus, name, u);
+ (void) hashmap_remove_value(u->manager->watch_bus, name, u);
u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
}
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index d55d896df4..1f6fb5de1e 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -642,7 +642,11 @@ static int get_process_container_parent_cmdline(pid_t pid, char** cmdline) {
if (r < 0)
return r;
- return get_process_cmdline(container_pid, 0, false, cmdline);
+ r = get_process_cmdline(container_pid, 0, false, cmdline);
+ if (r < 0)
+ return r;
+
+ return 1;
}
static int change_uid_gid(const char *context[]) {
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index cefd1b85df..91c653312a 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -594,7 +594,7 @@ static int help(void) {
int main(int argc, char *argv[]) {
struct crypt_device *cd = NULL;
- int r;
+ int r = -EINVAL;
if (argc <= 1) {
r = help();
@@ -603,7 +603,6 @@ int main(int argc, char *argv[]) {
if (argc < 3) {
log_error("This program requires at least two arguments.");
- r = -EINVAL;
goto finish;
}
@@ -750,7 +749,6 @@ int main(int argc, char *argv[]) {
} else {
log_error("Unknown verb %s.", argv[1]);
- r = -EINVAL;
goto finish;
}
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c
index fd9db5ba87..59bd7d9e84 100644
--- a/src/dissect/dissect.c
+++ b/src/dissect/dissect.c
@@ -191,6 +191,14 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ if (!arg_root_hash) {
+ r = root_hash_load(arg_image, &arg_root_hash, &arg_root_hash_size);
+ if (r < 0) {
+ log_error_errno(r, "Failed to read root hash file for %s: %m", arg_image);
+ goto finish;
+ }
+ }
+
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);
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index 3c601a63e2..d97bafd1fb 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -57,6 +57,34 @@ static char *arg_usr_fstype = NULL;
static char *arg_usr_options = NULL;
static VolatileMode arg_volatile_mode = _VOLATILE_MODE_INVALID;
+static int write_options(FILE *f, const char *options) {
+ _cleanup_free_ char *o = NULL;
+
+ if (isempty(options))
+ return 0;
+
+ if (streq(options, "defaults"))
+ return 0;
+
+ o = strreplace(options, "%", "%%");
+ if (!o)
+ return log_oom();
+
+ fprintf(f, "Options=%s\n", o);
+ return 1;
+}
+
+static int write_what(FILE *f, const char *what) {
+ _cleanup_free_ char *w = NULL;
+
+ w = strreplace(what, "%", "%%");
+ if (!w)
+ return log_oom();
+
+ fprintf(f, "What=%s\n", w);
+ return 1;
+}
+
static int add_swap(
const char *what,
struct mntent *me,
@@ -96,17 +124,19 @@ static int add_swap(
"Failed to create unit file %s: %m",
unit);
- fprintf(f,
- "# Automatically generated by systemd-fstab-generator\n\n"
- "[Unit]\n"
- "SourcePath=/etc/fstab\n"
- "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
- "[Swap]\n"
- "What=%s\n",
- what);
+ fputs("# Automatically generated by systemd-fstab-generator\n\n"
+ "[Unit]\n"
+ "SourcePath=/etc/fstab\n"
+ "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
+ "[Swap]\n", f);
+
+ r = write_what(f, what);
+ if (r < 0)
+ return r;
- if (!isempty(me->mnt_opts) && !streq(me->mnt_opts, "defaults"))
- fprintf(f, "Options=%s\n", me->mnt_opts);
+ r = write_options(f, me->mnt_opts);
+ if (r < 0)
+ return r;
r = fflush_and_check(f);
if (r < 0)
@@ -331,11 +361,13 @@ static int add_mount(
fprintf(f,
"\n"
"[Mount]\n"
- "What=%s\n"
"Where=%s\n",
- what,
where);
+ r = write_what(f, what);
+ if (r < 0)
+ return r;
+
if (!isempty(fstype) && !streq(fstype, "auto"))
fprintf(f, "Type=%s\n", fstype);
@@ -347,8 +379,9 @@ static int add_mount(
if (r < 0)
return r;
- if (!isempty(filtered) && !streq(filtered, "defaults"))
- fprintf(f, "Options=%s\n", filtered);
+ r = write_options(f, filtered);
+ if (r < 0)
+ return r;
r = fflush_and_check(f);
if (r < 0)
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index e61ef8f249..b958070c90 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -658,7 +658,7 @@ static int get_block_device_harder(const char *path, dev_t *dev) {
FOREACH_DIRENT_ALL(de, d, return -errno) {
- if (STR_IN_SET(de->d_name, ".", ".."))
+ if (dot_or_dot_dot(de->d_name))
continue;
if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c
index 07c57fb567..f5a9de94a6 100644
--- a/src/hostname/hostnamectl.c
+++ b/src/hostname/hostnamectl.c
@@ -137,10 +137,8 @@ static int show_one_name(sd_bus *bus, const char* attr) {
"org.freedesktop.hostname1",
attr,
&error, &reply, "s");
- if (r < 0) {
- log_error("Could not get property: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not get property: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "s", &s);
if (r < 0)
@@ -151,7 +149,7 @@ static int show_one_name(sd_bus *bus, const char* attr) {
return 0;
}
-static int show_all_names(sd_bus *bus) {
+static int show_all_names(sd_bus *bus, sd_bus_error *error) {
StatusInfo info = {};
static const struct bus_properties_map hostname_map[] = {
@@ -181,6 +179,7 @@ static int show_all_names(sd_bus *bus) {
"org.freedesktop.hostname1",
"/org/freedesktop/hostname1",
hostname_map,
+ error,
&info);
if (r < 0)
goto fail;
@@ -189,6 +188,7 @@ static int show_all_names(sd_bus *bus) {
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
manager_map,
+ error,
&info);
print_status_info(&info);
@@ -212,6 +212,8 @@ fail:
}
static int show_status(sd_bus *bus, char **args, unsigned n) {
+ int r;
+
assert(args);
if (arg_pretty || arg_static || arg_transient) {
@@ -226,8 +228,15 @@ static int show_status(sd_bus *bus, char **args, unsigned n) {
arg_static ? "StaticHostname" : "Hostname";
return show_one_name(bus, attr);
- } else
- return show_all_names(bus);
+ } else {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ r = show_all_names(bus, &error);
+ if (r < 0)
+ return log_error_errno(r, "Failed to query system properties: %s", bus_error_message(&error, r));
+
+ return 0;
+ }
}
static int set_simple_string(sd_bus *bus, const char *method, const char *value) {
diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c
index 74256e4444..4657cf8c77 100644
--- a/src/hostname/hostnamed.c
+++ b/src/hostname/hostnamed.c
@@ -283,7 +283,7 @@ static int context_update_kernel_hostname(Context *c) {
/* ... and the ultimate fallback */
else
- hn = "localhost";
+ hn = FALLBACK_HOSTNAME;
if (sethostname_idempotent(hn) < 0)
return -errno;
@@ -419,7 +419,7 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *
name = c->data[PROP_STATIC_HOSTNAME];
if (isempty(name))
- name = "localhost";
+ name = FALLBACK_HOSTNAME;
if (!hostname_is_valid(name, false))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index 8ad9738edf..9a1c5b76ca 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -912,7 +912,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hD:", options, NULL)) >= 0)
switch(c) {
@@ -958,6 +958,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
#else
log_error("Option --trust is not available.");
+ return -EINVAL;
#endif
case 'D':
arg_directory = optarg;
@@ -1028,10 +1029,9 @@ int main(int argc, char *argv[]) {
{ MHD_OPTION_END, 0, NULL }};
int opts_pos = 2;
- /* We force MHD_USE_PIPE_FOR_SHUTDOWN here, in order
- * to make sure libmicrohttpd doesn't use shutdown()
- * on our listening socket, which would break socket
- * re-activation. See
+ /* We force MHD_USE_ITC here, in order to make sure
+ * libmicrohttpd doesn't use shutdown() on our listening
+ * socket, which would break socket re-activation. See
*
* https://lists.gnu.org/archive/html/libmicrohttpd/2015-09/msg00014.html
* https://github.com/systemd/systemd/pull/1286
@@ -1040,7 +1040,7 @@ int main(int argc, char *argv[]) {
int flags =
MHD_USE_DEBUG |
MHD_USE_DUAL_STACK |
- MHD_USE_PIPE_FOR_SHUTDOWN |
+ MHD_USE_ITC |
MHD_USE_POLL |
MHD_USE_THREAD_PER_CONNECTION;
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index d86c3681b1..d0d8d936e3 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -648,9 +648,9 @@ static int setup_microhttpd_server(RemoteServer *s,
int flags =
MHD_USE_DEBUG |
MHD_USE_DUAL_STACK |
- MHD_USE_EPOLL_LINUX_ONLY |
+ MHD_USE_EPOLL |
MHD_USE_PEDANTIC_CHECKS |
- MHD_USE_PIPE_FOR_SHUTDOWN;
+ MHD_USE_ITC;
const union MHD_DaemonInfo *info;
int r, epoll_fd;
diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h
index af26ab69fe..49def4f630 100644
--- a/src/journal-remote/microhttpd-util.h
+++ b/src/journal-remote/microhttpd-util.h
@@ -24,13 +24,25 @@
#include "macro.h"
+/* Those defines are added when options are renamed, hence the check for the *old* name. */
+
/* Compatiblity with libmicrohttpd < 0.9.38 */
#ifndef MHD_HTTP_NOT_ACCEPTABLE
-#define MHD_HTTP_NOT_ACCEPTABLE MHD_HTTP_METHOD_NOT_ACCEPTABLE
+# define MHD_HTTP_NOT_ACCEPTABLE MHD_HTTP_METHOD_NOT_ACCEPTABLE
+#endif
+
+/* Renamed in µhttpd 0.9.52 */
+#ifndef MHD_USE_EPOLL_LINUX_ONLY
+# define MHD_USE_EPOLL MHD_USE_EPOLL_LINUX_ONLY
+#endif
+
+/* Renamed in µhttpd 0.9.51 */
+#ifndef MHD_USE_PIPE_FOR_SHUTDOWN
+# define MHD_USE_ITC MHD_USE_PIPE_FOR_SHUTDOWN
#endif
#if MHD_VERSION < 0x00094203
-#define MHD_create_response_from_fd_at_offset64 MHD_create_response_from_fd_at_offset
+# define MHD_create_response_from_fd_at_offset64 MHD_create_response_from_fd_at_offset
#endif
void microhttpd_logger(void *arg, const char *fmt, va_list ap) _printf_(2, 0);
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index d3e0214731..ef87b176fa 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -42,6 +42,7 @@
#include "sd-event.h"
#include "set.h"
#include "string-util.h"
+#include "strv.h"
#include "xattr-util.h"
#define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*sizeof(HashItem))
@@ -285,7 +286,7 @@ static int journal_file_set_online(JournalFile *f) {
continue;
/* Canceled restart from offlining, must wait for offlining to complete however. */
- /* fall through to wait */
+ /* fall through */
default: {
int r;
@@ -507,42 +508,58 @@ static int journal_file_refresh_header(JournalFile *f) {
return r;
}
-static int journal_file_verify_header(JournalFile *f) {
+static bool warn_wrong_flags(const JournalFile *f, bool compatible) {
+ const uint32_t any = compatible ? HEADER_COMPATIBLE_ANY : HEADER_INCOMPATIBLE_ANY,
+ supported = compatible ? HEADER_COMPATIBLE_SUPPORTED : HEADER_INCOMPATIBLE_SUPPORTED;
+ const char *type = compatible ? "compatible" : "incompatible";
uint32_t flags;
+ flags = le32toh(compatible ? f->header->compatible_flags : f->header->incompatible_flags);
+
+ if (flags & ~supported) {
+ if (flags & ~any)
+ log_debug("Journal file %s has unknown %s flags 0x%"PRIx32,
+ f->path, type, flags & ~any);
+ flags = (flags & any) & ~supported;
+ if (flags) {
+ const char* strv[3];
+ unsigned n = 0;
+ _cleanup_free_ char *t = NULL;
+
+ if (compatible && (flags & HEADER_COMPATIBLE_SEALED))
+ strv[n++] = "sealed";
+ if (!compatible && (flags & HEADER_INCOMPATIBLE_COMPRESSED_XZ))
+ strv[n++] = "xz-compressed";
+ if (!compatible && (flags & HEADER_INCOMPATIBLE_COMPRESSED_LZ4))
+ strv[n++] = "lz4-compressed";
+ strv[n] = NULL;
+ assert(n < ELEMENTSOF(strv));
+
+ t = strv_join((char**) strv, ", ");
+ log_debug("Journal file %s uses %s %s %s disabled at compilation time.",
+ f->path, type, n > 1 ? "flags" : "flag", strnull(t));
+ }
+ return true;
+ }
+
+ return false;
+}
+
+static int journal_file_verify_header(JournalFile *f) {
assert(f);
assert(f->header);
if (memcmp(f->header->signature, HEADER_SIGNATURE, 8))
return -EBADMSG;
- /* In both read and write mode we refuse to open files with
- * incompatible flags we don't know */
- flags = le32toh(f->header->incompatible_flags);
- if (flags & ~HEADER_INCOMPATIBLE_SUPPORTED) {
- if (flags & ~HEADER_INCOMPATIBLE_ANY)
- log_debug("Journal file %s has unknown incompatible flags %"PRIx32,
- f->path, flags & ~HEADER_INCOMPATIBLE_ANY);
- flags = (flags & HEADER_INCOMPATIBLE_ANY) & ~HEADER_INCOMPATIBLE_SUPPORTED;
- if (flags)
- log_debug("Journal file %s uses incompatible flags %"PRIx32
- " disabled at compilation time.", f->path, flags);
+ /* In both read and write mode we refuse to open files with incompatible
+ * flags we don't know. */
+ if (warn_wrong_flags(f, false))
return -EPROTONOSUPPORT;
- }
- /* When open for writing we refuse to open files with
- * compatible flags, too */
- flags = le32toh(f->header->compatible_flags);
- if (f->writable && (flags & ~HEADER_COMPATIBLE_SUPPORTED)) {
- if (flags & ~HEADER_COMPATIBLE_ANY)
- log_debug("Journal file %s has unknown compatible flags %"PRIx32,
- f->path, flags & ~HEADER_COMPATIBLE_ANY);
- flags = (flags & HEADER_COMPATIBLE_ANY) & ~HEADER_COMPATIBLE_SUPPORTED;
- if (flags)
- log_debug("Journal file %s uses compatible flags %"PRIx32
- " disabled at compilation time.", f->path, flags);
+ /* When open for writing we refuse to open files with compatible flags, too. */
+ if (f->writable && warn_wrong_flags(f, true))
return -EPROTONOSUPPORT;
- }
if (f->header->state >= _STATE_MAX)
return -EBADMSG;
@@ -580,12 +597,12 @@ static int journal_file_verify_header(JournalFile *f) {
state = f->header->state;
- if (state == STATE_ONLINE) {
+ if (state == STATE_ARCHIVED)
+ return -ESHUTDOWN; /* Already archived */
+ else if (state == STATE_ONLINE) {
log_debug("Journal file %s is already online. Assuming unclean closing.", f->path);
return -EBUSY;
- } else if (state == STATE_ARCHIVED)
- return -ESHUTDOWN;
- else if (state != STATE_OFFLINE) {
+ } else if (state != STATE_OFFLINE) {
log_debug("Journal file %s has unknown state %i.", f->path, state);
return -EBUSY;
}
@@ -3087,13 +3104,18 @@ int journal_file_open(
}
}
- if (fname)
+ if (fname) {
f->path = strdup(fname);
- else /* If we don't know the path, fill in something explanatory and vaguely useful */
- asprintf(&f->path, "/proc/self/%i", fd);
- if (!f->path) {
- r = -ENOMEM;
- goto fail;
+ if (!f->path) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ } else {
+ /* If we don't know the path, fill in something explanatory and vaguely useful */
+ if (asprintf(&f->path, "/proc/self/%i", fd) < 0) {
+ r = -ENOMEM;
+ goto fail;
+ }
}
f->chain_cache = ordered_hashmap_new(&uint64_hash_ops);
@@ -3330,12 +3352,12 @@ int journal_file_open_reliably(
r = journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret);
if (!IN_SET(r,
- -EBADMSG, /* corrupted */
- -ENODATA, /* truncated */
- -EHOSTDOWN, /* other machine */
- -EPROTONOSUPPORT, /* incompatible feature */
- -EBUSY, /* unclean shutdown */
- -ESHUTDOWN, /* already archived */
+ -EBADMSG, /* Corrupted */
+ -ENODATA, /* Truncated */
+ -EHOSTDOWN, /* Other machine */
+ -EPROTONOSUPPORT, /* Incompatible feature */
+ -EBUSY, /* Unclean shutdown */
+ -ESHUTDOWN, /* Already archived */
-EIO, /* IO error, including SIGBUS on mmap */
-EIDRM, /* File has been deleted */
-ETXTBSY)) /* File is from the future */
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 2a5f2b37e8..9ad6f115a1 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -686,7 +686,9 @@ static int parse_argv(int argc, char *argv[]) {
r = free_and_strdup(&arg_verify_key, optarg);
if (r < 0)
return r;
- string_erase(optarg);
+ /* Use memset not string_erase so this doesn't look confusing
+ * in ps or htop output. */
+ memset(optarg, 'x', strlen(optarg));
arg_merge = false;
break;
@@ -1910,7 +1912,9 @@ static int access_check(sd_journal *j) {
break;
case EPROTONOSUPPORT:
- log_warning_errno(err, "Journal file %s uses an unsupported feature, ignoring file.", path);
+ log_warning_errno(err, "Journal file %1$s uses an unsupported feature, ignoring file.\n"
+ "Use SYSTEMD_LOG_LEVEL=debug journalctl --file=%1$s to see the details.",
+ path);
break;
case EBADMSG:
diff --git a/src/journal/lookup3.c b/src/journal/lookup3.c
index d8f1a4977d..ec725ce46c 100644
--- a/src/journal/lookup3.c
+++ b/src/journal/lookup3.c
@@ -48,6 +48,10 @@ on 1 byte), but shoehorning those bytes into integers efficiently is messy.
# include <endian.h> /* attempt to define endianness */
#endif
+#if __GNUC__ >= 7
+_Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
+#endif
+
/*
* My best guess at if you are big-endian or little-endian. This may
* need adjustment.
diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c
index 5462e03476..f8056dbc4b 100644
--- a/src/libsystemd-network/dhcp6-option.c
+++ b/src/libsystemd-network/dhcp6-option.c
@@ -339,7 +339,7 @@ int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char ***str_arr) {
size_t pos = 0, idx = 0;
- _cleanup_free_ char **names = NULL;
+ _cleanup_strv_free_ char **names = NULL;
int r;
assert_return(optlen > 1, -ENODATA);
diff --git a/src/libsystemd-network/lldp-network.c b/src/libsystemd-network/lldp-network.c
index 59c25598e9..ae2f6744d5 100644
--- a/src/libsystemd-network/lldp-network.c
+++ b/src/libsystemd-network/lldp-network.c
@@ -47,6 +47,13 @@ int lldp_network_bind_raw_socket(int ifindex) {
.filter = (struct sock_filter*) filter,
};
+ struct packet_mreq mreq = {
+ .mr_ifindex = ifindex,
+ .mr_type = PACKET_MR_MULTICAST,
+ .mr_alen = ETH_ALEN,
+ .mr_address = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 }
+ };
+
union sockaddr_union saddrll = {
.ll.sll_family = AF_PACKET,
.ll.sll_ifindex = ifindex,
@@ -66,6 +73,20 @@ int lldp_network_bind_raw_socket(int ifindex) {
if (r < 0)
return -errno;
+ r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+ if (r < 0)
+ return -errno;
+
+ mreq.mr_address[ETH_ALEN - 1] = 0x03;
+ r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+ if (r < 0)
+ return -errno;
+
+ mreq.mr_address[ETH_ALEN - 1] = 0x0E;
+ r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+ if (r < 0)
+ return -errno;
+
r = bind(fd, &saddrll.sa, sizeof(saddrll.ll));
if (r < 0)
return -errno;
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index 0e57ab6b69..2890681561 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -857,7 +857,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
if (!existing_lease) {
lease = new0(DHCPLease, 1);
- lease->address = req->requested_ip;
+ lease->address = address;
lease->client_id.data = memdup(req->client_id.data,
req->client_id.length);
if (!lease->client_id.data) {
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index e81215f7d7..6444b0ce94 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -999,7 +999,7 @@ static int client_receive_message(
break;
}
- /* fall through for Soliciation Rapid Commit option check */
+ /* fall through */ /* for Soliciation Rapid Commit option check */
case DHCP6_STATE_REQUEST:
case DHCP6_STATE_RENEW:
case DHCP6_STATE_REBIND:
diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c
index ab59977a3f..8396407e9f 100644
--- a/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libsystemd-network/sd-dhcp6-lease.c
@@ -226,7 +226,7 @@ int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
if (r < 0)
return 0;
- free(lease->domains);
+ strv_free(lease->domains);
lease->domains = domains;
lease->domains_count = r;
diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c
index d2a826bf6e..b40ba2520c 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.c
+++ b/src/libsystemd/sd-bus/bus-common-errors.c
@@ -38,7 +38,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, EDEADLK),
SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, EDEADLK),
SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, EDEADLK),
- SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_MASKED, ESHUTDOWN),
+ SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_MASKED, ERFKILL),
SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_GENERATED, EADDRNOTAVAIL),
SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_LINKED, ELOOP),
SD_BUS_ERROR_MAP(BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, EBADR),
@@ -47,6 +47,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_SCOPE_NOT_RUNNING, EHOSTDOWN),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DYNAMIC_USER, ESRCH),
SD_BUS_ERROR_MAP(BUS_ERROR_NOT_REFERENCED, EUNATCH),
+ SD_BUS_ERROR_MAP(BUS_ERROR_DISK_FULL, ENOSPC),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_MACHINE, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_IMAGE, ENOENT),
diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h
index 525b79fa77..4523be05ce 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.h
+++ b/src/libsystemd/sd-bus/bus-common-errors.h
@@ -43,6 +43,7 @@
#define BUS_ERROR_SCOPE_NOT_RUNNING "org.freedesktop.systemd1.ScopeNotRunning"
#define BUS_ERROR_NO_SUCH_DYNAMIC_USER "org.freedesktop.systemd1.NoSuchDynamicUser"
#define BUS_ERROR_NOT_REFERENCED "org.freedesktop.systemd1.NotReferenced"
+#define BUS_ERROR_DISK_FULL "org.freedesktop.systemd1.DiskFull"
#define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine"
#define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage"
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index bc5e92f8fe..efeadf0cd4 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -560,7 +560,7 @@ int device_read_uevent_file(sd_device *device) {
value = &uevent[i];
state = VALUE;
- /* fall through to handle empty property */
+ /* fall through */ /* to handle empty property */
case VALUE:
if (strchr(NEWLINE, uevent[i])) {
uevent[i] = '\0';
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index f94959adac..b4686d0065 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -730,7 +730,6 @@ static void event_unmask_signal_data(sd_event *e, struct signal_data *d, int sig
/* If all the mask is all-zero we can get rid of the structure */
hashmap_remove(e->signal_data, &d->priority);
- assert(!d->current);
safe_close(d->fd);
free(d);
return;
@@ -2226,11 +2225,16 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) {
}
static int source_dispatch(sd_event_source *s) {
+ EventSourceType saved_type;
int r = 0;
assert(s);
assert(s->pending || s->type == SOURCE_EXIT);
+ /* Save the event source type, here, so that we still know it after the event callback which might invalidate
+ * the event. */
+ saved_type = s->type;
+
if (s->type != SOURCE_DEFER && s->type != SOURCE_EXIT) {
r = source_set_pending(s, false);
if (r < 0)
@@ -2318,7 +2322,7 @@ static int source_dispatch(sd_event_source *s) {
if (r < 0)
log_debug_errno(r, "Event source %s (type %s) returned error, disabling: %m",
- strna(s->description), event_source_type_to_string(s->type));
+ strna(s->description), event_source_type_to_string(saved_type));
if (s->n_ref == 0)
source_free(s);
diff --git a/src/libudev/libudev-util.c b/src/libudev/libudev-util.c
index a9819b9db3..1d73d8f090 100644
--- a/src/libudev/libudev-util.c
+++ b/src/libudev/libudev-util.c
@@ -161,6 +161,20 @@ void util_remove_trailing_chars(char *path, char c)
path[--len] = '\0';
}
+/*
+ * Copy from 'str' to 'to', while removing all leading and trailing whitespace,
+ * and replacing each run of consecutive whitespace with a single underscore.
+ * The chars from 'str' are copied up to the \0 at the end of the string, or
+ * at most 'len' chars. This appends \0 to 'to', at the end of the copied
+ * characters.
+ *
+ * If 'len' chars are copied into 'to', the final \0 is placed at len+1
+ * (i.e. 'to[len] = \0'), so the 'to' buffer must have at least len+1
+ * chars available.
+ *
+ * Note this may be called with 'str' == 'to', i.e. to replace whitespace
+ * in-place in a buffer. This function can handle that situation.
+ */
int util_replace_whitespace(const char *str, char *to, size_t len)
{
size_t i, j;
diff --git a/src/locale/localectl.c b/src/locale/localectl.c
index 81afb4909f..0bd18a5c0b 100644
--- a/src/locale/localectl.c
+++ b/src/locale/localectl.c
@@ -166,6 +166,8 @@ static int show_status(sd_bus *bus, char **args, unsigned n) {
{ "Locale", "as", NULL, offsetof(StatusInfo, locale) },
{}
};
+
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(bus);
@@ -174,9 +176,10 @@ static int show_status(sd_bus *bus, char **args, unsigned n) {
"org.freedesktop.locale1",
"/org/freedesktop/locale1",
map,
+ &error,
&info);
if (r < 0)
- return log_error_errno(r, "Could not get properties: %m");
+ return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
print_overridden_variables();
print_status_info(&info);
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index 4c618ed19e..7dea5c0859 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -240,7 +240,6 @@ static int list_seats(int argc, char *argv[], void *userdata) {
sd_bus *bus = userdata;
unsigned k = 0;
int r;
-
assert(bus);
assert(argv);
@@ -280,35 +279,17 @@ static int list_seats(int argc, char *argv[], void *userdata) {
}
static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit, pid_t leader) {
+ _cleanup_free_ char *cgroup = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_free_ char *path = NULL;
- const char *cgroup;
unsigned c;
int r;
assert(bus);
assert(unit);
- path = unit_dbus_path_from_name(unit);
- if (!path)
- return log_oom();
-
- r = sd_bus_get_property(
- bus,
- "org.freedesktop.systemd1",
- path,
- interface,
- "ControlGroup",
- &error,
- &reply,
- "s");
- if (r < 0)
- return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r));
-
- r = sd_bus_message_read(reply, "s", &cgroup);
+ r = show_cgroup_get_unit_path_and_warn(bus, unit, &cgroup);
if (r < 0)
- return bus_log_parse_error(r);
+ return r;
if (isempty(cgroup))
return 0;
@@ -501,14 +482,15 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li
{}
};
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
char since2[FORMAT_TIMESTAMP_MAX], *s2;
_cleanup_(session_status_info_clear) SessionStatusInfo i = {};
int r;
- r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
+ r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &error, &i);
if (r < 0)
- return log_error_errno(r, "Could not get properties: %m");
+ return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
if (*new_line)
printf("\n");
@@ -630,14 +612,15 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line)
{}
};
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
char since2[FORMAT_TIMESTAMP_MAX], *s2;
_cleanup_(user_status_info_clear) UserStatusInfo i = {};
int r;
- r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
+ r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &error, &i);
if (r < 0)
- return log_error_errno(r, "Could not get properties: %m");
+ return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
if (*new_line)
printf("\n");
@@ -704,12 +687,13 @@ static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line)
{}
};
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(seat_status_info_clear) SeatStatusInfo i = {};
int r;
- r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
+ r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &error, &i);
if (r < 0)
- return log_error_errno(r, "Could not get properties: %m");
+ return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
if (*new_line)
printf("\n");
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 3873bf3e96..ad44ca290e 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -2396,13 +2396,9 @@ static int method_set_wall_message(
if (r == 0)
return 1; /* Will call us back */
- if (isempty(wall_message))
- m->wall_message = mfree(m->wall_message);
- else {
- r = free_and_strdup(&m->wall_message, wall_message);
- if (r < 0)
- return log_oom();
- }
+ r = free_and_strdup(&m->wall_message, empty_to_null(wall_message));
+ if (r < 0)
+ return log_oom();
m->enable_wall_messages = enable_wall_messages;
diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c
index f0d8a1f554..bfbd07309d 100644
--- a/src/login/logind-seat.c
+++ b/src/login/logind-seat.c
@@ -541,8 +541,6 @@ int seat_attach_session(Seat *s, Session *session) {
LIST_PREPEND(sessions_by_seat, s->sessions, session);
seat_assign_position(s, session);
- seat_send_changed(s, "Sessions", NULL);
-
/* On seats with VTs, the VT logic defines which session is active. On
* seats without VTs, we automatically activate new sessions. */
if (!seat_has_vts(s))
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index d8bb1ea0a8..fd7fcf7f2c 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -586,12 +586,10 @@ int session_start(Session *s) {
/* Send signals */
session_send_signal(s, true);
- user_send_changed(s->user, "Sessions", "Display", NULL);
+ user_send_changed(s->user, "Display", NULL);
if (s->seat) {
if (s->seat->active == s)
- seat_send_changed(s->seat, "Sessions", "ActiveSession", NULL);
- else
- seat_send_changed(s->seat, "Sessions", NULL);
+ seat_send_changed(s->seat, "ActiveSession", NULL);
}
return 0;
@@ -698,11 +696,10 @@ int session_finalize(Session *s) {
seat_set_active(s->seat, NULL);
seat_save(s->seat);
- seat_send_changed(s->seat, "Sessions", NULL);
}
user_save(s->user);
- user_send_changed(s->user, "Sessions", "Display", NULL);
+ user_send_changed(s->user, "Display", NULL);
return 0;
}
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index 31a40d47c3..fe4f1b7726 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -477,35 +477,17 @@ static int list_images(int argc, char *argv[], void *userdata) {
}
static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
+ _cleanup_free_ char *cgroup = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_free_ char *path = NULL;
- const char *cgroup;
int r;
unsigned c;
assert(bus);
assert(unit);
- path = unit_dbus_path_from_name(unit);
- if (!path)
- return log_oom();
-
- r = sd_bus_get_property(
- bus,
- "org.freedesktop.systemd1",
- path,
- unit_dbus_interface_from_name(unit),
- "ControlGroup",
- &error,
- &reply,
- "s");
+ r = show_cgroup_get_unit_path_and_warn(bus, unit, &cgroup);
if (r < 0)
- return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r));
-
- r = sd_bus_message_read(reply, "s", &cgroup);
- if (r < 0)
- return bus_log_parse_error(r);
+ return r;
if (isempty(cgroup))
return 0;
@@ -790,6 +772,7 @@ static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bo
{}
};
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(machine_status_info_clear) MachineStatusInfo info = {};
int r;
@@ -802,9 +785,10 @@ static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bo
"org.freedesktop.machine1",
path,
map,
+ &error,
&info);
if (r < 0)
- return log_error_errno(r, "Could not get properties: %m");
+ return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
if (*new_line)
printf("\n");
@@ -980,6 +964,7 @@ static int show_image_info(sd_bus *bus, const char *path, bool *new_line) {
{}
};
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(image_status_info_clear) ImageStatusInfo info = {};
int r;
@@ -991,9 +976,10 @@ static int show_image_info(sd_bus *bus, const char *path, bool *new_line) {
"org.freedesktop.machine1",
path,
map,
+ &error,
&info);
if (r < 0)
- return log_error_errno(r, "Could not get properties: %m");
+ return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
if (*new_line)
printf("\n");
@@ -1047,6 +1033,8 @@ static int show_pool_info(sd_bus *bus) {
.usage = (uint64_t) -1,
.limit = (uint64_t) -1,
};
+
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(bus);
@@ -1055,9 +1043,10 @@ static int show_pool_info(sd_bus *bus) {
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
map,
+ &error,
&info);
if (r < 0)
- return log_error_errno(r, "Could not get properties: %m");
+ return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
print_pool_status_info(bus, &info);
diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c
index 9d79e81918..4b3cac8a22 100644
--- a/src/mount/mount-tool.c
+++ b/src/mount/mount-tool.c
@@ -40,6 +40,7 @@ enum {
ACTION_DEFAULT,
ACTION_MOUNT,
ACTION_AUTOMOUNT,
+ ACTION_UMOUNT,
ACTION_LIST,
} arg_action = ACTION_DEFAULT;
@@ -99,6 +100,7 @@ static void help(void) {
" Set automount unit property\n"
" --bind-device Bind automount unit to device\n"
" --list List mountable block devices\n"
+ " -u --umount Unmount mount points\n"
, program_invocation_short_name);
}
@@ -137,6 +139,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "discover", no_argument, NULL, ARG_DISCOVER },
{ "type", required_argument, NULL, 't' },
{ "options", required_argument, NULL, 'o' },
+ { "fsck", required_argument, NULL, ARG_FSCK },
{ "description", required_argument, NULL, ARG_DESCRIPTION },
{ "property", required_argument, NULL, 'p' },
{ "automount", required_argument, NULL, ARG_AUTOMOUNT },
@@ -144,6 +147,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "automount-property", required_argument, NULL, ARG_AUTOMOUNT_PROPERTY },
{ "bind-device", no_argument, NULL, ARG_BIND_DEVICE },
{ "list", no_argument, NULL, ARG_LIST },
+ { "umount", no_argument, NULL, 'u' },
+ { "unmount", no_argument, NULL, 'u' },
{},
};
@@ -152,7 +157,10 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hqH:M:t:o:p:A", options, NULL)) >= 0)
+ if (strstr(program_invocation_short_name, "systemd-umount"))
+ arg_action = ACTION_UMOUNT;
+
+ while ((c = getopt_long(argc, argv, "hqH:M:t:o:p:Au", options, NULL)) >= 0)
switch (c) {
@@ -263,6 +271,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_action = ACTION_LIST;
break;
+ case 'u':
+ arg_action = ACTION_UMOUNT;
+ break;
+
case '?':
return -EINVAL;
@@ -607,6 +619,89 @@ static int start_transient_automount(
return 0;
}
+static int stop_mount(
+ sd_bus *bus,
+ char **argv,
+ const char *suffix) {
+
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
+ _cleanup_free_ char *mount_unit = NULL;
+ int r;
+
+ if (!arg_no_block) {
+ r = bus_wait_for_jobs_new(bus, &w);
+ if (r < 0)
+ return log_error_errno(r, "Could not watch jobs: %m");
+ }
+
+ r = unit_name_from_path(arg_mount_where, suffix, &mount_unit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make mount unit name: %m");
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "StopUnit");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ /* Name and mode */
+ r = sd_bus_message_append(m, "ss", mount_unit, "fail");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ polkit_agent_open_if_enabled();
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ if (r < 0)
+ return log_error_errno(r, "Failed to stop mount unit: %s", bus_error_message(&error, r));
+
+ if (w) {
+ const char *object;
+
+ r = sd_bus_message_read(reply, "o", &object);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = bus_wait_for_jobs_one(w, object, arg_quiet);
+ if (r < 0)
+ return r;
+ }
+
+ if (!arg_quiet)
+ log_info("Stopped unit %s%s%s for mount point: %s%s%s",
+ ansi_highlight(), mount_unit, ansi_normal(),
+ ansi_highlight(), arg_mount_where, ansi_normal());
+
+ return 0;
+}
+
+static int stop_mounts(
+ sd_bus *bus,
+ char **argv) {
+
+ int r;
+
+ r = stop_mount(bus, argv + optind, ".mount");
+ if (r < 0)
+ return r;
+
+ r = stop_mount(bus, argv + optind, ".automount");
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
static int acquire_mount_type(struct udev_device *d) {
const char *v;
@@ -1093,6 +1188,10 @@ int main(int argc, char* argv[]) {
r = start_transient_automount(bus, argv + optind);
break;
+ case ACTION_UMOUNT:
+ r = stop_mounts(bus, argv + optind);
+ break;
+
default:
assert_not_reached("Unexpected action.");
}
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 2b698d9531..ffd2e18a45 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -809,8 +809,8 @@ int config_parse_label(
if (r < 0)
return r;
- if (!ifname_valid(rvalue)) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not valid or too long, ignoring assignment: %s", rvalue);
+ if (strlen(rvalue) >= IFNAMSIZ) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is too long, ignoring assignment: %s", rvalue);
return 0;
}
diff --git a/src/network/networkd-ipv6-proxy-ndp.c b/src/network/networkd-ipv6-proxy-ndp.c
new file mode 100644
index 0000000000..11c1cd9268
--- /dev/null
+++ b/src/network/networkd-ipv6-proxy-ndp.c
@@ -0,0 +1,209 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2017 Florian Klink <flokli@flokli.de>
+
+ 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 <netinet/ether.h>
+#include <linux/if.h>
+#include <unistd.h>
+
+#include "fileio.h"
+#include "netlink-util.h"
+#include "networkd-ipv6-proxy-ndp.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
+#include "networkd-network.h"
+#include "string-util.h"
+
+static bool ipv6_proxy_ndp_is_needed(Link *link) {
+ assert(link);
+
+ if (link->flags & IFF_LOOPBACK)
+ return false;
+
+ if (!link->network)
+ return false;
+
+ if (link->network->n_ipv6_proxy_ndp_addresses == 0)
+ return false;
+
+ return true;
+}
+
+static int ipv6_proxy_ndp_set(Link *link) {
+ const char *p = NULL;
+ int r, v;
+
+ assert(link);
+
+ v = ipv6_proxy_ndp_is_needed(link);
+ p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/proxy_ndp");
+
+ r = write_string_file(p, one_zero(v), WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Cannot configure proxy NDP for interface: %m");
+
+ return 0;
+}
+
+int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress **ret) {
+ _cleanup_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL;
+
+ assert(network);
+ assert(ret);
+
+ /* allocate space for IPv6ProxyNDPAddress entry */
+ ipv6_proxy_ndp_address = new0(IPv6ProxyNDPAddress, 1);
+ if (!ipv6_proxy_ndp_address)
+ return -ENOMEM;
+
+ ipv6_proxy_ndp_address->network = network;
+
+ LIST_PREPEND(ipv6_proxy_ndp_addresses, network->ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address);
+ network->n_ipv6_proxy_ndp_addresses++;
+
+ *ret = ipv6_proxy_ndp_address;
+ ipv6_proxy_ndp_address = NULL;
+
+ return 0;
+}
+
+void ipv6_proxy_ndp_address_free(IPv6ProxyNDPAddress *ipv6_proxy_ndp_address) {
+ if (!ipv6_proxy_ndp_address)
+ return;
+
+ if (ipv6_proxy_ndp_address->network) {
+ LIST_REMOVE(ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address->network->ipv6_proxy_ndp_addresses,
+ ipv6_proxy_ndp_address);
+
+ assert(ipv6_proxy_ndp_address->network->n_ipv6_proxy_ndp_addresses > 0);
+ ipv6_proxy_ndp_address->network->n_ipv6_proxy_ndp_addresses--;
+ }
+
+ free(ipv6_proxy_ndp_address);
+}
+
+int config_parse_ipv6_proxy_ndp_address(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = userdata;
+ _cleanup_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL;
+ int r;
+ union in_addr_union buffer;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = ipv6_proxy_ndp_address_new_static(network, &ipv6_proxy_ndp_address);
+ if (r < 0)
+ return r;
+
+ r = in_addr_from_string(AF_INET6, rvalue, &buffer);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 proxy NDP address, ignoring: %s",
+ rvalue);
+ return 0;
+ }
+
+ r = in_addr_is_null(AF_INET6, &buffer);
+ if (r != 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "IPv6 proxy NDP address can not be the ANY address, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ ipv6_proxy_ndp_address->in_addr = buffer.in6;
+ ipv6_proxy_ndp_address = NULL;
+
+ return 0;
+}
+
+static int set_ipv6_proxy_ndp_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+ Link *link = userdata;
+ int r;
+
+ assert(link);
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -EEXIST)
+ log_link_error_errno(link, r, "Could not add IPv6 proxy ndp address entry: %m");
+
+ return 1;
+}
+
+/* send a request to the kernel to add a IPv6 Proxy entry to the neighbour table */
+int ipv6_proxy_ndp_address_configure(Link *link, IPv6ProxyNDPAddress *ipv6_proxy_ndp_address) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ sd_netlink *rtnl;
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(link->manager);
+ assert(ipv6_proxy_ndp_address);
+
+ rtnl = link->manager->rtnl;
+
+ /* create new netlink message */
+ r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, AF_INET6);
+ if (r < 0)
+ return rtnl_log_create_error(r);
+
+ r = sd_rtnl_message_neigh_set_flags(req, NLM_F_REQUEST | NTF_PROXY);
+ if (r < 0)
+ return rtnl_log_create_error(r);
+
+ r = sd_netlink_message_append_in6_addr(req, NDA_DST, &ipv6_proxy_ndp_address->in_addr);
+ if (r < 0)
+ return rtnl_log_create_error(r);
+
+ r = sd_netlink_call_async(rtnl, req, set_ipv6_proxy_ndp_address_handler, link, 0, NULL);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+ return 0;
+}
+
+/* configure all ipv6 proxy ndp addresses */
+int ipv6_proxy_ndp_addresses_configure(Link *link) {
+ IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
+ int r;
+
+ /* enable or disable proxy_ndp itself depending on whether ipv6_proxy_ndp_addresses are set or not */
+ r = ipv6_proxy_ndp_set(link);
+ if (r != 0)
+ return r;
+
+ LIST_FOREACH(ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address, link->network->ipv6_proxy_ndp_addresses) {
+ r = ipv6_proxy_ndp_address_configure(link, ipv6_proxy_ndp_address);
+ if (r != 0)
+ return r;
+ }
+ return 0;
+}
diff --git a/src/network/networkd-ipv6-proxy-ndp.h b/src/network/networkd-ipv6-proxy-ndp.h
new file mode 100644
index 0000000000..f09169f40f
--- /dev/null
+++ b/src/network/networkd-ipv6-proxy-ndp.h
@@ -0,0 +1,44 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2017 Florian Klink <flokli@flokli.de>
+
+ 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 "list.h"
+#include "macro.h"
+
+typedef struct Network Network;
+typedef struct IPv6ProxyNDPAddress IPv6ProxyNDPAddress;
+typedef struct Link Link;
+
+struct IPv6ProxyNDPAddress {
+ Network *network;
+ struct in6_addr in_addr;
+
+ LIST_FIELDS(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
+};
+
+
+int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress ** ipv6_proxy_ndp_address);
+void ipv6_proxy_ndp_address_free(IPv6ProxyNDPAddress *ipv6_proxy_ndp_address);
+int ipv6_proxy_ndp_address_configure(Link *link, IPv6ProxyNDPAddress *ipv6_proxy_ndp_address);
+int ipv6_proxy_ndp_addresses_configure(Link *link);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(IPv6ProxyNDPAddress*, ipv6_proxy_ndp_address_free);
+
+int config_parse_ipv6_proxy_ndp_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index b993d27c2f..0c1229336b 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -28,6 +28,7 @@
#include "fileio.h"
#include "netlink-util.h"
#include "network-internal.h"
+#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-lldp-tx.h"
#include "networkd-manager.h"
#include "networkd-ndisc.h"
@@ -2448,6 +2449,10 @@ static int link_configure(Link *link) {
if (r < 0)
return r;
+ r = ipv6_proxy_ndp_addresses_configure(link);
+ if (r < 0)
+ return r;
+
r = link_set_ipv4_forward(link);
if (r < 0)
return r;
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 7b54e81fb8..68052ba544 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -67,6 +67,7 @@ Network.ActiveSlave, config_parse_bool,
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.IPv6ProxyNDPAddress, config_parse_ipv6_proxy_ndp_address, 0, 0
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.c b/src/network/networkd-network.c
index bc4dc95ff9..92062ca00c 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -70,6 +70,7 @@ static int network_load_one(Manager *manager, const char *filename) {
LIST_HEAD_INIT(network->static_addresses);
LIST_HEAD_INIT(network->static_routes);
LIST_HEAD_INIT(network->static_fdb_entries);
+ LIST_HEAD_INIT(network->ipv6_proxy_ndp_addresses);
network->stacked_netdevs = hashmap_new(&string_hash_ops);
if (!network->stacked_netdevs)
@@ -152,6 +153,7 @@ static int network_load_one(Manager *manager, const char *filename) {
"DHCPv4\0" /* compat */
"DHCPServer\0"
"IPv6AcceptRA\0"
+ "IPv6NDPProxyAddress\0"
"Bridge\0"
"BridgeFDB\0"
"BridgeVLAN\0",
@@ -224,6 +226,7 @@ void network_free(Network *network) {
Route *route;
Address *address;
FdbEntry *fdb_entry;
+ IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
Iterator i;
if (!network)
@@ -268,6 +271,9 @@ void network_free(Network *network) {
while ((fdb_entry = network->static_fdb_entries))
fdb_entry_free(fdb_entry);
+ while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
+ ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
+
hashmap_free(network->addresses_by_section);
hashmap_free(network->routes_by_section);
hashmap_free(network->fdb_entries_by_section);
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index b7da9d22d4..f06828a899 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -31,6 +31,7 @@
#include "networkd-brvlan.h"
#include "networkd-fdb.h"
#include "networkd-lldp-tx.h"
+#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-route.h"
#include "networkd-util.h"
#include "netdev/netdev.h"
@@ -188,10 +189,12 @@ struct Network {
LIST_HEAD(Address, static_addresses);
LIST_HEAD(Route, static_routes);
LIST_HEAD(FdbEntry, static_fdb_entries);
+ LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
unsigned n_static_addresses;
unsigned n_static_routes;
unsigned n_static_fdb_entries;
+ unsigned n_ipv6_proxy_ndp_addresses;
Hashmap *addresses_by_section;
Hashmap *routes_by_section;
diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf
index c0fa4bfa1f..e5fdf63162 100644
--- a/src/nspawn/nspawn-gperf.gperf
+++ b/src/nspawn/nspawn-gperf.gperf
@@ -26,6 +26,7 @@ Exec.KillSignal, config_parse_signal, 0, offsetof(Settings,
Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality)
Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id)
Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, working_directory)
+Exec.PivotRoot, config_parse_pivot_root, 0, 0
Exec.PrivateUsers, config_parse_private_users, 0, 0
Exec.NotifyReady, config_parse_bool, 0, offsetof(Settings, notify_ready)
Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only)
diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c
index 72c007f204..4b2838b752 100644
--- a/src/nspawn/nspawn-mount.c
+++ b/src/nspawn/nspawn-mount.c
@@ -1349,3 +1349,116 @@ fail:
(void) rmdir(template);
return r;
}
+
+/* Expects *pivot_root_new and *pivot_root_old to be initialised to allocated memory or NULL. */
+int pivot_root_parse(char **pivot_root_new, char **pivot_root_old, const char *s) {
+ _cleanup_free_ char *root_new = NULL, *root_old = NULL;
+ const char *p = s;
+ int r;
+
+ assert(pivot_root_new);
+ assert(pivot_root_old);
+
+ r = extract_first_word(&p, &root_new, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ if (isempty(p))
+ root_old = NULL;
+ else {
+ root_old = strdup(p);
+ if (!root_old)
+ return -ENOMEM;
+ }
+
+ if (!path_is_absolute(root_new))
+ return -EINVAL;
+ if (root_old && !path_is_absolute(root_old))
+ return -EINVAL;
+
+ free_and_replace(*pivot_root_new, root_new);
+ free_and_replace(*pivot_root_old, root_old);
+
+ return 0;
+}
+
+int setup_pivot_root(const char *directory, const char *pivot_root_new, const char *pivot_root_old) {
+ _cleanup_free_ char *directory_pivot_root_new = NULL;
+ _cleanup_free_ char *pivot_tmp_pivot_root_old = NULL;
+ char pivot_tmp[] = "/tmp/nspawn-pivot-XXXXXX";
+ bool remove_pivot_tmp = false;
+ int r;
+
+ assert(directory);
+
+ if (!pivot_root_new)
+ return 0;
+
+ /* Pivot pivot_root_new to / and the existing / to pivot_root_old.
+ * If pivot_root_old is NULL, the existing / disappears.
+ * This requires a temporary directory, pivot_tmp, which is
+ * not a child of either.
+ *
+ * This is typically used for OSTree-style containers, where
+ * the root partition contains several sysroots which could be
+ * run. Normally, one would be chosen by the bootloader and
+ * pivoted to / by initramfs.
+ *
+ * For example, for an OSTree deployment, pivot_root_new
+ * would be: /ostree/deploy/$os/deploy/$checksum. Note that this
+ * code doesn’t do the /var mount which OSTree expects: use
+ * --bind +/sysroot/ostree/deploy/$os/var:/var for that.
+ *
+ * So in the OSTree case, we’ll end up with something like:
+ * - directory = /tmp/nspawn-root-123456
+ * - pivot_root_new = /ostree/deploy/os/deploy/123abc
+ * - pivot_root_old = /sysroot
+ * - directory_pivot_root_new =
+ * /tmp/nspawn-root-123456/ostree/deploy/os/deploy/123abc
+ * - pivot_tmp = /tmp/nspawn-pivot-123456
+ * - pivot_tmp_pivot_root_old = /tmp/nspawn-pivot-123456/sysroot
+ *
+ * Requires all file systems at directory and below to be mounted
+ * MS_PRIVATE or MS_SLAVE so they can be moved.
+ */
+ directory_pivot_root_new = prefix_root(directory, pivot_root_new);
+
+ /* Remount directory_pivot_root_new to make it movable. */
+ r = mount_verbose(LOG_ERR, directory_pivot_root_new, directory_pivot_root_new, NULL, MS_BIND, NULL);
+ if (r < 0)
+ goto done;
+
+ if (pivot_root_old) {
+ if (!mkdtemp(pivot_tmp)) {
+ r = log_error_errno(errno, "Failed to create temporary directory: %m");
+ goto done;
+ }
+
+ remove_pivot_tmp = true;
+ pivot_tmp_pivot_root_old = prefix_root(pivot_tmp, pivot_root_old);
+
+ r = mount_verbose(LOG_ERR, directory_pivot_root_new, pivot_tmp, NULL, MS_MOVE, NULL);
+ if (r < 0)
+ goto done;
+
+ r = mount_verbose(LOG_ERR, directory, pivot_tmp_pivot_root_old, NULL, MS_MOVE, NULL);
+ if (r < 0)
+ goto done;
+
+ r = mount_verbose(LOG_ERR, pivot_tmp, directory, NULL, MS_MOVE, NULL);
+ if (r < 0)
+ goto done;
+ } else {
+ r = mount_verbose(LOG_ERR, directory_pivot_root_new, directory, NULL, MS_MOVE, NULL);
+ if (r < 0)
+ goto done;
+ }
+
+done:
+ if (remove_pivot_tmp)
+ (void) rmdir(pivot_tmp);
+
+ return r;
+}
diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h
index 6b33fbff57..2777d2169b 100644
--- a/src/nspawn/nspawn-mount.h
+++ b/src/nspawn/nspawn-mount.h
@@ -70,3 +70,6 @@ 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);
+
+int pivot_root_parse(char **pivot_root_new, char **pivot_root_old, const char *s);
+int setup_pivot_root(const char *directory, const char *pivot_root_new, const char *pivot_root_old);
diff --git a/src/nspawn/nspawn-patch-uid.c b/src/nspawn/nspawn-patch-uid.c
index ded5866d05..1a3f129db0 100644
--- a/src/nspawn/nspawn-patch-uid.c
+++ b/src/nspawn/nspawn-patch-uid.c
@@ -375,7 +375,7 @@ static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift
FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) {
struct stat fst;
- if (STR_IN_SET(de->d_name, ".", ".."))
+ if (dot_or_dot_dot(de->d_name))
continue;
if (fstatat(dirfd(d), de->d_name, &fst, AT_SYMLINK_NOFOLLOW) < 0) {
diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c
index 22b74d88e4..5217d10665 100644
--- a/src/nspawn/nspawn-settings.c
+++ b/src/nspawn/nspawn-settings.c
@@ -90,6 +90,8 @@ Settings* settings_free(Settings *s) {
strv_free(s->parameters);
strv_free(s->environment);
free(s->user);
+ free(s->pivot_root_new);
+ free(s->pivot_root_old);
free(s->working_directory);
strv_free(s->network_interfaces);
@@ -237,6 +239,34 @@ int config_parse_id128(
return 0;
}
+int config_parse_pivot_root(
+ 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) {
+
+ Settings *settings = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = pivot_root_parse(&settings->pivot_root_new, &settings->pivot_root_old, rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid pivot root mount specification %s: %m", rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
int config_parse_bind(
const char *unit,
const char *filename,
diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h
index e9ea087191..021403258f 100644
--- a/src/nspawn/nspawn-settings.h
+++ b/src/nspawn/nspawn-settings.h
@@ -57,7 +57,8 @@ typedef enum SettingsMask {
SETTING_WORKING_DIRECTORY = 1 << 12,
SETTING_USERNS = 1 << 13,
SETTING_NOTIFY_READY = 1 << 14,
- _SETTINGS_MASK_ALL = (1 << 15) -1
+ SETTING_PIVOT_ROOT = 1 << 15,
+ _SETTINGS_MASK_ALL = (1 << 16) -1
} SettingsMask;
typedef struct Settings {
@@ -72,6 +73,8 @@ typedef struct Settings {
unsigned long personality;
sd_id128_t machine_id;
char *working_directory;
+ char *pivot_root_new;
+ char *pivot_root_old;
UserNamespaceMode userns_mode;
uid_t uid_shift, uid_range;
bool notify_ready;
@@ -109,6 +112,7 @@ int config_parse_capability(const char *unit, const char *filename, unsigned lin
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);
int config_parse_expose_port(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_volatile_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_pivot_root(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_bind(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_tmpfs(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_overlay(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-stub-pid1.c b/src/nspawn/nspawn-stub-pid1.c
index 38ab37367e..7f2a53a245 100644
--- a/src/nspawn/nspawn-stub-pid1.c
+++ b/src/nspawn/nspawn-stub-pid1.c
@@ -87,7 +87,7 @@ int stub_pid1(sd_id128_t uuid) {
/* Flush out /proc/self/environ, so that we don't leak the environment from the host into the container. Also,
* set $container= and $container_uuid= so that clients in the container that query it from /proc/1/environ
- * find them set set. */
+ * find them set. */
sd_id128_to_string(uuid, new_environment + sizeof(new_environment) - SD_ID128_STRING_MAX);
reset_environ(new_environment, sizeof(new_environment));
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 532be148a6..b172b44933 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -132,6 +132,8 @@ typedef enum LinkJournal {
static char *arg_directory = NULL;
static char *arg_template = NULL;
static char *arg_chdir = NULL;
+static char *arg_pivot_root_new = NULL;
+static char *arg_pivot_root_old = NULL;
static char *arg_user = NULL;
static sd_id128_t arg_uuid = {};
static char *arg_machine = NULL;
@@ -221,6 +223,8 @@ static void help(void) {
" -a --as-pid2 Maintain a stub init as PID1, invoke binary as PID2\n"
" -b --boot Boot up full system (i.e. invoke init)\n"
" --chdir=PATH Set working directory in the container\n"
+ " --pivot-root=PATH[:PATH]\n"
+ " Pivot root to given directory in the container\n"
" -u --user=USER Run the command under specified user or uid\n"
" -M --machine=NAME Set the machine name for the container\n"
" --uuid=UUID Set a specific machine UUID for the container\n"
@@ -427,6 +431,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_KILL_SIGNAL,
ARG_SETTINGS,
ARG_CHDIR,
+ ARG_PIVOT_ROOT,
ARG_PRIVATE_USERS_CHOWN,
ARG_NOTIFY_READY,
ARG_ROOT_HASH,
@@ -478,6 +483,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL },
{ "settings", required_argument, NULL, ARG_SETTINGS },
{ "chdir", required_argument, NULL, ARG_CHDIR },
+ { "pivot-root", required_argument, NULL, ARG_PIVOT_ROOT },
{ "notify-ready", required_argument, NULL, ARG_NOTIFY_READY },
{ "root-hash", required_argument, NULL, ARG_ROOT_HASH },
{}
@@ -676,9 +682,8 @@ static int parse_argv(int argc, char *argv[]) {
r = free_and_strdup(&arg_machine, optarg);
if (r < 0)
return log_oom();
-
- break;
}
+ break;
case 'Z':
arg_selinux_context = optarg;
@@ -1013,6 +1018,14 @@ static int parse_argv(int argc, char *argv[]) {
arg_settings_mask |= SETTING_WORKING_DIRECTORY;
break;
+ case ARG_PIVOT_ROOT:
+ r = pivot_root_parse(&arg_pivot_root_new, &arg_pivot_root_old, optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --pivot-root= argument %s: %m", optarg);
+
+ arg_settings_mask |= SETTING_PIVOT_ROOT;
+ break;
+
case ARG_NOTIFY_READY:
r = parse_boolean(optarg);
if (r < 0) {
@@ -1918,7 +1931,7 @@ static int wait_for_container(pid_t pid, ContainerStatus *container) {
return 0;
}
- /* CLD_KILLED fallthrough */
+ /* fall through */
case CLD_DUMPED:
log_error("Container %s terminated by signal %s.", arg_machine, signal_to_string(status.si_status));
@@ -2131,6 +2144,7 @@ static int inner_child(
NULL, /* NOTIFY_SOCKET */
NULL
};
+ const char *exec_target;
_cleanup_strv_free_ char **env_use = NULL;
int r;
@@ -2325,20 +2339,25 @@ static int inner_child(
a[0] = (char*) "/sbin/init";
execve(a[0], a, env_use);
- } else if (!strv_isempty(arg_parameters))
+
+ exec_target = "/usr/lib/systemd/systemd, /lib/systemd/systemd, /sbin/init";
+ } else if (!strv_isempty(arg_parameters)) {
+ exec_target = arg_parameters[0];
execvpe(arg_parameters[0], arg_parameters, env_use);
- else {
+ } else {
if (!arg_chdir)
/* If we cannot change the directory, we'll end up in /, that is expected. */
(void) chdir(home ?: "/root");
execle("/bin/bash", "-bash", NULL, env_use);
execle("/bin/sh", "-sh", NULL, env_use);
+
+ exec_target = "/bin/bash, /bin/sh";
}
r = -errno;
(void) log_open();
- return log_error_errno(r, "execv() failed: %m");
+ return log_error_errno(r, "execv(%s) failed: %m", exec_target);
}
static int setup_sd_notify_child(void) {
@@ -2488,6 +2507,13 @@ static int outer_child(
if (r < 0)
return r;
+ r = setup_pivot_root(
+ directory,
+ arg_pivot_root_new,
+ arg_pivot_root_old);
+ if (r < 0)
+ return r;
+
r = setup_volatile(
directory,
arg_volatile_mode,
@@ -2910,6 +2936,12 @@ static int load_settings(void) {
settings->parameters = NULL;
}
+ if ((arg_settings_mask & SETTING_PIVOT_ROOT) == 0 &&
+ settings->pivot_root_new) {
+ free_and_replace(arg_pivot_root_new, settings->pivot_root_new);
+ free_and_replace(arg_pivot_root_old, settings->pivot_root_old);
+ }
+
if ((arg_settings_mask & SETTING_WORKING_DIRECTORY) == 0 &&
settings->working_directory) {
free(arg_chdir);
@@ -3475,53 +3507,6 @@ static int run(int master,
return 1; /* loop again */
}
-static int load_root_hash(const char *image) {
- _cleanup_free_ char *text = NULL, *fn = NULL;
- char *n, *e;
- void *k;
- size_t l;
- int r;
-
- assert_se(image);
-
- /* Try to load the root hash from a file next to the image file if it exists. */
-
- if (arg_root_hash)
- return 0;
-
- fn = new(char, strlen(image) + strlen(".roothash") + 1);
- if (!fn)
- return log_oom();
-
- n = stpcpy(fn, image);
- e = endswith(fn, ".raw");
- if (e)
- n = e;
-
- strcpy(n, ".roothash");
-
- r = read_one_line_file(fn, &text);
- if (r == -ENOENT)
- return 0;
- if (r < 0) {
- log_warning_errno(r, "Failed to read %s, ignoring: %m", fn);
- return 0;
- }
-
- r = unhexmem(text, strlen(text), &k, &l);
- if (r < 0)
- return log_error_errno(r, "Invalid root hash: %s", text);
- if (l < sizeof(sd_id128_t)) {
- free(k);
- return log_error_errno(r, "Root hash too short: %s", text);
- }
-
- arg_root_hash = k;
- arg_root_hash_size = l;
-
- return 0;
-}
-
int main(int argc, char *argv[]) {
_cleanup_free_ char *console = NULL;
@@ -3737,9 +3722,13 @@ int main(int argc, char *argv[]) {
goto finish;
}
- r = load_root_hash(arg_image);
- if (r < 0)
- goto finish;
+ if (!arg_root_hash) {
+ r = root_hash_load(arg_image, &arg_root_hash, &arg_root_hash_size);
+ if (r < 0) {
+ log_error_errno(r, "Failed to load root hash file for %s: %m", arg_image);
+ goto finish;
+ }
+ }
}
if (!mkdtemp(tmprootdir)) {
@@ -3910,6 +3899,8 @@ finish:
free(arg_image);
free(arg_machine);
free(arg_user);
+ free(arg_pivot_root_new);
+ free(arg_pivot_root_old);
free(arg_chdir);
strv_free(arg_setenv);
free(arg_network_bridge);
diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c
index 11c27575c0..326672cab5 100644
--- a/src/nss-myhostname/nss-myhostname.c
+++ b/src/nss-myhostname/nss-myhostname.c
@@ -55,7 +55,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
_cleanup_free_ struct local_address *addresses = NULL;
_cleanup_free_ char *hn = NULL;
const char *canonical = NULL;
- int n_addresses = 0, lo_ifi;
+ int n_addresses = 0;
uint32_t local_address_ipv4;
struct local_address *a;
size_t l, idx, ms;
@@ -111,9 +111,6 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
local_address_ipv4 = LOCALADDRESS_IPV4;
}
- /* If this call fails we fill in 0 as scope. Which is fine */
- lo_ifi = n_addresses <= 0 ? LOOPBACK_IFINDEX : 0;
-
l = strlen(canonical);
ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
if (buflen < ms) {
@@ -135,7 +132,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
r_tuple->name = r_name;
r_tuple->family = AF_INET6;
memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
- r_tuple->scopeid = (uint32_t) lo_ifi;
+ r_tuple->scopeid = 0;
idx += ALIGN(sizeof(struct gaih_addrtuple));
r_tuple_prev = r_tuple;
@@ -146,7 +143,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
r_tuple->name = r_name;
r_tuple->family = AF_INET;
*(uint32_t*) r_tuple->addr = local_address_ipv4;
- r_tuple->scopeid = (uint32_t) lo_ifi;
+ r_tuple->scopeid = 0;
idx += ALIGN(sizeof(struct gaih_addrtuple));
r_tuple_prev = r_tuple;
@@ -158,7 +155,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
r_tuple->next = r_tuple_prev;
r_tuple->name = r_name;
r_tuple->family = a->family;
- r_tuple->scopeid = a->ifindex;
+ r_tuple->scopeid = a->family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&a->address.in6) ? a->ifindex : 0;
memcpy(r_tuple->addr, &a->address, 16);
idx += ALIGN(sizeof(struct gaih_addrtuple));
diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c
index d46a3afe91..ce8d59d390 100644
--- a/src/nss-resolve/nss-resolve.c
+++ b/src/nss-resolve/nss-resolve.c
@@ -110,6 +110,20 @@ static int count_addresses(sd_bus_message *m, int af, const char **canonical) {
return c;
}
+static uint32_t ifindex_to_scopeid(int family, const void *a, int ifindex) {
+ struct in6_addr in6;
+
+ if (family != AF_INET6)
+ return 0;
+
+ /* Some apps can't deal with the scope ID attached to non-link-local addresses. Hence, let's suppress that. */
+
+ assert(sizeof(in6) == FAMILY_ADDRESS_SIZE(AF_INET));
+ memcpy(&in6, a, sizeof(struct in6_addr));
+
+ return IN6_IS_ADDR_LINKLOCAL(&in6) ? ifindex : 0;
+}
+
enum nss_status _nss_resolve_gethostbyname4_r(
const char *name,
struct gaih_addrtuple **pat,
@@ -245,7 +259,7 @@ enum nss_status _nss_resolve_gethostbyname4_r(
r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
r_tuple->name = r_name;
r_tuple->family = family;
- r_tuple->scopeid = ifindex;
+ r_tuple->scopeid = ifindex_to_scopeid(family, a, ifindex);
memcpy(r_tuple->addr, a, sz);
idx += ALIGN(sizeof(struct gaih_addrtuple));
diff --git a/src/reply-password/reply-password.c b/src/reply-password/reply-password.c
index 17eab9772e..a17c8a62b8 100644
--- a/src/reply-password/reply-password.c
+++ b/src/reply-password/reply-password.c
@@ -90,7 +90,7 @@ int main(int argc, char *argv[]) {
r = send_on_socket(fd, argv[2], packet, length);
finish:
- memory_erase(packet, sizeof(packet));
+ explicit_bzero(packet, sizeof(packet));
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c
index 0c62e5b385..3cec36817a 100644
--- a/src/resolve/resolve-tool.c
+++ b/src/resolve/resolve-tool.c
@@ -1186,6 +1186,7 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, bool *empt
{}
};
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *ifi = NULL, *p = NULL;
char ifname[IF_NAMESIZE] = "";
char **i;
@@ -1213,9 +1214,10 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, bool *empt
"org.freedesktop.resolve1",
p,
property_map,
+ &error,
&link_info);
if (r < 0) {
- log_error_errno(r, "Failed to get link data for %i: %m", ifindex);
+ log_error_errno(r, "Failed to get link data for %i: %s", ifindex, bus_error_message(&error, r));
goto finish;
}
@@ -1405,6 +1407,7 @@ static int status_global(sd_bus *bus, bool *empty_line) {
{}
};
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char **i;
int r;
@@ -1415,9 +1418,10 @@ static int status_global(sd_bus *bus, bool *empty_line) {
"org.freedesktop.resolve1",
"/org/freedesktop/resolve1",
property_map,
+ &error,
&global_info);
if (r < 0) {
- log_error_errno(r, "Failed to get global data: %m");
+ log_error_errno(r, "Failed to get global data: %s", bus_error_message(&error, r));
goto finish;
}
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 2ca65e6953..2c50109388 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -211,7 +211,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
r = sd_bus_message_append(
reply, "st",
normalized,
- SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
+ SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, dns_query_fully_authenticated(q)));
if (r < 0)
goto finish;
@@ -439,7 +439,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
+ r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, dns_query_fully_authenticated(q)));
if (r < 0)
goto finish;
@@ -605,7 +605,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
+ r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, dns_query_fully_authenticated(q)));
if (r < 0)
goto finish;
@@ -979,7 +979,7 @@ static void resolve_service_all_complete(DnsQuery *q) {
reply,
"ssst",
name, type, domain,
- SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
+ SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, dns_query_fully_authenticated(q)));
if (r < 0)
goto finish;
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index e03db4d003..c58845c3b6 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -403,6 +403,7 @@ DnsQuery *dns_query_free(DnsQuery *q) {
sd_bus_track_unref(q->bus_track);
dns_packet_unref(q->request_dns_packet);
+ dns_packet_unref(q->reply_dns_packet);
if (q->request_dns_stream) {
/* Detach the stream from our query, in case something else keeps a reference to it. */
@@ -1028,6 +1029,9 @@ int dns_query_process_cname(DnsQuery *q) {
if (q->flags & SD_RESOLVED_NO_CNAME)
return -ELOOP;
+ if (!q->answer_authenticated)
+ q->previous_redirect_unauthenticated = true;
+
/* OK, let's actually follow the CNAME */
r = dns_query_cname_redirect(q, cname);
if (r < 0)
@@ -1115,3 +1119,9 @@ const char *dns_query_string(DnsQuery *q) {
return dns_question_first_name(q->question_idna);
}
+
+bool dns_query_fully_authenticated(DnsQuery *q) {
+ assert(q);
+
+ return q->answer_authenticated && !q->previous_redirect_unauthenticated;
+}
diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h
index 49a35b846b..b8ea48f6af 100644
--- a/src/resolve/resolved-dns-query.h
+++ b/src/resolve/resolved-dns-query.h
@@ -71,7 +71,6 @@ struct DnsQuery {
* family */
bool suppress_unroutable_family;
-
/* If true, the RR TTLs of the answer will be clamped by their current left validity in the cache */
bool clamp_ttl;
@@ -90,6 +89,7 @@ struct DnsQuery {
int answer_family;
DnsSearchDomain *answer_search_domain;
int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */
+ bool previous_redirect_unauthenticated;
/* Bus client information */
sd_bus_message *request;
@@ -102,6 +102,7 @@ struct DnsQuery {
/* DNS stub information */
DnsPacket *request_dns_packet;
DnsStream *request_dns_stream;
+ DnsPacket *reply_dns_packet;
/* Completion callback */
void (*complete)(DnsQuery* q);
@@ -139,3 +140,5 @@ DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol);
const char *dns_query_string(DnsQuery *q);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuery*, dns_query_free);
+
+bool dns_query_fully_authenticated(DnsQuery *q);
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 22c64e8491..10562d03ec 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -451,18 +451,22 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_EDNS0;
} else if (s->n_failed_udp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
- s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_UDP) {
+ s->possible_feature_level >= (dns_server_get_dnssec_mode(s) == DNSSEC_YES ? DNS_SERVER_FEATURE_LEVEL_LARGE : DNS_SERVER_FEATURE_LEVEL_UDP)) {
/* We lost too many UDP packets in a row, and are on a feature level of UDP or higher. If the
* packets are lost, maybe the server cannot parse them, hence downgrading sounds like a good
- * idea. We might downgrade all the way down to TCP this way. */
+ * idea. We might downgrade all the way down to TCP this way.
+ *
+ * If strict DNSSEC mode is used we won't downgrade below DO level however, as packet loss
+ * might have many reasons, a broken DNSSEC implementation being only one reason. And if the
+ * user is strict on DNSSEC, then let's assume that DNSSEC is not the fault here. */
log_debug("Lost too many UDP packets, downgrading feature level...");
s->possible_feature_level--;
} else if (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
s->packet_truncated &&
- s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) {
+ s->possible_feature_level > (dns_server_get_dnssec_mode(s) == DNSSEC_YES ? DNS_SERVER_FEATURE_LEVEL_LARGE : DNS_SERVER_FEATURE_LEVEL_UDP)) {
/* We got too many TCP connection failures in a row, we had at least one truncated packet, and
* are on a feature level above UDP. By downgrading things and getting rid of DNSSEC or EDNS0
@@ -779,6 +783,15 @@ bool dns_server_address_valid(int family, const union in_addr_union *sa) {
return true;
}
+DnssecMode dns_server_get_dnssec_mode(DnsServer *s) {
+ assert(s);
+
+ if (s->link)
+ return link_get_dnssec_mode(s->link);
+
+ return manager_get_dnssec_mode(s->manager);
+}
+
static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = {
[DNS_SERVER_SYSTEM] = "system",
[DNS_SERVER_FALLBACK] = "fallback",
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index 83e288a202..406282d864 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -144,6 +144,8 @@ void manager_next_dns_server(Manager *m);
bool dns_server_address_valid(int family, const union in_addr_union *sa);
+DnssecMode dns_server_get_dnssec_mode(DnsServer *s);
+
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
extern const struct hash_ops dns_server_hash_ops;
diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c
index 932e5b58a2..7d43825960 100644
--- a/src/resolve/resolved-dns-stub.c
+++ b/src/resolve/resolved-dns-stub.c
@@ -29,49 +29,33 @@ static int manager_dns_stub_udp_fd(Manager *m);
static int manager_dns_stub_tcp_fd(Manager *m);
static int dns_stub_make_reply_packet(
- uint16_t id,
- int rcode,
+ DnsPacket **p,
DnsQuestion *q,
- DnsAnswer *answer,
- bool add_opt, /* add an OPT RR to this packet */
- bool edns0_do, /* set the EDNS0 DNSSEC OK bit */
- bool ad, /* set the DNSSEC authenticated data bit */
- DnsPacket **ret) {
+ DnsAnswer *answer) {
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
DnsResourceRecord *rr;
unsigned c = 0;
int r;
+ assert(p);
+
/* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence
* roundtrips aren't expensive. */
- r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
- if (r < 0)
- return r;
-
- /* If the client didn't do EDNS, clamp the rcode to 4 bit */
- if (!add_opt && rcode > 0xF)
- rcode = DNS_RCODE_SERVFAIL;
+ if (!*p) {
+ r = dns_packet_new(p, DNS_PROTOCOL_DNS, 0);
+ if (r < 0)
+ return r;
- DNS_PACKET_HEADER(p)->id = id;
- DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
- 1 /* qr */,
- 0 /* opcode */,
- 0 /* aa */,
- 0 /* tc */,
- 1 /* rd */,
- 1 /* ra */,
- ad /* ad */,
- 0 /* cd */,
- rcode));
+ r = dns_packet_append_question(*p, q);
+ if (r < 0)
+ return r;
- r = dns_packet_append_question(p, q);
- if (r < 0)
- return r;
- DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(q));
+ DNS_PACKET_HEADER(*p)->qdcount = htobe16(dns_question_size(q));
+ }
DNS_ANSWER_FOREACH(rr, answer) {
+
r = dns_question_matches_rr(q, rr, NULL);
if (r < 0)
return r;
@@ -86,13 +70,46 @@ static int dns_stub_make_reply_packet(
continue;
add:
- r = dns_packet_append_rr(p, rr, 0, NULL, NULL);
+ r = dns_packet_append_rr(*p, rr, 0, NULL, NULL);
if (r < 0)
return r;
c++;
}
- DNS_PACKET_HEADER(p)->ancount = htobe16(c);
+
+ DNS_PACKET_HEADER(*p)->ancount = htobe16(be16toh(DNS_PACKET_HEADER(*p)->ancount) + c);
+
+ return 0;
+}
+
+static int dns_stub_finish_reply_packet(
+ DnsPacket *p,
+ uint16_t id,
+ int rcode,
+ bool add_opt, /* add an OPT RR to this packet? */
+ bool edns0_do, /* set the EDNS0 DNSSEC OK bit? */
+ bool ad) { /* set the DNSSEC authenticated data bit? */
+
+ int r;
+
+ assert(p);
+
+ /* If the client didn't do EDNS, clamp the rcode to 4 bit */
+ if (!add_opt && rcode > 0xF)
+ rcode = DNS_RCODE_SERVFAIL;
+
+ DNS_PACKET_HEADER(p)->id = id;
+
+ DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
+ 1 /* qr */,
+ 0 /* opcode */,
+ 0 /* aa */,
+ 0 /* tc */,
+ 1 /* rd */,
+ 1 /* ra */,
+ ad /* ad */,
+ 0 /* cd */,
+ rcode));
if (add_opt) {
r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL);
@@ -100,9 +117,6 @@ static int dns_stub_make_reply_packet(
return r;
}
- *ret = p;
- p = NULL;
-
return 0;
}
@@ -155,7 +169,11 @@ static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rco
assert(m);
assert(p);
- r = dns_stub_make_reply_packet(DNS_PACKET_ID(p), rcode, p->question, NULL, !!p->opt, DNS_PACKET_DO(p), false, &reply);
+ r = dns_stub_make_reply_packet(&reply, p->question, NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to make failure packet: %m");
+
+ r = dns_stub_finish_reply_packet(reply, DNS_PACKET_ID(p), rcode, !!p->opt, DNS_PACKET_DO(p), false);
if (r < 0)
return log_debug_errno(r, "Failed to build failure packet: %m");
@@ -170,26 +188,40 @@ static void dns_stub_query_complete(DnsQuery *q) {
switch (q->state) {
- case DNS_TRANSACTION_SUCCESS: {
- _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
+ case DNS_TRANSACTION_SUCCESS:
+
+ r = dns_stub_make_reply_packet(&q->reply_dns_packet, q->question_idna, q->answer);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to build reply packet: %m");
+ break;
+ }
+
+ r = dns_query_process_cname(q);
+ if (r == -ELOOP) {
+ (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL);
+ break;
+ }
+ if (r < 0) {
+ log_debug_errno(r, "Failed to process CNAME: %m");
+ break;
+ }
+ if (r == DNS_QUERY_RESTARTED)
+ return;
- r = dns_stub_make_reply_packet(
+ r = dns_stub_finish_reply_packet(
+ q->reply_dns_packet,
DNS_PACKET_ID(q->request_dns_packet),
q->answer_rcode,
- q->question_idna,
- q->answer,
!!q->request_dns_packet->opt,
DNS_PACKET_DO(q->request_dns_packet),
- DNS_PACKET_DO(q->request_dns_packet) && q->answer_authenticated,
- &reply);
+ DNS_PACKET_DO(q->request_dns_packet) && dns_query_fully_authenticated(q));
if (r < 0) {
- log_debug_errno(r, "Failed to build reply packet: %m");
+ log_debug_errno(r, "Failed to finish reply packet: %m");
break;
}
- (void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, reply);
+ (void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, q->reply_dns_packet);
break;
- }
case DNS_TRANSACTION_RCODE_FAILURE:
(void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode);
@@ -301,7 +333,7 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
goto fail;
}
- r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH|SD_RESOLVED_NO_CNAME);
+ r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH);
if (r < 0) {
log_error_errno(r, "Failed to generate query object: %m");
dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
@@ -328,7 +360,7 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
goto fail;
}
- log_info("Processing query...");
+ log_debug("Processing query...");
return;
fail:
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 81131df91e..0c7a8867fb 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -926,7 +926,16 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
dns_transaction_retry(t, false /* use the same server */);
return;
- } else if (DNS_PACKET_TC(p))
+ }
+
+ if (DNS_PACKET_RCODE(p) == DNS_RCODE_REFUSED) {
+ /* This server refused our request? If so, try again, use a different server */
+ log_debug("Server returned REFUSED, switching servers, and retrying.");
+ dns_transaction_retry(t, true /* pick a new server */);
+ return;
+ }
+
+ if (DNS_PACKET_TC(p))
dns_server_packet_truncated(t->server, t->current_feature_level);
break;
diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c
index 364812250f..59cd6cf1cb 100644
--- a/src/resolve/resolved-link-bus.c
+++ b/src/resolve/resolved-link-bus.c
@@ -462,7 +462,7 @@ int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_e
int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_set_free_free_ Set *ns = NULL;
- _cleanup_free_ char **ntas = NULL;
+ _cleanup_strv_free_ char **ntas = NULL;
Link *l = userdata;
int r;
char **i;
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index f81d6f0a55..791c125613 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -322,12 +322,13 @@ static int manager_network_monitor_listen(Manager *m) {
return 0;
}
-static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) {
+static int determine_hostname(char **full_hostname, char **llmnr_hostname, char **mdns_hostname) {
_cleanup_free_ char *h = NULL, *n = NULL;
char label[DNS_LABEL_MAX];
const char *p;
int r, k;
+ assert(full_hostname);
assert(llmnr_hostname);
assert(mdns_hostname);
@@ -374,32 +375,33 @@ static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) {
*llmnr_hostname = n;
n = NULL;
+ *full_hostname = h;
+ h = NULL;
+
return 0;
}
static int on_hostname_change(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
- _cleanup_free_ char *llmnr_hostname = NULL, *mdns_hostname = NULL;
+ _cleanup_free_ char *full_hostname = NULL, *llmnr_hostname = NULL, *mdns_hostname = NULL;
Manager *m = userdata;
int r;
assert(m);
- r = determine_hostname(&llmnr_hostname, &mdns_hostname);
+ r = determine_hostname(&full_hostname, &llmnr_hostname, &mdns_hostname);
if (r < 0)
return 0; /* ignore invalid hostnames */
- if (streq(llmnr_hostname, m->llmnr_hostname) && streq(mdns_hostname, m->mdns_hostname))
+ if (streq(full_hostname, m->full_hostname) &&
+ streq(llmnr_hostname, m->llmnr_hostname) &&
+ streq(mdns_hostname, m->mdns_hostname))
return 0;
- log_info("System hostname changed to '%s'.", llmnr_hostname);
-
- free(m->llmnr_hostname);
- free(m->mdns_hostname);
+ log_info("System hostname changed to '%s'.", full_hostname);
- m->llmnr_hostname = llmnr_hostname;
- m->mdns_hostname = mdns_hostname;
-
- llmnr_hostname = mdns_hostname = NULL;
+ free_and_replace(m->full_hostname, full_hostname);
+ free_and_replace(m->llmnr_hostname, llmnr_hostname);
+ free_and_replace(m->mdns_hostname, mdns_hostname);
manager_refresh_rrs(m);
@@ -428,9 +430,14 @@ static int manager_watch_hostname(Manager *m) {
(void) sd_event_source_set_description(m->hostname_event_source, "hostname");
- r = determine_hostname(&m->llmnr_hostname, &m->mdns_hostname);
+ r = determine_hostname(&m->full_hostname, &m->llmnr_hostname, &m->mdns_hostname);
if (r < 0) {
log_info("Defaulting to hostname 'linux'.");
+
+ m->full_hostname = strdup("linux");
+ if (!m->full_hostname)
+ return log_oom();
+
m->llmnr_hostname = strdup("linux");
if (!m->llmnr_hostname)
return log_oom();
@@ -439,7 +446,7 @@ static int manager_watch_hostname(Manager *m) {
if (!m->mdns_hostname)
return log_oom();
} else
- log_info("Using system hostname '%s'.", m->llmnr_hostname);
+ log_info("Using system hostname '%s'.", m->full_hostname);
return 0;
}
@@ -626,6 +633,8 @@ Manager *manager_free(Manager *m) {
sd_event_source_unref(m->hostname_event_source);
safe_close(m->hostname_fd);
+
+ free(m->full_hostname);
free(m->llmnr_hostname);
free(m->mdns_hostname);
@@ -1150,8 +1159,14 @@ int manager_is_own_hostname(Manager *m, const char *name) {
return r;
}
- if (m->mdns_hostname)
- return dns_name_equal(name, m->mdns_hostname);
+ if (m->mdns_hostname) {
+ r = dns_name_equal(name, m->mdns_hostname);
+ if (r != 0)
+ return r;
+ }
+
+ if (m->full_hostname)
+ return dns_name_equal(name, m->full_hostname);
return 0;
}
@@ -1353,7 +1368,7 @@ void manager_cleanup_saved_user(Manager *m) {
if (!IN_SET(de->d_type, DT_UNKNOWN, DT_REG))
continue;
- if (STR_IN_SET(de->d_name, ".", ".."))
+ if (dot_or_dot_dot(de->d_name))
continue;
r = parse_ifindex(de->d_name, &ifindex);
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index 0b52ca594a..97c52b7729 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -109,6 +109,7 @@ struct Manager {
sd_event_source *bus_retry_event_source;
/* The hostname we publish on LLMNR and mDNS */
+ char *full_hostname;
char *llmnr_hostname;
char *mdns_hostname;
DnsResourceKey *llmnr_host_ipv4_key;
diff --git a/src/run/run.c b/src/run/run.c
index 99f03465b0..f8257abc93 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -403,6 +403,11 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
+ if (arg_pty && arg_no_block) {
+ log_error("--pty is not compatible with --no-block.");
+ return -EINVAL;
+ }
+
if (arg_scope && with_timer()) {
log_error("Timer options are not supported in --scope mode.");
return -EINVAL;
@@ -784,21 +789,23 @@ static void run_context_free(RunContext *c) {
}
static void run_context_check_done(RunContext *c) {
- bool done = true;
+ bool done;
assert(c);
if (c->match)
- done = done && (c->active_state && STR_IN_SET(c->active_state, "inactive", "failed"));
+ done = STRPTR_IN_SET(c->active_state, "inactive", "failed");
+ else
+ done = true;
- if (c->forward)
- done = done && pty_forward_is_done(c->forward);
+ if (c->forward && done) /* If the service is gone, it's time to drain the output */
+ done = pty_forward_drain(c->forward);
if (done)
sd_event_exit(c->event, EXIT_SUCCESS);
}
-static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int run_context_update(RunContext *c, const char *path) {
static const struct bus_properties_map map[] = {
{ "ActiveState", "s", NULL, offsetof(RunContext, active_state) },
@@ -811,23 +818,33 @@ static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error
{}
};
- RunContext *c = userdata;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
r = bus_map_all_properties(c->bus,
"org.freedesktop.systemd1",
- sd_bus_message_get_path(m),
+ path,
map,
+ &error,
c);
if (r < 0) {
sd_event_exit(c->event, EXIT_FAILURE);
- return log_error_errno(r, "Failed to query unit state: %m");
+ return log_error_errno(r, "Failed to query unit state: %s", bus_error_message(&error, r));
}
run_context_check_done(c);
return 0;
}
+static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ RunContext *c = userdata;
+
+ assert(m);
+ assert(c);
+
+ return run_context_update(c, sd_bus_message_get_path(m));
+}
+
static int pty_forward_handler(PTYForward *f, int rcode, void *userdata) {
RunContext *c = userdata;
@@ -985,6 +1002,8 @@ static int start_transient_service(
if (arg_wait || master >= 0) {
_cleanup_(run_context_free) RunContext c = {};
+ _cleanup_free_ char *path = NULL;
+ const char *mt;
c.bus = sd_bus_ref(bus);
@@ -1007,27 +1026,27 @@ static int start_transient_service(
pty_forward_set_handler(c.forward, pty_forward_handler, &c);
}
- if (arg_wait) {
- _cleanup_free_ char *path = NULL;
- const char *mt;
- path = unit_dbus_path_from_name(service);
- if (!path)
- return log_oom();
+ path = unit_dbus_path_from_name(service);
+ if (!path)
+ return log_oom();
- mt = strjoina("type='signal',"
- "sender='org.freedesktop.systemd1',"
- "path='", path, "',"
- "interface='org.freedesktop.DBus.Properties',"
- "member='PropertiesChanged'");
- r = sd_bus_add_match(bus, &c.match, mt, on_properties_changed, &c);
- if (r < 0)
- return log_error_errno(r, "Failed to add properties changed signal.");
+ mt = strjoina("type='signal',"
+ "sender='org.freedesktop.systemd1',"
+ "path='", path, "',"
+ "interface='org.freedesktop.DBus.Properties',"
+ "member='PropertiesChanged'");
+ r = sd_bus_add_match(bus, &c.match, mt, on_properties_changed, &c);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add properties changed signal.");
- r = sd_bus_attach_event(bus, c.event, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to attach bus to event loop.");
- }
+ r = sd_bus_attach_event(bus, c.event, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach bus to event loop.");
+
+ r = run_context_update(&c, path);
+ if (r < 0)
+ return r;
r = sd_event_loop(c.event);
if (r < 0)
@@ -1041,7 +1060,13 @@ static int start_transient_service(
fputc('\n', stdout);
}
- if (!arg_quiet) {
+ if (arg_wait && !arg_quiet) {
+
+ /* Explicitly destroy the PTY forwarder, so that the PTY device is usable again, in its
+ * original settings (i.e. proper line breaks), so that we can show the summary in a pretty
+ * way. */
+ c.forward = pty_forward_free(c.forward);
+
if (!isempty(c.result))
log_info("Finished with result: %s", strna(c.result));
@@ -1416,7 +1441,7 @@ int main(int argc, char* argv[]) {
/* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the limited direct
* connection */
- if (arg_wait)
+ if (arg_wait || arg_pty)
r = bus_connect_transport(arg_transport, arg_host, arg_user, &bus);
else
r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index 3e877920da..e3b29e390c 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -95,7 +95,7 @@ static int retrieve_key(key_serial_t serial, char ***ret) {
if (n < m)
break;
- memory_erase(p, n);
+ explicit_bzero(p, n);
free(p);
m *= 2;
}
@@ -104,7 +104,7 @@ static int retrieve_key(key_serial_t serial, char ***ret) {
if (!l)
return -ENOMEM;
- memory_erase(p, n);
+ explicit_bzero(p, n);
*ret = l;
return 0;
@@ -140,7 +140,7 @@ static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **pa
return r;
serial = add_key("user", keyname, p, n, KEY_SPEC_USER_KEYRING);
- memory_erase(p, n);
+ explicit_bzero(p, n);
if (serial == -1)
return -errno;
@@ -390,7 +390,7 @@ int ask_password_tty(
}
x = strndup(passphrase, p);
- memory_erase(passphrase, p);
+ explicit_bzero(passphrase, p);
if (!x) {
r = -ENOMEM;
goto finish;
@@ -647,7 +647,7 @@ int ask_password_agent(
l = strv_new("", NULL);
else
l = strv_parse_nulstr(passphrase+1, n-1);
- memory_erase(passphrase, n);
+ explicit_bzero(passphrase, n);
if (!l) {
r = -ENOMEM;
goto finish;
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 829be2c6da..4ca614f647 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -208,7 +208,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges",
"SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute",
"RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables",
- "ProtectKernelModules", "ProtectControlGroups")) {
+ "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS")) {
r = parse_boolean(eq);
if (r < 0)
@@ -266,7 +266,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
"StandardInput", "StandardOutput", "StandardError",
"Description", "Slice", "Type", "WorkingDirectory",
"RootDirectory", "SyslogIdentifier", "ProtectSystem",
- "ProtectHome", "SELinuxContext", "Restart"))
+ "ProtectHome", "SELinuxContext", "Restart", "RootImage"))
r = sd_bus_message_append(m, "v", "s", eq);
else if (streq(field, "SyslogLevel")) {
@@ -484,7 +484,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
for (p = eq;;) {
_cleanup_free_ char *word = NULL;
- int offset;
+ size_t offset;
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
if (r < 0) {
@@ -500,6 +500,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
}
offset = word[0] == '-';
+ offset += word[offset] == '+';
+
if (!path_is_absolute(word + offset)) {
log_error("Failed to parse %s value %s", field, eq);
return -EINVAL;
@@ -552,7 +554,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
} else if (streq(field, "RestrictNamespaces")) {
bool invert = false;
- uint64_t flags = 0;
+ unsigned long flags = 0;
if (eq[0] == '~') {
invert = true;
@@ -573,7 +575,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
if (invert)
flags = (~flags) & NAMESPACE_FLAGS_ALL;
- r = sd_bus_message_append(m, "v", "t", flags);
+ r = sd_bus_message_append(m, "v", "t", (uint64_t) flags);
} else if ((dep = unit_dependency_from_string(field)) >= 0)
r = sd_bus_message_append(m, "v", "as", 1, eq);
else if (streq(field, "MountFlags")) {
@@ -583,7 +585,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
if (r < 0)
return log_error_errno(r, "Failed to parse mount propagation flags: %s", eq);
- r = sd_bus_message_append(m, "v", "t", f);
+ r = sd_bus_message_append(m, "v", "t", (uint64_t) f);
} else if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) {
const char *p = eq;
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 6aebe18fc0..8ddfb584ea 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -1116,9 +1116,9 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_
int bus_message_map_all_properties(
sd_bus_message *m,
const struct bus_properties_map *map,
+ sd_bus_error *error,
void *userdata) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(m);
@@ -1156,9 +1156,9 @@ int bus_message_map_all_properties(
v = (uint8_t *)userdata + prop->offset;
if (map[i].set)
- r = prop->set(sd_bus_message_get_bus(m), member, m, &error, v);
+ r = prop->set(sd_bus_message_get_bus(m), member, m, error, v);
else
- r = map_basic(sd_bus_message_get_bus(m), member, m, &error, v);
+ r = map_basic(sd_bus_message_get_bus(m), member, m, error, v);
if (r < 0)
return r;
@@ -1184,6 +1184,7 @@ int bus_message_map_all_properties(
int bus_message_map_properties_changed(
sd_bus_message *m,
const struct bus_properties_map *map,
+ sd_bus_error *error,
void *userdata) {
const char *member;
@@ -1192,7 +1193,7 @@ int bus_message_map_properties_changed(
assert(m);
assert(map);
- r = bus_message_map_all_properties(m, map, userdata);
+ r = bus_message_map_all_properties(m, map, error, userdata);
if (r < 0)
return r;
@@ -1222,10 +1223,10 @@ int bus_map_all_properties(
const char *destination,
const char *path,
const struct bus_properties_map *map,
+ sd_bus_error *error,
void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(bus);
@@ -1239,13 +1240,13 @@ int bus_map_all_properties(
path,
"org.freedesktop.DBus.Properties",
"GetAll",
- &error,
+ error,
&m,
"s", "");
if (r < 0)
return r;
- return bus_message_map_all_properties(m, map, userdata);
+ return bus_message_map_all_properties(m, map, error, userdata);
}
int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **ret) {
diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
index af5f133912..d9ce4263bb 100644
--- a/src/shared/bus-util.h
+++ b/src/shared/bus-util.h
@@ -50,9 +50,9 @@ struct bus_properties_map {
int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
-int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, void *userdata);
-int bus_message_map_properties_changed(sd_bus_message *m, const struct bus_properties_map *map, void *userdata);
-int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map, void *userdata);
+int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, sd_bus_error *error, void *userdata);
+int bus_message_map_properties_changed(sd_bus_message *m, const struct bus_properties_map *map, sd_bus_error *error, void *userdata);
+int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map, sd_bus_error *error, void *userdata);
int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name);
diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c
index 5d21742a09..8765cf2f49 100644
--- a/src/shared/cgroup-show.c
+++ b/src/shared/cgroup-show.c
@@ -24,10 +24,15 @@
#include <stdlib.h>
#include <string.h>
+#include <systemd/sd-bus.h>
+
#include "alloc-util.h"
+#include "bus-error.h"
+#include "bus-util.h"
#include "cgroup-show.h"
#include "cgroup-util.h"
#include "fd-util.h"
+#include "fileio.h"
#include "format-util.h"
#include "locale-util.h"
#include "macro.h"
@@ -36,6 +41,7 @@
#include "process-util.h"
#include "string-util.h"
#include "terminal-util.h"
+#include "unit-name.h"
static void show_pid_array(
pid_t pids[],
@@ -73,7 +79,7 @@ static void show_pid_array(
for (i = 0; i < n_pids; i++) {
_cleanup_free_ char *t = NULL;
- get_process_cmdline(pids[i], n_columns, true, &t);
+ (void) get_process_cmdline(pids[i], n_columns, true, &t);
if (extra)
printf("%s%s ", prefix, special_glyph(TRIANGULAR_BULLET));
@@ -310,3 +316,81 @@ int show_cgroup_and_extra_by_spec(
return show_cgroup_and_extra(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
}
+
+int show_cgroup_get_unit_path_and_warn(
+ sd_bus *bus,
+ const char *unit,
+ char **ret) {
+
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ path = unit_dbus_path_from_name(unit);
+ if (!path)
+ return log_oom();
+
+ r = sd_bus_get_property_string(
+ bus,
+ "org.freedesktop.systemd1",
+ path,
+ unit_dbus_interface_from_name(unit),
+ "ControlGroup",
+ &error,
+ ret);
+ if (r < 0)
+ return log_error_errno(r, "Failed to query unit control group path: %s",
+ bus_error_message(&error, r));
+
+ return 0;
+}
+
+int show_cgroup_get_path_and_warn(
+ const char *machine,
+ const char *prefix,
+ char **ret) {
+
+ int r;
+ _cleanup_free_ char *root = NULL;
+
+ if (machine) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_free_ char *unit = NULL;
+ const char *m;
+
+ m = strjoina("/run/systemd/machines/", machine);
+ r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load machine data: %m");
+
+ r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
+
+ r = show_cgroup_get_unit_path_and_warn(bus, unit, &root);
+ if (r < 0)
+ return r;
+ } else {
+ r = cg_get_root_path(&root);
+ if (r == -ENOMEDIUM)
+ return log_error_errno(r, "Failed to get root control group path.\n"
+ "No cgroup filesystem mounted on /sys/fs/cgroup");
+ else if (r < 0)
+ return log_error_errno(r, "Failed to get root control group path: %m");
+ }
+
+ if (prefix) {
+ char *t;
+
+ t = strjoin(root, prefix);
+ if (!t)
+ return log_oom();
+
+ *ret = t;
+ } else {
+ *ret = root;
+ root = NULL;
+ }
+
+ return 0;
+}
diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h
index 5c1d6e6d98..736f0f34c8 100644
--- a/src/shared/cgroup-show.h
+++ b/src/shared/cgroup-show.h
@@ -22,6 +22,8 @@
#include <stdbool.h>
#include <sys/types.h>
+#include <systemd/sd-bus.h>
+
#include "logs-show.h"
#include "output-mode.h"
@@ -30,3 +32,12 @@ int show_cgroup(const char *controller, const char *path, const char *prefix, un
int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
+
+int show_cgroup_get_unit_path_and_warn(
+ sd_bus *bus,
+ const char *unit,
+ char **ret);
+int show_cgroup_get_path_and_warn(
+ const char *machine,
+ const char *prefix,
+ char **ret);
diff --git a/src/shared/clean-ipc.c b/src/shared/clean-ipc.c
index f2d1555c13..f59f6f23ae 100644
--- a/src/shared/clean-ipc.c
+++ b/src/shared/clean-ipc.c
@@ -225,7 +225,7 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid) {
FOREACH_DIRENT_ALL(de, dir, goto fail) {
struct stat st;
- if (STR_IN_SET(de->d_name, "..", "."))
+ if (dot_or_dot_dot(de->d_name))
continue;
if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
@@ -310,7 +310,7 @@ static int clean_posix_mq(uid_t uid, gid_t gid) {
struct stat st;
char fn[1+strlen(de->d_name)+1];
- if (STR_IN_SET(de->d_name, "..", "."))
+ if (dot_or_dot_dot(de->d_name))
continue;
if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index 878cb008aa..410a7764ed 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -28,14 +28,19 @@
#include "blkid-util.h"
#include "dissect-image.h"
#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
#include "gpt.h"
+#include "hexdecoct.h"
#include "mount-util.h"
#include "path-util.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
+#include "strv.h"
#include "udev-util.h"
+#include "xattr-util.h"
static int probe_filesystem(const char *node, char **ret_fstype) {
#ifdef HAVE_BLKID
@@ -347,9 +352,6 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
sd_id128_t type_id, id;
bool rw = true;
- if (pflags & GPT_FLAG_NO_AUTO)
- continue;
-
sid = blkid_partition_get_uuid(pp);
if (!sid)
continue;
@@ -363,18 +365,37 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
continue;
if (sd_id128_equal(type_id, GPT_HOME)) {
+
+ if (pflags & GPT_FLAG_NO_AUTO)
+ continue;
+
designator = PARTITION_HOME;
rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_SRV)) {
+
+ if (pflags & GPT_FLAG_NO_AUTO)
+ continue;
+
designator = PARTITION_SRV;
rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_ESP)) {
+
+ /* Note that we don't check the GPT_FLAG_NO_AUTO flag for the ESP, as it is not defined
+ * there. We instead check the GPT_FLAG_NO_BLOCK_IO_PROTOCOL, as recommended by the
+ * UEFI spec (See "12.3.3 Number and Location of System Partitions"). */
+
+ if (pflags & GPT_FLAG_NO_BLOCK_IO_PROTOCOL)
+ continue;
+
designator = PARTITION_ESP;
fstype = "vfat";
}
#ifdef GPT_ROOT_NATIVE
else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
+ if (pflags & GPT_FLAG_NO_AUTO)
+ continue;
+
/* If a root ID is specified, ignore everything but the root id */
if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
continue;
@@ -384,6 +405,9 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) {
+ if (pflags & GPT_FLAG_NO_AUTO)
+ continue;
+
m->can_verity = true;
/* Ignore verity unless a root hash is specified */
@@ -399,6 +423,9 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
#ifdef GPT_ROOT_SECONDARY
else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
+ if (pflags & GPT_FLAG_NO_AUTO)
+ continue;
+
/* If a root ID is specified, ignore everything but the root id */
if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
continue;
@@ -407,6 +434,10 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
architecture = SECONDARY_ARCHITECTURE;
rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) {
+
+ if (pflags & GPT_FLAG_NO_AUTO)
+ continue;
+
m->can_verity = true;
/* Ignore verity unless root has is specified */
@@ -420,10 +451,17 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
}
#endif
else if (sd_id128_equal(type_id, GPT_SWAP)) {
+
+ if (pflags & GPT_FLAG_NO_AUTO)
+ continue;
+
designator = PARTITION_SWAP;
fstype = "swap";
} else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
+ if (pflags & GPT_FLAG_NO_AUTO)
+ continue;
+
if (generic_node)
multiple_generic = true;
else {
@@ -624,7 +662,9 @@ static int mount_partition(
DissectImageFlags flags) {
const char *p, *options = NULL, *node, *fstype;
+ _cleanup_free_ char *chased = NULL;
bool rw;
+ int r;
assert(m);
assert(where);
@@ -641,9 +681,13 @@ static int mount_partition(
rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY);
- if (directory)
- p = strjoina(where, directory);
- else
+ if (directory) {
+ r = chase_symlinks(directory, where, CHASE_PREFIX_ROOT, &chased);
+ if (r < 0)
+ return r;
+
+ p = chased;
+ } else
p = where;
/* If requested, turn on discard support. */
@@ -677,22 +721,23 @@ int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlag
return r;
if (m->partitions[PARTITION_ESP].found) {
- const char *mp, *x;
+ const char *mp;
/* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */
- mp = "/efi";
- x = strjoina(where, mp);
- r = dir_is_empty(x);
- if (r == -ENOENT) {
- mp = "/boot";
- x = strjoina(where, mp);
- r = dir_is_empty(x);
- }
- if (r > 0) {
- r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags);
+ FOREACH_STRING(mp, "/efi", "/boot") {
+ _cleanup_free_ char *p = NULL;
+
+ r = chase_symlinks(mp, where, CHASE_PREFIX_ROOT, &p);
if (r < 0)
- return r;
+ continue;
+
+ r = dir_is_empty(p);
+ if (r > 0) {
+ r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags);
+ if (r < 0)
+ return r;
+ }
}
}
@@ -1078,6 +1123,62 @@ int decrypted_image_relinquish(DecryptedImage *d) {
return 0;
}
+int root_hash_load(const char *image, void **ret, size_t *ret_size) {
+ _cleanup_free_ char *text = NULL;
+ _cleanup_free_ void *k = NULL;
+ size_t l;
+ int r;
+
+ assert(image);
+ assert(ret);
+ assert(ret_size);
+
+ if (is_device_path(image)) {
+ /* If we are asked to load the root hash for a device node, exit early */
+ *ret = NULL;
+ *ret_size = 0;
+ return 0;
+ }
+
+ r = getxattr_malloc(image, "user.verity.roothash", &text, true);
+ if (r < 0) {
+ char *fn, *e, *n;
+
+ if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT))
+ return r;
+
+ fn = newa(char, strlen(image) + strlen(".roothash") + 1);
+ n = stpcpy(fn, image);
+ e = endswith(fn, ".raw");
+ if (e)
+ n = e;
+
+ strcpy(n, ".roothash");
+
+ r = read_one_line_file(fn, &text);
+ if (r == -ENOENT) {
+ *ret = NULL;
+ *ret_size = 0;
+ return 0;
+ }
+ if (r < 0)
+ return r;
+ }
+
+ r = unhexmem(text, strlen(text), &k, &l);
+ if (r < 0)
+ return r;
+ if (l < sizeof(sd_id128_t))
+ return -EINVAL;
+
+ *ret = k;
+ *ret_size = l;
+
+ k = NULL;
+
+ return 1;
+}
+
static const char *const partition_designator_table[] = {
[PARTITION_ROOT] = "root",
[PARTITION_ROOT_SECONDARY] = "root-secondary",
diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h
index 26319bd8e7..cdb083be6f 100644
--- a/src/shared/dissect-image.h
+++ b/src/shared/dissect-image.h
@@ -94,3 +94,5 @@ int decrypted_image_relinquish(DecryptedImage *d);
const char* partition_designator_to_string(int i) _const_;
int partition_designator_from_string(const char *name) _pure_;
+
+int root_hash_load(const char *image, void **ret, size_t *ret_size);
diff --git a/src/shared/dropin.c b/src/shared/dropin.c
index 3cbfe13f4c..15ccd1b6ca 100644
--- a/src/shared/dropin.c
+++ b/src/shared/dropin.c
@@ -29,6 +29,7 @@
#include "escape.h"
#include "fd-util.h"
#include "fileio-label.h"
+#include "fs-util.h"
#include "hashmap.h"
#include "log.h"
#include "macro.h"
@@ -42,11 +43,10 @@
int drop_in_file(const char *dir, const char *unit, unsigned level,
const char *name, char **_p, char **_q) {
+ char prefix[DECIMAL_STR_MAX(unsigned)];
_cleanup_free_ char *b = NULL;
char *p, *q;
- char prefix[DECIMAL_STR_MAX(unsigned)];
-
assert(unit);
assert(name);
assert(_p);
@@ -116,125 +116,99 @@ int write_drop_in_format(const char *dir, const char *unit, unsigned level,
return write_drop_in(dir, unit, level, name, p);
}
-static int iterate_dir(
+static int unit_file_find_dir(
+ const char *original_root,
const char *path,
- UnitDependency dependency,
- dependency_consumer_t consumer,
- void *arg,
- char ***strv) {
+ char ***dirs) {
- _cleanup_closedir_ DIR *d = NULL;
- struct dirent *de;
+ _cleanup_free_ char *chased = NULL;
int r;
assert(path);
- /* The config directories are special, since the order of the
- * drop-ins matters */
- if (dependency < 0) {
- r = strv_extend(strv, path);
- if (r < 0)
- return log_oom();
-
+ r = chase_symlinks(path, original_root, 0, &chased);
+ if (r == -ENOENT) /* Ignore -ENOENT, after all most units won't have a drop-in dir */
return 0;
- }
-
- assert(consumer);
-
- d = opendir(path);
- if (!d) {
- if (errno == ENOENT)
- return 0;
-
- return log_error_errno(errno, "Failed to open directory %s: %m", path);
- }
-
- FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read directory %s: %m", path)) {
- _cleanup_free_ char *f = NULL;
-
- f = strjoin(path, "/", de->d_name);
- if (!f)
- return log_oom();
+ if (r < 0)
+ return log_full_errno(LOG_WARNING, r, "Failed to canonicalize path %s: %m", path);
- r = consumer(dependency, de->d_name, f, arg);
- if (r < 0)
- return r;
- }
+ r = strv_push(dirs, chased);
+ if (r < 0)
+ return log_oom();
+ chased = NULL;
return 0;
}
-int unit_file_process_dir(
+static int unit_file_find_dirs(
+ const char *original_root,
Set *unit_path_cache,
const char *unit_path,
const char *name,
const char *suffix,
- UnitDependency dependency,
- dependency_consumer_t consumer,
- void *arg,
- char ***strv) {
+ char ***dirs) {
- _cleanup_free_ char *path = NULL;
+ char *path;
int r;
assert(unit_path);
assert(name);
assert(suffix);
- path = strjoin(unit_path, "/", name, suffix);
- if (!path)
- return log_oom();
+ path = strjoina(unit_path, "/", name, suffix);
- if (!unit_path_cache || set_get(unit_path_cache, path))
- (void) iterate_dir(path, dependency, consumer, arg, strv);
+ if (!unit_path_cache || set_get(unit_path_cache, path)) {
+ r = unit_file_find_dir(original_root, path, dirs);
+ if (r < 0)
+ return r;
+ }
if (unit_name_is_valid(name, UNIT_NAME_INSTANCE)) {
- _cleanup_free_ char *template = NULL, *p = NULL;
/* Also try the template dir */
+ _cleanup_free_ char *template = NULL;
+
r = unit_name_template(name, &template);
if (r < 0)
return log_error_errno(r, "Failed to generate template from unit name: %m");
- p = strjoin(unit_path, "/", template, suffix);
- if (!p)
- return log_oom();
-
- if (!unit_path_cache || set_get(unit_path_cache, p))
- (void) iterate_dir(p, dependency, consumer, arg, strv);
+ return unit_file_find_dirs(original_root, unit_path_cache, unit_path, template, suffix, dirs);
}
return 0;
}
int unit_file_find_dropin_paths(
+ const char *original_root,
char **lookup_path,
Set *unit_path_cache,
+ const char *dir_suffix,
+ const char *file_suffix,
Set *names,
- char ***paths) {
+ char ***ret) {
- _cleanup_strv_free_ char **strv = NULL, **ans = NULL;
+ _cleanup_strv_free_ char **dirs = NULL, **ans = NULL;
Iterator i;
- char *t;
+ char *t, **p;
int r;
- assert(paths);
-
- SET_FOREACH(t, names, i) {
- char **p;
+ assert(ret);
+ SET_FOREACH(t, names, i)
STRV_FOREACH(p, lookup_path)
- unit_file_process_dir(unit_path_cache, *p, t, ".d", _UNIT_DEPENDENCY_INVALID, NULL, NULL, &strv);
- }
+ unit_file_find_dirs(original_root, unit_path_cache, *p, t, dir_suffix, &dirs);
- if (strv_isempty(strv))
+ if (strv_isempty(dirs)) {
+ *ret = NULL;
return 0;
+ }
- r = conf_files_list_strv(&ans, ".conf", NULL, (const char**) strv);
+ r = conf_files_list_strv(&ans, file_suffix, NULL, (const char**) dirs);
if (r < 0)
- return log_warning_errno(r, "Failed to get list of configuration files: %m");
+ return log_warning_errno(r, "Failed to sort the list of configuration files: %m");
- *paths = ans;
+ *ret = ans;
ans = NULL;
+
return 1;
}
diff --git a/src/shared/dropin.h b/src/shared/dropin.h
index c1936f397b..a2b8cdce61 100644
--- a/src/shared/dropin.h
+++ b/src/shared/dropin.h
@@ -33,29 +33,24 @@ int write_drop_in(const char *dir, const char *unit, unsigned level,
int write_drop_in_format(const char *dir, const char *unit, unsigned level,
const char *name, const char *format, ...) _printf_(5, 6);
-/**
- * This callback will be called for each directory entry @entry,
- * with @filepath being the full path to the entry.
- *
- * If return value is negative, loop will be aborted.
- */
-typedef int (*dependency_consumer_t)(UnitDependency dependency,
- const char *entry,
- const char* filepath,
- void *arg);
-
-int unit_file_process_dir(
- Set * unit_path_cache,
- const char *unit_path,
- const char *name,
- const char *suffix,
- UnitDependency dependency,
- dependency_consumer_t consumer,
- void *arg,
- char ***strv);
-
int unit_file_find_dropin_paths(
+ const char *original_root,
char **lookup_path,
Set *unit_path_cache,
+ const char *dir_suffix,
+ const char *file_suffix,
Set *names,
char ***paths);
+
+static inline int unit_file_find_dropin_conf_paths(
+ const char *original_root,
+ char **lookup_path,
+ Set *unit_path_cache,
+ Set *names,
+ char ***paths) {
+ return unit_file_find_dropin_paths(original_root,
+ lookup_path,
+ unit_path_cache,
+ ".d", ".conf",
+ names, paths);
+}
diff --git a/src/shared/gpt.h b/src/shared/gpt.h
index 13d80d611c..cc752006fa 100644
--- a/src/shared/gpt.h
+++ b/src/shared/gpt.h
@@ -71,6 +71,8 @@
# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_ARM_VERITY
#endif
+#define GPT_FLAG_NO_BLOCK_IO_PROTOCOL (1ULL << 1)
+
/* Flags we recognize on the root, swap, home and srv partitions when
* doing auto-discovery. These happen to be identical to what
* Microsoft defines for its own Basic Data Partitions, but that's
diff --git a/src/shared/install.c b/src/shared/install.c
index 478abac8ab..58c8e852b2 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -208,7 +208,7 @@ static int path_is_control(const LookupPaths *p, const char *path) {
path_equal_ptr(parent, p->runtime_control);
}
-static int path_is_config(const LookupPaths *p, const char *path) {
+static int path_is_config(const LookupPaths *p, const char *path, bool check_parent) {
_cleanup_free_ char *parent = NULL;
assert(p);
@@ -217,15 +217,19 @@ static int path_is_config(const LookupPaths *p, const char *path) {
/* Note that we do *not* have generic checks for /etc or /run in place, since with
* them we couldn't discern configuration from transient or generated units */
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
+ if (check_parent) {
+ parent = dirname_malloc(path);
+ if (!parent)
+ return -ENOMEM;
+
+ path = parent;
+ }
- return path_equal_ptr(parent, p->persistent_config) ||
- path_equal_ptr(parent, p->runtime_config);
+ return path_equal_ptr(path, p->persistent_config) ||
+ path_equal_ptr(path, p->runtime_config);
}
-static int path_is_runtime(const LookupPaths *p, const char *path) {
+static int path_is_runtime(const LookupPaths *p, const char *path, bool check_parent) {
_cleanup_free_ char *parent = NULL;
const char *rpath;
@@ -239,16 +243,20 @@ static int path_is_runtime(const LookupPaths *p, const char *path) {
if (rpath && path_startswith(rpath, "/run"))
return true;
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
+ if (check_parent) {
+ parent = dirname_malloc(path);
+ if (!parent)
+ return -ENOMEM;
- return path_equal_ptr(parent, p->runtime_config) ||
- path_equal_ptr(parent, p->generator) ||
- path_equal_ptr(parent, p->generator_early) ||
- path_equal_ptr(parent, p->generator_late) ||
- path_equal_ptr(parent, p->transient) ||
- path_equal_ptr(parent, p->runtime_control);
+ path = parent;
+ }
+
+ return path_equal_ptr(path, p->runtime_config) ||
+ path_equal_ptr(path, p->generator) ||
+ path_equal_ptr(path, p->generator_early) ||
+ path_equal_ptr(path, p->generator_late) ||
+ path_equal_ptr(path, p->transient) ||
+ path_equal_ptr(path, p->runtime_control);
}
static int path_is_vendor(const LookupPaths *p, const char *path) {
@@ -381,6 +389,12 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang
verb, changes[i].path);
logged = true;
break;
+
+ case -ENOENT:
+ log_error_errno(changes[i].type, "Failed to %s unit, unit %s does not exist.", verb, changes[i].path);
+ logged = true;
+ break;
+
default:
assert(changes[i].type < 0);
log_error_errno(changes[i].type, "Failed to %s unit, file %s: %m.",
@@ -677,7 +691,6 @@ static int find_symlinks_fd(
int fd,
const char *path,
const char *config_path,
- const LookupPaths *lp,
bool *same_name_link) {
_cleanup_closedir_ DIR *d = NULL;
@@ -688,7 +701,6 @@ static int find_symlinks_fd(
assert(fd >= 0);
assert(path);
assert(config_path);
- assert(lp);
assert(same_name_link);
d = fdopendir(fd);
@@ -722,7 +734,7 @@ static int find_symlinks_fd(
}
/* This will close nfd, regardless whether it succeeds or not */
- q = find_symlinks_fd(root_dir, name, nfd, p, config_path, lp, same_name_link);
+ q = find_symlinks_fd(root_dir, name, nfd, p, config_path, same_name_link);
if (q > 0)
return 1;
if (r == 0)
@@ -800,7 +812,6 @@ static int find_symlinks(
const char *root_dir,
const char *name,
const char *config_path,
- const LookupPaths *lp,
bool *same_name_link) {
int fd;
@@ -817,44 +828,82 @@ static int find_symlinks(
}
/* This takes possession of fd and closes it */
- return find_symlinks_fd(root_dir, name, fd, config_path, config_path, lp, same_name_link);
+ return find_symlinks_fd(root_dir, name, fd, config_path, config_path, same_name_link);
}
static int find_symlinks_in_scope(
- UnitFileScope scope,
const LookupPaths *paths,
const char *name,
UnitFileState *state) {
- bool same_name_link_runtime = false, same_name_link = false;
+ bool same_name_link_runtime = false, same_name_link_config = false;
+ bool enabled_in_runtime = false, enabled_at_all = false;
+ char **p;
int r;
- assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(paths);
assert(name);
- /* First look in the persistent config path */
- r = find_symlinks(paths->root_dir, name, paths->persistent_config, paths, &same_name_link);
- if (r < 0)
- return r;
- if (r > 0) {
- *state = UNIT_FILE_ENABLED;
- return r;
+ STRV_FOREACH(p, paths->search_path) {
+ bool same_name_link = false;
+
+ r = find_symlinks(paths->root_dir, name, *p, &same_name_link);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */
+
+ r = path_is_config(paths, *p, false);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* This is the best outcome, let's return it immediately. */
+ *state = UNIT_FILE_ENABLED;
+ return 1;
+ }
+
+ r = path_is_runtime(paths, *p, false);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ enabled_in_runtime = true;
+ else
+ enabled_at_all = true;
+
+ } else if (same_name_link) {
+
+ r = path_is_config(paths, *p, false);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ same_name_link_config = true;
+ else {
+ r = path_is_runtime(paths, *p, false);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ same_name_link_runtime = true;
+ }
+ }
}
- /* Then look in runtime config path */
- r = find_symlinks(paths->root_dir, name, paths->runtime_config, paths, &same_name_link_runtime);
- if (r < 0)
- return r;
- if (r > 0) {
+ if (enabled_in_runtime) {
*state = UNIT_FILE_ENABLED_RUNTIME;
- return r;
+ return 1;
+ }
+
+ /* Here's a special rule: if the unit we are looking for is an instance, and it symlinked in the search path
+ * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only
+ * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate
+ * something, and hence are a much stronger concept. */
+ if (enabled_at_all && unit_name_is_valid(name, UNIT_NAME_INSTANCE)) {
+ *state = UNIT_FILE_STATIC;
+ return 1;
}
/* Hmm, we didn't find it, but maybe we found the same name
* link? */
- if (same_name_link) {
+ if (same_name_link_config) {
*state = UNIT_FILE_LINKED;
return 1;
}
@@ -1354,7 +1403,8 @@ static int install_info_follow(
InstallContext *c,
UnitFileInstallInfo *i,
const char *root_dir,
- SearchFlags flags) {
+ SearchFlags flags,
+ bool ignore_different_name) {
assert(c);
assert(i);
@@ -1367,7 +1417,7 @@ static int install_info_follow(
/* If the basename doesn't match, the caller should add a
* complete new entry for this. */
- if (!streq(basename(i->symlink_target), i->name))
+ if (!ignore_different_name && !streq(basename(i->symlink_target), i->name))
return -EXDEV;
free_and_replace(i->path, i->symlink_target);
@@ -1408,14 +1458,14 @@ static int install_info_traverse(
return -ELOOP;
if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) {
- r = path_is_config(paths, i->path);
+ r = path_is_config(paths, i->path, true);
if (r < 0)
return r;
if (r > 0)
return -ELOOP;
}
- r = install_info_follow(c, i, paths->root_dir, flags);
+ r = install_info_follow(c, i, paths->root_dir, flags, false);
if (r == -EXDEV) {
_cleanup_free_ char *buffer = NULL;
const char *bn;
@@ -1439,6 +1489,18 @@ static int install_info_traverse(
if (r < 0)
return r;
+ if (streq(buffer, i->name)) {
+
+ /* We filled in the instance, and the target stayed the same? If so, then let's
+ * honour the link as it is. */
+
+ r = install_info_follow(c, i, paths->root_dir, flags, true);
+ if (r < 0)
+ return r;
+
+ continue;
+ }
+
bn = buffer;
}
@@ -1751,7 +1813,9 @@ static int install_context_mark_for_removal(
InstallContext *c,
const LookupPaths *paths,
Set **remove_symlinks_to,
- const char *config_path) {
+ const char *config_path,
+ UnitFileChange **changes,
+ unsigned *n_changes) {
UnitFileInstallInfo *i;
int r;
@@ -1777,19 +1841,26 @@ static int install_context_mark_for_removal(
r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
if (r == -ENOLINK) {
- log_debug_errno(r, "Name %s leads to a dangling symlink, ignoring.", i->name);
- continue;
- } else if (r == -ENOENT && i->auxiliary) {
- /* some unit specified in Also= or similar is missing */
- log_debug_errno(r, "Auxiliary unit %s not found, ignoring.", i->name);
- continue;
- } else if (r < 0)
- return log_debug_errno(r, "Failed to find unit %s: %m", i->name);
+ log_debug_errno(r, "Name %s leads to a dangling symlink, removing name.", i->name);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_DANGLING, i->path ?: i->name, NULL);
+ } else if (r == -ENOENT) {
+
+ if (i->auxiliary) /* some unit specified in Also= or similar is missing */
+ log_debug_errno(r, "Auxiliary unit of %s not found, removing name.", i->name);
+ else {
+ log_debug_errno(r, "Unit %s not found, removing name.", i->name);
+ unit_file_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
+ }
- if (i->type != UNIT_FILE_TYPE_REGULAR) {
- log_debug("Unit %s has type %s, ignoring.",
- i->name,
- unit_file_type_to_string(i->type) ?: "invalid");
+ } else if (r < 0) {
+ log_debug_errno(r, "Failed to find unit %s, removing name: %m", i->name);
+ unit_file_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
+ } else if (i->type == UNIT_FILE_TYPE_MASKED) {
+ log_debug("Unit file %s is masked, ignoring.", i->name);
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path ?: i->name, NULL);
+ continue;
+ } else if (i->type != UNIT_FILE_TYPE_REGULAR) {
+ log_debug("Unit %s has type %s, ignoring.", i->name, unit_file_type_to_string(i->type) ?: "invalid");
continue;
}
@@ -1822,6 +1893,8 @@ int unit_file_mask(
return r;
config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
+ if (!config_path)
+ return -ENXIO;
STRV_FOREACH(i, files) {
_cleanup_free_ char *path = NULL;
@@ -1870,6 +1943,9 @@ int unit_file_unmask(
return r;
config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
+ if (!config_path)
+ return -ENXIO;
+
dry_run = !!(flags & UNIT_FILE_DRY_RUN);
STRV_FOREACH(i, files) {
@@ -1959,6 +2035,8 @@ int unit_file_link(
return r;
config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
+ if (!config_path)
+ return -ENXIO;
STRV_FOREACH(i, files) {
_cleanup_free_ char *full = NULL;
@@ -2027,7 +2105,7 @@ static int path_shall_revert(const LookupPaths *paths, const char *path) {
/* Checks whether the path is one where the drop-in directories shall be removed. */
- r = path_is_config(paths, path);
+ r = path_is_config(paths, path, true);
if (r != 0)
return r;
@@ -2135,7 +2213,7 @@ int unit_file_revert(
if (errno != ENOENT)
return -errno;
} else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
- r = path_is_config(&paths, path);
+ r = path_is_config(&paths, path, true);
if (r < 0)
return r;
if (r > 0) {
@@ -2226,6 +2304,8 @@ int unit_file_add_dependency(
return r;
config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
+ if (!config_path)
+ return -ENXIO;
r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS,
&target_info, changes, n_changes);
@@ -2291,6 +2371,8 @@ int unit_file_enable(
return r;
config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
+ if (!config_path)
+ return -ENXIO;
STRV_FOREACH(f, files) {
r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
@@ -2335,6 +2417,8 @@ int unit_file_disable(
return r;
config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
+ if (!config_path)
+ return -ENXIO;
STRV_FOREACH(i, files) {
if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
@@ -2345,7 +2429,7 @@ int unit_file_disable(
return r;
}
- r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path);
+ r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, changes, n_changes);
if (r < 0)
return r;
@@ -2481,7 +2565,7 @@ static int unit_file_lookup_state(
switch (i->type) {
case UNIT_FILE_TYPE_MASKED:
- r = path_is_runtime(paths, i->path);
+ r = path_is_runtime(paths, i->path, true);
if (r < 0)
return r;
@@ -2505,7 +2589,7 @@ static int unit_file_lookup_state(
break;
}
- r = find_symlinks_in_scope(scope, paths, i->name, &state);
+ r = find_symlinks_in_scope(paths, i->name, &state);
if (r < 0)
return r;
if (r == 0) {
@@ -2734,7 +2818,7 @@ static int execute_preset(
if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
- r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path);
+ r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, changes, n_changes);
if (r < 0)
return r;
@@ -2829,6 +2913,8 @@ int unit_file_preset(
return r;
config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
+ if (!config_path)
+ return -ENXIO;
r = read_presets(scope, root_dir, &presets);
if (r < 0)
@@ -2867,6 +2953,8 @@ int unit_file_preset_all(
return r;
config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
+ if (!config_path)
+ return -ENXIO;
r = read_presets(scope, root_dir, &presets);
if (r < 0)
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index 75ea25c8ac..72c43e80cb 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -246,6 +246,11 @@ static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, Ou
if (r < 0)
return log_error_errno(r, "Failed to get realtime timestamp: %m");
+ if (x > USEC_TIMESTAMP_FORMATTABLE_MAX) {
+ log_error("Timestamp cannot be printed");
+ return -EINVAL;
+ }
+
if (mode == OUTPUT_SHORT_FULL) {
const char *k;
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index 586ef64e72..09a44534e2 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -33,6 +33,7 @@
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
+#include "user-util.h"
#include "util.h"
static int user_runtime_dir(char **ret, const char *suffix) {
@@ -57,6 +58,7 @@ static int user_runtime_dir(char **ret, const char *suffix) {
static int user_config_dir(char **ret, const char *suffix) {
const char *e;
char *j;
+ int r;
assert(ret);
@@ -64,11 +66,11 @@ static int user_config_dir(char **ret, const char *suffix) {
if (e)
j = strappend(e, suffix);
else {
- const char *home;
+ _cleanup_free_ char *home = NULL;
- home = getenv("HOME");
- if (!home)
- return -ENXIO;
+ r = get_home_dir(&home);
+ if (r < 0)
+ return r;
j = strjoin(home, "/.config", suffix);
}
@@ -83,6 +85,7 @@ static int user_config_dir(char **ret, const char *suffix) {
static int user_data_dir(char **ret, const char *suffix) {
const char *e;
char *j;
+ int r;
assert(ret);
assert(suffix);
@@ -95,12 +98,11 @@ static int user_data_dir(char **ret, const char *suffix) {
if (e)
j = strappend(e, suffix);
else {
- const char *home;
-
- home = getenv("HOME");
- if (!home)
- return -ENXIO;
+ _cleanup_free_ char *home = NULL;
+ r = get_home_dir(&home);
+ if (r < 0)
+ return r;
j = strjoin(home, "/.local/share", suffix);
}
@@ -136,10 +138,10 @@ static char** user_dirs(
NULL
};
- const char *e;
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
_cleanup_free_ char *data_home = NULL;
_cleanup_strv_free_ char **res = NULL;
+ const char *e;
char **tmp;
int r;
@@ -186,9 +188,8 @@ static char** user_dirs(
if (strv_extend(&res, generator_early) < 0)
return NULL;
- if (!strv_isempty(config_dirs))
- if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
- return NULL;
+ if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
+ return NULL;
if (strv_extend(&res, persistent_config) < 0)
return NULL;
@@ -205,9 +206,8 @@ static char** user_dirs(
if (strv_extend(&res, data_home) < 0)
return NULL;
- if (!strv_isempty(data_dirs))
- if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
- return NULL;
+ if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
+ return NULL;
if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0)
return NULL;
@@ -220,6 +220,7 @@ static char** user_dirs(
tmp = res;
res = NULL;
+
return tmp;
}
@@ -328,12 +329,18 @@ static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **ru
case UNIT_FILE_USER:
r = user_config_dir(&a, "/systemd/user");
- if (r < 0)
+ if (r < 0 && r != -ENXIO)
return r;
r = user_runtime_dir(runtime, "/systemd/user");
- if (r < 0)
- return r;
+ if (r < 0) {
+ if (r != -ENXIO)
+ return r;
+
+ /* If XDG_RUNTIME_DIR is not set, don't consider that fatal, simply initialize the runtime
+ * directory to NULL */
+ *runtime = NULL;
+ }
*persistent = a;
a = NULL;
@@ -382,12 +389,18 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r
case UNIT_FILE_USER:
r = user_config_dir(&a, "/systemd/system.control");
- if (r < 0)
+ if (r < 0 && r != -ENXIO)
return r;
r = user_runtime_dir(runtime, "/systemd/system.control");
- if (r < 0)
- return r;
+ if (r < 0) {
+ if (r != -ENXIO)
+ return r;
+
+ /* If XDG_RUNTIME_DIR is not set, don't consider this fatal, simply initialize the directory to
+ * NULL */
+ *runtime = NULL;
+ }
break;
@@ -474,22 +487,26 @@ int lookup_paths_init(
return -ENOMEM;
}
+ /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_config to NULL */
r = acquire_config_dirs(scope, &persistent_config, &runtime_config);
- if (r < 0 && r != -ENXIO)
+ if (r < 0)
return r;
if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) {
+ /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
r = acquire_generator_dirs(scope, &generator, &generator_early, &generator_late);
if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO)
return r;
}
+ /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
r = acquire_transient_dir(scope, &transient);
if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO)
return r;
+ /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_control to NULL */
r = acquire_control_dirs(scope, &persistent_control, &runtime_control);
- if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO)
+ if (r < 0 && r != -EOPNOTSUPP)
return r;
/* First priority is whatever has been passed to us via env vars */
diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c
index 293c6673fc..59b541d519 100644
--- a/src/shared/ptyfwd.c
+++ b/src/shared/ptyfwd.c
@@ -69,6 +69,7 @@ struct PTYForward {
bool read_from_master:1;
bool done:1;
+ bool drain:1;
bool last_char_set:1;
char last_char;
@@ -302,6 +303,11 @@ static int shovel(PTYForward *f) {
return pty_forward_done(f, 0);
}
+ /* If we were asked to drain, and there's nothing more to handle from the master, then call the callback
+ * too. */
+ if (f->drain && f->out_buffer_full == 0 && !f->master_readable)
+ return pty_forward_done(f, 0);
+
return 0;
}
@@ -438,6 +444,9 @@ int pty_forward_new(
r = sd_event_add_io(f->event, &f->stdin_event_source, STDIN_FILENO, EPOLLIN|EPOLLET, on_stdin_event, f);
if (r < 0 && r != -EPERM)
return r;
+
+ if (r >= 0)
+ (void) sd_event_source_set_description(f->stdin_event_source, "ptyfwd-stdin");
}
r = sd_event_add_io(f->event, &f->stdout_event_source, STDOUT_FILENO, EPOLLOUT|EPOLLET, on_stdout_event, f);
@@ -446,15 +455,21 @@ int pty_forward_new(
f->stdout_writable = true;
else if (r < 0)
return r;
+ else
+ (void) sd_event_source_set_description(f->stdout_event_source, "ptyfwd-stdout");
r = sd_event_add_io(f->event, &f->master_event_source, master, EPOLLIN|EPOLLOUT|EPOLLET, on_master_event, f);
if (r < 0)
return r;
+ (void) sd_event_source_set_description(f->master_event_source, "ptyfwd-master");
+
r = sd_event_add_signal(f->event, &f->sigwinch_event_source, SIGWINCH, on_sigwinch_event, f);
if (r < 0)
return r;
+ (void) sd_event_source_set_description(f->sigwinch_event_source, "ptyfwd-sigwinch");
+
*ret = f;
f = NULL;
@@ -519,3 +534,18 @@ void pty_forward_set_handler(PTYForward *f, PTYForwardHandler cb, void *userdata
f->handler = cb;
f->userdata = userdata;
}
+
+bool pty_forward_drain(PTYForward *f) {
+ assert(f);
+
+ /* Starts draining the forwarder. Specifically:
+ *
+ * - Returns true if there are no unprocessed bytes from the pty, false otherwise
+ *
+ * - Makes sure the handler function is called the next time the number of unprocessed bytes hits zero
+ */
+
+ f->drain = true;
+
+ return f->out_buffer_full == 0 && !f->master_readable;
+}
diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h
index bd5d5fec0d..3fad1d3b26 100644
--- a/src/shared/ptyfwd.h
+++ b/src/shared/ptyfwd.h
@@ -51,4 +51,6 @@ bool pty_forward_is_done(PTYForward *f);
void pty_forward_set_handler(PTYForward *f, PTYForwardHandler handler, void *userdata);
+bool pty_forward_drain(PTYForward *f);
+
DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free);
diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c
index 2c73cb8fa4..84964f750f 100644
--- a/src/shared/seccomp-util.c
+++ b/src/shared/seccomp-util.c
@@ -36,31 +36,72 @@
const uint32_t seccomp_local_archs[] = {
-#if defined(__i386__) || defined(__x86_64__)
+ /* Note: always list the native arch we are compiled as last, so that users can blacklist seccomp(), but our own calls to it still succeed */
+
+#if defined(__x86_64__) && defined(__ILP32__)
SCMP_ARCH_X86,
SCMP_ARCH_X86_64,
+ SCMP_ARCH_X32, /* native */
+#elif defined(__x86_64__) && !defined(__ILP32__)
+ SCMP_ARCH_X86,
SCMP_ARCH_X32,
-
-#elif defined(__arm__) || defined(__aarch64__)
+ SCMP_ARCH_X86_64, /* native */
+#elif defined(__i386__)
+ SCMP_ARCH_X86,
+#elif defined(__aarch64__)
SCMP_ARCH_ARM,
- SCMP_ARCH_AARCH64,
-
-#elif defined(__mips__) || defined(__mips64__)
+ SCMP_ARCH_AARCH64, /* native */
+#elif defined(__arm__)
+ SCMP_ARCH_ARM,
+#elif defined(__mips__) && __BYTE_ORDER == __BIG_ENDIAN && _MIPS_SIM == _MIPS_SIM_ABI32
+ SCMP_ARCH_MIPSEL,
+ SCMP_ARCH_MIPS, /* native */
+#elif defined(__mips__) && __BYTE_ORDER == __LITTLE_ENDIAN && _MIPS_SIM == _MIPS_SIM_ABI32
SCMP_ARCH_MIPS,
- SCMP_ARCH_MIPS64,
+ SCMP_ARCH_MIPSEL, /* native */
+#elif defined(__mips__) && __BYTE_ORDER == __BIG_ENDIAN && _MIPS_SIM == _MIPS_SIM_ABI64
+ SCMP_ARCH_MIPSEL,
+ SCMP_ARCH_MIPS,
+ SCMP_ARCH_MIPSEL64N32,
+ SCMP_ARCH_MIPS64N32,
+ SCMP_ARCH_MIPSEL64,
+ SCMP_ARCH_MIPS64, /* native */
+#elif defined(__mips__) && __BYTE_ORDER == __LITTLE_ENDIAN && _MIPS_SIM == _MIPS_SIM_ABI64
+ SCMP_ARCH_MIPS,
+ SCMP_ARCH_MIPSEL,
SCMP_ARCH_MIPS64N32,
+ SCMP_ARCH_MIPSEL64N32,
+ SCMP_ARCH_MIPS64,
+ SCMP_ARCH_MIPSEL64, /* native */
+#elif defined(__mips__) && __BYTE_ORDER == __BIG_ENDIAN && _MIPS_SIM == _MIPS_SIM_NABI32
SCMP_ARCH_MIPSEL,
+ SCMP_ARCH_MIPS,
SCMP_ARCH_MIPSEL64,
+ SCMP_ARCH_MIPS64,
SCMP_ARCH_MIPSEL64N32,
-
-#elif defined(__powerpc__) || defined(__powerpc64__)
+ SCMP_ARCH_MIPS64N32, /* native */
+#elif defined(__mips__) && __BYTE_ORDER == __LITTLE_ENDIAN && _MIPS_SIM == _MIPS_SIM_NABI32
+ SCMP_ARCH_MIPS,
+ SCMP_ARCH_MIPSEL,
+ SCMP_ARCH_MIPS64,
+ SCMP_ARCH_MIPSEL64,
+ SCMP_ARCH_MIPS64N32,
+ SCMP_ARCH_MIPSEL64N32, /* native */
+#elif defined(__powerpc64__) && __BYTE_ORDER == __BIG_ENDIAN
SCMP_ARCH_PPC,
- SCMP_ARCH_PPC64,
SCMP_ARCH_PPC64LE,
-
-#elif defined(__s390__) || defined(__s390x__)
+ SCMP_ARCH_PPC64, /* native */
+#elif defined(__powerpc64__) && __BYTE_ORDER == __LITTLE_ENDIAN
+ SCMP_ARCH_PPC,
+ SCMP_ARCH_PPC64,
+ SCMP_ARCH_PPC64LE, /* native */
+#elif defined(__powerpc__)
+ SCMP_ARCH_PPC,
+#elif defined(__s390x__)
+ SCMP_ARCH_S390,
+ SCMP_ARCH_S390X, /* native */
+#elif defined(__s390__)
SCMP_ARCH_S390,
- SCMP_ARCH_S390X,
#endif
(uint32_t) -1
};
@@ -171,11 +212,11 @@ int seccomp_init_for_arch(scmp_filter_ctx *ret, uint32_t arch, uint32_t default_
if (arch != SCMP_ARCH_NATIVE &&
arch != seccomp_arch_native()) {
- r = seccomp_arch_add(seccomp, arch);
+ r = seccomp_arch_remove(seccomp, seccomp_arch_native());
if (r < 0)
goto finish;
- r = seccomp_arch_remove(seccomp, seccomp_arch_native());
+ r = seccomp_arch_add(seccomp, arch);
if (r < 0)
goto finish;
@@ -344,6 +385,7 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = {
"mknodat\0"
"mmap2\0"
"mmap\0"
+ "munmap\0"
"newfstatat\0"
"open\0"
"openat\0"
@@ -750,10 +792,35 @@ int seccomp_restrict_namespaces(unsigned long retain) {
SECCOMP_FOREACH_LOCAL_ARCH(arch) {
_cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL;
+ int clone_reversed_order = -1;
unsigned i;
log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch));
+ switch (arch) {
+
+ case SCMP_ARCH_X86_64:
+ case SCMP_ARCH_X86:
+ case SCMP_ARCH_X32:
+ clone_reversed_order = 0;
+ break;
+
+ case SCMP_ARCH_S390:
+ case SCMP_ARCH_S390X:
+ /* On s390/s390x the first two parameters to clone are switched */
+ clone_reversed_order = 1;
+ break;
+
+ /* Please add more definitions here, if you port systemd to other architectures! */
+
+#if !defined(__i386__) && !defined(__x86_64__) && !defined(__s390__) && !defined(__s390x__)
+#warning "Consider adding the right clone() syscall definitions here!"
+#endif
+ }
+
+ if (clone_reversed_order < 0) /* we don't know the right order, let's ignore this arch... */
+ continue;
+
r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW);
if (r < 0)
return r;
@@ -802,12 +869,20 @@ int seccomp_restrict_namespaces(unsigned long retain) {
break;
}
- r = seccomp_rule_add_exact(
- seccomp,
- SCMP_ACT_ERRNO(EPERM),
- SCMP_SYS(clone),
- 1,
- SCMP_A0(SCMP_CMP_MASKED_EQ, f, f));
+ if (clone_reversed_order == 0)
+ r = seccomp_rule_add_exact(
+ seccomp,
+ SCMP_ACT_ERRNO(EPERM),
+ SCMP_SYS(clone),
+ 1,
+ SCMP_A0(SCMP_CMP_MASKED_EQ, f, f));
+ else
+ r = seccomp_rule_add_exact(
+ seccomp,
+ SCMP_ACT_ERRNO(EPERM),
+ SCMP_SYS(clone),
+ 1,
+ SCMP_A1(SCMP_CMP_MASKED_EQ, f, f));
if (r < 0) {
log_debug_errno(r, "Failed to add clone() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch));
break;
@@ -878,10 +953,37 @@ int seccomp_restrict_address_families(Set *address_families, bool whitelist) {
SECCOMP_FOREACH_LOCAL_ARCH(arch) {
_cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL;
+ bool supported;
Iterator i;
log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch));
+ switch (arch) {
+
+ case SCMP_ARCH_X86_64:
+ case SCMP_ARCH_X32:
+ case SCMP_ARCH_ARM:
+ case SCMP_ARCH_AARCH64:
+ /* These we know we support (i.e. are the ones that do not use socketcall()) */
+ supported = true;
+ break;
+
+ case SCMP_ARCH_X86:
+ case SCMP_ARCH_S390:
+ case SCMP_ARCH_S390X:
+ case SCMP_ARCH_PPC:
+ case SCMP_ARCH_PPC64:
+ case SCMP_ARCH_PPC64LE:
+ default:
+ /* These we either know we don't support (i.e. are the ones that do use socketcall()), or we
+ * don't know */
+ supported = false;
+ break;
+ }
+
+ if (!supported)
+ continue;
+
r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW);
if (r < 0)
return r;
@@ -1083,27 +1185,81 @@ int seccomp_restrict_realtime(void) {
}
int seccomp_memory_deny_write_execute(void) {
+
uint32_t arch;
int r;
SECCOMP_FOREACH_LOCAL_ARCH(arch) {
_cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL;
+ int filter_syscall = 0, block_syscall = 0, shmat_syscall = 0;
log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch));
+ switch (arch) {
+
+ case SCMP_ARCH_X86:
+ filter_syscall = SCMP_SYS(mmap2);
+ block_syscall = SCMP_SYS(mmap);
+
+ /* Note that shmat() isn't available on i386, where the call is multiplexed through ipc(). We
+ * ignore that here, which means there's still a way to get writable/executable memory, if an
+ * IPC key is mapped like this on i386. That's a pity, but no total loss. */
+ break;
+
+ case SCMP_ARCH_X86_64:
+ case SCMP_ARCH_X32:
+ filter_syscall = SCMP_SYS(mmap);
+ shmat_syscall = SCMP_SYS(shmat);
+ break;
+
+ /* Please add more definitions here, if you port systemd to other architectures! */
+
+#if !defined(__i386__) && !defined(__x86_64__)
+#warning "Consider adding the right mmap() syscall definitions here!"
+#endif
+ }
+
+ /* Can't filter mmap() on this arch, then skip it */
+ if (filter_syscall == 0)
+ continue;
+
r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW);
if (r < 0)
return r;
- r = seccomp_rule_add_exact(
- seccomp,
- SCMP_ACT_ERRNO(EPERM),
- SCMP_SYS(mmap),
- 1,
- SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC|PROT_WRITE, PROT_EXEC|PROT_WRITE));
- if (r < 0) {
- log_debug_errno(r, "Failed to add mmap() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch));
- continue;
+ if (filter_syscall != 0) {
+ r = seccomp_rule_add_exact(
+ seccomp,
+ SCMP_ACT_ERRNO(EPERM),
+ filter_syscall,
+ 1,
+ SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC|PROT_WRITE, PROT_EXEC|PROT_WRITE));
+ if (r < 0) {
+ _cleanup_free_ char *n = NULL;
+
+ n = seccomp_syscall_resolve_num_arch(arch, filter_syscall);
+ log_debug_errno(r, "Failed to add %s() rule for architecture %s, skipping: %m",
+ strna(n),
+ seccomp_arch_to_string(arch));
+ continue;
+ }
+ }
+
+ if (block_syscall != 0) {
+ r = seccomp_rule_add_exact(
+ seccomp,
+ SCMP_ACT_ERRNO(EPERM),
+ block_syscall,
+ 0);
+ if (r < 0) {
+ _cleanup_free_ char *n = NULL;
+
+ n = seccomp_syscall_resolve_num_arch(arch, block_syscall);
+ log_debug_errno(r, "Failed to add %s() rule for architecture %s, skipping: %m",
+ strna(n),
+ seccomp_arch_to_string(arch));
+ continue;
+ }
}
r = seccomp_rule_add_exact(
@@ -1117,15 +1273,17 @@ int seccomp_memory_deny_write_execute(void) {
continue;
}
- r = seccomp_rule_add_exact(
- seccomp,
- SCMP_ACT_ERRNO(EPERM),
- SCMP_SYS(shmat),
- 1,
- SCMP_A2(SCMP_CMP_MASKED_EQ, SHM_EXEC, SHM_EXEC));
- if (r < 0) {
- log_debug_errno(r, "Failed to add shmat() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch));
- continue;
+ if (shmat_syscall != 0) {
+ r = seccomp_rule_add_exact(
+ seccomp,
+ SCMP_ACT_ERRNO(EPERM),
+ SCMP_SYS(shmat),
+ 1,
+ SCMP_A2(SCMP_CMP_MASKED_EQ, SHM_EXEC, SHM_EXEC));
+ if (r < 0) {
+ log_debug_errno(r, "Failed to add shmat() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch));
+ continue;
+ }
}
r = seccomp_load(seccomp);
diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h
index 4438e87fa6..61f94de638 100644
--- a/src/shared/seccomp-util.h
+++ b/src/shared/seccomp-util.h
@@ -76,6 +76,28 @@ int seccomp_restrict_address_families(Set *address_families, bool whitelist);
int seccomp_restrict_realtime(void);
int seccomp_memory_deny_write_execute(void);
+#if defined(__i386__) || defined(__s390x__) || defined(__s390__) || defined(__powerpc64__) || defined(__powerpc__) || defined (__mips__)
+/* On these archs, socket() is implemented via the socketcall() syscall multiplexer, and we can't restrict it hence via
+ * seccomp */
+#define SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN 1
+#else
+#define SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN 0
+#endif
+
+/* mmap() blocking is only available on some archs for now */
+#if defined(__x86_64__) || defined(__i386__)
+#define SECCOMP_MEMORY_DENY_WRITE_EXECUTE_BROKEN 0
+#else
+#define SECCOMP_MEMORY_DENY_WRITE_EXECUTE_BROKEN 1
+#endif
+
+/* we don't know the right order of the clone() parameters except for these archs, for now */
+#if defined(__x86_64__) || defined(__i386__) || defined(__s390x__) || defined(__s390__)
+#define SECCOMP_RESTRICT_NAMESPACES_BROKEN 0
+#else
+#define SECCOMP_RESTRICT_NAMESPACES_BROKEN 1
+#endif
+
extern const uint32_t seccomp_local_archs[];
#define SECCOMP_FOREACH_LOCAL_ARCH(arch) \
diff --git a/src/system-update-generator/system-update-generator.c b/src/system-update-generator/system-update-generator.c
index 6ffb169b21..f1514c9638 100644
--- a/src/system-update-generator/system-update-generator.c
+++ b/src/system-update-generator/system-update-generator.c
@@ -22,6 +22,7 @@
#include "fs-util.h"
#include "log.h"
+#include "proc-cmdline.h"
#include "special.h"
#include "string-util.h"
#include "util.h"
@@ -47,11 +48,27 @@ static int generate_symlink(void) {
if (symlink(SYSTEM_DATA_UNIT_PATH "/system-update.target", p) < 0)
return log_error_errno(errno, "Failed to create symlink %s: %m", p);
+ return 1;
+}
+
+static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
+ assert(key);
+
+ /* Check if a run level is specified on the kernel command line. The
+ * command line has higher priority than any on-disk configuration, so
+ * it'll make any symlink we create moot.
+ */
+
+ if (streq(key, "systemd.unit") && !proc_cmdline_value_missing(key, value))
+ log_warning("Offline system update overriden by kernel command line systemd.unit= setting");
+ else if (!value && runlevel_to_target(key))
+ log_warning("Offline system update overriden by runlevel \"%s\" on the kernel command line", key);
+
return 0;
}
int main(int argc, char *argv[]) {
- int r;
+ int r, k;
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
@@ -69,5 +86,11 @@ int main(int argc, char *argv[]) {
r = generate_symlink();
+ if (r > 0) {
+ k = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
+ if (k < 0)
+ log_warning_errno(k, "Failed to parse kernel command line, ignoring: %m");
+ }
+
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 41e8d6075a..2336ae34f4 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -1922,7 +1922,7 @@ static int get_machine_properties(sd_bus *bus, struct machine_info *mi) {
bus = container;
}
- r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, mi);
+ r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, NULL, mi);
if (r < 0)
return r;
@@ -1957,7 +1957,7 @@ static int get_machine_list(
machine_infos[c].name = hn;
hn = NULL;
- get_machine_properties(bus, &machine_infos[c]);
+ (void) get_machine_properties(bus, &machine_infos[c]);
c++;
}
@@ -1987,7 +1987,7 @@ static int get_machine_list(
return log_oom();
}
- get_machine_properties(NULL, &machine_infos[c]);
+ (void) get_machine_properties(NULL, &machine_infos[c]);
c++;
}
@@ -2484,7 +2484,7 @@ static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **un
_cleanup_free_ char *path = NULL, *lpath = NULL;
int r;
- path = path_join(arg_root, *p, unit_name);
+ path = path_join(NULL, *p, unit_name);
if (!path)
return log_oom();
@@ -2601,7 +2601,8 @@ static int unit_find_paths(
return log_error_errno(r, "Failed to add unit name: %m");
if (dropin_paths) {
- r = unit_file_find_dropin_paths(lp->search_path, NULL, names, &dropins);
+ r = unit_file_find_dropin_conf_paths(arg_root, lp->search_path,
+ NULL, names, &dropins);
if (r < 0)
return r;
}
@@ -4952,7 +4953,7 @@ static int show_one(
return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r));
if (unit) {
- r = bus_message_map_all_properties(reply, property_map, &info);
+ r = bus_message_map_all_properties(reply, property_map, &error, &info);
if (r < 0)
return log_error_errno(r, "Failed to map properties: %s", bus_error_message(&error, r));
@@ -5029,13 +5030,10 @@ static int show_one(
r = 0;
if (show_properties) {
char **pp;
- int not_found_level = streq(verb, "show") ? LOG_DEBUG : LOG_WARNING;
STRV_FOREACH(pp, arg_properties)
- if (!set_contains(found_properties, *pp)) {
- log_full(not_found_level, "Property %s does not exist.", *pp);
- r = -ENXIO;
- }
+ if (!set_contains(found_properties, *pp))
+ log_debug("Property %s does not exist.", *pp);
} else if (streq(verb, "help"))
show_unit_help(&info);
@@ -5127,8 +5125,9 @@ static int show_all(
static int show_system_status(sd_bus *bus) {
char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], since2[FORMAT_TIMESTAMP_MAX];
- _cleanup_free_ char *hn = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(machine_info_clear) struct machine_info mi = {};
+ _cleanup_free_ char *hn = NULL;
const char *on, *off;
int r;
@@ -5136,9 +5135,9 @@ static int show_system_status(sd_bus *bus) {
if (!hn)
return log_oom();
- r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, &mi);
+ r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, &error, &mi);
if (r < 0)
- return log_error_errno(r, "Failed to read server status: %m");
+ return log_error_errno(r, "Failed to read server status: %s", bus_error_message(&error, r));
if (streq_ptr(mi.state, "degraded")) {
on = ansi_highlight_red();
@@ -5655,16 +5654,22 @@ static int switch_root(int argc, char *argv[], void *userdata) {
}
/* Instruct PID1 to exclude us from its killing spree applied during
- * the transition from the initrd to the main system otherwise we would
- * exit with a failure status even though the switch to the new root
- * has succeed. */
- if (in_initrd())
- argv_cmdline[0] = '@';
+ * the transition. Otherwise we would exit with a failure status even
+ * though the switch to the new root has succeed. */
+ argv_cmdline[0] = '@';
r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
return r;
+ /* If we are slow to exit after the root switch, the new systemd instance
+ * will send us a signal to terminate. Just ignore it and exit normally.
+ * This way the unit does not end up as failed.
+ */
+ r = ignore_signals(SIGTERM, -1);
+ if (r < 0)
+ log_warning_errno(r, "Failed to change disposition of SIGTERM to ignore: %m");
+
log_debug("Switching root - root: %s; init: %s", root, strna(init));
r = sd_bus_call_method(
@@ -5676,8 +5681,11 @@ static int switch_root(int argc, char *argv[], void *userdata) {
&error,
NULL,
"ss", root, init);
- if (r < 0)
+ if (r < 0) {
+ (void) default_signals(SIGTERM, -1);
+
return log_error_errno(r, "Failed to switch root: %s", bus_error_message(&error, r));
+ }
return 0;
}
@@ -6021,7 +6029,7 @@ static int unit_exists(const char *unit) {
if (r < 0)
return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r));
- r = bus_message_map_all_properties(reply, property_map, &info);
+ r = bus_message_map_all_properties(reply, property_map, &error, &info);
if (r < 0)
return log_error_errno(r, "Failed to map properties: %s", bus_error_message(&error, r));
@@ -6211,21 +6219,25 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
"4) In case of template units, the unit is meant to be enabled with some\n"
" instance name specified.");
- if (arg_now && n_changes > 0 && STR_IN_SET(argv[0], "enable", "disable", "mask")) {
- char *new_args[n_changes + 2];
+ if (arg_now && STR_IN_SET(argv[0], "enable", "disable", "mask")) {
sd_bus *bus;
- unsigned i;
+ unsigned len, i;
r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
goto finish;
- new_args[0] = (char*) (streq(argv[0], "enable") ? "start" : "stop");
- for (i = 0; i < n_changes; i++)
- new_args[i + 1] = basename(changes[i].path);
- new_args[i + 1] = NULL;
+ len = strv_length(names);
+ {
+ char *new_args[len + 2];
+
+ new_args[0] = (char*) (streq(argv[0], "enable") ? "start" : "stop");
+ for (i = 0; i < len; i++)
+ new_args[i + 1] = basename(names[i]);
+ new_args[i + 1] = NULL;
- r = start_unit(strv_length(new_args), new_args, userdata);
+ r = start_unit(len + 1, new_args, userdata);
+ }
}
finish:
@@ -6928,6 +6940,7 @@ static void systemctl_help(void) {
" -a --all Show all properties/all units currently in memory,\n"
" including dead/empty ones. To list all units installed on\n"
" the system, use the 'list-unit-files' command instead.\n"
+ " --failed Same as --state=failed\n"
" -l --full Don't ellipsize unit names on output\n"
" -r --recursive Show unit list of host and local containers\n"
" --reverse Show reverse dependencies with 'list-dependencies'\n"
diff --git a/src/test/test-af-list.c b/src/test/test-af-list.c
index e2479133de..e5ca54c8e7 100644
--- a/src/test/test-af-list.c
+++ b/src/test/test-af-list.c
@@ -24,6 +24,7 @@
#include "string-util.h"
#include "util.h"
+_unused_ \
static const struct af_name* lookup_af(register const char *str, register GPERF_LEN_TYPE len);
#include "af-from-name.h"
diff --git a/src/test/test-arphrd-list.c b/src/test/test-arphrd-list.c
index 8f4f342faa..bb51518c9c 100644
--- a/src/test/test-arphrd-list.c
+++ b/src/test/test-arphrd-list.c
@@ -24,6 +24,7 @@
#include "string-util.h"
#include "util.h"
+_unused_ \
static const struct arphrd_name* lookup_arphrd(register const char *str, register GPERF_LEN_TYPE len);
#include "arphrd-from-name.h"
diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c
index 1f34a91b10..f90b73aeaf 100644
--- a/src/test/test-calendarspec.c
+++ b/src/test/test-calendarspec.c
@@ -192,6 +192,7 @@ int main(int argc, char* argv[]) {
test_one("00..07-*-*", "2000..2007-*-* 00:00:00");
test_one("*:20..39/5", "*-*-* *:20..35/5:00");
test_one("00:00:20..40/1", "*-*-* 00:00:20..40");
+ test_one("*~03/1,03..05", "*-*~03/1,03..05 00:00:00");
test_next("2016-03-27 03:17:00", "", 12345, 1459048620000000);
test_next("2016-03-27 03:17:00", "CET", 12345, 1459041420000000);
diff --git a/src/test/test-date.c b/src/test/test-date.c
index a8d3f1e083..b77598c81d 100644
--- a/src/test/test-date.c
+++ b/src/test/test-date.c
@@ -95,6 +95,16 @@ int main(int argc, char *argv[]) {
test_one_noutc("@1395716396");
test_should_parse("today UTC");
test_should_fail("today UTC UTC");
+ test_should_parse("1970-1-1 UTC");
+ test_should_fail("1969-1-1 UTC");
+#if SIZEOF_TIME_T == 8
+ test_should_parse("9999-12-30 23:59:59 UTC");
+ test_should_fail("9999-12-31 00:00:00 UTC");
+ test_should_fail("10000-01-01 00:00:00 UTC");
+#elif SIZEOF_TIME_T == 4
+ test_should_parse("2038-01-19 03:14:07 UTC");
+ test_should_fail( "2038-01-19 03:14:08 UTC");
+#endif
return 0;
}
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index bc9a2021f9..1e479b9843 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -422,6 +422,10 @@ static void test_exec_spec_interpolation(Manager *m) {
test(m, "exec-spec-interpolation.service", 0, CLD_EXITED);
}
+static void test_exec_read_only_path_suceed(Manager *m) {
+ test(m, "exec-read-only-path-succeed.service", 0, CLD_EXITED);
+}
+
static int run_tests(UnitFileScope scope, const test_function_t *tests) {
const test_function_t *test = NULL;
Manager *m = NULL;
@@ -475,6 +479,7 @@ int main(int argc, char *argv[]) {
test_exec_oomscoreadjust,
test_exec_ioschedulingclass,
test_exec_spec_interpolation,
+ test_exec_read_only_path_suceed,
NULL,
};
static const test_function_t system_tests[] = {
diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c
index ae68587be9..e774f567e0 100644
--- a/src/test/test-fs-util.c
+++ b/src/test/test-fs-util.c
@@ -186,6 +186,13 @@ static void test_chase_symlinks(void) {
r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
assert_se(r == -ENOENT);
+ p = strjoina(temp, "/target");
+ q = strjoina(temp, "/top");
+ assert_se(symlink(q, p) >= 0);
+ p = strjoina(temp, "/target/idontexist");
+ r = chase_symlinks(p, NULL, 0, &result);
+ assert_se(r == -ENOENT);
+
assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
@@ -298,12 +305,23 @@ static void test_var_tmp(void) {
}
}
+static void test_dot_or_dot_dot(void) {
+ assert_se(!dot_or_dot_dot(NULL));
+ assert_se(!dot_or_dot_dot(""));
+ assert_se(!dot_or_dot_dot("xxx"));
+ assert_se(dot_or_dot_dot("."));
+ assert_se(dot_or_dot_dot(".."));
+ assert_se(!dot_or_dot_dot(".foo"));
+ assert_se(!dot_or_dot_dot("..foo"));
+}
+
int main(int argc, char *argv[]) {
test_unlink_noerrno();
test_readlink_and_make_absolute();
test_get_files_in_directory();
test_var_tmp();
test_chase_symlinks();
+ test_dot_or_dot_dot();
return 0;
}
diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c
index d0bc8004f3..575401cb91 100644
--- a/src/test/test-install-root.c
+++ b/src/test/test-install-root.c
@@ -736,6 +736,28 @@ static void test_preset_order(const char *root) {
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
}
+static void test_static_instance(const char *root) {
+ UnitFileState state;
+ const char *p;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@foo.service", &state) == -ENOENT);
+
+ p = strjoina(root, "/usr/lib/systemd/system/static-instance@.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ p = strjoina(root, "/usr/lib/systemd/system/static-instance@foo.service");
+ assert_se(symlink("static-instance@.service", p) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_STATIC);
+}
+
int main(int argc, char *argv[]) {
char root[] = "/tmp/rootXXXXXX";
const char *p;
@@ -766,6 +788,7 @@ int main(int argc, char *argv[]) {
test_preset_and_list(root);
test_preset_order(root);
test_revert(root);
+ test_static_instance(root);
assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
diff --git a/src/test/test-ns.c b/src/test/test-ns.c
index c99bcb371b..0125d905a6 100644
--- a/src/test/test-ns.c
+++ b/src/test/test-ns.c
@@ -77,6 +77,7 @@ int main(int argc, char *argv[]) {
log_info("Not chrooted");
r = setup_namespace(root_directory,
+ NULL,
&ns_info,
(char **) writable,
(char **) readonly,
@@ -86,6 +87,7 @@ int main(int argc, char *argv[]) {
var_tmp_dir,
PROTECT_HOME_NO,
PROTECT_SYSTEM_NO,
+ 0,
0);
if (r < 0) {
log_error_errno(r, "Failed to setup namespace: %m");
diff --git a/src/test/test-nss.c b/src/test/test-nss.c
index 4ccfe75147..b59cb7aa69 100644
--- a/src/test/test-nss.c
+++ b/src/test/test-nss.c
@@ -511,7 +511,7 @@ int main(int argc, char **argv) {
_cleanup_free_ char *dir = NULL;
_cleanup_strv_free_ char **modules = NULL, **names = NULL;
_cleanup_free_ struct local_address *addresses = NULL;
- int n_addresses;
+ int n_addresses = 0;
char **module;
int r;
diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c
index 6f15879c45..34a1275162 100644
--- a/src/test/test-seccomp.c
+++ b/src/test/test-seccomp.c
@@ -158,6 +158,8 @@ static void test_restrict_namespace(void) {
assert_se(streq(s, "cgroup ipc net mnt pid user uts"));
assert_se(namespace_flag_from_string_many(s, &ul) == 0 && ul == NAMESPACE_FLAGS_ALL);
+#if SECCOMP_RESTRICT_NAMESPACES_BROKEN == 0
+
if (!is_seccomp_available())
return;
if (geteuid() != 0)
@@ -216,6 +218,7 @@ static void test_restrict_namespace(void) {
}
assert_se(wait_for_terminate_and_warn("nsseccomp", pid, true) == EXIT_SUCCESS);
+#endif
}
static void test_protect_sysctl(void) {
@@ -283,8 +286,14 @@ static void test_restrict_address_families(void) {
assert_se(fd >= 0);
safe_close(fd);
+#if SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN
+ fd = socket(AF_UNIX, SOCK_DGRAM, 0);
+ assert_se(fd >= 0);
+ safe_close(fd);
+#else
assert_se(socket(AF_UNIX, SOCK_DGRAM, 0) < 0);
assert_se(errno == EAFNOSUPPORT);
+#endif
fd = socket(AF_NETLINK, SOCK_DGRAM, 0);
assert_se(fd >= 0);
@@ -300,11 +309,21 @@ static void test_restrict_address_families(void) {
assert_se(fd >= 0);
safe_close(fd);
+#if SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN
+ fd = socket(AF_UNIX, SOCK_DGRAM, 0);
+ assert_se(fd >= 0);
+ safe_close(fd);
+
+ fd = socket(AF_NETLINK, SOCK_DGRAM, 0);
+ assert_se(fd >= 0);
+ safe_close(fd);
+#else
assert_se(socket(AF_UNIX, SOCK_DGRAM, 0) < 0);
assert_se(errno == EAFNOSUPPORT);
assert_se(socket(AF_NETLINK, SOCK_DGRAM, 0) < 0);
assert_se(errno == EAFNOSUPPORT);
+#endif
_exit(EXIT_SUCCESS);
}
@@ -368,11 +387,21 @@ static void test_memory_deny_write_execute(void) {
assert_se(p != MAP_FAILED);
assert_se(munmap(p, page_size()) >= 0);
- seccomp_memory_deny_write_execute();
+ p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
+ assert_se(p != MAP_FAILED);
+ assert_se(munmap(p, page_size()) >= 0);
+ assert_se(seccomp_memory_deny_write_execute() >= 0);
+
+#if SECCOMP_MEMORY_DENY_WRITE_EXECUTE_BROKEN
+ p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
+ assert_se(p != MAP_FAILED);
+ assert_se(munmap(p, page_size()) >= 0);
+#else
p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
assert_se(p == MAP_FAILED);
assert_se(errno == EPERM);
+#endif
p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
assert_se(p != MAP_FAILED);
diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c
index e43373b0f5..4b3e924cfb 100644
--- a/src/test/test-string-util.c
+++ b/src/test/test-string-util.c
@@ -29,31 +29,20 @@ static void test_string_erase(void) {
assert_se(streq(string_erase(x), ""));
x = strdupa("1");
- assert_se(streq(string_erase(x), "x"));
-
- x = strdupa("12");
- assert_se(streq(string_erase(x), "xx"));
-
- x = strdupa("123");
- assert_se(streq(string_erase(x), "xxx"));
-
- x = strdupa("1234");
- assert_se(streq(string_erase(x), "xxxx"));
-
- x = strdupa("12345");
- assert_se(streq(string_erase(x), "xxxxx"));
-
- x = strdupa("123456");
- assert_se(streq(string_erase(x), "xxxxxx"));
-
- x = strdupa("1234567");
- assert_se(streq(string_erase(x), "xxxxxxx"));
-
- x = strdupa("12345678");
- assert_se(streq(string_erase(x), "xxxxxxxx"));
+ assert_se(streq(string_erase(x), ""));
x = strdupa("123456789");
- assert_se(streq(string_erase(x), "xxxxxxxxx"));
+ assert_se(streq(string_erase(x), ""));
+
+ assert_se(x[1] == '\0');
+ assert_se(x[2] == '\0');
+ assert_se(x[3] == '\0');
+ assert_se(x[4] == '\0');
+ assert_se(x[5] == '\0');
+ assert_se(x[6] == '\0');
+ assert_se(x[7] == '\0');
+ assert_se(x[8] == '\0');
+ assert_se(x[9] == '\0');
}
static void test_ascii_strcasecmp_n(void) {
diff --git a/src/test/test-time.c b/src/test/test-time.c
index 8259491813..911282bf0c 100644
--- a/src/test/test-time.c
+++ b/src/test/test-time.c
@@ -17,9 +17,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "random-util.h"
+#include "string-util.h"
#include "strv.h"
#include "time-util.h"
-#include "random-util.h"
static void test_parse_sec(void) {
usec_t u;
@@ -248,6 +249,30 @@ static void test_format_timestamp(void) {
}
}
+static void test_format_timestamp_utc_one(usec_t t, const char *result) {
+ char buf[FORMAT_TIMESTAMP_MAX];
+
+ assert_se(!format_timestamp_utc(buf, sizeof(buf), t) == !result);
+
+ if (result)
+ assert_se(streq(result, buf));
+}
+
+static void test_format_timestamp_utc(void) {
+ test_format_timestamp_utc_one(0, NULL);
+ test_format_timestamp_utc_one(1, "Thu 1970-01-01 00:00:00 UTC");
+ test_format_timestamp_utc_one(USEC_PER_SEC, "Thu 1970-01-01 00:00:01 UTC");
+
+#if SIZEOF_TIME_T == 8
+ test_format_timestamp_utc_one(USEC_TIMESTAMP_FORMATTABLE_MAX, "Thu 9999-12-30 23:59:59 UTC");
+#elif SIZEOF_TIME_T == 4
+ test_format_timestamp_utc_one(USEC_TIMESTAMP_FORMATTABLE_MAX, "Tue 2038-01-19 03:14:07 UTC");
+#endif
+
+ test_format_timestamp_utc_one(USEC_TIMESTAMP_FORMATTABLE_MAX+1, NULL);
+ test_format_timestamp_utc_one(USEC_INFINITY, NULL);
+}
+
int main(int argc, char *argv[]) {
uintmax_t x;
@@ -262,6 +287,7 @@ int main(int argc, char *argv[]) {
test_usec_add();
test_usec_sub();
test_format_timestamp();
+ test_format_timestamp_utc();
/* Ensure time_t is signed */
assert_cc((time_t) -1 < (time_t) 1);
diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c
index 553ef67011..281b1534a3 100644
--- a/src/timedate/timedatectl.c
+++ b/src/timedate/timedatectl.c
@@ -165,6 +165,8 @@ static int show_status(sd_bus *bus, char **args, unsigned n) {
{ "RTCTimeUSec", "t", NULL, offsetof(StatusInfo, rtc_time) },
{}
};
+
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(bus);
@@ -173,9 +175,10 @@ static int show_status(sd_bus *bus, char **args, unsigned n) {
"org.freedesktop.timedate1",
"/org/freedesktop/timedate1",
map,
+ &error,
&info);
if (r < 0)
- return log_error_errno(r, "Failed to query server: %m");
+ return log_error_errno(r, "Failed to query server: %s", bus_error_message(&error, r));
print_status_info(&info);
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index f4ce9791fb..c4f4d46ca1 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -385,7 +385,7 @@ static int dir_cleanup(
usec_t age;
_cleanup_free_ char *sub_path = NULL;
- if (STR_IN_SET(dent->d_name, ".", ".."))
+ if (dot_or_dot_dot(dent->d_name))
continue;
if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
@@ -1070,9 +1070,7 @@ static int item_do_children(Item *i, const char *path, action_t action) {
_cleanup_free_ char *p = NULL;
int q;
- errno = 0;
-
- if (STR_IN_SET(de->d_name, ".", ".."))
+ if (dot_or_dot_dot(de->d_name))
continue;
p = strjoin(path, "/", de->d_name);
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
index b45490be1a..a17c006d57 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -243,7 +243,7 @@ static int ask_password_plymouth(
r = 0;
finish:
- memory_erase(buffer, sizeof(buffer));
+ explicit_bzero(buffer, sizeof(buffer));
return r;
}
@@ -283,7 +283,7 @@ static int send_passwords(const char *socket_name, char **passwords) {
r = log_debug_errno(errno, "sendto(): %m");
finish:
- memory_erase(packet, packet_length);
+ explicit_bzero(packet, packet_length);
return r;
}
diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index deffefd60b..3f9c3ed0cf 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -73,29 +73,219 @@ void udev_event_unref(struct udev_event *event) {
free(event);
}
+enum subst_type {
+ SUBST_UNKNOWN,
+ SUBST_DEVNODE,
+ SUBST_ATTR,
+ SUBST_ENV,
+ SUBST_KERNEL,
+ SUBST_KERNEL_NUMBER,
+ SUBST_DRIVER,
+ SUBST_DEVPATH,
+ SUBST_ID,
+ SUBST_MAJOR,
+ SUBST_MINOR,
+ SUBST_RESULT,
+ SUBST_PARENT,
+ SUBST_NAME,
+ SUBST_LINKS,
+ SUBST_ROOT,
+ SUBST_SYS,
+};
+
+static size_t subst_format_var(struct udev_event *event, struct udev_device *dev,
+ enum subst_type type, char *attr,
+ char *dest, size_t l) {
+ char *s = dest;
+
+ switch (type) {
+ case SUBST_DEVPATH:
+ l = strpcpy(&s, l, udev_device_get_devpath(dev));
+ break;
+ case SUBST_KERNEL:
+ l = strpcpy(&s, l, udev_device_get_sysname(dev));
+ break;
+ case SUBST_KERNEL_NUMBER:
+ if (udev_device_get_sysnum(dev) == NULL)
+ break;
+ l = strpcpy(&s, l, udev_device_get_sysnum(dev));
+ break;
+ case SUBST_ID:
+ if (event->dev_parent == NULL)
+ break;
+ l = strpcpy(&s, l, udev_device_get_sysname(event->dev_parent));
+ break;
+ case SUBST_DRIVER: {
+ const char *driver;
+
+ if (event->dev_parent == NULL)
+ break;
+
+ driver = udev_device_get_driver(event->dev_parent);
+ if (driver == NULL)
+ break;
+ l = strpcpy(&s, l, driver);
+ break;
+ }
+ case SUBST_MAJOR: {
+ char num[UTIL_PATH_SIZE];
+
+ sprintf(num, "%u", major(udev_device_get_devnum(dev)));
+ l = strpcpy(&s, l, num);
+ break;
+ }
+ case SUBST_MINOR: {
+ char num[UTIL_PATH_SIZE];
+
+ sprintf(num, "%u", minor(udev_device_get_devnum(dev)));
+ l = strpcpy(&s, l, num);
+ break;
+ }
+ case SUBST_RESULT: {
+ char *rest;
+ int i;
+
+ if (event->program_result == NULL)
+ break;
+ /* get part of the result string */
+ i = 0;
+ if (attr != NULL)
+ i = strtoul(attr, &rest, 10);
+ if (i > 0) {
+ char result[UTIL_PATH_SIZE];
+ char tmp[UTIL_PATH_SIZE];
+ char *cpos;
+
+ strscpy(result, sizeof(result), event->program_result);
+ cpos = result;
+ while (--i) {
+ while (cpos[0] != '\0' && !isspace(cpos[0]))
+ cpos++;
+ while (isspace(cpos[0]))
+ cpos++;
+ if (cpos[0] == '\0')
+ break;
+ }
+ if (i > 0) {
+ log_error("requested part of result string not found");
+ break;
+ }
+ strscpy(tmp, sizeof(tmp), cpos);
+ /* %{2+}c copies the whole string from the second part on */
+ if (rest[0] != '+') {
+ cpos = strchr(tmp, ' ');
+ if (cpos)
+ cpos[0] = '\0';
+ }
+ l = strpcpy(&s, l, tmp);
+ } else {
+ l = strpcpy(&s, l, event->program_result);
+ }
+ break;
+ }
+ case SUBST_ATTR: {
+ const char *value = NULL;
+ char vbuf[UTIL_NAME_SIZE];
+ size_t len;
+ int count;
+
+ if (attr == NULL) {
+ log_error("missing file parameter for attr");
+ break;
+ }
+
+ /* try to read the value specified by "[dmi/id]product_name" */
+ if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0)
+ value = vbuf;
+
+ /* try to read the attribute the device */
+ if (value == NULL)
+ value = udev_device_get_sysattr_value(event->dev, attr);
+
+ /* try to read the attribute of the parent device, other matches have selected */
+ if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev)
+ value = udev_device_get_sysattr_value(event->dev_parent, attr);
+
+ if (value == NULL)
+ break;
+
+ /* strip trailing whitespace, and replace unwanted characters */
+ if (value != vbuf)
+ strscpy(vbuf, sizeof(vbuf), value);
+ len = strlen(vbuf);
+ while (len > 0 && isspace(vbuf[--len]))
+ vbuf[len] = '\0';
+ count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT);
+ if (count > 0)
+ log_debug("%i character(s) replaced" , count);
+ l = strpcpy(&s, l, vbuf);
+ break;
+ }
+ case SUBST_PARENT: {
+ struct udev_device *dev_parent;
+ const char *devnode;
+
+ dev_parent = udev_device_get_parent(event->dev);
+ if (dev_parent == NULL)
+ break;
+ devnode = udev_device_get_devnode(dev_parent);
+ if (devnode != NULL)
+ l = strpcpy(&s, l, devnode + strlen("/dev/"));
+ break;
+ }
+ case SUBST_DEVNODE:
+ if (udev_device_get_devnode(dev) != NULL)
+ l = strpcpy(&s, l, udev_device_get_devnode(dev));
+ break;
+ case SUBST_NAME:
+ if (event->name != NULL)
+ l = strpcpy(&s, l, event->name);
+ else if (udev_device_get_devnode(dev) != NULL)
+ l = strpcpy(&s, l, udev_device_get_devnode(dev) + strlen("/dev/"));
+ else
+ l = strpcpy(&s, l, udev_device_get_sysname(dev));
+ break;
+ case SUBST_LINKS: {
+ struct udev_list_entry *list_entry;
+
+ list_entry = udev_device_get_devlinks_list_entry(dev);
+ if (list_entry == NULL)
+ break;
+ l = strpcpy(&s, l, udev_list_entry_get_name(list_entry) + strlen("/dev/"));
+ udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry))
+ l = strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry) + strlen("/dev/"), NULL);
+ break;
+ }
+ case SUBST_ROOT:
+ l = strpcpy(&s, l, "/dev");
+ break;
+ case SUBST_SYS:
+ l = strpcpy(&s, l, "/sys");
+ break;
+ case SUBST_ENV:
+ if (attr == NULL) {
+ break;
+ } else {
+ const char *value;
+
+ value = udev_device_get_property_value(event->dev, attr);
+ if (value == NULL)
+ break;
+ l = strpcpy(&s, l, value);
+ break;
+ }
+ default:
+ log_error("unknown substitution type=%i", type);
+ break;
+ }
+
+ return s - dest;
+}
+
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,
- SUBST_DEVNODE,
- SUBST_ATTR,
- SUBST_ENV,
- SUBST_KERNEL,
- SUBST_KERNEL_NUMBER,
- SUBST_DRIVER,
- SUBST_DEVPATH,
- SUBST_ID,
- SUBST_MAJOR,
- SUBST_MINOR,
- SUBST_RESULT,
- SUBST_PARENT,
- SUBST_NAME,
- SUBST_LINKS,
- SUBST_ROOT,
- SUBST_SYS,
- };
static const struct subst_map {
const char *name;
const char fmt;
@@ -132,10 +322,9 @@ size_t udev_event_apply_format(struct udev_event *event,
for (;;) {
enum subst_type type = SUBST_UNKNOWN;
- char attrbuf[UTIL_PATH_SIZE], sbuf[UTIL_PATH_SIZE];
- char *attr = NULL, *_s;
- size_t _l;
- bool replws = replace_whitespace;
+ char attrbuf[UTIL_PATH_SIZE];
+ char *attr = NULL;
+ size_t subst_len;
while (from[0] != '\0') {
if (from[0] == '$') {
@@ -204,213 +393,17 @@ 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));
- break;
- case SUBST_KERNEL:
- l = strpcpy(&s, l, udev_device_get_sysname(dev));
- break;
- case SUBST_KERNEL_NUMBER:
- if (udev_device_get_sysnum(dev) == NULL)
- break;
- l = strpcpy(&s, l, udev_device_get_sysnum(dev));
- break;
- case SUBST_ID:
- if (event->dev_parent == NULL)
- break;
- l = strpcpy(&s, l, udev_device_get_sysname(event->dev_parent));
- break;
- case SUBST_DRIVER: {
- const char *driver;
-
- if (event->dev_parent == NULL)
- break;
-
- driver = udev_device_get_driver(event->dev_parent);
- if (driver == NULL)
- break;
- l = strpcpy(&s, l, driver);
- break;
- }
- case SUBST_MAJOR: {
- char num[UTIL_PATH_SIZE];
-
- sprintf(num, "%u", major(udev_device_get_devnum(dev)));
- l = strpcpy(&s, l, num);
- break;
- }
- case SUBST_MINOR: {
- char num[UTIL_PATH_SIZE];
-
- sprintf(num, "%u", minor(udev_device_get_devnum(dev)));
- l = strpcpy(&s, l, num);
- break;
- }
- case SUBST_RESULT: {
- char *rest;
- int i;
-
- if (event->program_result == NULL)
- break;
- /* get part of the result string */
- i = 0;
- if (attr != NULL)
- i = strtoul(attr, &rest, 10);
- if (i > 0) {
- char result[UTIL_PATH_SIZE];
- char tmp[UTIL_PATH_SIZE];
- char *cpos;
-
- strscpy(result, sizeof(result), event->program_result);
- cpos = result;
- while (--i) {
- while (cpos[0] != '\0' && !isspace(cpos[0]))
- cpos++;
- while (isspace(cpos[0]))
- cpos++;
- if (cpos[0] == '\0')
- break;
- }
- if (i > 0) {
- log_error("requested part of result string not found");
- break;
- }
- strscpy(tmp, sizeof(tmp), cpos);
- /* %{2+}c copies the whole string from the second part on */
- if (rest[0] != '+') {
- cpos = strchr(tmp, ' ');
- if (cpos)
- cpos[0] = '\0';
- }
- l = strpcpy(&s, l, tmp);
- } else {
- l = strpcpy(&s, l, event->program_result);
- }
- break;
- }
- case SUBST_ATTR: {
- const char *value = NULL;
- char vbuf[UTIL_NAME_SIZE];
- size_t len;
- int count;
-
- if (attr == NULL) {
- log_error("missing file parameter for attr");
- break;
- }
-
- /* try to read the value specified by "[dmi/id]product_name" */
- if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0)
- value = vbuf;
-
- /* try to read the attribute the device */
- if (value == NULL)
- value = udev_device_get_sysattr_value(event->dev, attr);
-
- /* try to read the attribute of the parent device, other matches have selected */
- if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev)
- value = udev_device_get_sysattr_value(event->dev_parent, attr);
-
- if (value == NULL)
- break;
+ subst_len = subst_format_var(event, dev, type, attr, s, l);
- /* strip trailing whitespace, and replace unwanted characters */
- if (value != vbuf)
- strscpy(vbuf, sizeof(vbuf), value);
- len = strlen(vbuf);
- while (len > 0 && isspace(vbuf[--len]))
- vbuf[len] = '\0';
- count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT);
- if (count > 0)
- log_debug("%i character(s) replaced" , count);
- l = strpcpy(&s, l, vbuf);
- break;
- }
- case SUBST_PARENT: {
- struct udev_device *dev_parent;
- const char *devnode;
-
- dev_parent = udev_device_get_parent(event->dev);
- if (dev_parent == NULL)
- break;
- devnode = udev_device_get_devnode(dev_parent);
- if (devnode != NULL)
- l = strpcpy(&s, l, devnode + strlen("/dev/"));
- break;
- }
- case SUBST_DEVNODE:
- if (udev_device_get_devnode(dev) != NULL)
- l = strpcpy(&s, l, udev_device_get_devnode(dev));
- break;
- case SUBST_NAME:
- if (event->name != NULL)
- l = strpcpy(&s, l, event->name);
- else if (udev_device_get_devnode(dev) != NULL)
- l = strpcpy(&s, l, udev_device_get_devnode(dev) + strlen("/dev/"));
- else
- l = strpcpy(&s, l, udev_device_get_sysname(dev));
- break;
- case SUBST_LINKS: {
- struct udev_list_entry *list_entry;
+ /* SUBST_RESULT handles spaces itself */
+ if (replace_whitespace && type != SUBST_RESULT)
+ /* util_replace_whitespace can replace in-place,
+ * and does nothing if subst_len == 0
+ */
+ subst_len = util_replace_whitespace(s, s, subst_len);
- list_entry = udev_device_get_devlinks_list_entry(dev);
- if (list_entry == NULL)
- break;
- l = strpcpy(&s, l, udev_list_entry_get_name(list_entry) + strlen("/dev/"));
- udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry))
- l = strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry) + strlen("/dev/"), NULL);
- break;
- }
- case SUBST_ROOT:
- l = strpcpy(&s, l, "/dev");
- break;
- case SUBST_SYS:
- l = strpcpy(&s, l, "/sys");
- break;
- case SUBST_ENV:
- if (attr == NULL) {
- break;
- } else {
- const char *value;
-
- value = udev_device_get_property_value(event->dev, attr);
- if (value == NULL)
- break;
- l = strpcpy(&s, l, value);
- break;
- }
- default:
- 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;
- }
+ s += subst_len;
+ l -= subst_len;
}
out:
diff --git a/src/veritysetup/veritysetup-generator.c b/src/veritysetup/veritysetup-generator.c
index 519ac050f9..0bb0bd6e8f 100644
--- a/src/veritysetup/veritysetup-generator.c
+++ b/src/veritysetup/veritysetup-generator.c
@@ -76,10 +76,10 @@ static int create_device(void) {
r = unit_name_from_path(u, ".device", &d);
if (r < 0)
- return log_error_errno(r, "Failed to to generate unit name: %m");
+ return log_error_errno(r, "Failed 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");
+ return log_error_errno(r, "Failed to generate unit name: %m");
f = fopen(p, "wxe");
if (!f)