summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyze/analyze.c4
-rw-r--r--src/basic/architecture.c3
-rw-r--r--src/basic/architecture.h19
-rw-r--r--src/basic/btrfs-util.c2
-rw-r--r--src/basic/build.h5
-rw-r--r--src/basic/calendarspec.c3
-rw-r--r--src/basic/cgroup-util.c378
-rw-r--r--src/basic/cgroup-util.h8
-rw-r--r--src/basic/conf-files.c2
-rw-r--r--src/basic/copy.c131
-rw-r--r--src/basic/copy.h22
-rw-r--r--src/basic/def.h18
-rw-r--r--src/basic/env-util.c212
-rw-r--r--src/basic/env-util.h18
-rw-r--r--src/basic/exec-util.c360
-rw-r--r--src/basic/exec-util.h40
-rw-r--r--src/basic/fileio.c147
-rw-r--r--src/basic/fileio.h3
-rw-r--r--src/basic/fs-util.h6
-rw-r--r--src/basic/hostname-util.c2
-rw-r--r--src/basic/in-addr-util.c12
-rw-r--r--src/basic/in-addr-util.h2
-rw-r--r--src/basic/journal-importer.c481
-rw-r--r--src/basic/journal-importer.h70
-rw-r--r--src/basic/khash.h2
-rw-r--r--src/basic/log.c9
-rw-r--r--src/basic/log.h4
-rw-r--r--src/basic/missing_syscall.h2
-rw-r--r--src/basic/mount-util.c9
-rw-r--r--src/basic/proc-cmdline.c2
-rw-r--r--src/basic/process-util.c4
-rw-r--r--src/basic/socket-util.c20
-rw-r--r--src/basic/socket-util.h1
-rw-r--r--src/basic/util.c143
-rw-r--r--src/basic/util.h2
-rw-r--r--src/basic/virt.c47
-rw-r--r--src/boot/bootctl.c208
-rw-r--r--src/boot/efi/boot.c2
-rw-r--r--src/boot/efi/stub.c2
-rw-r--r--src/cgls/cgls.c2
-rw-r--r--src/cgtop/cgtop.c23
-rw-r--r--src/core/automount.c78
-rw-r--r--src/core/cgroup.c59
-rw-r--r--src/core/dbus-manager.c166
-rw-r--r--src/core/dbus-unit.c8
-rw-r--r--src/core/dbus.c20
-rw-r--r--src/core/execute.c39
-rw-r--r--src/core/hostname-setup.c6
-rw-r--r--src/core/job.c16
-rw-r--r--src/core/killall.c3
-rw-r--r--src/core/macros.systemd.in2
-rw-r--r--src/core/main.c15
-rw-r--r--src/core/manager.c152
-rw-r--r--src/core/mount-setup.c10
-rw-r--r--src/core/namespace.c2
-rw-r--r--src/core/scope.c2
-rw-r--r--src/core/service.c16
-rw-r--r--src/core/shutdown.c3
-rw-r--r--src/core/socket.c6
-rw-r--r--src/core/unit.c62
-rw-r--r--src/coredump/coredump.c546
-rw-r--r--src/coredump/coredumpctl.c340
-rw-r--r--src/cryptsetup/cryptsetup-generator.c8
-rw-r--r--src/dissect/dissect.c8
l---------src/environment-d-generator/Makefile1
-rw-r--r--src/environment-d-generator/environment-d-generator.c107
-rw-r--r--src/firstboot/firstboot.c2
-rw-r--r--src/fstab-generator/fstab-generator.c36
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c2
-rw-r--r--src/hostname/hostnamectl.c23
-rw-r--r--src/hostname/hostnamed.c5
-rw-r--r--src/import/curl-util.c2
-rw-r--r--src/import/importd.c7
-rw-r--r--src/import/pull-raw.c4
-rw-r--r--src/import/pull-tar.c2
-rw-r--r--src/journal-remote/journal-remote-parse.c431
-rw-r--r--src/journal-remote/journal-remote-parse.h35
-rw-r--r--src/journal-remote/journal-remote-write.c33
-rw-r--r--src/journal-remote/journal-remote-write.h12
-rw-r--r--src/journal-remote/journal-remote.c21
-rw-r--r--src/journal/journal-file.c2
-rw-r--r--src/journal/journalctl.c130
-rw-r--r--src/journal/journald-kmsg.c3
-rw-r--r--src/journal/journald-native.c1
-rw-r--r--src/journal/journald-native.h5
-rw-r--r--src/journal/journald-server.c40
-rw-r--r--src/journal/journald-server.h2
-rw-r--r--src/journal/journald-syslog.c3
-rw-r--r--src/journal/journald.c6
-rw-r--r--src/journal/test-compress.c2
-rw-r--r--src/libsystemd-network/arp-util.c2
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c9
-rw-r--r--src/libsystemd-network/sd-ipv4acd.c2
-rw-r--r--src/libsystemd-network/test-lldp.c10
-rw-r--r--src/libsystemd/sd-bus/test-bus-creds.c2
-rw-r--r--src/libsystemd/sd-device/device-internal.h2
-rw-r--r--src/libsystemd/sd-device/sd-device.c43
-rw-r--r--src/libsystemd/sd-netlink/netlink-socket.c4
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.c2
-rw-r--r--src/libsystemd/sd-netlink/sd-netlink.c4
-rw-r--r--src/libsystemd/sd-network/sd-network.c2
-rw-r--r--src/locale/localectl.c5
-rw-r--r--src/login/loginctl.c15
-rw-r--r--src/login/logind-button.c14
-rw-r--r--src/login/logind-dbus.c2
-rw-r--r--src/login/logind-seat.c13
-rw-r--r--src/login/logind-session.c4
-rw-r--r--src/machine/image-dbus.c2
-rw-r--r--src/machine/machine-dbus.c49
-rw-r--r--src/machine/machine-dbus.h1
-rw-r--r--src/machine/machine.c95
-rw-r--r--src/machine/machine.h2
-rw-r--r--src/machine/machinectl.c72
-rw-r--r--src/machine/machined-dbus.c21
-rw-r--r--src/machine/operation.c4
-rw-r--r--src/machine/org.freedesktop.machine1.conf8
-rw-r--r--src/mount/mount-tool.c2
l---------src/network/netdev/Makefile1
-rw-r--r--src/network/netdev/netdev-gperf.gperf184
-rw-r--r--src/network/netdev/tunnel.c4
-rw-r--r--src/network/netdev/vxlan.c70
-rw-r--r--src/network/netdev/vxlan.h27
-rw-r--r--src/network/networkd-address.c46
-rw-r--r--src/network/networkd-address.h5
-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.c55
-rw-r--r--src/network/networkd-network.h14
-rw-r--r--src/network/networkd-route.c39
-rw-r--r--src/network/networkd-route.h5
l---------src/network/wait-online/Makefile1
-rw-r--r--src/nspawn/nspawn-cgroup.c23
-rw-r--r--src/nspawn/nspawn-mount.c93
-rw-r--r--src/nspawn/nspawn.c63
-rw-r--r--src/nss-myhostname/nss-myhostname.c19
-rw-r--r--src/nss-mymachines/nss-mymachines.c18
-rw-r--r--src/nss-resolve/nss-resolve.c28
-rw-r--r--src/nss-systemd/nss-systemd.c8
-rw-r--r--src/resolve/resolve-tool.c18
-rw-r--r--src/resolve/resolved-bus.c8
-rw-r--r--src/resolve/resolved-dns-answer.c2
-rw-r--r--src/resolve/resolved-dns-answer.h8
-rw-r--r--src/resolve/resolved-dns-cache.c133
-rw-r--r--src/resolve/resolved-dns-dnssec.c8
-rw-r--r--src/resolve/resolved-dns-packet.c21
-rw-r--r--src/resolve/resolved-dns-packet.h4
-rw-r--r--src/resolve/resolved-dns-query.c22
-rw-r--r--src/resolve/resolved-dns-query.h5
-rw-r--r--src/resolve/resolved-dns-rr.c2
-rw-r--r--src/resolve/resolved-dns-scope.c106
-rw-r--r--src/resolve/resolved-dns-scope.h6
-rw-r--r--src/resolve/resolved-dns-server.c62
-rw-r--r--src/resolve/resolved-dns-server.h4
-rw-r--r--src/resolve/resolved-dns-stub.c161
-rw-r--r--src/resolve/resolved-dns-transaction.c94
-rw-r--r--src/resolve/resolved-dns-transaction.h16
-rw-r--r--src/resolve/resolved-dns-trust-anchor.c99
-rw-r--r--src/resolve/resolved-dns-zone.c1
-rw-r--r--src/resolve/resolved-dns-zone.h3
-rw-r--r--src/resolve/resolved-link.c135
-rw-r--r--src/resolve/resolved-link.h3
-rw-r--r--src/resolve/resolved-manager.c123
-rw-r--r--src/resolve/resolved-manager.h3
-rw-r--r--src/resolve/resolved-mdns.c72
-rw-r--r--src/resolve/resolved-mdns.h1
-rw-r--r--src/resolve/resolved-resolv-conf.c6
-rw-r--r--src/resolve/test-data/_443._tcp.fedoraproject.org.pktsbin169 -> 0 bytes
-rw-r--r--src/resolve/test-data/_openpgpkey.fedoraproject.org.pktsbin986 -> 0 bytes
-rw-r--r--src/resolve/test-data/fake-caa.pktsbin196 -> 0 bytes
-rw-r--r--src/resolve/test-data/fedoraproject.org.pktsbin1483 -> 0 bytes
-rw-r--r--src/resolve/test-data/gandi.net.pktsbin1010 -> 0 bytes
-rw-r--r--src/resolve/test-data/google.com.pktsbin747 -> 0 bytes
-rw-r--r--src/resolve/test-data/kyhwana.org.pktsbin1803 -> 0 bytes
-rw-r--r--src/resolve/test-data/root.pktsbin1061 -> 0 bytes
-rw-r--r--src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pktsbin330 -> 0 bytes
-rw-r--r--src/resolve/test-data/teamits.com.pktsbin1021 -> 0 bytes
-rw-r--r--src/resolve/test-data/zbyszek@fedoraproject.org.pktsbin2533 -> 0 bytes
-rw-r--r--src/resolve/test-dns-packet.c3
-rw-r--r--src/run/run.c7
-rw-r--r--src/shared/bus-unit-util.c8
-rw-r--r--src/shared/bus-util.c15
-rw-r--r--src/shared/bus-util.h6
-rw-r--r--src/shared/dissect-image.c32
-rw-r--r--src/shared/dropin.c38
-rw-r--r--src/shared/firewall-util.c5
-rw-r--r--src/shared/fstab-util.c2
-rw-r--r--src/shared/install.c62
-rw-r--r--src/shared/journal-util.c151
-rw-r--r--src/shared/journal-util.h25
-rw-r--r--src/shared/machine-image.c8
-rw-r--r--src/shared/pager.c10
-rw-r--r--src/shared/path-lookup.c68
-rw-r--r--src/shared/seccomp-util.c104
-rw-r--r--src/shared/seccomp-util.h2
-rw-r--r--src/shared/tests.c40
-rw-r--r--src/shared/tests.h1
-rw-r--r--src/sleep/sleep.c9
-rw-r--r--src/systemctl/systemctl.c140
-rw-r--r--src/systemd/sd-bus-vtable.h54
-rw-r--r--src/systemd/sd-id128.h3
-rw-r--r--src/systemd/sd-messages.h157
-rw-r--r--src/sysusers/sysusers.c2
-rw-r--r--src/test/test-calendarspec.c1
-rw-r--r--src/test/test-cgroup-mask.c6
-rw-r--r--src/test/test-cgroup-util.c47
-rw-r--r--src/test/test-conf-files.c24
-rw-r--r--src/test/test-copy.c25
-rw-r--r--src/test/test-dns-domain.c8
-rw-r--r--src/test/test-engine.c5
-rw-r--r--src/test/test-env-util.c117
-rw-r--r--src/test/test-exec-util.c348
-rw-r--r--src/test/test-execute.c57
-rw-r--r--src/test/test-fd-util.c10
-rw-r--r--src/test/test-fileio.c112
-rw-r--r--src/test/test-ipcrm.c2
-rw-r--r--src/test/test-journal-importer.c90
-rw-r--r--src/test/test-path.c2
-rw-r--r--src/test/test-sched-prio.c5
-rw-r--r--src/test/test-socket-util.c19
-rw-r--r--src/test/test-stat-util.c12
-rw-r--r--src/test/test-util.c45
-rw-r--r--src/timedate/timedatectl.c5
-rw-r--r--src/timedate/timedated.c4
-rw-r--r--src/tmpfiles/tmpfiles.c4
-rw-r--r--src/udev/udev-builtin-blkid.c4
-rw-r--r--src/udev/udev-builtin-net_id.c59
-rw-r--r--src/udev/udev-builtin-path_id.c5
-rw-r--r--src/udev/udevd.c2
-rw-r--r--src/update-done/update-done.c2
231 files changed, 6550 insertions, 2815 deletions
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index 51d881c5fb..ac0470b20d 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));
@@ -1360,7 +1362,7 @@ static void help(void) {
" blame Print list of running units ordered by time to init\n"
" critical-chain Print a tree of the time critical chain of units\n"
" plot Output SVG graphic showing service initialization\n"
- " dot Output dependency graph in dot(1) format\n"
+ " dot Output dependency graph in man:dot(1) format\n"
" set-log-level LEVEL Set logging threshold for manager\n"
" set-log-target TARGET Set logging target for manager\n"
" dump Output state serialization of service manager\n"
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..46883719d1 100644
--- a/src/basic/architecture.h
+++ b/src/basic/architecture.h
@@ -124,13 +124,21 @@ int uname_architecture(void);
#elif defined(__sparc__)
# define native_architecture() ARCHITECTURE_SPARC
# define LIB_ARCH_TUPLE "sparc-linux-gnu"
-#elif defined(__mips64__)
+#elif defined(__mips64) && defined(__LP64__)
# if __BYTE_ORDER == __BIG_ENDIAN
# define native_architecture() ARCHITECTURE_MIPS64
-# error "Missing LIB_ARCH_TUPLE for MIPS64"
+# define LIB_ARCH_TUPLE "mips64-linux-gnuabi64"
# else
# define native_architecture() ARCHITECTURE_MIPS64_LE
-# error "Missing LIB_ARCH_TUPLE for MIPS64_LE"
+# define LIB_ARCH_TUPLE "mips64el-linux-gnuabi64"
+# endif
+#elif defined(__mips64)
+# if __BYTE_ORDER == __BIG_ENDIAN
+# define native_architecture() ARCHITECTURE_MIPS64
+# define LIB_ARCH_TUPLE "mips64-linux-gnuabin32"
+# else
+# define native_architecture() ARCHITECTURE_MIPS64_LE
+# define LIB_ARCH_TUPLE "mips64el-linux-gnuabin32"
# endif
#elif defined(__mips__)
# if __BYTE_ORDER == __BIG_ENDIAN
@@ -187,14 +195,15 @@ int uname_architecture(void);
# define LIB_ARCH_TUPLE "m68k-linux-gnu"
#elif defined(__tilegx__)
# define native_architecture() ARCHITECTURE_TILEGX
-# error "Missing LIB_ARCH_TUPLE for TILEGX"
+# define LIB_ARCH_TUPLE "tilegx-linux-gnu"
#elif defined(__cris__)
# define native_architecture() ARCHITECTURE_CRIS
# error "Missing LIB_ARCH_TUPLE for CRIS"
#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/btrfs-util.c b/src/basic/btrfs-util.c
index 5f9e21dcba..5505499312 100644
--- a/src/basic/btrfs-util.c
+++ b/src/basic/btrfs-util.c
@@ -1737,7 +1737,7 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
} else if (r < 0)
return r;
- r = copy_directory_fd(old_fd, new_path, true);
+ r = copy_directory_fd(old_fd, new_path, COPY_MERGE|COPY_REFLINK);
if (r < 0)
goto fallback_fail;
diff --git a/src/basic/build.h b/src/basic/build.h
index 633c2aaccb..91312bd2a3 100644
--- a/src/basic/build.h
+++ b/src/basic/build.h
@@ -133,6 +133,8 @@
#define _IDN_FEATURE_ "-IDN"
#endif
+#define _CGROUP_HIEARCHY_ "default-hierarchy=" DEFAULT_HIERARCHY_NAME
+
#define SYSTEMD_FEATURES \
_PAM_FEATURE_ " " \
_AUDIT_FEATURE_ " " \
@@ -152,4 +154,5 @@
_BLKID_FEATURE_ " " \
_ELFUTILS_FEATURE_ " " \
_KMOD_FEATURE_ " " \
- _IDN_FEATURE_
+ _IDN_FEATURE_ " " \
+ _CGROUP_HIEARCHY_
diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c
index 3fa1c51ace..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;
}
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 6948ed3931..bda5c555ad 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -208,6 +208,18 @@ int cg_rmdir(const char *controller, const char *path) {
if (r < 0 && errno != ENOENT)
return -errno;
+ r = cg_hybrid_unified();
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 0;
+
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ r = cg_rmdir(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
+ if (r < 0)
+ log_warning_errno(r, "Failed to remove compat systemd cgroup %s: %m", path);
+ }
+
return 0;
}
@@ -542,6 +554,13 @@ static const char *controller_to_dirname(const char *controller) {
* just cuts off the name= prefixed used for named
* hierarchies, if it is specified. */
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ if (cg_hybrid_unified() > 0)
+ controller = SYSTEMD_CGROUP_CONTROLLER_HYBRID;
+ else
+ controller = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
+ }
+
e = startswith(controller, "name=");
if (e)
return e;
@@ -594,7 +613,7 @@ static int join_path_unified(const char *path, const char *suffix, char **fs) {
}
int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
- int unified, r;
+ int r;
assert(fs);
@@ -623,11 +642,10 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
if (!cg_controller_is_valid(controller))
return -EINVAL;
- unified = cg_all_unified();
- if (unified < 0)
- return unified;
-
- if (unified > 0)
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0)
r = join_path_unified(path, suffix, fs);
else
r = join_path_legacy(controller, path, suffix, fs);
@@ -639,7 +657,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
}
static int controller_is_accessible(const char *controller) {
- int unified;
+ int r;
assert(controller);
@@ -651,10 +669,10 @@ static int controller_is_accessible(const char *controller) {
if (!cg_controller_is_valid(controller))
return -EINVAL;
- unified = cg_all_unified();
- if (unified < 0)
- return unified;
- if (unified > 0) {
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0) {
/* We don't support named hierarchies if we are using
* the unified hierarchy. */
@@ -708,7 +726,7 @@ static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct
int cg_trim(const char *controller, const char *path, bool delete_root) {
_cleanup_free_ char *fs = NULL;
- int r = 0;
+ int r = 0, q;
assert(path);
@@ -731,6 +749,15 @@ int cg_trim(const char *controller, const char *path, bool delete_root) {
return -errno;
}
+ q = cg_hybrid_unified();
+ if (q < 0)
+ return q;
+ if (q > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ q = cg_trim(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, delete_root);
+ if (q < 0)
+ log_warning_errno(q, "Failed to trim compat systemd cgroup %s: %m", path);
+ }
+
return r;
}
@@ -754,6 +781,16 @@ int cg_create(const char *controller, const char *path) {
return -errno;
}
+ r = cg_hybrid_unified();
+ if (r < 0)
+ return r;
+
+ if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ r = cg_create(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
+ if (r < 0)
+ log_warning_errno(r, "Failed to create compat systemd cgroup %s: %m", path);
+ }
+
return 1;
}
@@ -791,7 +828,21 @@ int cg_attach(const char *controller, const char *path, pid_t pid) {
xsprintf(c, PID_FMT "\n", pid);
- return write_string_file(fs, c, 0);
+ r = write_string_file(fs, c, 0);
+ if (r < 0)
+ return r;
+
+ r = cg_hybrid_unified();
+ if (r < 0)
+ return r;
+
+ if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ r = cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, pid);
+ if (r < 0)
+ log_warning_errno(r, "Failed to attach %d to compat systemd cgroup %s: %m", pid, path);
+ }
+
+ return 0;
}
int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
@@ -840,7 +891,20 @@ int cg_set_group_access(
if (r < 0)
return r;
- return chmod_and_chown(fs, mode, uid, gid);
+ r = chmod_and_chown(fs, mode, uid, gid);
+ if (r < 0)
+ return r;
+
+ r = cg_hybrid_unified();
+ if (r < 0)
+ return r;
+ if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ r = cg_set_group_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, mode, uid, gid);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set group access on compat systemd cgroup %s: %m", path);
+ }
+
+ return 0;
}
int cg_set_task_access(
@@ -851,7 +915,7 @@ int cg_set_task_access(
gid_t gid) {
_cleanup_free_ char *fs = NULL, *procs = NULL;
- int r, unified;
+ int r;
assert(path);
@@ -869,16 +933,24 @@ int cg_set_task_access(
if (r < 0)
return r;
- unified = cg_unified(controller);
- if (unified < 0)
- return unified;
- if (unified)
- return 0;
+ r = cg_unified_controller(controller);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Compatibility, Always keep values for "tasks" in sync with
+ * "cgroup.procs" */
+ if (cg_get_path(controller, path, "tasks", &procs) >= 0)
+ (void) chmod_and_chown(procs, mode, uid, gid);
+ }
- /* Compatibility, Always keep values for "tasks" in sync with
- * "cgroup.procs" */
- if (cg_get_path(controller, path, "tasks", &procs) >= 0)
- (void) chmod_and_chown(procs, mode, uid, gid);
+ r = cg_hybrid_unified();
+ if (r < 0)
+ return r;
+ if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ r = cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, mode, uid, gid);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set task access on compat systemd cgroup %s: %m", path);
+ }
return 0;
}
@@ -923,7 +995,7 @@ int cg_get_xattr(const char *controller, const char *path, const char *name, voi
int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
- const char *fs;
+ const char *fs, *controller_str;
size_t cs = 0;
int unified;
@@ -936,11 +1008,17 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
} else
controller = SYSTEMD_CGROUP_CONTROLLER;
- unified = cg_unified(controller);
+ unified = cg_unified_controller(controller);
if (unified < 0)
return unified;
- if (unified == 0)
- cs = strlen(controller);
+ if (unified == 0) {
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
+ controller_str = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
+ else
+ controller_str = controller;
+
+ cs = strlen(controller_str);
+ }
fs = procfs_file_alloca(pid, "cgroup");
f = fopen(fs, "re");
@@ -977,7 +1055,7 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
*e = 0;
FOREACH_WORD_SEPARATOR(word, k, l, ",", state) {
- if (k == cs && memcmp(word, controller, cs) == 0) {
+ if (k == cs && memcmp(word, controller_str, cs) == 0) {
found = true;
break;
}
@@ -1001,14 +1079,14 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
int cg_install_release_agent(const char *controller, const char *agent) {
_cleanup_free_ char *fs = NULL, *contents = NULL;
const char *sc;
- int r, unified;
+ int r;
assert(agent);
- unified = cg_unified(controller);
- if (unified < 0)
- return unified;
- if (unified) /* doesn't apply to unified hierarchy */
+ r = cg_unified_controller(controller);
+ if (r < 0)
+ return r;
+ if (r > 0) /* doesn't apply to unified hierarchy */
return -EOPNOTSUPP;
r = cg_get_path(controller, NULL, "release_agent", &fs);
@@ -1054,12 +1132,12 @@ int cg_install_release_agent(const char *controller, const char *agent) {
int cg_uninstall_release_agent(const char *controller) {
_cleanup_free_ char *fs = NULL;
- int r, unified;
+ int r;
- unified = cg_unified(controller);
- if (unified < 0)
- return unified;
- if (unified) /* Doesn't apply to unified hierarchy */
+ r = cg_unified_controller(controller);
+ if (r < 0)
+ return r;
+ if (r > 0) /* Doesn't apply to unified hierarchy */
return -EOPNOTSUPP;
r = cg_get_path(controller, NULL, "notify_on_release", &fs);
@@ -1104,7 +1182,7 @@ int cg_is_empty(const char *controller, const char *path) {
}
int cg_is_empty_recursive(const char *controller, const char *path) {
- int unified, r;
+ int r;
assert(path);
@@ -1112,11 +1190,10 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
if (controller && (isempty(path) || path_equal(path, "/")))
return false;
- unified = cg_unified(controller);
- if (unified < 0)
- return unified;
-
- if (unified > 0) {
+ r = cg_unified_controller(controller);
+ if (r < 0)
+ return r;
+ if (r > 0) {
_cleanup_free_ char *t = NULL;
/* On the unified hierarchy we can check empty state
@@ -1833,6 +1910,9 @@ bool cg_controller_is_valid(const char *p) {
if (!p)
return false;
+ if (streq(p, SYSTEMD_CGROUP_CONTROLLER))
+ return true;
+
s = startswith(p, "name=");
if (s)
p = s;
@@ -1986,7 +2066,7 @@ int cg_get_keyed_attribute(const char *controller, const char *path, const char
int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
CGroupController c;
- int r, unified;
+ int r;
/* This one will create a cgroup in our private tree, but also
* duplicate it in the trees specified in mask, and remove it
@@ -1998,10 +2078,10 @@ int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path
return r;
/* If we are in the unified hierarchy, we are done now */
- unified = cg_all_unified();
- if (unified < 0)
- return unified;
- if (unified > 0)
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0)
return 0;
/* Otherwise, do the same in the other hierarchies */
@@ -2022,16 +2102,16 @@ int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path
int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
CGroupController c;
- int r, unified;
+ int r;
r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
if (r < 0)
return r;
- unified = cg_all_unified();
- if (unified < 0)
- return unified;
- if (unified > 0)
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0)
return 0;
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
@@ -2072,7 +2152,7 @@ int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids,
int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
CGroupController c;
- int r = 0, unified;
+ int r = 0, q;
if (!path_equal(from, to)) {
r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE);
@@ -2080,10 +2160,10 @@ int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to
return r;
}
- unified = cg_all_unified();
- if (unified < 0)
- return unified;
- if (unified > 0)
+ q = cg_all_unified();
+ if (q < 0)
+ return q;
+ if (q > 0)
return r;
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
@@ -2107,16 +2187,16 @@ int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to
int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
CGroupController c;
- int r, unified;
+ int r, q;
r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
if (r < 0)
return r;
- unified = cg_all_unified();
- if (unified < 0)
- return unified;
- if (unified > 0)
+ q = cg_all_unified();
+ if (q < 0)
+ return q;
+ if (q > 0)
return r;
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
@@ -2133,16 +2213,16 @@ int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root)
int cg_mask_supported(CGroupMask *ret) {
CGroupMask mask = 0;
- int r, unified;
+ int r;
/* Determines the mask of supported cgroup controllers. Only
* includes controllers we can make sense of and that are
* actually accessible. */
- unified = cg_all_unified();
- if (unified < 0)
- return unified;
- if (unified > 0) {
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0) {
_cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL;
const char *c;
@@ -2262,7 +2342,18 @@ int cg_kernel_controllers(Set *controllers) {
static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
-static int cg_update_unified(void) {
+/* The hybrid mode was initially implemented in v232 and simply mounted cgroup v2 on /sys/fs/cgroup/systemd. This
+ * unfortunately broke other tools (such as docker) which expected the v1 "name=systemd" hierarchy on
+ * /sys/fs/cgroup/systemd. From v233 and on, the hybrid mode mountnbs v2 on /sys/fs/cgroup/unified and maintains
+ * "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility with other tools.
+ *
+ * To keep live upgrade working, we detect and support v232 layout. When v232 layout is detected, to keep cgroup v2
+ * process management but disable the compat dual layout, we return %true on
+ * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and %false on cg_hybrid_unified().
+ */
+static thread_local bool unified_systemd_v232;
+
+static int cg_unified_update(void) {
struct statfs fs;
@@ -2280,54 +2371,83 @@ static int cg_update_unified(void) {
if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC))
unified_cache = CGROUP_UNIFIED_ALL;
else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
- if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0)
- return -errno;
-
- unified_cache = F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC) ?
- CGROUP_UNIFIED_SYSTEMD : CGROUP_UNIFIED_NONE;
+ if (statfs("/sys/fs/cgroup/unified/", &fs) == 0 &&
+ F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
+ unified_cache = CGROUP_UNIFIED_SYSTEMD;
+ unified_systemd_v232 = false;
+ } else if (statfs("/sys/fs/cgroup/systemd/", &fs) == 0 &&
+ F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
+ unified_cache = CGROUP_UNIFIED_SYSTEMD;
+ unified_systemd_v232 = true;
+ } else {
+ if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0)
+ return -errno;
+ if (!F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC))
+ return -ENOMEDIUM;
+ unified_cache = CGROUP_UNIFIED_NONE;
+ }
} else
return -ENOMEDIUM;
return 0;
}
-int cg_unified(const char *controller) {
-
+int cg_unified_controller(const char *controller) {
int r;
- r = cg_update_unified();
+ r = cg_unified_update();
if (r < 0)
return r;
- if (streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER))
- return unified_cache >= CGROUP_UNIFIED_SYSTEMD;
- else
- return unified_cache >= CGROUP_UNIFIED_ALL;
+ if (unified_cache == CGROUP_UNIFIED_NONE)
+ return false;
+
+ if (unified_cache >= CGROUP_UNIFIED_ALL)
+ return true;
+
+ return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER);
}
int cg_all_unified(void) {
+ int r;
+
+ r = cg_unified_update();
+ if (r < 0)
+ return r;
- return cg_unified(NULL);
+ return unified_cache >= CGROUP_UNIFIED_ALL;
}
-void cg_unified_flush(void) {
+int cg_hybrid_unified(void) {
+ int r;
+
+ r = cg_unified_update();
+ if (r < 0)
+ return r;
+
+ return unified_cache == CGROUP_UNIFIED_SYSTEMD && !unified_systemd_v232;
+}
+
+int cg_unified_flush(void) {
unified_cache = CGROUP_UNIFIED_UNKNOWN;
+
+ return cg_unified_update();
}
int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
_cleanup_free_ char *fs = NULL;
CGroupController c;
- int r, unified;
+ int r;
assert(p);
if (supported == 0)
return 0;
- unified = cg_all_unified();
- if (unified < 0)
- return unified;
- if (!unified) /* on the legacy hiearchy there's no joining of controllers defined */
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r == 0) /* on the legacy hiearchy there's no joining of controllers defined */
return 0;
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
@@ -2359,63 +2479,69 @@ int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
bool cg_is_unified_wanted(void) {
static thread_local int wanted = -1;
- int r, unified;
+ int r;
bool b;
+ const bool is_default = DEFAULT_HIERARCHY == CGROUP_UNIFIED_ALL;
- /* If the hierarchy is already mounted, then follow whatever
- * was chosen for it. */
- unified = cg_all_unified();
- if (unified >= 0)
- return unified;
-
- /* Otherwise, let's see what the kernel command line has to
- * say. Since checking that is expensive, let's cache the
- * result. */
+ /* If we have a cached value, return that. */
if (wanted >= 0)
return wanted;
+ /* If the hierarchy is already mounted, then follow whatever
+ * was chosen for it. */
+ if (cg_unified_flush() >= 0)
+ return (wanted = unified_cache >= CGROUP_UNIFIED_ALL);
+
+ /* Otherwise, let's see what the kernel command line has to say.
+ * Since checking is expensive, cache a non-error result. */
r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
- if (r < 0)
- return false;
- return (wanted = r > 0 ? b : false);
+ return (wanted = r > 0 ? b : is_default);
}
bool cg_is_legacy_wanted(void) {
- return !cg_is_unified_wanted();
+ static thread_local int wanted = -1;
+
+ /* If we have a cached value, return that. */
+ if (wanted >= 0)
+ return wanted;
+
+ /* Check if we have cgroups2 already mounted. */
+ if (cg_unified_flush() >= 0 &&
+ unified_cache == CGROUP_UNIFIED_ALL)
+ return (wanted = false);
+
+ /* Otherwise, assume that at least partial legacy is wanted,
+ * since cgroups2 should already be mounted at this point. */
+ return (wanted = true);
}
-bool cg_is_unified_systemd_controller_wanted(void) {
+bool cg_is_hybrid_wanted(void) {
static thread_local int wanted = -1;
- int r, unified;
+ int r;
bool b;
+ const bool is_default = DEFAULT_HIERARCHY >= CGROUP_UNIFIED_SYSTEMD;
+ /* We default to true if the default is "hybrid", obviously,
+ * but also when the default is "unified", because if we get
+ * called, it means that unified hierarchy was not mounted. */
- /* If the unified hierarchy is requested in full, no need to
- * bother with this. */
- if (cg_is_unified_wanted())
- return 0;
+ /* If we have a cached value, return that. */
+ if (wanted >= 0)
+ return wanted;
/* If the hierarchy is already mounted, then follow whatever
* was chosen for it. */
- unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
- if (unified >= 0)
- return unified;
-
- /* Otherwise, let's see what the kernel command line has to
- * say. Since checking that is expensive, let's cache the
- * result. */
- if (wanted >= 0)
- return wanted;
+ if (cg_unified_flush() >= 0 &&
+ unified_cache == CGROUP_UNIFIED_ALL)
+ return (wanted = false);
+ /* Otherwise, let's see what the kernel command line has to say.
+ * Since checking is expensive, cache a non-error result. */
r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
- if (r < 0)
- return false;
-
- return (wanted = r > 0 ? b : false);
-}
-bool cg_is_legacy_systemd_controller_wanted(void) {
- return cg_is_legacy_wanted() && !cg_is_unified_systemd_controller_wanted();
+ /* The meaning of the kernel option is reversed wrt. to the return value
+ * of this function, hence the negation. */
+ return (wanted = r > 0 ? !b : is_default);
}
int cg_weight_parse(const char *s, uint64_t *ret) {
diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
index 0aa27c4cd7..a522095d95 100644
--- a/src/basic/cgroup-util.h
+++ b/src/basic/cgroup-util.h
@@ -241,13 +241,13 @@ int cg_kernel_controllers(Set *controllers);
bool cg_ns_supported(void);
int cg_all_unified(void);
-int cg_unified(const char *controller);
-void cg_unified_flush(void);
+int cg_hybrid_unified(void);
+int cg_unified_controller(const char *controller);
+int cg_unified_flush(void);
bool cg_is_unified_wanted(void);
bool cg_is_legacy_wanted(void);
-bool cg_is_unified_systemd_controller_wanted(void);
-bool cg_is_legacy_systemd_controller_wanted(void);
+bool cg_is_hybrid_wanted(void);
const char* cgroup_controller_to_string(CGroupController c) _const_;
CGroupController cgroup_controller_from_string(const char *s) _pure_;
diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c
index b5780194df..b8f0f5d03d 100644
--- a/src/basic/conf-files.c
+++ b/src/basic/conf-files.c
@@ -137,7 +137,6 @@ int conf_files_list(char ***strv, const char *suffix, const char *root, const ch
va_list ap;
assert(strv);
- assert(suffix);
va_start(ap, dir);
dirs = strv_new_ap(dir, ap);
@@ -153,7 +152,6 @@ int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, c
_cleanup_strv_free_ char **dirs = NULL;
assert(strv);
- assert(suffix);
dirs = strv_split_nulstr(d);
if (!dirs)
diff --git a/src/basic/copy.c b/src/basic/copy.c
index e9a7efd232..e120b9eb4e 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -45,6 +45,7 @@
#include "strv.h"
#include "time-util.h"
#include "umask-util.h"
+#include "user-util.h"
#include "xattr-util.h"
#define COPY_BUFFER_SIZE (16*1024u)
@@ -68,7 +69,7 @@ static ssize_t try_copy_file_range(int fd_in, loff_t *off_in,
return -errno;
}
-int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
+int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) {
bool try_cfr = true, try_sendfile = true, try_splice = true;
int r;
size_t m = SSIZE_MAX; /* that is the maximum that sendfile and c_f_r accept */
@@ -77,7 +78,7 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
assert(fdt >= 0);
/* Try btrfs reflinks first. */
- if (try_reflink &&
+ if ((copy_flags & COPY_REFLINK) &&
max_bytes == (uint64_t) -1 &&
lseek(fdf, 0, SEEK_CUR) == 0 &&
lseek(fdt, 0, SEEK_CUR) == 0) {
@@ -176,7 +177,16 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
return 0; /* return 0 if we hit EOF earlier than the size limit */
}
-static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
+static int fd_copy_symlink(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags) {
+
_cleanup_free_ char *target = NULL;
int r;
@@ -191,13 +201,25 @@ static int fd_copy_symlink(int df, const char *from, const struct stat *st, int
if (symlinkat(target, dt, to) < 0)
return -errno;
- if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+ if (fchownat(dt, to,
+ uid_is_valid(override_uid) ? override_uid : st->st_uid,
+ gid_is_valid(override_gid) ? override_gid : st->st_gid,
+ AT_SYMLINK_NOFOLLOW) < 0)
return -errno;
return 0;
}
-static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
+static int fd_copy_regular(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags) {
+
_cleanup_close_ int fdf = -1, fdt = -1;
struct timespec ts[2];
int r, q;
@@ -214,13 +236,15 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
if (fdt < 0)
return -errno;
- r = copy_bytes(fdf, fdt, (uint64_t) -1, true);
+ r = copy_bytes(fdf, fdt, (uint64_t) -1, copy_flags);
if (r < 0) {
unlinkat(dt, to, 0);
return r;
}
- if (fchown(fdt, st->st_uid, st->st_gid) < 0)
+ if (fchown(fdt,
+ uid_is_valid(override_uid) ? override_uid : st->st_uid,
+ gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
r = -errno;
if (fchmod(fdt, st->st_mode & 07777) < 0)
@@ -229,7 +253,6 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
ts[0] = st->st_atim;
ts[1] = st->st_mtim;
(void) futimens(fdt, ts);
-
(void) copy_xattr(fdf, fdt);
q = close(fdt);
@@ -243,7 +266,15 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
return r;
}
-static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
+static int fd_copy_fifo(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags) {
int r;
assert(from);
@@ -254,7 +285,10 @@ static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt,
if (r < 0)
return -errno;
- if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+ if (fchownat(dt, to,
+ uid_is_valid(override_uid) ? override_uid : st->st_uid,
+ gid_is_valid(override_gid) ? override_gid : st->st_gid,
+ AT_SYMLINK_NOFOLLOW) < 0)
r = -errno;
if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
@@ -263,7 +297,15 @@ static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt,
return r;
}
-static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
+static int fd_copy_node(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags) {
int r;
assert(from);
@@ -274,7 +316,10 @@ static int fd_copy_node(int df, const char *from, const struct stat *st, int dt,
if (r < 0)
return -errno;
- if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+ if (fchownat(dt, to,
+ uid_is_valid(override_uid) ? override_uid : st->st_uid,
+ gid_is_valid(override_gid) ? override_gid : st->st_gid,
+ AT_SYMLINK_NOFOLLOW) < 0)
r = -errno;
if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
@@ -290,7 +335,9 @@ static int fd_copy_directory(
int dt,
const char *to,
dev_t original_device,
- bool merge) {
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags) {
_cleanup_close_ int fdf = -1, fdt = -1;
_cleanup_closedir_ DIR *d = NULL;
@@ -316,7 +363,7 @@ static int fd_copy_directory(
r = mkdirat(dt, to, st->st_mode & 07777);
if (r >= 0)
created = true;
- else if (errno == EEXIST && merge)
+ else if (errno == EEXIST && (copy_flags & COPY_MERGE))
created = false;
else
return -errno;
@@ -343,19 +390,19 @@ static int fd_copy_directory(
continue;
if (S_ISREG(buf.st_mode))
- q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else if (S_ISDIR(buf.st_mode))
- q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
+ q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, override_uid, override_gid, copy_flags);
else if (S_ISLNK(buf.st_mode))
- q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else if (S_ISFIFO(buf.st_mode))
- q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode))
- q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else
q = -EOPNOTSUPP;
- if (q == -EEXIST && merge)
+ if (q == -EEXIST && (copy_flags & COPY_MERGE))
q = 0;
if (q < 0)
@@ -368,7 +415,9 @@ static int fd_copy_directory(
st->st_mtim
};
- if (fchown(fdt, st->st_uid, st->st_gid) < 0)
+ if (fchown(fdt,
+ uid_is_valid(override_uid) ? override_uid : st->st_uid,
+ gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
r = -errno;
if (fchmod(fdt, st->st_mode & 07777) < 0)
@@ -381,7 +430,7 @@ static int fd_copy_directory(
return r;
}
-int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
+int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
struct stat st;
assert(from);
@@ -391,24 +440,24 @@ int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge)
return -errno;
if (S_ISREG(st.st_mode))
- return fd_copy_regular(fdf, from, &st, fdt, to);
+ return fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else if (S_ISDIR(st.st_mode))
- return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
+ return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, override_uid, override_gid, copy_flags);
else if (S_ISLNK(st.st_mode))
- return fd_copy_symlink(fdf, from, &st, fdt, to);
+ return fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else if (S_ISFIFO(st.st_mode))
- return fd_copy_fifo(fdf, from, &st, fdt, to);
+ return fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode))
- return fd_copy_node(fdf, from, &st, fdt, to);
+ return fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else
return -EOPNOTSUPP;
}
-int copy_tree(const char *from, const char *to, bool merge) {
- return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
+int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
+ return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags);
}
-int copy_directory_fd(int dirfd, const char *to, bool merge) {
+int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) {
struct stat st;
assert(dirfd >= 0);
@@ -420,10 +469,10 @@ int copy_directory_fd(int dirfd, const char *to, bool merge) {
if (!S_ISDIR(st.st_mode))
return -ENOTDIR;
- return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
+ return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, UID_INVALID, GID_INVALID, copy_flags);
}
-int copy_directory(const char *from, const char *to, bool merge) {
+int copy_directory(const char *from, const char *to, CopyFlags copy_flags) {
struct stat st;
assert(from);
@@ -435,10 +484,10 @@ int copy_directory(const char *from, const char *to, bool merge) {
if (!S_ISDIR(st.st_mode))
return -ENOTDIR;
- return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge);
+ return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, UID_INVALID, GID_INVALID, copy_flags);
}
-int copy_file_fd(const char *from, int fdt, bool try_reflink) {
+int copy_file_fd(const char *from, int fdt, CopyFlags copy_flags) {
_cleanup_close_ int fdf = -1;
int r;
@@ -449,7 +498,7 @@ int copy_file_fd(const char *from, int fdt, bool try_reflink) {
if (fdf < 0)
return -errno;
- r = copy_bytes(fdf, fdt, (uint64_t) -1, try_reflink);
+ r = copy_bytes(fdf, fdt, (uint64_t) -1, copy_flags);
(void) copy_times(fdf, fdt);
(void) copy_xattr(fdf, fdt);
@@ -457,7 +506,7 @@ int copy_file_fd(const char *from, int fdt, bool try_reflink) {
return r;
}
-int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
+int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
int fdt = -1, r;
assert(from);
@@ -472,7 +521,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
if (chattr_flags != 0)
(void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
- r = copy_file_fd(from, fdt, true);
+ r = copy_file_fd(from, fdt, copy_flags);
if (r < 0) {
close(fdt);
unlink(to);
@@ -487,7 +536,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
return 0;
}
-int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
+int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
_cleanup_free_ char *t = NULL;
int r;
@@ -498,18 +547,18 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace
if (r < 0)
return r;
- r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
+ r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags, copy_flags);
if (r < 0)
return r;
- if (replace) {
+ if (copy_flags & COPY_REPLACE) {
r = renameat(AT_FDCWD, t, AT_FDCWD, to);
if (r < 0)
r = -errno;
} else
r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
if (r < 0) {
- (void) unlink_noerrno(t);
+ (void) unlink(t);
return r;
}
diff --git a/src/basic/copy.h b/src/basic/copy.h
index b5d08ebafe..4f3e11423e 100644
--- a/src/basic/copy.h
+++ b/src/basic/copy.h
@@ -24,13 +24,19 @@
#include <stdint.h>
#include <sys/types.h>
-int copy_file_fd(const char *from, int to, bool try_reflink);
-int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags);
-int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags);
-int copy_tree(const char *from, const char *to, bool merge);
-int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge);
-int copy_directory_fd(int dirfd, const char *to, bool merge);
-int copy_directory(const char *from, const char *to, bool merge);
-int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink);
+typedef enum CopyFlags {
+ COPY_REFLINK = 0x1, /* try to reflink */
+ COPY_MERGE = 0x2, /* merge existing trees with our new one to copy */
+ COPY_REPLACE = 0x4, /* replace an existing file if there's one */
+} CopyFlags;
+
+int copy_file_fd(const char *from, int to, CopyFlags copy_flags);
+int copy_file(const char *from, const char *to, int open_flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags);
+int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags);
+int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags);
+int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags);
+int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags);
+int copy_directory(const char *from, const char *to, CopyFlags copy_flags);
+int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags);
int copy_times(int fdf, int fdt);
int copy_xattr(int fdf, int fdt);
diff --git a/src/basic/def.h b/src/basic/def.h
index 2266eff650..200ea973c1 100644
--- a/src/basic/def.h
+++ b/src/basic/def.h
@@ -36,7 +36,9 @@
/* The default value for the net.unix.max_dgram_qlen sysctl */
#define DEFAULT_UNIX_MAX_DGRAM_QLEN 512UL
-#define SYSTEMD_CGROUP_CONTROLLER "name=systemd"
+#define SYSTEMD_CGROUP_CONTROLLER_LEGACY "name=systemd"
+#define SYSTEMD_CGROUP_CONTROLLER_HYBRID "name=unified"
+#define SYSTEMD_CGROUP_CONTROLLER "_systemd"
#define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT
#define SIGNALS_IGNORE SIGPIPE
@@ -73,18 +75,18 @@
#define NOTIFY_BUFFER_MAX PIPE_BUF
#ifdef HAVE_SPLIT_USR
-#define _CONF_PATHS_SPLIT_USR(n) "/lib/" n "\0"
+# define _CONF_PATHS_SPLIT_USR(n) "/lib/" n "\0"
#else
-#define _CONF_PATHS_SPLIT_USR(n)
+# define _CONF_PATHS_SPLIT_USR(n)
#endif
/* Return a nulstr for a standard cascade of configuration paths,
* suitable to pass to conf_files_list_nulstr() or config_parse_many_nulstr()
* to implement drop-in directories for extending configuration
* files. */
-#define CONF_PATHS_NULSTR(n) \
- "/etc/" n "\0" \
- "/run/" n "\0" \
- "/usr/local/lib/" n "\0" \
- "/usr/lib/" n "\0" \
+#define CONF_PATHS_NULSTR(n) \
+ "/etc/" n "\0" \
+ "/run/" n "\0" \
+ "/usr/local/lib/" n "\0" \
+ "/usr/lib/" n "\0" \
_CONF_PATHS_SPLIT_USR(n)
diff --git a/src/basic/env-util.c b/src/basic/env-util.c
index 96da38d45e..1ec574e8a0 100644
--- a/src/basic/env-util.c
+++ b/src/basic/env-util.c
@@ -26,6 +26,7 @@
#include "alloc-util.h"
#include "env-util.h"
+#include "escape.h"
#include "extract-word.h"
#include "macro.h"
#include "parse-util.h"
@@ -247,7 +248,7 @@ fail:
return NULL;
}
-_pure_ static bool env_match(const char *t, const char *pattern) {
+static bool env_match(const char *t, const char *pattern) {
assert(t);
assert(pattern);
@@ -273,6 +274,19 @@ _pure_ static bool env_match(const char *t, const char *pattern) {
return false;
}
+static bool env_entry_has_name(const char *entry, const char *name) {
+ const char *t;
+
+ assert(entry);
+ assert(name);
+
+ t = startswith(entry, name);
+ if (!t)
+ return false;
+
+ return *t == '=';
+}
+
char **strv_env_delete(char **x, unsigned n_lists, ...) {
size_t n, i = 0;
char **k, **r;
@@ -386,18 +400,24 @@ char **strv_env_unset_many(char **l, ...) {
int strv_env_replace(char ***l, char *p) {
char **f;
+ const char *t, *name;
assert(p);
/* Replace first occurrence of the env var or add a new one in the
* string list. Drop other occurences. Edits in-place. Does not copy p.
+ * p must be a valid key=value assignment.
*/
+ t = strchr(p, '=');
+ assert(t);
+
+ name = strndupa(p, t - p);
+
for (f = *l; f && *f; f++)
- if (env_match(*f, p)) {
- free(*f);
- *f = p;
- strv_env_unset(f + 1, p);
+ if (env_entry_has_name(*f, name)) {
+ free_and_replace(*f, p);
+ strv_env_unset(f + 1, *f);
return 0;
}
@@ -434,7 +454,7 @@ fail:
return NULL;
}
-char *strv_env_get_n(char **l, const char *name, size_t k) {
+char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
char **i;
assert(name);
@@ -442,18 +462,25 @@ char *strv_env_get_n(char **l, const char *name, size_t k) {
if (k <= 0)
return NULL;
- STRV_FOREACH(i, l)
+ STRV_FOREACH_BACKWARDS(i, l)
if (strneq(*i, name, k) &&
(*i)[k] == '=')
return *i + k + 1;
+ if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
+ const char *t;
+
+ t = strndupa(name, k);
+ return getenv(t);
+ };
+
return NULL;
}
char *strv_env_get(char **l, const char *name) {
assert(name);
- return strv_env_get_n(l, name, strlen(name));
+ return strv_env_get_n(l, name, strlen(name), 0);
}
char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
@@ -492,19 +519,26 @@ char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const cha
return e;
}
-char *replace_env(const char *format, char **env) {
+char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
enum {
WORD,
CURLY,
- VARIABLE
+ VARIABLE,
+ VARIABLE_RAW,
+ TEST,
+ DEFAULT_VALUE,
+ ALTERNATE_VALUE,
} state = WORD;
- const char *e, *word = format;
- char *r = NULL, *k;
+ const char *e, *word = format, *test_value;
+ char *k;
+ _cleanup_free_ char *r = NULL;
+ size_t i, len;
+ int nest = 0;
assert(format);
- for (e = format; *e; e ++) {
+ for (e = format, i = 0; *e && i < n; e ++, i ++) {
switch (state) {
@@ -517,24 +551,36 @@ char *replace_env(const char *format, char **env) {
if (*e == '{') {
k = strnappend(r, word, e-word-1);
if (!k)
- goto fail;
+ return NULL;
free(r);
r = k;
word = e-1;
state = VARIABLE;
-
+ nest++;
} else if (*e == '$') {
k = strnappend(r, word, e-word);
if (!k)
- goto fail;
+ return NULL;
free(r);
r = k;
word = e+1;
state = WORD;
+
+ } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) {
+ k = strnappend(r, word, e-word-1);
+ if (!k)
+ return NULL;
+
+ free(r);
+ r = k;
+
+ word = e-1;
+ state = VARIABLE_RAW;
+
} else
state = WORD;
break;
@@ -543,31 +589,109 @@ char *replace_env(const char *format, char **env) {
if (*e == '}') {
const char *t;
- t = strempty(strv_env_get_n(env, word+2, e-word-2));
+ t = strv_env_get_n(env, word+2, e-word-2, flags);
k = strappend(r, t);
if (!k)
- goto fail;
+ return NULL;
free(r);
r = k;
word = e+1;
state = WORD;
+ } else if (*e == ':') {
+ if (!(flags & REPLACE_ENV_ALLOW_EXTENDED))
+ /* Treat this as unsupported syntax, i.e. do no replacement */
+ state = WORD;
+ else {
+ len = e-word-2;
+ state = TEST;
+ }
+ }
+ break;
+
+ case TEST:
+ if (*e == '-')
+ state = DEFAULT_VALUE;
+ else if (*e == '+')
+ state = ALTERNATE_VALUE;
+ else {
+ state = WORD;
+ break;
+ }
+
+ test_value = e+1;
+ break;
+
+ case DEFAULT_VALUE: /* fall through */
+ case ALTERNATE_VALUE:
+ assert(flags & REPLACE_ENV_ALLOW_EXTENDED);
+
+ if (*e == '{') {
+ nest++;
+ break;
+ }
+
+ if (*e != '}')
+ break;
+
+ nest--;
+ if (nest == 0) {
+ const char *t;
+ _cleanup_free_ char *v = NULL;
+
+ t = strv_env_get_n(env, word+2, len, flags);
+
+ if (t && state == ALTERNATE_VALUE)
+ t = v = replace_env_n(test_value, e-test_value, env, flags);
+ else if (!t && state == DEFAULT_VALUE)
+ t = v = replace_env_n(test_value, e-test_value, env, flags);
+
+ k = strappend(r, t);
+ if (!k)
+ return NULL;
+
+ free(r);
+ r = k;
+
+ word = e+1;
+ state = WORD;
+ }
+ break;
+
+ case VARIABLE_RAW:
+ assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
+
+ if (!strchr(VALID_CHARS_ENV_NAME, *e)) {
+ const char *t;
+
+ t = strv_env_get_n(env, word+1, e-word-1, flags);
+
+ k = strappend(r, t);
+ if (!k)
+ return NULL;
+
+ free(r);
+ r = k;
+
+ word = e--;
+ i--;
+ state = WORD;
}
break;
}
}
- k = strnappend(r, word, e-word);
- if (!k)
- goto fail;
+ if (state == VARIABLE_RAW) {
+ const char *t;
- free(r);
- return k;
+ assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
-fail:
- return mfree(r);
+ t = strv_env_get_n(env, word+1, e-word-1, flags);
+ return strappend(r, t);
+ } else
+ return strnappend(r, word, e-word);
}
char **replace_env_argv(char **argv, char **env) {
@@ -623,7 +747,7 @@ char **replace_env_argv(char **argv, char **env) {
}
/* If ${FOO} appears as part of a word, replace it by the variable as-is */
- ret[k] = replace_env(*i, env);
+ ret[k] = replace_env(*i, env, 0);
if (!ret[k]) {
strv_free(ret);
return NULL;
@@ -644,3 +768,39 @@ int getenv_bool(const char *p) {
return parse_boolean(e);
}
+
+int serialize_environment(FILE *f, char **environment) {
+ char **e;
+
+ STRV_FOREACH(e, environment) {
+ _cleanup_free_ char *ce;
+
+ ce = cescape(*e);
+ if (!ce)
+ return -ENOMEM;
+
+ fprintf(f, "env=%s\n", *e);
+ }
+
+ /* caller should call ferror() */
+
+ return 0;
+}
+
+int deserialize_environment(char ***environment, const char *line) {
+ char *uce = NULL;
+ int r;
+
+ assert(line);
+ assert(environment);
+
+ assert(startswith(line, "env="));
+ r = cunescape(line + 4, UNESCAPE_RELAX, &uce);
+ if (r < 0)
+ return r;
+
+ if (!env_assignment_is_valid(uce))
+ return -EINVAL;
+
+ return strv_env_replace(environment, uce);
+}
diff --git a/src/basic/env-util.h b/src/basic/env-util.h
index 8cb0fc2131..e88fa6aac0 100644
--- a/src/basic/env-util.h
+++ b/src/basic/env-util.h
@@ -21,6 +21,7 @@
#include <stdbool.h>
#include <stddef.h>
+#include <stdio.h>
#include "macro.h"
@@ -28,9 +29,19 @@ bool env_name_is_valid(const char *e);
bool env_value_is_valid(const char *e);
bool env_assignment_is_valid(const char *e);
-char *replace_env(const char *format, char **env);
+enum {
+ REPLACE_ENV_USE_ENVIRONMENT = 1u,
+ REPLACE_ENV_ALLOW_BRACELESS = 2u,
+ REPLACE_ENV_ALLOW_EXTENDED = 4u,
+};
+
+char *replace_env_n(const char *format, size_t n, char **env, unsigned flags);
char **replace_env_argv(char **argv, char **env);
+static inline char *replace_env(const char *format, char **env, unsigned flags) {
+ return replace_env_n(format, strlen(format), env, flags);
+}
+
bool strv_env_is_valid(char **e);
#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL)
char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
@@ -46,7 +57,10 @@ char **strv_env_unset(char **l, const char *p); /* In place ... */
char **strv_env_unset_many(char **l, ...) _sentinel_;
int strv_env_replace(char ***l, char *p); /* In place ... */
-char *strv_env_get_n(char **l, const char *name, size_t k) _pure_;
+char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
char *strv_env_get(char **x, const char *n) _pure_;
int getenv_bool(const char *p);
+
+int serialize_environment(FILE *f, char **environment);
+int deserialize_environment(char ***environment, const char *line);
diff --git a/src/basic/exec-util.c b/src/basic/exec-util.c
new file mode 100644
index 0000000000..aced9e8e3d
--- /dev/null
+++ b/src/basic/exec-util.c
@@ -0,0 +1,360 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dirent.h>
+#include <errno.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "alloc-util.h"
+#include "conf-files.h"
+#include "env-util.h"
+#include "exec-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "macro.h"
+#include "process-util.h"
+#include "set.h"
+#include "signal-util.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "util.h"
+
+/* Put this test here for a lack of better place */
+assert_cc(EAGAIN == EWOULDBLOCK);
+
+static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
+
+ pid_t _pid;
+
+ if (null_or_empty_path(path)) {
+ log_debug("%s is empty (a mask).", path);
+ return 0;
+ }
+
+ _pid = fork();
+ if (_pid < 0)
+ return log_error_errno(errno, "Failed to fork: %m");
+ if (_pid == 0) {
+ char *_argv[2];
+
+ assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
+
+ if (stdout_fd >= 0) {
+ /* If the fd happens to be in the right place, go along with that */
+ if (stdout_fd != STDOUT_FILENO &&
+ dup2(stdout_fd, STDOUT_FILENO) < 0)
+ return -errno;
+
+ fd_cloexec(STDOUT_FILENO, false);
+ }
+
+ if (!argv) {
+ _argv[0] = (char*) path;
+ _argv[1] = NULL;
+ argv = _argv;
+ } else
+ argv[0] = (char*) path;
+
+ execv(path, argv);
+ log_error_errno(errno, "Failed to execute %s: %m", path);
+ _exit(EXIT_FAILURE);
+ }
+
+ log_debug("Spawned %s as " PID_FMT ".", path, _pid);
+ *pid = _pid;
+ return 1;
+}
+
+static int do_execute(
+ char **directories,
+ usec_t timeout,
+ gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
+ void* const callback_args[_STDOUT_CONSUME_MAX],
+ int output_fd,
+ char *argv[]) {
+
+ _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
+ _cleanup_strv_free_ char **paths = NULL;
+ char **path;
+ int r;
+
+ /* We fork this all off from a child process so that we can somewhat cleanly make
+ * use of SIGALRM to set a time limit.
+ *
+ * If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
+ */
+
+ (void) reset_all_signal_handlers();
+ (void) reset_signal_mask();
+
+ assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
+
+ r = conf_files_list_strv(&paths, NULL, NULL, (const char* const*) directories);
+ if (r < 0)
+ return r;
+
+ if (!callbacks) {
+ pids = hashmap_new(NULL);
+ if (!pids)
+ return log_oom();
+ }
+
+ /* Abort execution of this process after the timout. We simply rely on SIGALRM as
+ * default action terminating the process, and turn on alarm(). */
+
+ if (timeout != USEC_INFINITY)
+ alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
+
+ STRV_FOREACH(path, paths) {
+ _cleanup_free_ char *t = NULL;
+ _cleanup_close_ int fd = -1;
+ pid_t pid;
+
+ t = strdup(*path);
+ if (!t)
+ return log_oom();
+
+ if (callbacks) {
+ fd = open_serialization_fd(basename(*path));
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to open serialization file: %m");
+ }
+
+ r = do_spawn(t, argv, fd, &pid);
+ if (r <= 0)
+ continue;
+
+ if (pids) {
+ r = hashmap_put(pids, PID_TO_PTR(pid), t);
+ if (r < 0)
+ return log_oom();
+ t = NULL;
+ } else {
+ r = wait_for_terminate_and_warn(t, pid, true);
+ if (r < 0)
+ continue;
+
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ return log_error_errno(errno, "Failed to seek on serialization fd: %m");
+
+ r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
+ fd = -1;
+ if (r < 0)
+ return log_error_errno(r, "Failed to process output from %s: %m", *path);
+ }
+ }
+
+ if (callbacks) {
+ r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
+ if (r < 0)
+ return log_error_errno(r, "Callback two failed: %m");
+ }
+
+ while (!hashmap_isempty(pids)) {
+ _cleanup_free_ char *t = NULL;
+ pid_t pid;
+
+ pid = PTR_TO_PID(hashmap_first_key(pids));
+ assert(pid > 0);
+
+ t = hashmap_remove(pids, PID_TO_PTR(pid));
+ assert(t);
+
+ wait_for_terminate_and_warn(t, pid, true);
+ }
+
+ return 0;
+}
+
+int execute_directories(
+ const char* const* directories,
+ usec_t timeout,
+ gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
+ void* const callback_args[_STDOUT_CONSUME_MAX],
+ char *argv[]) {
+
+ pid_t executor_pid;
+ char *name;
+ char **dirs = (char**) directories;
+ _cleanup_close_ int fd = -1;
+ int r;
+
+ assert(!strv_isempty(dirs));
+
+ name = basename(dirs[0]);
+ assert(!isempty(name));
+
+ if (callbacks) {
+ assert(callback_args);
+ assert(callbacks[STDOUT_GENERATE]);
+ assert(callbacks[STDOUT_COLLECT]);
+ assert(callbacks[STDOUT_CONSUME]);
+
+ fd = open_serialization_fd(name);
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to open serialization file: %m");
+ }
+
+ /* Executes all binaries in the directories serially or in parallel and waits for
+ * them to finish. Optionally a timeout is applied. If a file with the same name
+ * exists in more than one directory, the earliest one wins. */
+
+ executor_pid = fork();
+ if (executor_pid < 0)
+ return log_error_errno(errno, "Failed to fork: %m");
+
+ if (executor_pid == 0) {
+ r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
+ _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+ }
+
+ r = wait_for_terminate_and_warn(name, executor_pid, true);
+ if (r < 0)
+ return log_error_errno(r, "Execution failed: %m");
+ if (r > 0) {
+ /* non-zero return code from child */
+ log_error("Forker process failed.");
+ return -EREMOTEIO;
+ }
+
+ if (!callbacks)
+ return 0;
+
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ return log_error_errno(errno, "Failed to rewind serialization fd: %m");
+
+ r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
+ fd = -1;
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse returned data: %m");
+ return 0;
+}
+
+static int gather_environment_generate(int fd, void *arg) {
+ char ***env = arg, **x, **y;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_strv_free_ char **new;
+ int r;
+
+ /* Read a series of VAR=value assignments from fd, use them to update the list of
+ * variables in env. Also update the exported environment.
+ *
+ * fd is always consumed, even on error.
+ */
+
+ assert(env);
+
+ f = fdopen(fd, "r");
+ if (!f) {
+ safe_close(fd);
+ return -errno;
+ }
+
+ r = load_env_file_pairs(f, NULL, NULL, &new);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH_PAIR(x, y, new) {
+ char *p;
+
+ if (!env_name_is_valid(*x)) {
+ log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
+ continue;
+ }
+
+ p = strjoin(*x, "=", *y);
+ if (!p)
+ return -ENOMEM;
+
+ r = strv_env_replace(env, p);
+ if (r < 0)
+ return r;
+
+ if (setenv(*x, *y, true) < 0)
+ return -errno;
+ }
+
+ return r;
+}
+
+static int gather_environment_collect(int fd, void *arg) {
+ char ***env = arg;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ /* Write out a series of env=cescape(VAR=value) assignments to fd. */
+
+ assert(env);
+
+ f = fdopen(fd, "w");
+ if (!f) {
+ safe_close(fd);
+ return -errno;
+ }
+
+ r = serialize_environment(f, *env);
+ if (r < 0)
+ return r;
+
+ if (ferror(f))
+ return errno > 0 ? -errno : -EIO;
+
+ return 0;
+}
+
+static int gather_environment_consume(int fd, void *arg) {
+ char ***env = arg;
+ _cleanup_fclose_ FILE *f = NULL;
+ char line[LINE_MAX];
+ int r = 0, k;
+
+ /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
+
+ assert(env);
+
+ f = fdopen(fd, "r");
+ if (!f) {
+ safe_close(fd);
+ return -errno;
+ }
+
+ FOREACH_LINE(line, f, return -EIO) {
+ truncate_nl(line);
+
+ k = deserialize_environment(env, line);
+ if (k < 0)
+ log_error_errno(k, "Invalid line \"%s\": %m", line);
+ if (k < 0 && r == 0)
+ r = k;
+ }
+
+ return r;
+}
+
+const gather_stdout_callback_t gather_environment[] = {
+ gather_environment_generate,
+ gather_environment_collect,
+ gather_environment_consume,
+};
diff --git a/src/basic/exec-util.h b/src/basic/exec-util.h
new file mode 100644
index 0000000000..72009799b2
--- /dev/null
+++ b/src/basic/exec-util.h
@@ -0,0 +1,40 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2017 Zbigniew Jędrzejewski-Szmek
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include "time-util.h"
+
+typedef int (*gather_stdout_callback_t) (int fd, void *arg);
+
+enum {
+ STDOUT_GENERATE, /* from generators to helper process */
+ STDOUT_COLLECT, /* from helper process to main process */
+ STDOUT_CONSUME, /* process data in main process */
+ _STDOUT_CONSUME_MAX,
+};
+
+int execute_directories(
+ const char* const* directories,
+ usec_t timeout,
+ gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
+ void* const callback_args[_STDOUT_CONSUME_MAX],
+ char *argv[]);
+
+extern const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX];
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index c43b0583a4..7c2c2b38f5 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -30,6 +30,7 @@
#include "alloc-util.h"
#include "ctype.h"
+#include "env-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
@@ -553,13 +554,14 @@ static int parse_env_file_internal(
}
}
- if (state == PRE_VALUE ||
- state == VALUE ||
- state == VALUE_ESCAPE ||
- state == SINGLE_QUOTE_VALUE ||
- state == SINGLE_QUOTE_VALUE_ESCAPE ||
- state == DOUBLE_QUOTE_VALUE ||
- state == DOUBLE_QUOTE_VALUE_ESCAPE) {
+ if (IN_SET(state,
+ PRE_VALUE,
+ VALUE,
+ VALUE_ESCAPE,
+ SINGLE_QUOTE_VALUE,
+ SINGLE_QUOTE_VALUE_ESCAPE,
+ DOUBLE_QUOTE_VALUE,
+ DOUBLE_QUOTE_VALUE_ESCAPE)) {
key[n_key] = 0;
@@ -586,14 +588,9 @@ fail:
return r;
}
-static int parse_env_file_push(
+static int check_utf8ness_and_warn(
const char *filename, unsigned line,
- const char *key, char *value,
- void *userdata,
- int *n_pushed) {
-
- const char *k;
- va_list aq, *ap = userdata;
+ const char *key, char *value) {
if (!utf8_is_valid(key)) {
_cleanup_free_ char *p = NULL;
@@ -611,6 +608,23 @@ static int parse_env_file_push(
return -EINVAL;
}
+ return 0;
+}
+
+static int parse_env_file_push(
+ const char *filename, unsigned line,
+ const char *key, char *value,
+ void *userdata,
+ int *n_pushed) {
+
+ const char *k;
+ va_list aq, *ap = userdata;
+ int r;
+
+ r = check_utf8ness_and_warn(filename, line, key, value);
+ if (r < 0)
+ return r;
+
va_copy(aq, *ap);
while ((k = va_arg(aq, const char *))) {
@@ -662,27 +676,19 @@ static int load_env_file_push(
char *p;
int r;
- if (!utf8_is_valid(key)) {
- _cleanup_free_ char *t = utf8_escape_invalid(key);
-
- log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
- return -EINVAL;
- }
-
- if (value && !utf8_is_valid(value)) {
- _cleanup_free_ char *t = utf8_escape_invalid(value);
-
- log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
- return -EINVAL;
- }
+ r = check_utf8ness_and_warn(filename, line, key, value);
+ if (r < 0)
+ return r;
- p = strjoin(key, "=", strempty(value));
+ p = strjoin(key, "=", value);
if (!p)
return -ENOMEM;
- r = strv_consume(m, p);
- if (r < 0)
+ r = strv_env_replace(m, p);
+ if (r < 0) {
+ free(p);
return r;
+ }
if (n_pushed)
(*n_pushed)++;
@@ -716,19 +722,9 @@ static int load_env_file_push_pairs(
char ***m = userdata;
int r;
- if (!utf8_is_valid(key)) {
- _cleanup_free_ char *t = utf8_escape_invalid(key);
-
- log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
- return -EINVAL;
- }
-
- if (value && !utf8_is_valid(value)) {
- _cleanup_free_ char *t = utf8_escape_invalid(value);
-
- log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
- return -EINVAL;
- }
+ r = check_utf8ness_and_warn(filename, line, key, value);
+ if (r < 0)
+ return r;
r = strv_extend(m, key);
if (r < 0)
@@ -767,6 +763,52 @@ int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char **
return 0;
}
+static int merge_env_file_push(
+ const char *filename, unsigned line,
+ const char *key, char *value,
+ void *userdata,
+ int *n_pushed) {
+
+ char ***env = userdata;
+ char *expanded_value;
+
+ assert(env);
+
+ if (!value) {
+ log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key);
+ return 0;
+ }
+
+ if (!env_name_is_valid(key)) {
+ log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key);
+ free(value);
+ return 0;
+ }
+
+ expanded_value = replace_env(value, *env,
+ REPLACE_ENV_USE_ENVIRONMENT|
+ REPLACE_ENV_ALLOW_BRACELESS|
+ REPLACE_ENV_ALLOW_EXTENDED);
+ if (!expanded_value)
+ return -ENOMEM;
+
+ free_and_replace(value, expanded_value);
+
+ return load_env_file_push(filename, line, key, value, env, n_pushed);
+}
+
+int merge_env_file(
+ char ***env,
+ FILE *f,
+ const char *fname) {
+
+ /* NOTE: this function supports braceful and braceless variable expansions,
+ * plus "extended" substitutions, unlike other exported parsing functions.
+ */
+
+ return parse_env_file_internal(f, fname, NEWLINE, merge_env_file_push, env, NULL);
+}
+
static void write_env_var(FILE *f, const char *v) {
const char *p;
@@ -1342,6 +1384,25 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
return fd;
}
+int open_serialization_fd(const char *ident) {
+ int fd = -1;
+
+ fd = memfd_create(ident, MFD_CLOEXEC);
+ if (fd < 0) {
+ const char *path;
+
+ path = getpid() == 1 ? "/run/systemd" : "/tmp";
+ fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ log_debug("Serializing %s to %s.", ident, path);
+ } else
+ log_debug("Serializing %s to memfd.", ident);
+
+ return fd;
+}
+
int link_tmpfile(int fd, const char *path, const char *target) {
assert(fd >= 0);
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index 17b38a5d60..e547614cc4 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -48,6 +48,8 @@ int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
int load_env_file(FILE *f, const char *fname, const char *separator, char ***l);
int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l);
+int merge_env_file(char ***env, FILE *f, const char *fname);
+
int write_env_file(const char *fname, char **l);
int executable_is_script(const char *path, char **interpreter);
@@ -84,6 +86,7 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space)
int open_tmpfile_unlinkable(const char *directory, int flags);
int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
+int open_serialization_fd(const char *ident);
int link_tmpfile(int fd, const char *path, const char *target);
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index 5fe5c71ff0..094acf1799 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -91,3 +91,9 @@ static inline void rmdir_and_free(char *p) {
free(p);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free);
+
+static inline void unlink_and_free(char *p) {
+ (void) unlink(p);
+ free(p);
+}
+DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c
index e44a357287..a94037b303 100644
--- a/src/basic/hostname-util.c
+++ b/src/basic/hostname-util.c
@@ -55,7 +55,7 @@ char* gethostname_malloc(void) {
assert_se(uname(&u) >= 0);
if (isempty(u.nodename) || streq(u.nodename, "(none)"))
- return strdup(u.sysname);
+ return strdup(FALLBACK_HOSTNAME);
return strdup(u.nodename);
}
diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c
index 3b06cb00ad..3927df2955 100644
--- a/src/basic/in-addr-util.c
+++ b/src/basic/in-addr-util.c
@@ -66,6 +66,18 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) {
return -EAFNOSUPPORT;
}
+int in_addr_is_multicast(int family, const union in_addr_union *u) {
+ assert(u);
+
+ if (family == AF_INET)
+ return IN_MULTICAST(be32toh(u->in.s_addr));
+
+ if (family == AF_INET6)
+ return IN6_IS_ADDR_MULTICAST(&u->in6);
+
+ return -EAFNOSUPPORT;
+}
+
bool in4_addr_is_localhost(const struct in_addr *a) {
assert(a);
diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h
index 64a812c322..51a5aa67e4 100644
--- a/src/basic/in-addr-util.h
+++ b/src/basic/in-addr-util.h
@@ -39,6 +39,8 @@ struct in_addr_data {
bool in4_addr_is_null(const struct in_addr *a);
int in_addr_is_null(int family, const union in_addr_union *u);
+int in_addr_is_multicast(int family, const union in_addr_union *u);
+
bool in4_addr_is_link_local(const struct in_addr *a);
int in_addr_is_link_local(int family, const union in_addr_union *u);
diff --git a/src/basic/journal-importer.c b/src/basic/journal-importer.c
new file mode 100644
index 0000000000..4c13e46a49
--- /dev/null
+++ b/src/basic/journal-importer.c
@@ -0,0 +1,481 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Zbigniew Jędrzejewski-Szmek
+
+ 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 <unistd.h>
+
+#include "alloc-util.h"
+#include "journal-importer.h"
+#include "fd-util.h"
+#include "parse-util.h"
+#include "string-util.h"
+
+enum {
+ IMPORTER_STATE_LINE = 0, /* waiting to read, or reading line */
+ IMPORTER_STATE_DATA_START, /* reading binary data header */
+ IMPORTER_STATE_DATA, /* reading binary data */
+ IMPORTER_STATE_DATA_FINISH, /* expecting newline */
+ IMPORTER_STATE_EOF, /* done */
+};
+
+static int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len) {
+ if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1))
+ return log_oom();
+
+ iovw->iovec[iovw->count++] = (struct iovec) {data, len};
+ return 0;
+}
+
+static void iovw_free_contents(struct iovec_wrapper *iovw) {
+ iovw->iovec = mfree(iovw->iovec);
+ iovw->size_bytes = iovw->count = 0;
+}
+
+static void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) {
+ size_t i;
+
+ for (i = 0; i < iovw->count; i++)
+ iovw->iovec[i].iov_base = (char*) iovw->iovec[i].iov_base - old + new;
+}
+
+size_t iovw_size(struct iovec_wrapper *iovw) {
+ size_t n = 0, i;
+
+ for (i = 0; i < iovw->count; i++)
+ n += iovw->iovec[i].iov_len;
+
+ return n;
+}
+
+void journal_importer_cleanup(JournalImporter *imp) {
+ if (imp->fd >= 0 && !imp->passive_fd) {
+ log_debug("Closing %s (fd=%d)", imp->name ?: "importer", imp->fd);
+ safe_close(imp->fd);
+ }
+
+ free(imp->buf);
+ iovw_free_contents(&imp->iovw);
+}
+
+static char* realloc_buffer(JournalImporter *imp, size_t size) {
+ char *b, *old = imp->buf;
+
+ b = GREEDY_REALLOC(imp->buf, imp->size, size);
+ if (!b)
+ return NULL;
+
+ iovw_rebase(&imp->iovw, old, imp->buf);
+
+ return b;
+}
+
+static int get_line(JournalImporter *imp, char **line, size_t *size) {
+ ssize_t n;
+ char *c = NULL;
+
+ assert(imp);
+ assert(imp->state == IMPORTER_STATE_LINE);
+ assert(imp->offset <= imp->filled);
+ assert(imp->filled <= imp->size);
+ assert(imp->buf == NULL || imp->size > 0);
+ assert(imp->fd >= 0);
+
+ for (;;) {
+ if (imp->buf) {
+ size_t start = MAX(imp->scanned, imp->offset);
+
+ c = memchr(imp->buf + start, '\n',
+ imp->filled - start);
+ if (c != NULL)
+ break;
+ }
+
+ imp->scanned = imp->filled;
+ if (imp->scanned >= DATA_SIZE_MAX) {
+ log_error("Entry is bigger than %u bytes.", DATA_SIZE_MAX);
+ return -E2BIG;
+ }
+
+ if (imp->passive_fd)
+ /* we have to wait for some data to come to us */
+ return -EAGAIN;
+
+ /* We know that imp->filled is at most DATA_SIZE_MAX, so if
+ we reallocate it, we'll increase the size at least a bit. */
+ assert_cc(DATA_SIZE_MAX < ENTRY_SIZE_MAX);
+ if (imp->size - imp->filled < LINE_CHUNK &&
+ !realloc_buffer(imp, MIN(imp->filled + LINE_CHUNK, ENTRY_SIZE_MAX)))
+ return log_oom();
+
+ assert(imp->buf);
+ assert(imp->size - imp->filled >= LINE_CHUNK ||
+ imp->size == ENTRY_SIZE_MAX);
+
+ n = read(imp->fd,
+ imp->buf + imp->filled,
+ imp->size - imp->filled);
+ if (n < 0) {
+ if (errno != EAGAIN)
+ log_error_errno(errno, "read(%d, ..., %zu): %m",
+ imp->fd,
+ imp->size - imp->filled);
+ return -errno;
+ } else if (n == 0)
+ return 0;
+
+ imp->filled += n;
+ }
+
+ *line = imp->buf + imp->offset;
+ *size = c + 1 - imp->buf - imp->offset;
+ imp->offset += *size;
+
+ return 1;
+}
+
+static int fill_fixed_size(JournalImporter *imp, void **data, size_t size) {
+
+ assert(imp);
+ assert(imp->state == IMPORTER_STATE_DATA_START ||
+ imp->state == IMPORTER_STATE_DATA ||
+ imp->state == IMPORTER_STATE_DATA_FINISH);
+ assert(size <= DATA_SIZE_MAX);
+ assert(imp->offset <= imp->filled);
+ assert(imp->filled <= imp->size);
+ assert(imp->buf != NULL || imp->size == 0);
+ assert(imp->buf == NULL || imp->size > 0);
+ assert(imp->fd >= 0);
+ assert(data);
+
+ while (imp->filled - imp->offset < size) {
+ int n;
+
+ if (imp->passive_fd)
+ /* we have to wait for some data to come to us */
+ return -EAGAIN;
+
+ if (!realloc_buffer(imp, imp->offset + size))
+ return log_oom();
+
+ n = read(imp->fd, imp->buf + imp->filled,
+ imp->size - imp->filled);
+ if (n < 0) {
+ if (errno != EAGAIN)
+ log_error_errno(errno, "read(%d, ..., %zu): %m", imp->fd,
+ imp->size - imp->filled);
+ return -errno;
+ } else if (n == 0)
+ return 0;
+
+ imp->filled += n;
+ }
+
+ *data = imp->buf + imp->offset;
+ imp->offset += size;
+
+ return 1;
+}
+
+static int get_data_size(JournalImporter *imp) {
+ int r;
+ void *data;
+
+ assert(imp);
+ assert(imp->state == IMPORTER_STATE_DATA_START);
+ assert(imp->data_size == 0);
+
+ r = fill_fixed_size(imp, &data, sizeof(uint64_t));
+ if (r <= 0)
+ return r;
+
+ imp->data_size = le64toh( *(uint64_t *) data );
+ if (imp->data_size > DATA_SIZE_MAX) {
+ log_error("Stream declares field with size %zu > DATA_SIZE_MAX = %u",
+ imp->data_size, DATA_SIZE_MAX);
+ return -EINVAL;
+ }
+ if (imp->data_size == 0)
+ log_warning("Binary field with zero length");
+
+ return 1;
+}
+
+static int get_data_data(JournalImporter *imp, void **data) {
+ int r;
+
+ assert(imp);
+ assert(data);
+ assert(imp->state == IMPORTER_STATE_DATA);
+
+ r = fill_fixed_size(imp, data, imp->data_size);
+ if (r <= 0)
+ return r;
+
+ return 1;
+}
+
+static int get_data_newline(JournalImporter *imp) {
+ int r;
+ char *data;
+
+ assert(imp);
+ assert(imp->state == IMPORTER_STATE_DATA_FINISH);
+
+ r = fill_fixed_size(imp, (void**) &data, 1);
+ if (r <= 0)
+ return r;
+
+ assert(data);
+ if (*data != '\n') {
+ log_error("expected newline, got '%c'", *data);
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+static int process_dunder(JournalImporter *imp, char *line, size_t n) {
+ const char *timestamp;
+ int r;
+
+ assert(line);
+ assert(n > 0);
+ assert(line[n-1] == '\n');
+
+ /* XXX: is it worth to support timestamps in extended format?
+ * We don't produce them, but who knows... */
+
+ timestamp = startswith(line, "__CURSOR=");
+ if (timestamp)
+ /* ignore __CURSOR */
+ return 1;
+
+ timestamp = startswith(line, "__REALTIME_TIMESTAMP=");
+ if (timestamp) {
+ long long unsigned x;
+ line[n-1] = '\0';
+ r = safe_atollu(timestamp, &x);
+ if (r < 0)
+ log_warning("Failed to parse __REALTIME_TIMESTAMP: '%s'", timestamp);
+ else
+ imp->ts.realtime = x;
+ return r < 0 ? r : 1;
+ }
+
+ timestamp = startswith(line, "__MONOTONIC_TIMESTAMP=");
+ if (timestamp) {
+ long long unsigned x;
+ line[n-1] = '\0';
+ r = safe_atollu(timestamp, &x);
+ if (r < 0)
+ log_warning("Failed to parse __MONOTONIC_TIMESTAMP: '%s'", timestamp);
+ else
+ imp->ts.monotonic = x;
+ return r < 0 ? r : 1;
+ }
+
+ timestamp = startswith(line, "__");
+ if (timestamp) {
+ log_notice("Unknown dunder line %s", line);
+ return 1;
+ }
+
+ /* no dunder */
+ return 0;
+}
+
+int journal_importer_process_data(JournalImporter *imp) {
+ int r;
+
+ switch(imp->state) {
+ case IMPORTER_STATE_LINE: {
+ char *line, *sep;
+ size_t n = 0;
+
+ assert(imp->data_size == 0);
+
+ r = get_line(imp, &line, &n);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ imp->state = IMPORTER_STATE_EOF;
+ return r;
+ }
+ assert(n > 0);
+ assert(line[n-1] == '\n');
+
+ if (n == 1) {
+ log_trace("Received empty line, event is ready");
+ return 1;
+ }
+
+ r = process_dunder(imp, line, n);
+ if (r != 0)
+ return r < 0 ? r : 0;
+
+ /* MESSAGE=xxx\n
+ or
+ COREDUMP\n
+ LLLLLLLL0011223344...\n
+ */
+ sep = memchr(line, '=', n);
+ if (sep) {
+ /* chomp newline */
+ n--;
+
+ r = iovw_put(&imp->iovw, line, n);
+ if (r < 0)
+ return r;
+ } else {
+ /* replace \n with = */
+ line[n-1] = '=';
+
+ imp->field_len = n;
+ imp->state = IMPORTER_STATE_DATA_START;
+
+ /* we cannot put the field in iovec until we have all data */
+ }
+
+ log_trace("Received: %.*s (%s)", (int) n, line, sep ? "text" : "binary");
+
+ return 0; /* continue */
+ }
+
+ case IMPORTER_STATE_DATA_START:
+ assert(imp->data_size == 0);
+
+ r = get_data_size(imp);
+ // log_debug("get_data_size() -> %d", r);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ imp->state = IMPORTER_STATE_EOF;
+ return 0;
+ }
+
+ imp->state = imp->data_size > 0 ?
+ IMPORTER_STATE_DATA : IMPORTER_STATE_DATA_FINISH;
+
+ return 0; /* continue */
+
+ case IMPORTER_STATE_DATA: {
+ void *data;
+ char *field;
+
+ assert(imp->data_size > 0);
+
+ r = get_data_data(imp, &data);
+ // log_debug("get_data_data() -> %d", r);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ imp->state = IMPORTER_STATE_EOF;
+ return 0;
+ }
+
+ assert(data);
+
+ field = (char*) data - sizeof(uint64_t) - imp->field_len;
+ memmove(field + sizeof(uint64_t), field, imp->field_len);
+
+ r = iovw_put(&imp->iovw, field + sizeof(uint64_t), imp->field_len + imp->data_size);
+ if (r < 0)
+ return r;
+
+ imp->state = IMPORTER_STATE_DATA_FINISH;
+
+ return 0; /* continue */
+ }
+
+ case IMPORTER_STATE_DATA_FINISH:
+ r = get_data_newline(imp);
+ // log_debug("get_data_newline() -> %d", r);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ imp->state = IMPORTER_STATE_EOF;
+ return 0;
+ }
+
+ imp->data_size = 0;
+ imp->state = IMPORTER_STATE_LINE;
+
+ return 0; /* continue */
+ default:
+ assert_not_reached("wtf?");
+ }
+}
+
+int journal_importer_push_data(JournalImporter *imp, const char *data, size_t size) {
+ assert(imp);
+ assert(imp->state != IMPORTER_STATE_EOF);
+
+ if (!realloc_buffer(imp, imp->filled + size)) {
+ log_error("Failed to store received data of size %zu "
+ "(in addition to existing %zu bytes with %zu filled): %s",
+ size, imp->size, imp->filled, strerror(ENOMEM));
+ return -ENOMEM;
+ }
+
+ memcpy(imp->buf + imp->filled, data, size);
+ imp->filled += size;
+
+ return 0;
+}
+
+void journal_importer_drop_iovw(JournalImporter *imp) {
+ size_t remain, target;
+
+ /* This function drops processed data that along with the iovw that points at it */
+
+ iovw_free_contents(&imp->iovw);
+
+ /* possibly reset buffer position */
+ remain = imp->filled - imp->offset;
+
+ if (remain == 0) /* no brainer */
+ imp->offset = imp->scanned = imp->filled = 0;
+ else if (imp->offset > imp->size - imp->filled &&
+ imp->offset > remain) {
+ memcpy(imp->buf, imp->buf + imp->offset, remain);
+ imp->offset = imp->scanned = 0;
+ imp->filled = remain;
+ }
+
+ target = imp->size;
+ while (target > 16 * LINE_CHUNK && imp->filled < target / 2)
+ target /= 2;
+ if (target < imp->size) {
+ char *tmp;
+
+ tmp = realloc(imp->buf, target);
+ if (!tmp)
+ log_warning("Failed to reallocate buffer to (smaller) size %zu",
+ target);
+ else {
+ log_debug("Reallocated buffer from %zu to %zu bytes",
+ imp->size, target);
+ imp->buf = tmp;
+ imp->size = target;
+ }
+ }
+}
+
+bool journal_importer_eof(const JournalImporter *imp) {
+ return imp->state == IMPORTER_STATE_EOF;
+}
diff --git a/src/basic/journal-importer.h b/src/basic/journal-importer.h
new file mode 100644
index 0000000000..b3e308dd6d
--- /dev/null
+++ b/src/basic/journal-importer.h
@@ -0,0 +1,70 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Zbigniew Jędrzejewski-Szmek
+
+ 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/>.
+***/
+
+#pragma once
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <sys/uio.h>
+
+#include "time-util.h"
+
+/* Make sure not to make this smaller than the maximum coredump size.
+ * See COREDUMP_MAX in coredump.c */
+#define ENTRY_SIZE_MAX (1024*1024*770u)
+#define DATA_SIZE_MAX (1024*1024*768u)
+#define LINE_CHUNK 8*1024u
+
+struct iovec_wrapper {
+ struct iovec *iovec;
+ size_t size_bytes;
+ size_t count;
+};
+
+size_t iovw_size(struct iovec_wrapper *iovw);
+
+typedef struct JournalImporter {
+ int fd;
+ bool passive_fd;
+ char *name;
+
+ char *buf;
+ size_t size; /* total size of the buffer */
+ size_t offset; /* offset to the beginning of live data in the buffer */
+ size_t scanned; /* number of bytes since the beginning of data without a newline */
+ size_t filled; /* total number of bytes in the buffer */
+
+ size_t field_len; /* used for binary fields: the field name length */
+ size_t data_size; /* and the size of the binary data chunk being processed */
+
+ struct iovec_wrapper iovw;
+
+ int state;
+ dual_timestamp ts;
+} JournalImporter;
+
+void journal_importer_cleanup(JournalImporter *);
+int journal_importer_process_data(JournalImporter *);
+int journal_importer_push_data(JournalImporter *, const char *data, size_t size);
+void journal_importer_drop_iovw(JournalImporter *);
+bool journal_importer_eof(const JournalImporter *);
+
+static inline size_t journal_importer_bytes_remaining(const JournalImporter *imp) {
+ return imp->filled;
+}
diff --git a/src/basic/khash.h b/src/basic/khash.h
index f404a68236..410f3020e0 100644
--- a/src/basic/khash.h
+++ b/src/basic/khash.h
@@ -28,7 +28,7 @@
typedef struct khash khash;
/* For plain hash functions. Hash functions commonly supported on today's kernels are: crc32c, crct10dif, crc32,
- * sha224, sha256, sha512, sha384, sha1, md5, md4, sha3-224, sha3-256, sha3-384, sha3-512, and more.*/
+ * sha224, sha256, sha512, sha384, sha1, md5, md4, sha3-224, sha3-256, sha3-384, sha3-512, and more. */
int khash_new(khash **ret, const char *algorithm);
/* For keyed hash functions. Hash functions commonly supported on today's kernels are: hmac(sha256), cmac(aes),
diff --git a/src/basic/log.c b/src/basic/log.c
index 1362b1c086..36efc9ac7d 100644
--- a/src/basic/log.c
+++ b/src/basic/log.c
@@ -72,6 +72,7 @@ static bool show_color = false;
static bool show_location = false;
static bool upgrade_syslog_to_journal = false;
+static bool always_reopen_console = false;
/* Akin to glibc's __abort_msg; which is private and we hence cannot
* use here. */
@@ -95,7 +96,7 @@ static int log_open_console(void) {
if (console_fd >= 0)
return 0;
- if (getpid() == 1) {
+ if (always_reopen_console) {
console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
if (console_fd < 0)
return console_fd;
@@ -1164,10 +1165,14 @@ int log_syntax_internal(
return log_struct_internal(
level, error,
file, line, func,
- LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION),
+ "MESSAGE_ID=" SD_MESSAGE_INVALID_CONFIGURATION_STR,
"CONFIG_FILE=%s", config_file,
"CONFIG_LINE=%u", config_line,
LOG_MESSAGE("%s:%u: %s", config_file, config_line, buffer),
unit_fmt, unit,
NULL);
}
+
+void log_set_always_reopen_console(bool b) {
+ always_reopen_console = b;
+}
diff --git a/src/basic/log.h b/src/basic/log.h
index 2afee20bb5..72714e02e5 100644
--- a/src/basic/log.h
+++ b/src/basic/log.h
@@ -214,13 +214,13 @@ bool log_on_console(void) _pure_;
const char *log_target_to_string(LogTarget target) _const_;
LogTarget log_target_from_string(const char *s) _pure_;
-/* Helpers to prepare various fields for structured logging */
+/* Helper to prepare various field for structured logging */
#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
-#define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x)
void log_received_signal(int level, const struct signalfd_siginfo *si);
void log_set_upgrade_syslog_to_journal(bool b);
+void log_set_always_reopen_console(bool b);
int log_syntax_internal(
const char *unit,
diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h
index e6fd67cb9d..9fc4156564 100644
--- a/src/basic/missing_syscall.h
+++ b/src/basic/missing_syscall.h
@@ -194,6 +194,8 @@ static inline pid_t raw_getpid(void) {
# define __NR_renameat2 316
# elif defined __arm__
# define __NR_renameat2 382
+# elif defined __aarch64__
+# define __NR_renameat2 276
# elif defined _MIPS_SIM
# if _MIPS_SIM == _MIPS_SIM_ABI32
# define __NR_renameat2 4351
diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c
index f0bc9cac18..a8fd63fb45 100644
--- a/src/basic/mount-util.c
+++ b/src/basic/mount-util.c
@@ -112,9 +112,10 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
if (r < 0) {
- if (errno == ENOSYS)
- /* This kernel does not support name_to_handle_at()
- * fall back to simpler logic. */
+ if (IN_SET(errno, ENOSYS, EACCES, EPERM))
+ /* This kernel does not support name_to_handle_at() at all, or the syscall was blocked (maybe
+ * through seccomp, because we are running inside of a container?): fall back to simpler
+ * logic. */
goto fallback_fdinfo;
else if (errno == EOPNOTSUPP)
/* This kernel or file system does not support
@@ -163,7 +164,7 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
fallback_fdinfo:
r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
- if (IN_SET(r, -EOPNOTSUPP, -EACCES))
+ if (IN_SET(r, -EOPNOTSUPP, -EACCES, -EPERM))
goto fallback_fstat;
if (r < 0)
return r;
diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c
index f703e7f145..8592a428d5 100644
--- a/src/basic/proc-cmdline.c
+++ b/src/basic/proc-cmdline.c
@@ -154,7 +154,7 @@ int proc_cmdline_get_key(const char *key, unsigned flags, char **value) {
*
* c) The "value" parameter is NULL. In this case a search for the exact "key" parameter is performed.
*
- * In all three cases, > 0 is returned if the key is found, 0 if not.*/
+ * In all three cases, > 0 is returned if the key is found, 0 if not. */
if (isempty(key))
return -EINVAL;
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index eead8b00da..0df3fed640 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -311,7 +311,7 @@ int rename_process(const char name[]) {
/* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but
* has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at
- * the end. This is the best option for changing /proc/self/cmdline.*/
+ * the end. This is the best option for changing /proc/self/cmdline. */
if (mm_size < l+1) {
size_t nn_size;
char *nn;
@@ -703,7 +703,7 @@ int kill_and_sigcont(pid_t pid, int sig) {
/* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't
* affected by a process being suspended anyway. */
- if (r >= 0 && !IN_SET(SIGCONT, SIGKILL))
+ if (r >= 0 && !IN_SET(sig, SIGCONT, SIGKILL))
(void) kill(pid, SIGCONT);
return r;
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index 17e90a8994..e5847dce00 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -900,6 +900,26 @@ bool ifname_valid(const char *p) {
return true;
}
+bool address_label_valid(const char *p) {
+
+ if (isempty(p))
+ return false;
+
+ if (strlen(p) >= IFNAMSIZ)
+ return false;
+
+ while (*p) {
+ if ((uint8_t) *p >= 127U)
+ return false;
+
+ if ((uint8_t) *p <= 31U)
+ return false;
+ p++;
+ }
+
+ return true;
+}
+
int getpeercred(int fd, struct ucred *ucred) {
socklen_t n = sizeof(struct ucred);
struct ucred u;
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index 3c42e220e5..73c3a339fc 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -126,6 +126,7 @@ int ip_tos_to_string_alloc(int i, char **s);
int ip_tos_from_string(const char *s);
bool ifname_valid(const char *p);
+bool address_label_valid(const char *p);
int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret);
diff --git a/src/basic/util.c b/src/basic/util.c
index 6204906f37..3dce0ea92e 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -59,9 +59,6 @@
#include "user-util.h"
#include "util.h"
-/* Put this test here for a lack of better place */
-assert_cc(EAGAIN == EWOULDBLOCK);
-
int saved_argc = 0;
char **saved_argv = NULL;
static int saved_in_initrd = -1;
@@ -80,146 +77,6 @@ size_t page_size(void) {
return pgsz;
}
-static int do_execute(char **directories, usec_t timeout, char *argv[]) {
- _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
- _cleanup_set_free_free_ Set *seen = NULL;
- char **directory;
-
- /* We fork this all off from a child process so that we can
- * somewhat cleanly make use of SIGALRM to set a time limit */
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
- pids = hashmap_new(NULL);
- if (!pids)
- return log_oom();
-
- seen = set_new(&string_hash_ops);
- if (!seen)
- return log_oom();
-
- STRV_FOREACH(directory, directories) {
- _cleanup_closedir_ DIR *d;
- struct dirent *de;
-
- d = opendir(*directory);
- if (!d) {
- if (errno == ENOENT)
- continue;
-
- return log_error_errno(errno, "Failed to open directory %s: %m", *directory);
- }
-
- FOREACH_DIRENT(de, d, break) {
- _cleanup_free_ char *path = NULL;
- pid_t pid;
- int r;
-
- if (!dirent_is_file(de))
- continue;
-
- if (set_contains(seen, de->d_name)) {
- log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name);
- continue;
- }
-
- r = set_put_strdup(seen, de->d_name);
- if (r < 0)
- return log_oom();
-
- path = strjoin(*directory, "/", de->d_name);
- if (!path)
- return log_oom();
-
- if (null_or_empty_path(path)) {
- log_debug("%s is empty (a mask).", path);
- continue;
- }
-
- pid = fork();
- if (pid < 0) {
- log_error_errno(errno, "Failed to fork: %m");
- continue;
- } else if (pid == 0) {
- char *_argv[2];
-
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
- if (!argv) {
- _argv[0] = path;
- _argv[1] = NULL;
- argv = _argv;
- } else
- argv[0] = path;
-
- execv(path, argv);
- return log_error_errno(errno, "Failed to execute %s: %m", path);
- }
-
- log_debug("Spawned %s as " PID_FMT ".", path, pid);
-
- r = hashmap_put(pids, PID_TO_PTR(pid), path);
- if (r < 0)
- return log_oom();
- path = NULL;
- }
- }
-
- /* Abort execution of this process after the timout. We simply
- * rely on SIGALRM as default action terminating the process,
- * and turn on alarm(). */
-
- if (timeout != USEC_INFINITY)
- alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
-
- while (!hashmap_isempty(pids)) {
- _cleanup_free_ char *path = NULL;
- pid_t pid;
-
- pid = PTR_TO_PID(hashmap_first_key(pids));
- assert(pid > 0);
-
- path = hashmap_remove(pids, PID_TO_PTR(pid));
- assert(path);
-
- wait_for_terminate_and_warn(path, pid, true);
- }
-
- return 0;
-}
-
-void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) {
- pid_t executor_pid;
- int r;
- char *name;
- char **dirs = (char**) directories;
-
- assert(!strv_isempty(dirs));
-
- name = basename(dirs[0]);
- assert(!isempty(name));
-
- /* Executes all binaries in the directories in parallel and waits
- * for them to finish. Optionally a timeout is applied. If a file
- * with the same name exists in more than one directory, the
- * earliest one wins. */
-
- executor_pid = fork();
- if (executor_pid < 0) {
- log_error_errno(errno, "Failed to fork: %m");
- return;
-
- } else if (executor_pid == 0) {
- r = do_execute(dirs, timeout, argv);
- _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
- }
-
- wait_for_terminate_and_warn(name, executor_pid, true);
-}
-
bool plymouth_running(void) {
return access("/run/plymouth/pid", F_OK) >= 0;
}
diff --git a/src/basic/util.h b/src/basic/util.h
index c3802a811c..c7da6c39bf 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -65,8 +65,6 @@ static inline const char* enable_disable(bool b) {
return b ? "enable" : "disable";
}
-void execute_directories(const char* const* directories, usec_t timeout, char *argv[]);
-
bool plymouth_running(void);
bool display_is_local(const char *display) _pure_;
diff --git a/src/basic/virt.c b/src/basic/virt.c
index 03ce71a728..ff4491d6d6 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -28,7 +28,6 @@
#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
-#include "fs-util.h"
#include "macro.h"
#include "process-util.h"
#include "stat-util.h"
@@ -316,25 +315,31 @@ static int detect_vm_zvm(void) {
/* Returns a short identifier for the various VM implementations */
int detect_vm(void) {
static thread_local int cached_found = _VIRTUALIZATION_INVALID;
- int r;
+ int r, dmi;
if (cached_found >= 0)
return cached_found;
/* We have to use the correct order here:
- * Some virtualization technologies do use KVM hypervisor but are
- * expected to be detected as something else. So detect DMI first.
*
- * An example is Virtualbox since version 5.0, which uses KVM backend.
- * Detection via DMI works corretly, the CPU ID would find KVM
- * only. */
- r = detect_vm_dmi();
+ * -> First try to detect Oracle Virtualbox, even if it uses KVM.
+ * -> Second try to detect from cpuid, this will report KVM for
+ * whatever software is used even if info in dmi is overwritten.
+ * -> Third try to detect from dmi. */
+
+ dmi = detect_vm_dmi();
+ if (dmi == VIRTUALIZATION_ORACLE) {
+ r = dmi;
+ goto finish;
+ }
+
+ r = detect_vm_cpuid();
if (r < 0)
return r;
if (r != VIRTUALIZATION_NONE)
goto finish;
- r = detect_vm_cpuid();
+ r = dmi;
if (r < 0)
return r;
if (r != VIRTUALIZATION_NONE)
@@ -564,30 +569,16 @@ int running_in_userns(void) {
}
int running_in_chroot(void) {
- _cleanup_free_ char *self_mnt = NULL, *pid1_mnt = NULL;
- int r;
-
- /* Try to detect whether we are running in a chroot() environment. Specifically, check whether we have a
- * different root directory than PID 1, even though we live in the same mount namespace as it. */
+ int ret;
if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0)
return 0;
- r = files_same("/proc/1/root", "/");
- if (r < 0)
- return r;
- if (r > 0)
- return 0;
-
- r = readlink_malloc("/proc/self/ns/mnt", &self_mnt);
- if (r < 0)
- return r;
-
- r = readlink_malloc("/proc/1/ns/mnt", &pid1_mnt);
- if (r < 0)
- return r;
+ ret = files_same("/proc/1/root", "/");
+ if (ret < 0)
+ return ret;
- return streq(self_mnt, pid1_mnt); /* Only if we live in the same namespace! */
+ return ret == 0;
}
static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index b747a95133..155bf278b2 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -38,20 +38,22 @@
#include "alloc-util.h"
#include "blkid-util.h"
+#include "copy.h"
#include "dirent-util.h"
#include "efivars.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "locale-util.h"
#include "parse-util.h"
#include "rm-rf.h"
+#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "umask-util.h"
#include "util.h"
#include "verbs.h"
#include "virt.h"
-#include "stat-util.h"
static char *arg_path = NULL;
static bool arg_touch_variables = true;
@@ -123,12 +125,8 @@ static int verify_esp(
errno = 0;
b = blkid_new_probe_from_filename(t);
- if (!b) {
- if (errno == 0)
- return log_oom();
-
- return log_error_errno(errno, "Failed to open file system \"%s\": %m", p);
- }
+ if (!b)
+ return log_error_errno(errno ?: ENOMEM, "Failed to open file system \"%s\": %m", p);
blkid_probe_enable_superblocks(b, 1);
blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
@@ -143,17 +141,13 @@ static int verify_esp(
} else if (r == 1) {
log_error("File system \"%s\" does not contain a label.", p);
return -ENODEV;
- } else if (r != 0) {
- r = errno ? -errno : -EIO;
- return log_error_errno(r, "Failed to probe file system \"%s\": %m", p);
- }
+ } else if (r != 0)
+ return log_error_errno(errno ?: EIO, "Failed to probe file system \"%s\": %m", p);
errno = 0;
r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
- if (r != 0) {
- r = errno ? -errno : -EIO;
- return log_error_errno(r, "Failed to probe file system type \"%s\": %m", p);
- }
+ if (r != 0)
+ return log_error_errno(errno ?: EIO, "Failed to probe file system type \"%s\": %m", p);
if (!streq(v, "vfat")) {
log_error("File system \"%s\" is not FAT.", p);
return -ENODEV;
@@ -161,10 +155,8 @@ static int verify_esp(
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
- if (r != 0) {
- r = errno ? -errno : -EIO;
- return log_error_errno(r, "Failed to probe partition scheme \"%s\": %m", p);
- }
+ if (r != 0)
+ return log_error_errno(errno ?: EIO, "Failed to probe partition scheme \"%s\": %m", p);
if (!streq(v, "gpt")) {
log_error("File system \"%s\" is not on a GPT partition table.", p);
return -ENODEV;
@@ -172,10 +164,8 @@ static int verify_esp(
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
- if (r != 0) {
- r = errno ? -errno : -EIO;
- return log_error_errno(r, "Failed to probe partition type UUID \"%s\": %m", p);
- }
+ if (r != 0)
+ return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID \"%s\": %m", p);
if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) {
log_error("File system \"%s\" has wrong type for an EFI System Partition (ESP).", p);
return -ENODEV;
@@ -183,10 +173,8 @@ static int verify_esp(
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
- if (r != 0) {
- r = errno ? -errno : -EIO;
- return log_error_errno(r, "Failed to probe partition entry UUID \"%s\": %m", p);
- }
+ if (r != 0)
+ return log_error_errno(errno ?: EIO, "Failed to probe partition entry UUID \"%s\": %m", p);
r = sd_id128_from_string(v, &uuid);
if (r < 0) {
log_error("Partition \"%s\" has invalid UUID \"%s\".", p, v);
@@ -195,30 +183,24 @@ static int verify_esp(
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
- if (r != 0) {
- r = errno ? -errno : -EIO;
- return log_error_errno(r, "Failed to probe partition number \"%s\": m", p);
- }
+ if (r != 0)
+ return log_error_errno(errno ?: EIO, "Failed to probe partition number \"%s\": m", p);
r = safe_atou32(v, &part);
if (r < 0)
return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
- if (r != 0) {
- r = errno ? -errno : -EIO;
- return log_error_errno(r, "Failed to probe partition offset \"%s\": %m", p);
- }
+ if (r != 0)
+ return log_error_errno(errno ?: EIO, "Failed to probe partition offset \"%s\": %m", p);
r = safe_atou64(v, &pstart);
if (r < 0)
return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
- if (r != 0) {
- r = errno ? -errno : -EIO;
- return log_error_errno(r, "Failed to probe partition size \"%s\": %m", p);
- }
+ if (r != 0)
+ return log_error_errno(errno ?: EIO, "Failed to probe partition size \"%s\": %m", p);
r = safe_atou64(v, &psize);
if (r < 0)
return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
@@ -476,16 +458,16 @@ static int compare_version(const char *a, const char *b) {
return strverscmp(a, b);
}
-static int version_check(int fd, const char *from, const char *to) {
+static int version_check(int fd_from, const char *from, int fd_to, const char *to) {
_cleanup_free_ char *a = NULL, *b = NULL;
- _cleanup_close_ int fd2 = -1;
int r;
- assert(fd >= 0);
+ assert(fd_from >= 0);
assert(from);
+ assert(fd_to >= 0);
assert(to);
- r = get_file_version(fd, &a);
+ r = get_file_version(fd_from, &a);
if (r < 0)
return r;
if (r == 0) {
@@ -493,15 +475,7 @@ static int version_check(int fd, const char *from, const char *to) {
return -EINVAL;
}
- fd2 = open(to, O_RDONLY|O_CLOEXEC);
- if (fd2 < 0) {
- if (errno == ENOENT)
- return 0;
-
- return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", to);
- }
-
- r = get_file_version(fd2, &b);
+ r = get_file_version(fd_to, &b);
if (r < 0)
return r;
if (r == 0 || compare_product(a, b) != 0) {
@@ -517,90 +491,59 @@ static int version_check(int fd, const char *from, const char *to) {
return 0;
}
-static int copy_file(const char *from, const char *to, bool force) {
- _cleanup_fclose_ FILE *f = NULL, *g = NULL;
- char *p;
+static int copy_file_with_version_check(const char *from, const char *to, bool force) {
+ _cleanup_close_ int fd_from = -1, fd_to = -1;
+ _cleanup_free_ char *t = NULL;
int r;
- struct timespec t[2];
- struct stat st;
- assert(from);
- assert(to);
-
- f = fopen(from, "re");
- if (!f)
+ fd_from = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd_from < 0)
return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", from);
if (!force) {
- /* If this is an update, then let's compare versions first */
- r = version_check(fileno(f), from, to);
- if (r < 0)
- return r;
- }
-
- p = strjoina(to, "~");
- g = fopen(p, "wxe");
- if (!g) {
- /* Directory doesn't exist yet? Then let's skip this... */
- if (!force && errno == ENOENT)
- return 0;
-
- return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", to);
- }
+ fd_to = open(to, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd_to < 0) {
+ if (errno != -ENOENT)
+ return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", to);
+ } else {
+ r = version_check(fd_from, from, fd_to, to);
+ if (r < 0)
+ return r;
- rewind(f);
- do {
- size_t k;
- uint8_t buf[32*1024];
+ if (lseek(fd_from, 0, SEEK_SET) == (off_t) -1)
+ return log_error_errno(errno, "Failed to seek in \%s\": %m", from);
- k = fread(buf, 1, sizeof(buf), f);
- if (ferror(f)) {
- r = log_error_errno(EIO, "Failed to read \"%s\": %m", from);
- goto error;
+ fd_to = safe_close(fd_to);
}
+ }
- if (k == 0)
- break;
-
- fwrite(buf, 1, k, g);
- if (ferror(g)) {
- r = log_error_errno(EIO, "Failed to write \"%s\": %m", to);
- goto error;
- }
- } while (!feof(f));
+ r = tempfn_random(to, NULL, &t);
+ if (r < 0)
+ return log_oom();
- r = fflush_and_check(g);
- if (r < 0) {
- log_error_errno(r, "Failed to write \"%s\": %m", to);
- goto error;
+ RUN_WITH_UMASK(0000) {
+ fd_to = open(t, O_WRONLY|O_CREAT|O_CLOEXEC|O_EXCL|O_NOFOLLOW, 0644);
+ if (fd_to < 0)
+ return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", t);
}
- r = fstat(fileno(f), &st);
+ r = copy_bytes(fd_from, fd_to, (uint64_t) -1, COPY_REFLINK);
if (r < 0) {
- r = log_error_errno(errno, "Failed to get file timestamps of \"%s\": %m", from);
- goto error;
+ unlink(t);
+ return log_error_errno(errno, "Failed to copy data from \"%s\" to \"%s\": %m", from, t);
}
- t[0] = st.st_atim;
- t[1] = st.st_mtim;
+ (void) copy_times(fd_from, fd_to);
- r = futimens(fileno(g), t);
+ r = renameat(AT_FDCWD, t, AT_FDCWD, to);
if (r < 0) {
- r = log_error_errno(errno, "Failed to set file timestamps on \"%s\": %m", p);
- goto error;
- }
-
- if (rename(p, to) < 0) {
- r = log_error_errno(errno, "Failed to rename \"%s\" to \"%s\": %m", p, to);
- goto error;
+ (void) unlink_noerrno(t);
+ return log_error_errno(errno, "Failed to rename \"%s\" to \"%s\": %m", t, to);
}
log_info("Copied \"%s\" to \"%s\".", from, to);
- return 0;
-error:
- (void) unlink(p);
- return r;
+ return 0;
}
static int mkdir_one(const char *prefix, const char *suffix) {
@@ -644,7 +587,7 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
p = strjoina(BOOTLIBDIR "/", name);
q = strjoina(esp_path, "/EFI/systemd/", name);
- r = copy_file(p, q, force);
+ r = copy_file_with_version_check(p, q, force);
if (startswith(name, "systemd-boot")) {
int k;
@@ -654,7 +597,7 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
v = strjoina(esp_path, "/EFI/BOOT/BOOT", name + strlen("systemd-boot"));
ascii_strupper(strrchr(v, '/') + 1);
- k = copy_file(p, v, force);
+ k = copy_file_with_version_check(p, v, force);
if (k < 0 && r == 0)
r = k;
}
@@ -950,20 +893,31 @@ static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) {
static int install_loader_config(const char *esp_path) {
- _cleanup_fclose_ FILE *f = NULL;
char machine_string[SD_ID128_STRING_MAX];
+ _cleanup_(unlink_and_freep) char *t = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
sd_id128_t machine_id;
const char *p;
- int r;
+ int r, fd;
r = sd_id128_get_machine(&machine_id);
if (r < 0)
return log_error_errno(r, "Failed to get machine did: %m");
p = strjoina(esp_path, "/loader/loader.conf");
- f = fopen(p, "wxe");
- if (!f)
- return log_error_errno(errno, "Failed to open loader.conf for writing: %m");
+
+ if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */
+ return 0;
+
+ fd = open_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t);
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to open \"%s\" for writing: %m", p);
+
+ f = fdopen(fd, "we");
+ if (!f) {
+ safe_close(fd);
+ return log_oom();
+ }
fprintf(f, "#timeout 3\n");
fprintf(f, "default %s-*\n", sd_id128_to_string(machine_id, machine_string));
@@ -972,7 +926,15 @@ static int install_loader_config(const char *esp_path) {
if (r < 0)
return log_error_errno(r, "Failed to write \"%s\": %m", p);
- return 0;
+ r = link_tmpfile(fd, t, p);
+ if (r == -EEXIST)
+ return 0; /* Silently skip creation if the file exists now (recheck) */
+ if (r < 0)
+ return log_error_errno(r, "Failed to move \"%s\" into place: %m", p);
+
+ t = mfree(t);
+
+ return 1;
}
static int help(int argc, char *argv[], void *userdata) {
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 7cc54a8cdd..681e783f2e 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -1787,7 +1787,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
config_title_generate(&config);
- /* select entry by configured pattern or EFI LoaderDefaultEntry= variable*/
+ /* select entry by configured pattern or EFI LoaderDefaultEntry= variable */
config_default_entry_select(&config);
/* if no configured entry to select from was found, enable the menu */
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index 7c1ffb1bca..b7d5d3cdae 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -87,7 +87,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
cmdline_len = szs[0];
/* if we are not in secure boot mode, accept a custom command line and replace the built-in one */
- if (!secure && loaded_image->LoadOptionsSize > 0) {
+ if (!secure && loaded_image->LoadOptionsSize > 0 && *(CHAR16 *)loaded_image->LoadOptions != 0) {
CHAR16 *options;
CHAR8 *line;
UINTN i;
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c
index 5574c14555..1b746a0e13 100644
--- a/src/cgls/cgls.c
+++ b/src/cgls/cgls.c
@@ -158,7 +158,7 @@ static int parse_argv(int argc, char *argv[]) {
static void show_cg_info(const char *controller, const char *path) {
- if (cg_all_unified() <= 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER))
+ if (cg_all_unified() == 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER))
printf("Controller %s; ", controller);
printf("Control group %s:\n", isempty(path) ? "/" : path);
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index 50ac6a58b0..a1c0f48c89 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -133,12 +133,16 @@ static int process(
Group **ret) {
Group *g;
- int r;
+ int r, all_unified;
assert(controller);
assert(path);
assert(a);
+ all_unified = cg_all_unified();
+ if (all_unified < 0)
+ return all_unified;
+
g = hashmap_get(a, path);
if (!g) {
g = hashmap_get(b, path);
@@ -214,7 +218,7 @@ static int process(
uint64_t new_usage;
nsec_t timestamp;
- if (cg_all_unified() > 0) {
+ if (all_unified) {
const char *keys[] = { "usage_usec", NULL };
_cleanup_free_ char *val = NULL;
@@ -274,10 +278,10 @@ static int process(
} else if (streq(controller, "memory")) {
_cleanup_free_ char *p = NULL, *v = NULL;
- if (cg_all_unified() <= 0)
- r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
- else
+ if (all_unified)
r = cg_get_path(controller, path, "memory.current", &p);
+ else
+ r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
if (r < 0)
return r;
@@ -294,15 +298,14 @@ static int process(
if (g->memory > 0)
g->memory_valid = true;
- } else if ((streq(controller, "io") && cg_all_unified() > 0) ||
- (streq(controller, "blkio") && cg_all_unified() <= 0)) {
+ } else if ((streq(controller, "io") && all_unified) ||
+ (streq(controller, "blkio") && !all_unified)) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
- bool unified = cg_all_unified() > 0;
uint64_t wr = 0, rd = 0;
nsec_t timestamp;
- r = cg_get_path(controller, path, unified ? "io.stat" : "blkio.io_service_bytes", &p);
+ r = cg_get_path(controller, path, all_unified ? "io.stat" : "blkio.io_service_bytes", &p);
if (r < 0)
return r;
@@ -325,7 +328,7 @@ static int process(
l += strcspn(l, WHITESPACE);
l += strspn(l, WHITESPACE);
- if (unified) {
+ if (all_unified) {
while (!isempty(l)) {
if (sscanf(l, "rbytes=%" SCNu64, &k))
rd += k;
diff --git a/src/core/automount.c b/src/core/automount.c
index 8ff1ca90f7..99e8047620 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -101,17 +101,17 @@ static void unmount_autofs(Automount *a) {
a->pipe_event_source = sd_event_source_unref(a->pipe_event_source);
a->pipe_fd = safe_close(a->pipe_fd);
- /* If we reload/reexecute things we keep the mount point
- * around */
- if (a->where &&
- (UNIT(a)->manager->exit_code != MANAGER_RELOAD &&
- UNIT(a)->manager->exit_code != MANAGER_REEXECUTE)) {
+ /* If we reload/reexecute things we keep the mount point around */
+ if (!IN_SET(UNIT(a)->manager->exit_code, MANAGER_RELOAD, MANAGER_REEXECUTE)) {
+
automount_send_ready(a, a->tokens, -EHOSTDOWN);
automount_send_ready(a, a->expire_tokens, -EHOSTDOWN);
- r = repeat_unmount(a->where, MNT_DETACH);
- if (r < 0)
- log_error_errno(r, "Failed to unmount: %m");
+ if (a->where) {
+ r = repeat_unmount(a->where, MNT_DETACH);
+ if (r < 0)
+ log_error_errno(r, "Failed to unmount: %m");
+ }
}
}
@@ -186,6 +186,22 @@ static int automount_verify(Automount *a) {
return 0;
}
+static int automount_set_where(Automount *a) {
+ int r;
+
+ assert(a);
+
+ if (a->where)
+ return 0;
+
+ r = unit_name_to_path(UNIT(a)->id, &a->where);
+ if (r < 0)
+ return r;
+
+ path_kill_slashes(a->where);
+ return 1;
+}
+
static int automount_load(Unit *u) {
Automount *a = AUTOMOUNT(u);
int r;
@@ -201,13 +217,9 @@ static int automount_load(Unit *u) {
if (u->load_state == UNIT_LOADED) {
Unit *x;
- if (!a->where) {
- r = unit_name_to_path(u->id, &a->where);
- if (r < 0)
- return r;
- }
-
- path_kill_slashes(a->where);
+ r = automount_set_where(a);
+ if (r < 0)
+ return r;
r = unit_load_related_unit(u, ".mount", &x);
if (r < 0)
@@ -256,26 +268,30 @@ static int automount_coldplug(Unit *u) {
assert(a);
assert(a->state == AUTOMOUNT_DEAD);
- if (a->deserialized_state != a->state) {
+ if (a->deserialized_state == a->state)
+ return 0;
+
+ if (IN_SET(a->deserialized_state, AUTOMOUNT_WAITING, AUTOMOUNT_RUNNING)) {
+
+ r = automount_set_where(a);
+ if (r < 0)
+ return r;
r = open_dev_autofs(u->manager);
if (r < 0)
return r;
- if (a->deserialized_state == AUTOMOUNT_WAITING ||
- a->deserialized_state == AUTOMOUNT_RUNNING) {
- assert(a->pipe_fd >= 0);
+ assert(a->pipe_fd >= 0);
- r = sd_event_add_io(u->manager->event, &a->pipe_event_source, a->pipe_fd, EPOLLIN, automount_dispatch_io, u);
- if (r < 0)
- return r;
+ r = sd_event_add_io(u->manager->event, &a->pipe_event_source, a->pipe_fd, EPOLLIN, automount_dispatch_io, u);
+ if (r < 0)
+ return r;
- (void) sd_event_source_set_description(a->pipe_event_source, "automount-io");
- if (a->deserialized_state == AUTOMOUNT_RUNNING) {
- r = automount_start_expire(a);
- if (r < 0)
- log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m");
- }
+ (void) sd_event_source_set_description(a->pipe_event_source, "automount-io");
+ if (a->deserialized_state == AUTOMOUNT_RUNNING) {
+ r = automount_start_expire(a);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m");
}
automount_set_state(a, a->deserialized_state);
@@ -733,6 +749,12 @@ static void automount_enter_runnning(Automount *a) {
assert(a);
+ /* If the user masked our unit in the meantime, fail */
+ if (UNIT(a)->load_state != UNIT_LOADED) {
+ log_unit_error(UNIT(a), "Suppressing automount event since unit is no longer loaded.");
+ goto fail;
+ }
+
/* We don't take mount requests anymore if we are supposed to
* shut down anyway */
if (unit_stop_pending(UNIT(a))) {
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 5789e2aa82..774b832a63 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -859,8 +859,7 @@ static void cgroup_context_apply(Unit *u, CGroupMask mask, ManagerState state) {
if ((mask & CGROUP_MASK_MEMORY) && !is_root) {
if (cg_all_unified() > 0) {
- uint64_t max;
- uint64_t swap_max = CGROUP_LIMIT_MAX;
+ uint64_t max, swap_max = CGROUP_LIMIT_MAX;
if (cgroup_context_has_unified_memory_config(c)) {
max = c->memory_max;
@@ -1260,9 +1259,9 @@ int unit_watch_cgroup(Unit *u) {
return 0;
/* Only applies to the unified hierarchy */
- r = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
+ r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
if (r < 0)
- return log_unit_error_errno(u, r, "Failed detect whether the unified hierarchy is used: %m");
+ return log_error_errno(r, "Failed to determine whether the name=systemd hierarchy is unified: %m");
if (r == 0)
return 0;
@@ -1673,6 +1672,8 @@ static int unit_watch_pids_in_path(Unit *u, const char *path) {
}
int unit_watch_all_pids(Unit *u) {
+ int r;
+
assert(u);
/* Adds all PIDs from our cgroup to the set of PIDs we
@@ -1683,7 +1684,10 @@ int unit_watch_all_pids(Unit *u) {
if (!u->cgroup_path)
return -ENOENT;
- if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) > 0) /* On unified we can use proper notifications */
+ r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
+ if (r < 0)
+ return r;
+ if (r > 0) /* On unified we can use proper notifications */
return 0;
return unit_watch_pids_in_path(u, u->cgroup_path);
@@ -1756,7 +1760,7 @@ static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents,
int manager_setup_cgroup(Manager *m) {
_cleanup_free_ char *path = NULL;
CGroupController c;
- int r, all_unified, systemd_unified;
+ int r, all_unified;
char *e;
assert(m);
@@ -1793,25 +1797,30 @@ int manager_setup_cgroup(Manager *m) {
if (r < 0)
return log_error_errno(r, "Cannot find cgroup mount point: %m");
- all_unified = cg_all_unified();
- systemd_unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
-
- if (all_unified < 0 || systemd_unified < 0)
- return log_error_errno(all_unified < 0 ? all_unified : systemd_unified,
- "Couldn't determine if we are running in the unified hierarchy: %m");
+ r = cg_unified_flush();
+ if (r < 0)
+ return log_error_errno(r, "Couldn't determine if we are running in the unified hierarchy: %m");
- if (all_unified > 0)
+ all_unified = cg_all_unified();
+ if (r < 0)
+ return log_error_errno(r, "Couldn't determine whether we are in all unified mode: %m");
+ if (r > 0)
log_debug("Unified cgroup hierarchy is located at %s.", path);
- else if (systemd_unified > 0)
- log_debug("Unified cgroup hierarchy is located at %s. Controllers are on legacy hierarchies.", path);
- else
- log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
+ else {
+ r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether systemd's own controller is in unified mode: %m");
+ if (r > 0)
+ log_debug("Unified cgroup hierarchy is located at %s. Controllers are on legacy hierarchies.", path);
+ else
+ log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER_LEGACY ". File system hierarchy is at %s.", path);
+ }
if (!m->test_run) {
const char *scope_path;
/* 3. Install agent */
- if (systemd_unified) {
+ if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0) {
/* In the unified hierarchy we can get
* cgroup empty notifications via inotify. */
@@ -1997,10 +2006,13 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) {
if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0)
return -ENODATA;
- if (cg_all_unified() <= 0)
- r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v);
- else
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0)
r = cg_get_attribute("memory", u->cgroup_path, "memory.current", &v);
+ else
+ r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
@@ -2042,7 +2054,10 @@ static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) {
if (!u->cgroup_path)
return -ENODATA;
- if (cg_all_unified() > 0) {
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0) {
const char *keys[] = { "usage_usec", NULL };
_cleanup_free_ char *val = NULL;
uint64_t us;
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 0136d38833..f87b52a266 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -848,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);
}
@@ -1342,7 +1338,7 @@ static int verify_run_space(const char *message, sd_bus_error *error) {
}
int verify_run_space_and_log(const char *message) {
- sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
r = verify_run_space(message, &error);
@@ -1916,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.
@@ -1986,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,
@@ -2002,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,
@@ -2061,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) {
@@ -2130,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(
@@ -2165,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) {
@@ -2200,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) {
@@ -2231,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) {
@@ -2274,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) {
@@ -2314,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-unit.c b/src/core/dbus-unit.c
index 60e889e1ef..f15bb2196c 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;
@@ -1127,7 +1131,7 @@ void bus_unit_send_change_signal(Unit *u) {
if (!u->id)
return;
- r = bus_foreach_bus(u->manager, NULL, u->sent_dbus_new_signal ? send_changed_signal : send_new_signal, u);
+ r = bus_foreach_bus(u->manager, u->bus_track, u->sent_dbus_new_signal ? send_changed_signal : send_new_signal, u);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to send unit change signal for %s: %m", u->id);
@@ -1173,7 +1177,7 @@ void bus_unit_send_removed_signal(Unit *u) {
if (!u->id)
return;
- r = bus_foreach_bus(u->manager, NULL, send_removed_signal, u);
+ r = bus_foreach_bus(u->manager, u->bus_track, send_removed_signal, u);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to send unit remove signal for %s: %m", u->id);
}
diff --git a/src/core/dbus.c b/src/core/dbus.c
index a3f701c064..065f2d81d6 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -298,7 +298,7 @@ static int bus_job_find(sd_bus *bus, const char *path, const char *interface, vo
}
static int find_unit(Manager *m, sd_bus *bus, const char *path, Unit **unit, sd_bus_error *error) {
- Unit *u;
+ Unit *u = NULL; /* just to appease gcc, initialization is not really necessary */
int r;
assert(m);
@@ -323,15 +323,15 @@ static int find_unit(Manager *m, sd_bus *bus, const char *path, Unit **unit, sd_
return r;
u = manager_get_unit_by_pid(m, pid);
+ if (!u)
+ return 0;
} else {
r = manager_load_unit_from_dbus_path(m, path, error, &u);
if (r < 0)
return 0;
+ assert(u);
}
- if (!u)
- return 0;
-
*unit = u;
return 1;
}
@@ -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 6041da46d6..d7798387c5 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -1144,11 +1144,13 @@ static int setup_pam(
/* Tell the parent that our setup is done. This is especially
* important regarding dropping privileges. Otherwise, unit
- * setup might race against our setresuid(2) call. */
- barrier_place(&barrier);
+ * setup might race against our setresuid(2) call.
+ *
+ * If the parent aborted, we'll detect this below, hence ignore
+ * return failure here. */
+ (void) barrier_place(&barrier);
- /* Check if our parent process might already have
- * died? */
+ /* Check if our parent process might already have died? */
if (getppid() == parent_pid) {
sigset_t ss;
@@ -1938,10 +1940,13 @@ 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;
+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, *root_image = NULL;
@@ -1953,6 +1958,8 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context,
.protect_kernel_modules = context->protect_kernel_modules,
.mount_apivfs = context->mount_apivfs,
};
+ bool apply_restrictions;
+ int r;
assert(context);
@@ -1986,16 +1993,18 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context,
if (!context->dynamic_user && root_dir)
ns_info.ignore_protect_paths = true;
+ apply_restrictions = (params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged;
+
r = setup_namespace(root_dir, root_image,
&ns_info, rw,
- context->read_only_paths,
- context->inaccessible_paths,
+ 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,
+ apply_restrictions ? context->protect_home : PROTECT_HOME_NO,
+ apply_restrictions ? context->protect_system : PROTECT_SYSTEM_NO,
context->mount_flags,
DISSECT_IMAGE_DISCARD_ON_LOOP);
@@ -2606,7 +2615,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;
@@ -2974,7 +2983,7 @@ int exec_spawn(Unit *unit,
log_open();
if (error_message)
log_struct_errno(LOG_ERR, r,
- LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED),
+ "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
LOG_UNIT_ID(unit),
LOG_UNIT_MESSAGE(unit, "%s: %m",
error_message),
@@ -2982,7 +2991,7 @@ int exec_spawn(Unit *unit,
NULL);
else
log_struct_errno(LOG_ERR, r,
- LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED),
+ "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
LOG_UNIT_ID(unit),
LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m",
exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD),
diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c
index 68be52856b..845e31e1c5 100644
--- a/src/core/hostname-setup.c
+++ b/src/core/hostname-setup.c
@@ -31,10 +31,10 @@
#include "util.h"
int hostname_setup(void) {
- int r;
_cleanup_free_ char *b = NULL;
- const char *hn;
bool enoent = false;
+ const char *hn;
+ int r;
r = read_hostname_config("/etc/hostname", &b);
if (r < 0) {
@@ -56,7 +56,7 @@ int hostname_setup(void) {
if (enoent)
log_info("No hostname configured.");
- hn = "localhost";
+ hn = FALLBACK_HOSTNAME;
}
r = sethostname_idempotent(hn);
diff --git a/src/core/job.c b/src/core/job.c
index 00f7d7998f..e2349830a8 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -627,6 +627,8 @@ int job_run_and_invalidate(Job *j) {
r = job_finish_and_invalidate(j, JOB_ASSERT, true, false);
else if (r == -EOPNOTSUPP)
r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true, false);
+ else if (r == -ENOLINK)
+ r = job_finish_and_invalidate(j, JOB_DEPENDENCY, true, false);
else if (r == -EAGAIN)
job_set_state(j, JOB_WAITING);
else if (r < 0)
@@ -744,9 +746,8 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
}
static void job_log_status_message(Unit *u, JobType t, JobResult result) {
- const char *format;
+ const char *format, *mid;
char buf[LINE_MAX];
- sd_id128_t mid;
static const int job_result_log_level[_JOB_RESULT_MAX] = {
[JOB_DONE] = LOG_INFO,
[JOB_CANCELED] = LOG_INFO,
@@ -782,16 +783,19 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
switch (t) {
case JOB_START:
- mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED;
+ if (result == JOB_DONE)
+ mid = "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTED_STR;
+ else
+ mid = "MESSAGE_ID=" SD_MESSAGE_UNIT_FAILED_STR;
break;
case JOB_RELOAD:
- mid = SD_MESSAGE_UNIT_RELOADED;
+ mid = "MESSAGE_ID=" SD_MESSAGE_UNIT_RELOADED_STR;
break;
case JOB_STOP:
case JOB_RESTART:
- mid = SD_MESSAGE_UNIT_STOPPED;
+ mid = "MESSAGE_ID=" SD_MESSAGE_UNIT_STOPPED_STR;
break;
default:
@@ -804,7 +808,7 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
}
log_struct(job_result_log_level[result],
- LOG_MESSAGE_ID(mid),
+ mid,
LOG_UNIT_ID(u),
LOG_MESSAGE("%s", buf),
"RESULT=%s", job_result_to_string(result),
diff --git a/src/core/killall.c b/src/core/killall.c
index 7a9df546ee..3fe9fa2ed0 100644
--- a/src/core/killall.c
+++ b/src/core/killall.c
@@ -213,7 +213,8 @@ static int killall(int sig, Set *pids, bool send_sighup) {
if (get_ctty_devnr(pid, NULL) >= 0)
- kill(pid, SIGHUP);
+ /* it's OK if the process is gone, just ignore the result */
+ (void) kill(pid, SIGHUP);
}
}
diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in
index 8d7ce1c238..a2a7edd1ee 100644
--- a/src/core/macros.systemd.in
+++ b/src/core/macros.systemd.in
@@ -31,6 +31,8 @@
%_binfmtdir @binfmtdir@
%_systemdgeneratordir @systemgeneratordir@
%_systemdusergeneratordir @usergeneratordir@
+%_systemd_system_env_generator_dir @systemenvgeneratordir@
+%_systemd_user_env_generator_dir @userenvgeneratordir@
%systemd_requires \
Requires(post): systemd \
diff --git a/src/core/main.c b/src/core/main.c
index ad2ce1330e..bcf9ea5f25 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -1414,10 +1414,17 @@ int main(int argc, char *argv[]) {
log_set_upgrade_syslog_to_journal(true);
- /* Disable the umask logic */
- if (getpid() == 1)
+ if (getpid() == 1) {
+ /* Disable the umask logic */
umask(0);
+ /* Always reopen /dev/console when running as PID 1 or one of its pre-execve() children. This is
+ * important so that we never end up logging to any foreign stderr, for example if we have to log in a
+ * child process right before execve()'ing the actual binary, at a point in time where socket
+ * activation stderr/stdout area already set up. */
+ log_set_always_reopen_console(true);
+ }
+
if (getpid() == 1 && detect_container() <= 0) {
/* Running outside of a container as PID 1 */
@@ -1830,8 +1837,10 @@ int main(int argc, char *argv[]) {
before_startup = now(CLOCK_MONOTONIC);
r = manager_startup(m, arg_serialization, fds);
- if (r < 0)
+ if (r < 0) {
log_error_errno(r, "Failed to fully start up daemon: %m");
+ goto finish;
+ }
/* This will close all file descriptors that were opened, but
* not claimed by any unit. */
diff --git a/src/core/manager.c b/src/core/manager.c
index e4da945777..cff38e28de 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -52,6 +52,7 @@
#include "dirent-util.h"
#include "env-util.h"
#include "escape.h"
+#include "exec-util.h"
#include "exit-status.h"
#include "fd-util.h"
#include "fileio.h"
@@ -102,6 +103,7 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32
static int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata);
static int manager_dispatch_run_queue(sd_event_source *source, void *userdata);
+static int manager_run_environment_generators(Manager *m);
static int manager_run_generators(Manager *m);
static void manager_watch_jobs_in_progress(Manager *m) {
@@ -530,9 +532,9 @@ static int manager_default_environment(Manager *m) {
if (MANAGER_IS_SYSTEM(m)) {
/* The system manager always starts with a clean
* environment for its children. It does not import
- * the kernel or the parents exported variables.
+ * the kernel's or the parents' exported variables.
*
- * The initial passed environ is untouched to keep
+ * The initial passed environment is untouched to keep
* /proc/self/environ valid; it is used for tagging
* the init process inside containers. */
m->environment = strv_new("PATH=" DEFAULT_PATH,
@@ -540,11 +542,10 @@ static int manager_default_environment(Manager *m) {
/* Import locale variables LC_*= from configuration */
locale_setup(&m->environment);
- } else {
+ } else
/* The user manager passes its own environment
* along to its children. */
m->environment = strv_copy(environ);
- }
if (!m->environment)
return -ENOMEM;
@@ -775,7 +776,10 @@ static int manager_setup_cgroups_agent(Manager *m) {
if (!MANAGER_IS_SYSTEM(m))
return 0;
- if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) > 0) /* We don't need this anymore on the unified hierarchy */
+ r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether unified cgroups hierarchy is used: %m");
+ if (r > 0) /* We don't need this anymore on the unified hierarchy */
return 0;
if (m->cgroups_agent_fd < 0) {
@@ -1262,6 +1266,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
if (r < 0)
return r;
+ r = manager_run_environment_generators(m);
+ if (r < 0)
+ return r;
+
/* Make sure the transient directory always exists, so that it remains in the search path */
if (!m->test_run) {
r = mkdir_p_label(m->lookup_paths.transient, 0755);
@@ -1398,7 +1406,7 @@ tr_abort:
}
int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, sd_bus_error *e, Job **ret) {
- Unit *unit;
+ Unit *unit = NULL; /* just to appease gcc, initialization is not really necessary */
int r;
assert(m);
@@ -1409,6 +1417,7 @@ int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode
r = manager_load_unit(m, name, NULL, NULL, &unit);
if (r < 0)
return r;
+ assert(unit);
return manager_add_job(m, type, unit, mode, e, ret);
}
@@ -1481,6 +1490,7 @@ int manager_load_unit_prepare(
assert(m);
assert(name || path);
+ assert(_ret);
/* This will prepare the unit for loading, but not actually
* load anything from disk. */
@@ -1528,8 +1538,7 @@ int manager_load_unit_prepare(
unit_add_to_dbus_queue(ret);
unit_add_to_gc_queue(ret);
- if (_ret)
- *_ret = ret;
+ *_ret = ret;
return 0;
}
@@ -1544,6 +1553,7 @@ int manager_load_unit(
int r;
assert(m);
+ assert(_ret);
/* This will load the service information files, but not actually
* start any services or anything. */
@@ -1554,8 +1564,7 @@ int manager_load_unit(
manager_dispatch_load_queue(m);
- if (_ret)
- *_ret = unit_follow_merge(*_ret);
+ *_ret = unit_follow_merge(*_ret);
return 0;
}
@@ -2170,7 +2179,7 @@ static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint
assert(m->time_change_fd == fd);
log_struct(LOG_DEBUG,
- LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE),
+ "MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR,
LOG_MESSAGE("Time has been changed"),
NULL);
@@ -2436,22 +2445,14 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
}
int manager_open_serialization(Manager *m, FILE **_f) {
- int fd = -1;
+ int fd;
FILE *f;
assert(_f);
- fd = memfd_create("systemd-serialization", MFD_CLOEXEC);
- if (fd < 0) {
- const char *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.");
+ fd = open_serialization_fd("systemd-state");
+ if (fd < 0)
+ return fd;
f = fdopen(fd, "w+");
if (!f) {
@@ -2460,7 +2461,6 @@ int manager_open_serialization(Manager *m, FILE **_f) {
}
*_f = f;
-
return 0;
}
@@ -2468,7 +2468,6 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
Iterator i;
Unit *u;
const char *t;
- char **e;
int r;
assert(m);
@@ -2498,17 +2497,8 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
dual_timestamp_serialize(f, "units-load-finish-timestamp", &m->units_load_finish_timestamp);
}
- if (!switching_root) {
- STRV_FOREACH(e, m->environment) {
- _cleanup_free_ char *ce;
-
- ce = cescape(*e);
- if (!ce)
- return -ENOMEM;
-
- fprintf(f, "env=%s\n", *e);
- }
- }
+ if (!switching_root)
+ (void) serialize_environment(f, m->environment);
if (m->notify_fd >= 0) {
int copy;
@@ -2671,21 +2661,9 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
else if ((val = startswith(l, "units-load-finish-timestamp=")))
dual_timestamp_deserialize(val, &m->units_load_finish_timestamp);
else if (startswith(l, "env=")) {
- _cleanup_free_ char *uce = NULL;
- char **e;
-
- r = cunescape(l + 4, UNESCAPE_RELAX, &uce);
+ r = deserialize_environment(&m->environment, l);
if (r < 0)
- goto finish;
-
- e = strv_env_set(m->environment, uce);
- if (!e) {
- r = -ENOMEM;
- goto finish;
- }
-
- strv_free(m->environment);
- m->environment = e;
+ return r;
} else if ((val = startswith(l, "notify-fd="))) {
int fd;
@@ -2826,6 +2804,10 @@ int manager_reload(Manager *m) {
if (q < 0 && r >= 0)
r = q;
+ q = manager_run_environment_generators(m);
+ if (q < 0 && r >= 0)
+ r = q;
+
/* Find new unit paths */
q = manager_run_generators(m);
if (q < 0 && r >= 0)
@@ -2929,7 +2911,7 @@ static void manager_notify_finished(Manager *m) {
initrd_usec = m->userspace_timestamp.monotonic - m->initrd_timestamp.monotonic;
log_struct(LOG_INFO,
- LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
+ "MESSAGE_ID=" SD_MESSAGE_STARTUP_FINISHED_STR,
"KERNEL_USEC="USEC_FMT, kernel_usec,
"INITRD_USEC="USEC_FMT, initrd_usec,
"USERSPACE_USEC="USEC_FMT, userspace_usec,
@@ -2944,7 +2926,7 @@ static void manager_notify_finished(Manager *m) {
initrd_usec = 0;
log_struct(LOG_INFO,
- LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
+ "MESSAGE_ID=" SD_MESSAGE_STARTUP_FINISHED_STR,
"KERNEL_USEC="USEC_FMT, kernel_usec,
"USERSPACE_USEC="USEC_FMT, userspace_usec,
LOG_MESSAGE("Startup finished in %s (kernel) + %s (userspace) = %s.",
@@ -2958,7 +2940,7 @@ static void manager_notify_finished(Manager *m) {
total_usec = userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic;
log_struct(LOG_INFO,
- LOG_MESSAGE_ID(SD_MESSAGE_USER_STARTUP_FINISHED),
+ "MESSAGE_ID=" SD_MESSAGE_USER_STARTUP_FINISHED_STR,
"USERSPACE_USEC="USEC_FMT, userspace_usec,
LOG_MESSAGE("Startup finished in %s.",
format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)),
@@ -3017,10 +2999,56 @@ void manager_check_finished(Manager *m) {
manager_invalidate_startup_units(m);
}
+static bool generator_path_any(const char* const* paths) {
+ char **path;
+ bool found = false;
+
+ /* Optimize by skipping the whole process by not creating output directories
+ * if no generators are found. */
+ STRV_FOREACH(path, (char**) paths)
+ if (access(*path, F_OK) == 0)
+ found = true;
+ else if (errno != ENOENT)
+ log_warning_errno(errno, "Failed to open generator directory %s: %m", *path);
+
+ return found;
+}
+
+static const char* system_env_generator_binary_paths[] = {
+ "/run/systemd/system-environment-generators",
+ "/etc/systemd/system-environment-generators",
+ "/usr/local/lib/systemd/system-environment-generators",
+ SYSTEM_ENV_GENERATOR_PATH,
+ NULL
+};
+
+static const char* user_env_generator_binary_paths[] = {
+ "/run/systemd/user-environment-generators",
+ "/etc/systemd/user-environment-generators",
+ "/usr/local/lib/systemd/user-environment-generators",
+ USER_ENV_GENERATOR_PATH,
+ NULL
+};
+
+static int manager_run_environment_generators(Manager *m) {
+ char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
+ const char **paths;
+ void* args[] = {&tmp, &tmp, &m->environment};
+
+ if (m->test_run)
+ return 0;
+
+ paths = MANAGER_IS_SYSTEM(m) ? system_env_generator_binary_paths : user_env_generator_binary_paths;
+
+ if (!generator_path_any(paths))
+ return 0;
+
+ return execute_directories(paths, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL);
+}
+
static int manager_run_generators(Manager *m) {
_cleanup_strv_free_ char **paths = NULL;
const char *argv[5];
- char **path;
int r;
assert(m);
@@ -3032,18 +3060,9 @@ static int manager_run_generators(Manager *m) {
if (!paths)
return log_oom();
- /* Optimize by skipping the whole process by not creating output directories
- * if no generators are found. */
- STRV_FOREACH(path, paths) {
- if (access(*path, F_OK) >= 0)
- goto found;
- if (errno != ENOENT)
- log_warning_errno(errno, "Failed to open generator directory %s: %m", *path);
- }
-
- return 0;
+ if (!generator_path_any((const char* const*) paths))
+ return 0;
- found:
r = lookup_paths_mkdir_generator(&m->lookup_paths);
if (r < 0)
goto finish;
@@ -3055,7 +3074,8 @@ static int manager_run_generators(Manager *m) {
argv[4] = NULL;
RUN_WITH_UMASK(0022)
- execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, (char**) argv);
+ execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC,
+ NULL, NULL, (char**) argv);
finish:
lookup_paths_trim_generator(&m->lookup_paths);
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index 9c2bf3a0ef..7295efbf31 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -96,15 +96,15 @@ static const MountPoint mount_table[] = {
{ "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
NULL, MNT_FATAL|MNT_IN_CONTAINER },
{ "cgroup", "/sys/fs/cgroup", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
- cg_is_unified_wanted, MNT_FATAL|MNT_IN_CONTAINER },
+ cg_is_unified_wanted, MNT_IN_CONTAINER },
{ "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
- { "cgroup", "/sys/fs/cgroup/systemd", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
- cg_is_unified_systemd_controller_wanted, MNT_IN_CONTAINER },
+ { "cgroup", "/sys/fs/cgroup/unified", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ cg_is_hybrid_wanted, MNT_IN_CONTAINER },
{ "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV,
- cg_is_legacy_systemd_controller_wanted, MNT_IN_CONTAINER },
+ cg_is_legacy_wanted, MNT_IN_CONTAINER },
{ "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV,
- cg_is_legacy_systemd_controller_wanted, MNT_FATAL|MNT_IN_CONTAINER },
+ cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
{ "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_NONE },
#ifdef ENABLE_EFI
diff --git a/src/core/namespace.c b/src/core/namespace.c
index 75dca5b791..4f29217bc4 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -799,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) { /* Superblock 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
diff --git a/src/core/scope.c b/src/core/scope.c
index 9540fb67d9..a1d5c1cfd5 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -475,7 +475,7 @@ static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* If the PID set is empty now, then let's finish this off
(On unified we use proper notifications) */
- if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) <= 0 && set_isempty(u->pids))
+ if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) == 0 && set_isempty(u->pids))
scope_notify_cgroup_empty_event(u);
}
diff --git a/src/core/service.c b/src/core/service.c
index 54074ff7bc..74054887b9 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -849,11 +849,8 @@ static int service_load_pid_file(Service *s, bool may_warn) {
return r;
r = unit_watch_pid(UNIT(s), pid);
- if (r < 0) {
- /* FIXME: we need to do something here */
- log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" for service: %m", pid);
- return r;
- }
+ if (r < 0) /* FIXME: we need to do something here */
+ return log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" for service: %m", pid);
return 0;
}
@@ -1374,8 +1371,7 @@ static int service_spawn(
return r;
r = unit_watch_pid(UNIT(s), pid);
- if (r < 0)
- /* FIXME: we need to do something here */
+ if (r < 0) /* FIXME: we need to do something here */
return r;
*_pid = pid;
@@ -2938,7 +2934,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* If the PID set is empty now, then let's finish this off
(On unified we use proper notifications) */
- if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) <= 0 && set_isempty(u->pids))
+ if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) == 0 && set_isempty(u->pids))
service_notify_cgroup_empty_event(u);
}
@@ -3097,6 +3093,8 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
log_unit_warning(u, "Failed to parse MAINPID= field in notification message: %s", e);
else if (pid == s->control_pid)
log_unit_warning(u, "A control process cannot also be the main process");
+ else if (pid == getpid() || pid == 1)
+ log_unit_warning(u, "Service manager can't be main process, ignoring sd_notify() MAINPID= field");
else {
service_set_main_pid(s, pid);
unit_watch_pid(UNIT(s), pid);
@@ -3286,7 +3284,7 @@ static void service_bus_name_owner_change(
if (r >= 0)
r = sd_bus_creds_get_pid(creds, &pid);
if (r >= 0) {
- log_unit_debug(u, "D-Bus name %s is now owned by process %u", name, (unsigned) pid);
+ log_unit_debug(u, "D-Bus name %s is now owned by process " PID_FMT, name, pid);
service_set_main_pid(s, pid);
unit_watch_pid(UNIT(s), pid);
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index a795d875bb..a2309b7726 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -32,6 +32,7 @@
#include "alloc-util.h"
#include "cgroup-util.h"
#include "def.h"
+#include "exec-util.h"
#include "fileio.h"
#include "killall.h"
#include "log.h"
@@ -321,7 +322,7 @@ int main(int argc, char *argv[]) {
arguments[0] = NULL;
arguments[1] = arg_verb;
arguments[2] = NULL;
- execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
+ execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
if (!in_container && !in_initrd() &&
access("/run/initramfs/shutdown", X_OK) == 0) {
diff --git a/src/core/socket.c b/src/core/socket.c
index a7b9ada65c..c4da227e09 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -1260,7 +1260,7 @@ static int usbffs_address_create(const char *path) {
if (fstat(fd, &st) < 0)
return -errno;
- /* Check whether this is a regular file (ffs endpoint)*/
+ /* Check whether this is a regular file (ffs endpoint) */
if (!S_ISREG(st.st_mode))
return -EEXIST;
@@ -1340,11 +1340,11 @@ static int usbffs_write_descs(int fd, Service *s) {
if (!s->usb_function_descriptors || !s->usb_function_strings)
return -EINVAL;
- r = copy_file_fd(s->usb_function_descriptors, fd, false);
+ r = copy_file_fd(s->usb_function_descriptors, fd, 0);
if (r < 0)
return r;
- return copy_file_fd(s->usb_function_strings, fd, false);
+ return copy_file_fd(s->usb_function_strings, fd, 0);
}
static int usbffs_select_ep(const struct dirent *d) {
diff --git a/src/core/unit.c b/src/core/unit.c
index 90d7eea956..f76b6c30a8 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -402,6 +402,7 @@ void unit_add_to_dbus_queue(Unit *u) {
/* Shortcut things if nobody cares */
if (sd_bus_track_count(u->manager->subscribed) <= 0 &&
+ sd_bus_track_count(u->bus_track) <= 0 &&
set_isempty(u->manager->private_buses)) {
u->sent_dbus_new_signal = true;
return;
@@ -1466,9 +1467,8 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) {
}
static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
- const char *format;
+ const char *format, *mid;
char buf[LINE_MAX];
- sd_id128_t mid;
assert(u);
@@ -1486,9 +1486,9 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
snprintf(buf, sizeof buf, format, unit_description(u));
REENABLE_WARNING;
- mid = t == JOB_START ? SD_MESSAGE_UNIT_STARTING :
- t == JOB_STOP ? SD_MESSAGE_UNIT_STOPPING :
- SD_MESSAGE_UNIT_RELOADING;
+ mid = t == JOB_START ? "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTING_STR :
+ t == JOB_STOP ? "MESSAGE_ID=" SD_MESSAGE_UNIT_STOPPING_STR :
+ "MESSAGE_ID=" SD_MESSAGE_UNIT_RELOADING_STR;
/* Note that we deliberately use LOG_MESSAGE() instead of
* LOG_UNIT_MESSAGE() here, since this is supposed to mimic
@@ -1497,7 +1497,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
* possible, which means we should avoid the low-level unit
* name. */
log_struct(LOG_INFO,
- LOG_MESSAGE_ID(mid),
+ mid,
LOG_UNIT_ID(u),
LOG_MESSAGE("%s", buf),
NULL);
@@ -1527,6 +1527,7 @@ int unit_start_limit_test(Unit *u) {
}
bool unit_shall_confirm_spawn(Unit *u) {
+ assert(u);
if (manager_is_confirm_spawn_disabled(u->manager))
return false;
@@ -1537,6 +1538,31 @@ bool unit_shall_confirm_spawn(Unit *u) {
return !unit_get_exec_context(u)->same_pgrp;
}
+static bool unit_verify_deps(Unit *u) {
+ Unit *other;
+ Iterator j;
+
+ assert(u);
+
+ /* Checks whether all BindsTo= dependencies of this unit are fulfilled — if they are also combined with
+ * After=. We do not check Requires= or Requisite= here as they only should have an effect on the job
+ * processing, but do not have any effect afterwards. We don't check BindsTo= dependencies that are not used in
+ * conjunction with After= as for them any such check would make things entirely racy. */
+
+ SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], j) {
+
+ if (!set_contains(u->dependencies[UNIT_AFTER], other))
+ continue;
+
+ if (!UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(other))) {
+ log_unit_notice(u, "Bound to unit %s, but unit isn't active.", other->id);
+ return false;
+ }
+ }
+
+ return true;
+}
+
/* Errors:
* -EBADR: This unit type does not support starting.
* -EALREADY: Unit is already started.
@@ -1545,6 +1571,7 @@ bool unit_shall_confirm_spawn(Unit *u) {
* -EPROTO: Assert failed
* -EINVAL: Unit not loaded
* -EOPNOTSUPP: Unit type not supported
+ * -ENOLINK: The necessary dependencies are not fulfilled.
*/
int unit_start(Unit *u) {
UnitActiveState state;
@@ -1590,6 +1617,12 @@ int unit_start(Unit *u) {
if (!unit_supported(u))
return -EOPNOTSUPP;
+ /* Let's make sure that the deps really are in order before we start this. Normally the job engine should have
+ * taken care of this already, but let's check this here again. After all, our dependencies might not be in
+ * effect anymore, due to a reload or due to a failed condition. */
+ if (!unit_verify_deps(u))
+ return -ENOLINK;
+
/* Forward to the main object, if we aren't it. */
following = unit_following(u);
if (following) {
@@ -2650,7 +2683,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);
}
@@ -3121,6 +3154,11 @@ static bool fragment_mtime_newer(const char *path, usec_t mtime, bool path_maske
if (!path)
return false;
+ /* If the source is some virtual kernel file system, then we assume we watch it anyway, and hence pretend we
+ * are never out-of-date. */
+ if (PATH_STARTSWITH_SET(path, "/proc", "/sys"))
+ return false;
+
if (stat(path, &st) < 0)
/* What, cannot access this anymore? */
return true;
@@ -3863,10 +3901,10 @@ int unit_kill_context(
* should not exist in non-delegated units. On
* the unified hierarchy that's different,
* there we get proper events. Hence rely on
- * them.*/
+ * them. */
- if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) > 0 ||
- (detect_container() == 0 && !unit_cgroup_delegate(u)))
+ if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0 ||
+ (detect_container() == 0 && !unit_cgroup_delegate(u)))
wait_for_exit = true;
if (send_sighup) {
@@ -4036,7 +4074,7 @@ void unit_warn_if_dir_nonempty(Unit *u, const char* where) {
}
log_struct(LOG_NOTICE,
- LOG_MESSAGE_ID(SD_MESSAGE_OVERMOUNTING),
+ "MESSAGE_ID=" SD_MESSAGE_OVERMOUNTING_STR,
LOG_UNIT_ID(u),
LOG_UNIT_MESSAGE(u, "Directory %s to mount over is not empty, mounting anyway.", where),
"WHERE=%s", where,
@@ -4058,7 +4096,7 @@ int unit_fail_if_symlink(Unit *u, const char* where) {
return 0;
log_struct(LOG_ERR,
- LOG_MESSAGE_ID(SD_MESSAGE_OVERMOUNTING),
+ "MESSAGE_ID=" SD_MESSAGE_OVERMOUNTING_STR,
LOG_UNIT_ID(u),
LOG_UNIT_MESSAGE(u, "Mount on symlink %s not allowed.", where),
"WHERE=%s", where,
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index 1f6fb5de1e..4c4f36aea0 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -47,13 +47,14 @@
#include "fileio.h"
#include "fs-util.h"
#include "io-util.h"
-#include "journald-native.h"
+#include "journal-importer.h"
#include "log.h"
#include "macro.h"
#include "missing.h"
#include "mkdir.h"
#include "parse-util.h"
#include "process-util.h"
+#include "signal-util.h"
#include "socket-util.h"
#include "special.h"
#include "stacktrace.h"
@@ -77,8 +78,22 @@
assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX);
enum {
- /* We use this as array indexes for a couple of special fields we use for naming coredumping files, and
- * attaching xattrs */
+ /* We use this as array indexes for a couple of special fields we use for
+ * naming coredump files, and attaching xattrs, and for indexing argv[].
+
+ * Our pattern for man:systectl(1) kernel.core_pattern is such that the
+ * kernel passes fields until CONTEXT_RLIMIT as arguments in argv[]. After
+ * that it gets complicated: the kernel passes "comm" as one or more fields
+ * starting at index CONTEXT_COMM (in other words, full "comm" is under index
+ * CONTEXT_COMM when it does not contain spaces, which is the common
+ * case). This mapping is not reversible, so we prefer to retrieve "comm"
+ * from /proc. We only fall back to argv[CONTEXT_COMM...] when that fails.
+ *
+ * In the internal context[] array, fields before CONTEXT_COMM are the
+ * strings from argv[], so they should not be freed. The strings at indices
+ * CONTEXT_COMM and higher are allocated by us and should be freed at the
+ * end.
+ */
CONTEXT_PID,
CONTEXT_UID,
CONTEXT_GID,
@@ -87,6 +102,7 @@ enum {
CONTEXT_RLIMIT,
CONTEXT_COMM,
CONTEXT_EXE,
+ CONTEXT_UNIT,
_CONTEXT_MAX
};
@@ -186,6 +202,7 @@ static int fix_xattr(int fd, const char *context[_CONTEXT_MAX]) {
[CONTEXT_GID] = "user.coredump.gid",
[CONTEXT_SIGNAL] = "user.coredump.signal",
[CONTEXT_TIMESTAMP] = "user.coredump.timestamp",
+ [CONTEXT_RLIMIT] = "user.coredump.rlimit",
[CONTEXT_COMM] = "user.coredump.comm",
[CONTEXT_EXE] = "user.coredump.exe",
};
@@ -308,7 +325,8 @@ static int save_external_coredump(
char **ret_filename,
int *ret_node_fd,
int *ret_data_fd,
- uint64_t *ret_size) {
+ uint64_t *ret_size,
+ bool *ret_truncated) {
_cleanup_free_ char *fn = NULL, *tmp = NULL;
_cleanup_close_ int fd = -1;
@@ -352,15 +370,17 @@ static int save_external_coredump(
if (fd < 0)
return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn);
- r = copy_bytes(input_fd, fd, max_size, false);
+ r = copy_bytes(input_fd, fd, max_size, 0);
if (r < 0) {
log_error_errno(r, "Cannot store coredump of %s (%s): %m", context[CONTEXT_PID], context[CONTEXT_COMM]);
goto fail;
- } else if (r == 1)
+ }
+ *ret_truncated = r == 1;
+ if (*ret_truncated)
log_struct(LOG_INFO,
LOG_MESSAGE("Core file was truncated to %zu bytes.", max_size),
"SIZE_LIMIT=%zu", max_size,
- LOG_MESSAGE_ID(SD_MESSAGE_TRUNCATED_CORE),
+ "MESSAGE_ID=" SD_MESSAGE_TRUNCATED_CORE_STR,
NULL);
if (fstat(fd, &st) < 0) {
@@ -675,6 +695,21 @@ static int change_uid_gid(const char *context[]) {
return drop_privileges(uid, gid, 0);
}
+static bool is_journald_crash(const char *context[_CONTEXT_MAX]) {
+ assert(context);
+
+ return streq_ptr(context[CONTEXT_UNIT], SPECIAL_JOURNALD_SERVICE);
+}
+
+static bool is_pid1_crash(const char *context[_CONTEXT_MAX]) {
+ assert(context);
+
+ return streq_ptr(context[CONTEXT_UNIT], SPECIAL_INIT_SCOPE) ||
+ streq_ptr(context[CONTEXT_PID], "1");
+}
+
+#define SUBMIT_COREDUMP_FIELDS 4
+
static int submit_coredump(
const char *context[_CONTEXT_MAX],
struct iovec *iovec,
@@ -685,18 +720,22 @@ static int submit_coredump(
_cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
_cleanup_free_ char *core_message = NULL, *filename = NULL, *coredump_data = NULL;
uint64_t coredump_size = UINT64_MAX;
+ bool truncated = false, journald_crash;
int r;
assert(context);
assert(iovec);
- assert(n_iovec_allocated >= n_iovec + 3);
+ assert(n_iovec_allocated >= n_iovec + SUBMIT_COREDUMP_FIELDS);
assert(input_fd >= 0);
+ journald_crash = is_journald_crash(context);
+
/* Vacuum before we write anything again */
(void) coredump_vacuum(-1, arg_keep_free, arg_max_use);
/* Always stream the coredump to disk, if that's possible */
- r = save_external_coredump(context, input_fd, &filename, &coredump_node_fd, &coredump_fd, &coredump_size);
+ r = save_external_coredump(context, input_fd,
+ &filename, &coredump_node_fd, &coredump_fd, &coredump_size, &truncated);
if (r < 0)
/* Skip whole core dumping part */
goto log;
@@ -735,8 +774,10 @@ static int submit_coredump(
if (r >= 0)
core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID],
" (", context[CONTEXT_COMM], ") of user ",
- context[CONTEXT_UID], " dumped core.\n\n",
- stacktrace);
+ context[CONTEXT_UID], " dumped core.",
+ journald_crash ? "\nCoredump diverted to " : "",
+ journald_crash ? filename : "",
+ "\n\n", stacktrace);
else if (r == -EINVAL)
log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
else
@@ -748,12 +789,27 @@ static int submit_coredump(
if (!core_message)
#endif
log:
- core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (",
- context[CONTEXT_COMM], ") of user ",
- context[CONTEXT_UID], " dumped core.");
+ core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID],
+ " (", context[CONTEXT_COMM], ") of user ",
+ context[CONTEXT_UID], " dumped core.",
+ journald_crash ? "\nCoredump diverted to " : NULL,
+ journald_crash ? filename : NULL);
+ if (!core_message)
+ return log_oom();
+
+ if (journald_crash) {
+ /* We cannot log to the journal, so just print the MESSAGE.
+ * The target was set previously to something safe. */
+ log_struct(LOG_ERR, core_message, NULL);
+ return 0;
+ }
+
if (core_message)
IOVEC_SET_STRING(iovec[n_iovec++], core_message);
+ if (truncated)
+ IOVEC_SET_STRING(iovec[n_iovec++], "COREDUMP_TRUNCATED=1");
+
/* Optionally store the entire coredump in the journal */
if (arg_storage == COREDUMP_STORAGE_JOURNAL) {
if (coredump_size <= arg_journal_size_max) {
@@ -782,17 +838,17 @@ log:
return 0;
}
-static void map_context_fields(const struct iovec *iovec, const char *context[]) {
+static void map_context_fields(const struct iovec *iovec, const char* context[]) {
- static const char * const context_field_names[_CONTEXT_MAX] = {
+ static const char * const context_field_names[] = {
[CONTEXT_PID] = "COREDUMP_PID=",
[CONTEXT_UID] = "COREDUMP_UID=",
[CONTEXT_GID] = "COREDUMP_GID=",
[CONTEXT_SIGNAL] = "COREDUMP_SIGNAL=",
[CONTEXT_TIMESTAMP] = "COREDUMP_TIMESTAMP=",
+ [CONTEXT_RLIMIT] = "COREDUMP_RLIMIT=",
[CONTEXT_COMM] = "COREDUMP_COMM=",
[CONTEXT_EXE] = "COREDUMP_EXE=",
- [CONTEXT_RLIMIT] = "COREDUMP_RLIMIT=",
};
unsigned i;
@@ -800,9 +856,12 @@ static void map_context_fields(const struct iovec *iovec, const char *context[])
assert(iovec);
assert(context);
- for (i = 0; i < _CONTEXT_MAX; i++) {
+ for (i = 0; i < ELEMENTSOF(context_field_names); i++) {
size_t l;
+ if (!context_field_names[i])
+ continue;
+
l = strlen(context_field_names[i]);
if (iovec->iov_len < l)
continue;
@@ -820,7 +879,7 @@ static void map_context_fields(const struct iovec *iovec, const char *context[])
static int process_socket(int fd) {
_cleanup_close_ int coredump_fd = -1;
struct iovec *iovec = NULL;
- size_t n_iovec = 0, n_iovec_allocated = 0, i;
+ size_t n_iovec = 0, n_allocated = 0, i, k;
const char *context[_CONTEXT_MAX] = {};
int r;
@@ -830,6 +889,8 @@ static int process_socket(int fd) {
log_parse_environment();
log_open();
+ log_debug("Processing coredump received on stdin...");
+
for (;;) {
union {
struct cmsghdr cmsghdr;
@@ -843,7 +904,7 @@ static int process_socket(int fd) {
ssize_t n;
ssize_t l;
- if (!GREEDY_REALLOC(iovec, n_iovec_allocated, n_iovec + 3)) {
+ if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + SUBMIT_COREDUMP_FIELDS)) {
r = log_oom();
goto finish;
}
@@ -907,7 +968,7 @@ static int process_socket(int fd) {
n_iovec++;
}
- if (!GREEDY_REALLOC(iovec, n_iovec_allocated, n_iovec + 3)) {
+ if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + SUBMIT_COREDUMP_FIELDS)) {
r = log_oom();
goto finish;
}
@@ -922,7 +983,14 @@ static int process_socket(int fd) {
assert(context[CONTEXT_COMM]);
assert(coredump_fd >= 0);
- r = submit_coredump(context, iovec, n_iovec_allocated, n_iovec, coredump_fd);
+ /* Small quirk: the journal fields contain the timestamp padded with six zeroes, so that the kernel-supplied 1s
+ * granularity timestamps becomes 1µs granularity, i.e. the granularity systemd usually operates in. Since we
+ * are reconstructing the original kernel context, we chop this off again, here. */
+ k = strlen(context[CONTEXT_TIMESTAMP]);
+ if (k > 6)
+ context[CONTEXT_TIMESTAMP] = strndupa(context[CONTEXT_TIMESTAMP], k - 6);
+
+ r = submit_coredump(context, iovec, n_allocated, n_iovec, coredump_fd);
finish:
for (i = 0; i < n_iovec; i++)
@@ -998,284 +1066,317 @@ static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd)
return 0;
}
-static int process_special_crash(const char *context[], int input_fd) {
- _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
- _cleanup_free_ char *filename = NULL;
- uint64_t coredump_size;
- int r;
-
- assert(context);
- assert(input_fd >= 0);
-
- /* If we are pid1 or journald, we cut things short, don't write to the journal, but still create a coredump. */
-
- if (arg_storage != COREDUMP_STORAGE_NONE)
- arg_storage = COREDUMP_STORAGE_EXTERNAL;
+static char* set_iovec_field(struct iovec iovec[27], size_t *n_iovec, const char *field, const char *value) {
+ char *x;
- r = save_external_coredump(context, input_fd, &filename, &coredump_node_fd, &coredump_fd, &coredump_size);
- if (r < 0)
- return r;
-
- r = maybe_remove_external_coredump(filename, coredump_size);
- if (r < 0)
- return r;
+ x = strappend(field, value);
+ if (x)
+ IOVEC_SET_STRING(iovec[(*n_iovec)++], x);
+ return x;
+}
- log_notice("Detected coredump of the journal daemon or PID 1, diverted to %s.", filename);
+static char* set_iovec_field_free(struct iovec iovec[27], size_t *n_iovec, const char *field, char *value) {
+ char *x;
- return 0;
+ x = set_iovec_field(iovec, n_iovec, field, value);
+ free(value);
+ return x;
}
-static int process_kernel(int argc, char* argv[]) {
+static int gather_pid_metadata(
+ char* context[_CONTEXT_MAX],
+ char **comm_fallback,
+ struct iovec *iovec, size_t *n_iovec) {
+
+ /* We need 26 empty slots in iovec!
+ *
+ * Note that if we fail on oom later on, we do not roll-back changes to the iovec structure. (It remains valid,
+ * with the first n_iovec fields initialized.) */
- /* The small core field we allocate on the stack, to keep things simple */
- char
- *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
- *core_session = NULL, *core_exe = NULL, *core_comm = NULL, *core_cmdline = NULL,
- *core_cgroup = NULL, *core_cwd = NULL, *core_root = NULL, *core_unit = NULL,
- *core_user_unit = NULL, *core_slice = NULL, *core_timestamp = NULL, *core_rlimit = NULL;
-
- /* The larger ones we allocate on the heap */
- _cleanup_free_ char
- *core_owner_uid = NULL, *core_open_fds = NULL, *core_proc_status = NULL,
- *core_proc_maps = NULL, *core_proc_limits = NULL, *core_proc_cgroup = NULL, *core_environ = NULL,
- *core_proc_mountinfo = NULL, *core_container_cmdline = NULL;
-
- _cleanup_free_ char *exe = NULL, *comm = NULL;
- const char *context[_CONTEXT_MAX];
- bool proc_self_root_is_slash;
- struct iovec iovec[27];
- size_t n_iovec = 0;
uid_t owner_uid;
- const char *p;
pid_t pid;
char *t;
- int r;
-
- if (argc < CONTEXT_COMM + 1) {
- log_error("Not enough arguments passed from kernel (%i, expected %i).", argc - 1, CONTEXT_COMM + 1 - 1);
- return -EINVAL;
- }
+ const char *p;
+ int r, signo;
- r = parse_pid(argv[CONTEXT_PID + 1], &pid);
+ r = parse_pid(context[CONTEXT_PID], &pid);
if (r < 0)
- return log_error_errno(r, "Failed to parse PID.");
+ return log_error_errno(r, "Failed to parse PID \"%s\": %m", context[CONTEXT_PID]);
- r = get_process_comm(pid, &comm);
+ r = get_process_comm(pid, &context[CONTEXT_COMM]);
if (r < 0) {
log_warning_errno(r, "Failed to get COMM, falling back to the command line: %m");
- comm = strv_join(argv + CONTEXT_COMM + 1, " ");
- if (!comm)
+ context[CONTEXT_COMM] = strv_join(comm_fallback, " ");
+ if (!context[CONTEXT_COMM])
return log_oom();
}
- r = get_process_exe(pid, &exe);
+ r = get_process_exe(pid, &context[CONTEXT_EXE]);
if (r < 0)
log_warning_errno(r, "Failed to get EXE, ignoring: %m");
- context[CONTEXT_PID] = argv[CONTEXT_PID + 1];
- context[CONTEXT_UID] = argv[CONTEXT_UID + 1];
- context[CONTEXT_GID] = argv[CONTEXT_GID + 1];
- context[CONTEXT_SIGNAL] = argv[CONTEXT_SIGNAL + 1];
- context[CONTEXT_TIMESTAMP] = argv[CONTEXT_TIMESTAMP + 1];
- context[CONTEXT_RLIMIT] = argv[CONTEXT_RLIMIT + 1];
- context[CONTEXT_COMM] = comm;
- context[CONTEXT_EXE] = exe;
-
- if (cg_pid_get_unit(pid, &t) >= 0) {
+ if (cg_pid_get_unit(pid, &context[CONTEXT_UNIT]) >= 0) {
+ if (!is_journald_crash((const char**) context)) {
+ /* OK, now we know it's not the journal, hence we can make use of it now. */
+ log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
+ log_open();
+ }
/* If this is PID 1 disable coredump collection, we'll unlikely be able to process it later on. */
- if (streq(t, SPECIAL_INIT_SCOPE)) {
+ if (is_pid1_crash((const char**) context)) {
log_notice("Due to PID 1 having crashed coredump collection will now be turned off.");
(void) write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
}
- /* Let's avoid dead-locks when processing journald and init crashes, as socket activation and logging
- * are unlikely to work then. */
- if (STR_IN_SET(t, SPECIAL_JOURNALD_SERVICE, SPECIAL_INIT_SCOPE)) {
- free(t);
- return process_special_crash(context, STDIN_FILENO);
- }
-
- core_unit = strjoina("COREDUMP_UNIT=", t);
- free(t);
-
- IOVEC_SET_STRING(iovec[n_iovec++], core_unit);
+ set_iovec_field(iovec, n_iovec, "COREDUMP_UNIT=", context[CONTEXT_UNIT]);
}
- /* OK, now we know it's not the journal, hence we can make use of it now. */
- log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
- log_open();
+ if (cg_pid_get_user_unit(pid, &t) >= 0)
+ set_iovec_field_free(iovec, n_iovec, "COREDUMP_USER_UNIT=", t);
- if (cg_pid_get_user_unit(pid, &t) >= 0) {
- core_user_unit = strjoina("COREDUMP_USER_UNIT=", t);
- free(t);
+ /* The next few are mandatory */
+ if (!set_iovec_field(iovec, n_iovec, "COREDUMP_PID=", context[CONTEXT_PID]))
+ return log_oom();
- IOVEC_SET_STRING(iovec[n_iovec++], core_user_unit);
- }
+ if (!set_iovec_field(iovec, n_iovec, "COREDUMP_UID=", context[CONTEXT_UID]))
+ return log_oom();
- core_pid = strjoina("COREDUMP_PID=", context[CONTEXT_PID]);
- IOVEC_SET_STRING(iovec[n_iovec++], core_pid);
+ if (!set_iovec_field(iovec, n_iovec, "COREDUMP_GID=", context[CONTEXT_GID]))
+ return log_oom();
- core_uid = strjoina("COREDUMP_UID=", context[CONTEXT_UID]);
- IOVEC_SET_STRING(iovec[n_iovec++], core_uid);
+ if (!set_iovec_field(iovec, n_iovec, "COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL]))
+ return log_oom();
- core_gid = strjoina("COREDUMP_GID=", context[CONTEXT_GID]);
- IOVEC_SET_STRING(iovec[n_iovec++], core_gid);
+ if (!set_iovec_field(iovec, n_iovec, "COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT]))
+ return log_oom();
- core_signal = strjoina("COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL]);
- IOVEC_SET_STRING(iovec[n_iovec++], core_signal);
+ if (!set_iovec_field(iovec, n_iovec, "COREDUMP_COMM=", context[CONTEXT_COMM]))
+ return log_oom();
- core_rlimit = strjoina("COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT]);
- IOVEC_SET_STRING(iovec[n_iovec++], core_rlimit);
+ if (context[CONTEXT_EXE] &&
+ !set_iovec_field(iovec, n_iovec, "COREDUMP_EXE=", context[CONTEXT_EXE]))
+ return log_oom();
- if (sd_pid_get_session(pid, &t) >= 0) {
- core_session = strjoina("COREDUMP_SESSION=", t);
- free(t);
-
- IOVEC_SET_STRING(iovec[n_iovec++], core_session);
- }
+ if (sd_pid_get_session(pid, &t) >= 0)
+ set_iovec_field_free(iovec, n_iovec, "COREDUMP_SESSION=", t);
if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
- r = asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
+ r = asprintf(&t, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
if (r > 0)
- IOVEC_SET_STRING(iovec[n_iovec++], core_owner_uid);
+ IOVEC_SET_STRING(iovec[(*n_iovec)++], t);
}
- if (sd_pid_get_slice(pid, &t) >= 0) {
- core_slice = strjoina("COREDUMP_SLICE=", t);
- free(t);
+ if (sd_pid_get_slice(pid, &t) >= 0)
+ set_iovec_field_free(iovec, n_iovec, "COREDUMP_SLICE=", t);
- IOVEC_SET_STRING(iovec[n_iovec++], core_slice);
- }
+ if (get_process_cmdline(pid, 0, false, &t) >= 0)
+ set_iovec_field_free(iovec, n_iovec, "COREDUMP_CMDLINE=", t);
- if (comm) {
- core_comm = strjoina("COREDUMP_COMM=", comm);
- IOVEC_SET_STRING(iovec[n_iovec++], core_comm);
- }
+ if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0)
+ set_iovec_field_free(iovec, n_iovec, "COREDUMP_CGROUP=", t);
- if (exe) {
- core_exe = strjoina("COREDUMP_EXE=", exe);
- IOVEC_SET_STRING(iovec[n_iovec++], core_exe);
- }
+ if (compose_open_fds(pid, &t) >= 0)
+ set_iovec_field_free(iovec, n_iovec, "COREDUMP_OPEN_FDS=", t);
- if (get_process_cmdline(pid, 0, false, &t) >= 0) {
- core_cmdline = strjoina("COREDUMP_CMDLINE=", t);
- free(t);
+ p = procfs_file_alloca(pid, "status");
+ if (read_full_file(p, &t, NULL) >= 0)
+ set_iovec_field_free(iovec, n_iovec, "COREDUMP_PROC_STATUS=", t);
- IOVEC_SET_STRING(iovec[n_iovec++], core_cmdline);
- }
+ p = procfs_file_alloca(pid, "maps");
+ if (read_full_file(p, &t, NULL) >= 0)
+ set_iovec_field_free(iovec, n_iovec, "COREDUMP_PROC_MAPS=", t);
- if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
- core_cgroup = strjoina("COREDUMP_CGROUP=", t);
- free(t);
+ p = procfs_file_alloca(pid, "limits");
+ if (read_full_file(p, &t, NULL) >= 0)
+ set_iovec_field_free(iovec, n_iovec, "COREDUMP_PROC_LIMITS=", t);
- IOVEC_SET_STRING(iovec[n_iovec++], core_cgroup);
- }
+ p = procfs_file_alloca(pid, "cgroup");
+ if (read_full_file(p, &t, NULL) >=0)
+ set_iovec_field_free(iovec, n_iovec, "COREDUMP_PROC_CGROUP=", t);
- if (compose_open_fds(pid, &t) >= 0) {
- core_open_fds = strappend("COREDUMP_OPEN_FDS=", t);
- free(t);
+ p = procfs_file_alloca(pid, "mountinfo");
+ if (read_full_file(p, &t, NULL) >=0)
+ set_iovec_field_free(iovec, n_iovec, "COREDUMP_PROC_MOUNTINFO=", t);
- if (core_open_fds)
- IOVEC_SET_STRING(iovec[n_iovec++], core_open_fds);
- }
+ if (get_process_cwd(pid, &t) >= 0)
+ set_iovec_field_free(iovec, n_iovec, "COREDUMP_CWD=", t);
- p = procfs_file_alloca(pid, "status");
- if (read_full_file(p, &t, NULL) >= 0) {
- core_proc_status = strappend("COREDUMP_PROC_STATUS=", t);
- free(t);
+ if (get_process_root(pid, &t) >= 0) {
+ bool proc_self_root_is_slash;
- if (core_proc_status)
- IOVEC_SET_STRING(iovec[n_iovec++], core_proc_status);
- }
+ proc_self_root_is_slash = strcmp(t, "/") == 0;
- p = procfs_file_alloca(pid, "maps");
- if (read_full_file(p, &t, NULL) >= 0) {
- core_proc_maps = strappend("COREDUMP_PROC_MAPS=", t);
- free(t);
+ set_iovec_field_free(iovec, n_iovec, "COREDUMP_ROOT=", t);
- if (core_proc_maps)
- IOVEC_SET_STRING(iovec[n_iovec++], core_proc_maps);
+ /* If the process' root is "/", then there is a chance it has
+ * mounted own root and hence being containerized. */
+ if (proc_self_root_is_slash && get_process_container_parent_cmdline(pid, &t) > 0)
+ set_iovec_field_free(iovec, n_iovec, "COREDUMP_CONTAINER_CMDLINE=", t);
}
- p = procfs_file_alloca(pid, "limits");
- if (read_full_file(p, &t, NULL) >= 0) {
- core_proc_limits = strappend("COREDUMP_PROC_LIMITS=", t);
- free(t);
+ if (get_process_environ(pid, &t) >= 0)
+ set_iovec_field_free(iovec, n_iovec, "COREDUMP_ENVIRON=", t);
- if (core_proc_limits)
- IOVEC_SET_STRING(iovec[n_iovec++], core_proc_limits);
- }
+ t = strjoin("COREDUMP_TIMESTAMP=", context[CONTEXT_TIMESTAMP], "000000", NULL);
+ if (t)
+ IOVEC_SET_STRING(iovec[(*n_iovec)++], t);
- p = procfs_file_alloca(pid, "cgroup");
- if (read_full_file(p, &t, NULL) >=0) {
- core_proc_cgroup = strappend("COREDUMP_PROC_CGROUP=", t);
- free(t);
+ if (safe_atoi(context[CONTEXT_SIGNAL], &signo) >= 0 && SIGNAL_VALID(signo))
+ set_iovec_field(iovec, n_iovec, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(signo));
- if (core_proc_cgroup)
- IOVEC_SET_STRING(iovec[n_iovec++], core_proc_cgroup);
- }
+ return 0; /* we successfully acquired all metadata */
+}
- p = procfs_file_alloca(pid, "mountinfo");
- if (read_full_file(p, &t, NULL) >=0) {
- core_proc_mountinfo = strappend("COREDUMP_PROC_MOUNTINFO=", t);
- free(t);
+static int process_kernel(int argc, char* argv[]) {
- if (core_proc_mountinfo)
- IOVEC_SET_STRING(iovec[n_iovec++], core_proc_mountinfo);
- }
+ char* context[_CONTEXT_MAX] = {};
+ struct iovec iovec[28 + SUBMIT_COREDUMP_FIELDS];
+ size_t i, n_iovec, n_to_free = 0;
+ int r;
- if (get_process_cwd(pid, &t) >= 0) {
- core_cwd = strjoina("COREDUMP_CWD=", t);
- free(t);
+ log_debug("Processing coredump received from the kernel...");
- IOVEC_SET_STRING(iovec[n_iovec++], core_cwd);
+ if (argc < CONTEXT_COMM + 1) {
+ log_error("Not enough arguments passed by the kernel (%i, expected %i).", argc - 1, CONTEXT_COMM + 1 - 1);
+ return -EINVAL;
}
- if (get_process_root(pid, &t) >= 0) {
- core_root = strjoina("COREDUMP_ROOT=", t);
+ context[CONTEXT_PID] = argv[1 + CONTEXT_PID];
+ context[CONTEXT_UID] = argv[1 + CONTEXT_UID];
+ context[CONTEXT_GID] = argv[1 + CONTEXT_GID];
+ context[CONTEXT_SIGNAL] = argv[1 + CONTEXT_SIGNAL];
+ context[CONTEXT_TIMESTAMP] = argv[1 + CONTEXT_TIMESTAMP];
+ context[CONTEXT_RLIMIT] = argv[1 + CONTEXT_RLIMIT];
- IOVEC_SET_STRING(iovec[n_iovec++], core_root);
+ r = gather_pid_metadata(context, argv + 1 + CONTEXT_COMM, iovec, &n_to_free);
+ if (r < 0)
+ goto finish;
- /* If the process' root is "/", then there is a chance it has
- * mounted own root and hence being containerized. */
- proc_self_root_is_slash = strcmp(t, "/") == 0;
- free(t);
- if (proc_self_root_is_slash && get_process_container_parent_cmdline(pid, &t) > 0) {
- core_container_cmdline = strappend("COREDUMP_CONTAINER_CMDLINE=", t);
- free(t);
+ n_iovec = n_to_free;
- if (core_container_cmdline)
- IOVEC_SET_STRING(iovec[n_iovec++], core_container_cmdline);
- }
+ IOVEC_SET_STRING(iovec[n_iovec++], "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR);
+
+ assert_cc(2 == LOG_CRIT);
+ IOVEC_SET_STRING(iovec[n_iovec++], "PRIORITY=2");
+
+ assert(n_iovec <= ELEMENTSOF(iovec));
+
+ if (is_journald_crash((const char**) context) || is_pid1_crash((const char**) context))
+ r = submit_coredump((const char**) context,
+ iovec, ELEMENTSOF(iovec), n_iovec,
+ STDIN_FILENO);
+ else
+ r = send_iovec(iovec, n_iovec, STDIN_FILENO);
+
+ finish:
+ for (i = 0; i < n_to_free; i++)
+ free(iovec[i].iov_base);
+
+ /* Those fields are allocated by gather_pid_metadata */
+ free(context[CONTEXT_COMM]);
+ free(context[CONTEXT_EXE]);
+ free(context[CONTEXT_UNIT]);
+
+ return r;
+}
+
+static int process_backtrace(int argc, char *argv[]) {
+ char *context[_CONTEXT_MAX] = {};
+ _cleanup_free_ char *message = NULL;
+ _cleanup_free_ struct iovec *iovec = NULL;
+ size_t n_iovec, n_allocated, n_to_free = 0, i;
+ int r;
+ JournalImporter importer = {
+ .fd = STDIN_FILENO,
+ };
+
+ log_debug("Processing backtrace on stdin...");
+
+ if (argc < CONTEXT_COMM + 1) {
+ log_error("Not enough arguments passed (%i, expected %i).", argc - 1, CONTEXT_COMM + 1 - 1);
+ return -EINVAL;
}
- if (get_process_environ(pid, &t) >= 0) {
- core_environ = strappend("COREDUMP_ENVIRON=", t);
- free(t);
+ context[CONTEXT_PID] = argv[2 + CONTEXT_PID];
+ context[CONTEXT_UID] = argv[2 + CONTEXT_UID];
+ context[CONTEXT_GID] = argv[2 + CONTEXT_GID];
+ context[CONTEXT_SIGNAL] = argv[2 + CONTEXT_SIGNAL];
+ context[CONTEXT_TIMESTAMP] = argv[2 + CONTEXT_TIMESTAMP];
+ context[CONTEXT_RLIMIT] = argv[2 + CONTEXT_RLIMIT];
+
+ n_allocated = 33 + COREDUMP_STORAGE_EXTERNAL;
+ /* 25 metadata, 2 static, +unknown input, 4 storage, rounded up */
+ iovec = new(struct iovec, n_allocated);
+ if (!iovec)
+ return log_oom();
+
+ r = gather_pid_metadata(context, argv + 2 + CONTEXT_COMM, iovec, &n_to_free);
+ if (r < 0)
+ goto finish;
+ if (r > 0) {
+ /* This was a special crash, and has already been processed. */
+ r = 0;
+ goto finish;
+ }
+ n_iovec = n_to_free;
- if (core_environ)
- IOVEC_SET_STRING(iovec[n_iovec++], core_environ);
+ for (;;) {
+ r = journal_importer_process_data(&importer);
+ if (r < 0) {
+ log_error_errno(r, "Failed to parse journal entry on stdin: %m");
+ goto finish;
+ }
+ if (r == 1)
+ break;
}
- core_timestamp = strjoina("COREDUMP_TIMESTAMP=", context[CONTEXT_TIMESTAMP], "000000");
- IOVEC_SET_STRING(iovec[n_iovec++], core_timestamp);
+ if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + importer.iovw.count + 2))
+ return log_oom();
- IOVEC_SET_STRING(iovec[n_iovec++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
+ if (journal_importer_eof(&importer)) {
+ log_warning("Did not receive a full journal entry on stdin, ignoring message sent by reporter");
+ message = strjoin("MESSAGE=Process ", context[CONTEXT_PID],
+ " (", context[CONTEXT_COMM], ")"
+ " of user ", context[CONTEXT_UID],
+ " failed with ", context[CONTEXT_SIGNAL]);
+ if (!message) {
+ r = log_oom();
+ goto finish;
+ }
+ IOVEC_SET_STRING(iovec[n_iovec++], message);
+ } else {
+ for (i = 0; i < importer.iovw.count; i++)
+ iovec[n_iovec++] = importer.iovw.iovec[i];
+ }
+
+ IOVEC_SET_STRING(iovec[n_iovec++], "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR);
assert_cc(2 == LOG_CRIT);
IOVEC_SET_STRING(iovec[n_iovec++], "PRIORITY=2");
- assert(n_iovec <= ELEMENTSOF(iovec));
+ assert(n_iovec <= n_allocated);
- return send_iovec(iovec, n_iovec, STDIN_FILENO);
+ r = sd_journal_sendv(iovec, n_iovec);
+ if (r < 0)
+ log_error_errno(r, "Failed to log backtrace: %m");
+
+ finish:
+ for (i = 0; i < n_to_free; i++)
+ free(iovec[i].iov_base);
+
+ /* Those fields are allocated by gather_pid_metadata */
+ free(context[CONTEXT_COMM]);
+ free(context[CONTEXT_EXE]);
+ free(context[CONTEXT_UNIT]);
+
+ return r;
}
int main(int argc, char *argv[]) {
int r;
- /* First, log to a safe place, since we don't know what crashed and it might be journald which we'd rather not
- * log to then. */
+ /* First, log to a safe place, since we don't know what crashed and it might
+ * be journald which we'd rather not log to then. */
log_set_target(LOG_TARGET_KMSG);
log_open();
@@ -1295,11 +1396,14 @@ int main(int argc, char *argv[]) {
goto finish;
}
- /* If we got an fd passed, we are running in coredumpd mode. Otherwise we are invoked from the kernel as
- * coredump handler */
- if (r == 0)
- r = process_kernel(argc, argv);
- else if (r == 1)
+ /* If we got an fd passed, we are running in coredumpd mode. Otherwise we
+ * are invoked from the kernel as coredump handler. */
+ if (r == 0) {
+ if (streq_ptr(argv[1], "--backtrace"))
+ r = process_backtrace(argc, argv);
+ else
+ r = process_kernel(argc, argv);
+ } else if (r == 1)
r = process_socket(SD_LISTEN_FDS_START);
else {
log_error("Received unexpected number of file descriptors.");
diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c
index 646757f9d9..114a13fc78 100644
--- a/src/coredump/coredumpctl.c
+++ b/src/coredump/coredumpctl.c
@@ -24,28 +24,37 @@
#include <string.h>
#include <unistd.h>
+#include "sd-bus.h"
#include "sd-journal.h"
+#include "sd-messages.h"
#include "alloc-util.h"
+#include "bus-error.h"
+#include "bus-util.h"
#include "compress.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "journal-internal.h"
+#include "journal-util.h"
#include "log.h"
#include "macro.h"
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
-#include "set.h"
#include "sigbus.h"
#include "signal-util.h"
#include "string-util.h"
+#include "strv.h"
#include "terminal-util.h"
#include "user-util.h"
#include "util.h"
+#define SHORT_BUS_CALL_TIMEOUT_USEC (3 * USEC_PER_SEC)
+
+static usec_t arg_since = USEC_INFINITY, arg_until = USEC_INFINITY;
+
static enum {
ACTION_NONE,
ACTION_INFO,
@@ -59,36 +68,11 @@ static bool arg_no_pager = false;
static int arg_no_legend = false;
static int arg_one = false;
static FILE* arg_output = NULL;
+static bool arg_reverse = false;
+static char** arg_matches = NULL;
+static bool arg_quiet = false;
-static Set *new_matches(void) {
- Set *set;
- char *tmp;
- int r;
-
- set = set_new(NULL);
- if (!set) {
- log_oom();
- return NULL;
- }
-
- tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
- if (!tmp) {
- log_oom();
- set_free(set);
- return NULL;
- }
-
- r = set_consume(set, tmp);
- if (r < 0) {
- log_error_errno(r, "failed to add to set: %m");
- set_free(set);
- return NULL;
- }
-
- return set;
-}
-
-static int add_match(Set *set, const char *match) {
+static int add_match(sd_journal *j, const char *match) {
_cleanup_free_ char *p = NULL;
char *pattern = NULL;
const char* prefix;
@@ -100,7 +84,8 @@ static int add_match(Set *set, const char *match) {
else if (strchr(match, '/')) {
r = path_make_absolute_cwd(match, &p);
if (r < 0)
- goto fail;
+ return log_error_errno(r, "path_make_absolute_cwd(\"%s\"): %m", match);
+
match = p;
prefix = "COREDUMP_EXE=";
} else if (parse_pid(match, &pid) >= 0)
@@ -109,19 +94,35 @@ static int add_match(Set *set, const char *match) {
prefix = "COREDUMP_COMM=";
pattern = strjoin(prefix, match);
- if (!pattern) {
- r = -ENOMEM;
- goto fail;
- }
+ if (!pattern)
+ return log_oom();
+
+ log_debug("Adding match: %s", pattern);
+ r = sd_journal_add_match(j, pattern, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add match \"%s\": %m", match);
+ return 0;
+}
+
+static int add_matches(sd_journal *j) {
+ char **match;
+ int r;
- log_debug("Adding pattern: %s", pattern);
- r = set_consume(set, pattern);
+ r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR, 0);
if (r < 0)
- goto fail;
+ return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR);
+
+ r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR);
+
+ STRV_FOREACH(match, arg_matches) {
+ r = add_match(j, *match);
+ if (r < 0)
+ return r;
+ }
return 0;
-fail:
- return log_error_errno(r, "Failed to add match: %m");
}
static void help(void) {
@@ -133,10 +134,13 @@ static void help(void) {
" --no-pager Do not pipe output into a pager\n"
" --no-legend Do not print the column headers.\n"
" -1 Show information about most recent entry only\n"
+ " -S --since=DATE Only print coredumps since the date\n"
+ " -U --until=DATE Only print coredumps until the date\n"
+ " -r --reverse Show the newest entries first\n"
" -F --field=FIELD List all values a certain field takes\n"
- " -o --output=FILE Write output to FILE\n\n"
+ " -o --output=FILE Write output to FILE\n"
" -D --directory=DIR Use journal files from directory\n\n"
-
+ " -q --quiet Do not show info messages and privilege warning\n"
"Commands:\n"
" list [MATCHES...] List available coredumps (default)\n"
" info [MATCHES...] Show detailed information about one or more coredumps\n"
@@ -145,14 +149,14 @@ static void help(void) {
, program_invocation_short_name);
}
-static int parse_argv(int argc, char *argv[], Set *matches) {
+static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_NO_PAGER,
ARG_NO_LEGEND,
};
- int r, c;
+ int c, r;
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
@@ -162,15 +166,18 @@ static int parse_argv(int argc, char *argv[], Set *matches) {
{ "output", required_argument, NULL, 'o' },
{ "field", required_argument, NULL, 'F' },
{ "directory", required_argument, NULL, 'D' },
+ { "reverse", no_argument, NULL, 'r' },
+ { "since", required_argument, NULL, 'S' },
+ { "until", required_argument, NULL, 'U' },
+ { "quiet", no_argument, NULL, 'q' },
{}
};
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "ho:F:1D:", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "ho:F:1D:rS:U:q", options, NULL)) >= 0)
switch(c) {
-
case 'h':
arg_action = ACTION_NONE;
help();
@@ -200,6 +207,18 @@ static int parse_argv(int argc, char *argv[], Set *matches) {
break;
+ case 'S':
+ r = parse_timestamp(optarg, &arg_since);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
+ break;
+
+ case 'U':
+ r = parse_timestamp(optarg, &arg_until);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
+ break;
+
case 'F':
if (arg_field) {
log_error("cannot use --field/-F more than once");
@@ -216,6 +235,14 @@ static int parse_argv(int argc, char *argv[], Set *matches) {
arg_directory = optarg;
break;
+ case 'r':
+ arg_reverse = true;
+ break;
+
+ case 'q':
+ arg_quiet = true;
+ break;
+
case '?':
return -EINVAL;
@@ -223,6 +250,12 @@ static int parse_argv(int argc, char *argv[], Set *matches) {
assert_not_reached("Unhandled option");
}
+ if (arg_since != USEC_INFINITY && arg_until != USEC_INFINITY &&
+ arg_since > arg_until) {
+ log_error("--since= must be before --until=.");
+ return -EINVAL;
+ }
+
if (optind < argc) {
const char *cmd = argv[optind++];
if (streq(cmd, "list"))
@@ -244,12 +277,8 @@ static int parse_argv(int argc, char *argv[], Set *matches) {
return -EINVAL;
}
- while (optind < argc) {
- r = add_match(matches, argv[optind]);
- if (r != 0)
- return r;
- optind++;
- }
+ if (optind < argc)
+ arg_matches = argv + optind;
return 0;
}
@@ -322,20 +351,22 @@ static int print_field(FILE* file, sd_journal *j) {
static int print_list(FILE* file, sd_journal *j, int had_legend) {
_cleanup_free_ char
- *pid = NULL, *uid = NULL, *gid = NULL,
+ *mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL,
*sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
- *filename = NULL, *coredump = NULL;
+ *filename = NULL, *truncated = NULL, *coredump = NULL;
const void *d;
size_t l;
usec_t t;
char buf[FORMAT_TIMESTAMP_MAX];
int r;
const char *present;
+ bool normal_coredump;
assert(file);
assert(j);
SD_JOURNAL_FOREACH_DATA(j, d, l) {
+ RETRIEVE(d, l, "MESSAGE_ID", mid);
RETRIEVE(d, l, "COREDUMP_PID", pid);
RETRIEVE(d, l, "COREDUMP_UID", uid);
RETRIEVE(d, l, "COREDUMP_GID", gid);
@@ -344,6 +375,7 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) {
RETRIEVE(d, l, "COREDUMP_COMM", comm);
RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline);
RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
+ RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated);
RETRIEVE(d, l, "COREDUMP", coredump);
}
@@ -359,15 +391,17 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) {
format_timestamp(buf, sizeof(buf), t);
if (!had_legend && !arg_no_legend)
- fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
+ fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n",
FORMAT_TIMESTAMP_WIDTH, "TIME",
6, "PID",
5, "UID",
5, "GID",
3, "SIG",
- 8, "COREFILE",
+ 9, "COREFILE",
"EXE");
+ normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR);
+
if (filename)
if (access(filename, R_OK) == 0)
present = "present";
@@ -377,16 +411,21 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) {
present = "error";
else if (coredump)
present = "journal";
- else
+ else if (normal_coredump)
present = "none";
+ else
+ present = "-";
+
+ if (STR_IN_SET(present, "present", "journal") && truncated && parse_boolean(truncated) > 0)
+ present = "truncated";
fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n",
FORMAT_TIMESTAMP_WIDTH, buf,
6, strna(pid),
5, strna(uid),
5, strna(gid),
- 3, strna(sgnl),
- 8, present,
+ 3, normal_coredump ? strna(sgnl) : "-",
+ 9, present,
strna(exe ?: (comm ?: cmdline)));
return 0;
@@ -394,21 +433,23 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) {
static int print_info(FILE *file, sd_journal *j, bool need_space) {
_cleanup_free_ char
- *pid = NULL, *uid = NULL, *gid = NULL,
+ *mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL,
*sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
*unit = NULL, *user_unit = NULL, *session = NULL,
*boot_id = NULL, *machine_id = NULL, *hostname = NULL,
*slice = NULL, *cgroup = NULL, *owner_uid = NULL,
*message = NULL, *timestamp = NULL, *filename = NULL,
- *coredump = NULL;
+ *truncated = NULL, *coredump = NULL;
const void *d;
size_t l;
+ bool normal_coredump;
int r;
assert(file);
assert(j);
SD_JOURNAL_FOREACH_DATA(j, d, l) {
+ RETRIEVE(d, l, "MESSAGE_ID", mid);
RETRIEVE(d, l, "COREDUMP_PID", pid);
RETRIEVE(d, l, "COREDUMP_UID", uid);
RETRIEVE(d, l, "COREDUMP_GID", gid);
@@ -424,6 +465,7 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
RETRIEVE(d, l, "COREDUMP_CGROUP", cgroup);
RETRIEVE(d, l, "COREDUMP_TIMESTAMP", timestamp);
RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
+ RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated);
RETRIEVE(d, l, "COREDUMP", coredump);
RETRIEVE(d, l, "_BOOT_ID", boot_id);
RETRIEVE(d, l, "_MACHINE_ID", machine_id);
@@ -434,6 +476,8 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
if (need_space)
fputs("\n", file);
+ normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR);
+
if (comm)
fprintf(file,
" PID: %s%s%s (%s)\n",
@@ -479,11 +523,12 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
if (sgnl) {
int sig;
+ const char *name = normal_coredump ? "Signal" : "Reason";
- if (safe_atoi(sgnl, &sig) >= 0)
- fprintf(file, " Signal: %s (%s)\n", sgnl, signal_to_string(sig));
+ if (normal_coredump && safe_atoi(sgnl, &sig) >= 0)
+ fprintf(file, " %s: %s (%s)\n", name, sgnl, signal_to_string(sig));
else
- fprintf(file, " Signal: %s\n", sgnl);
+ fprintf(file, " %s: %s\n", name, sgnl);
}
if (timestamp) {
@@ -539,9 +584,24 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
if (hostname)
fprintf(file, " Hostname: %s\n", hostname);
- if (filename)
- fprintf(file, " Storage: %s%s\n", filename,
- access(filename, R_OK) < 0 ? " (inaccessible)" : "");
+ if (filename) {
+ bool inacc, trunc;
+
+ inacc = access(filename, R_OK) < 0;
+ trunc = truncated && parse_boolean(truncated) > 0;
+
+ if (inacc || trunc)
+ fprintf(file, " Storage: %s%s (%s%s%s)%s\n",
+ ansi_highlight_red(),
+ filename,
+ inacc ? "inaccessible" : "",
+ inacc && trunc ? ", " : "",
+ trunc ? "truncated" : "",
+ ansi_normal());
+ else
+ fprintf(file, " Storage: %s\n", filename);
+ }
+
else if (coredump)
fprintf(file, " Storage: journal\n");
else
@@ -602,14 +662,57 @@ static int dump_list(sd_journal *j) {
return print_entry(j, 0);
} else {
- SD_JOURNAL_FOREACH(j) {
+ if (arg_since != USEC_INFINITY && !arg_reverse)
+ r = sd_journal_seek_realtime_usec(j, arg_since);
+ else if (arg_until != USEC_INFINITY && arg_reverse)
+ r = sd_journal_seek_realtime_usec(j, arg_until);
+ else if (arg_reverse)
+ r = sd_journal_seek_tail(j);
+ else
+ r = sd_journal_seek_head(j);
+ if (r < 0)
+ return log_error_errno(r, "Failed to seek to date: %m");
+
+ for (;;) {
+ if (!arg_reverse)
+ r = sd_journal_next(j);
+ else
+ r = sd_journal_previous(j);
+
+ if (r < 0)
+ return log_error_errno(r, "Failed to iterate through journal: %m");
+
+ if (r == 0)
+ break;
+
+ if (arg_until != USEC_INFINITY && !arg_reverse) {
+ usec_t usec;
+
+ r = sd_journal_get_realtime_usec(j, &usec);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine timestamp: %m");
+ if (usec > arg_until)
+ continue;
+ }
+
+ if (arg_since != USEC_INFINITY && arg_reverse) {
+ usec_t usec;
+
+ r = sd_journal_get_realtime_usec(j, &usec);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine timestamp: %m");
+ if (usec < arg_since)
+ continue;
+ }
+
r = print_entry(j, n_found++);
if (r < 0)
return r;
}
if (!arg_field && n_found <= 0) {
- log_notice("No coredumps found.");
+ if (!arg_quiet)
+ log_notice("No coredumps found.");
return -ESRCH;
}
}
@@ -771,8 +874,8 @@ static int dump_core(sd_journal* j) {
return r;
r = sd_journal_previous(j);
- if (r > 0)
- log_warning("More than one entry matches, ignoring rest.");
+ if (r > 0 && !arg_quiet)
+ log_notice("More than one entry matches, ignoring rest.");
return 0;
}
@@ -858,24 +961,75 @@ finish:
return r;
}
+static int check_units_active(void) {
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ int c = 0, r;
+ const char *id, *state, *substate;
+
+ if (arg_quiet)
+ return false;
+
+ r = sd_bus_default_system(&bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire bus: %m");
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ListUnitsByPatterns");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(m, NULL);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(m, STRV_MAKE("systemd-coredump@*.service"));
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, SHORT_BUS_CALL_TIMEOUT_USEC, &error, &reply);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if any systemd-coredump@.service units are running: %s",
+ bus_error_message(&error, r));
+
+ r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(
+ reply, "(ssssssouso)",
+ &id, NULL, NULL, &state, &substate,
+ NULL, NULL, NULL, NULL, NULL)) > 0) {
+ bool found = !STR_IN_SET(state, "inactive", "dead", "failed");
+ log_debug("Unit %s is %s/%s, %scounting it.", id, state, substate, found ? "" : "not ");
+ c += found;
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return c;
+}
+
int main(int argc, char *argv[]) {
_cleanup_(sd_journal_closep) sd_journal*j = NULL;
- const char* match;
- Iterator it;
- int r = 0;
- _cleanup_set_free_free_ Set *matches = NULL;
+ int r = 0, units_active;
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
- matches = new_matches();
- if (!matches) {
- r = -ENOMEM;
- goto end;
- }
-
- r = parse_argv(argc, argv, matches);
+ r = parse_argv(argc, argv);
if (r < 0)
goto end;
@@ -898,14 +1052,13 @@ int main(int argc, char *argv[]) {
}
}
- SET_FOREACH(match, matches, it) {
- r = sd_journal_add_match(j, match, strlen(match));
- if (r != 0) {
- log_error_errno(r, "Failed to add match '%s': %m",
- match);
- goto end;
- }
- }
+ r = journal_access_check_and_warn(j, arg_quiet);
+ if (r < 0)
+ goto end;
+
+ r = add_matches(j);
+ if (r < 0)
+ goto end;
if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
_cleanup_free_ char *filter;
@@ -914,6 +1067,8 @@ int main(int argc, char *argv[]) {
log_debug("Journal filter: %s", filter);
}
+ units_active = check_units_active(); /* error is treated the same as 0 */
+
switch(arg_action) {
case ACTION_LIST:
@@ -934,6 +1089,11 @@ int main(int argc, char *argv[]) {
assert_not_reached("Shouldn't be here");
}
+ if (units_active > 0)
+ printf("%s-- Notice: %d systemd-coredump@.service %s, output may be incomplete.%s\n",
+ ansi_highlight_red(),
+ units_active, units_active == 1 ? "unit is running" : "units are running",
+ ansi_normal());
end:
pager_close();
diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c
index 23bf014929..b58b6db7c9 100644
--- a/src/cryptsetup/cryptsetup-generator.c
+++ b/src/cryptsetup/cryptsetup-generator.c
@@ -144,13 +144,17 @@ static int create_disk(
}
}
- if (is_device_path(u))
+ if (is_device_path(u)) {
fprintf(f,
"BindsTo=%s\n"
"After=%s\n"
"Before=umount.target\n",
d, d);
- else
+
+ if (swap)
+ fputs("Before=dev-mapper-%i.swap\n",
+ f);
+ } else
fprintf(f,
"RequiresMountsFor=%s\n",
u);
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c
index 59bd7d9e84..06564e94b1 100644
--- a/src/dissect/dissect.c
+++ b/src/dissect/dissect.c
@@ -208,6 +208,14 @@ int main(int argc, char *argv[]) {
log_error_errno(r, "No root partition for specified root hash found in %s.", arg_image);
goto finish;
}
+ if (r == -ENOTUNIQ) {
+ log_error_errno(r, "Multiple suitable root partitions found in image %s.", arg_image);
+ goto finish;
+ }
+ if (r == -ENXIO) {
+ log_error_errno(r, "No suitable root partition found in image %s.", arg_image);
+ goto finish;
+ }
if (r < 0) {
log_error_errno(r, "Failed to dissect image: %m");
goto finish;
diff --git a/src/environment-d-generator/Makefile b/src/environment-d-generator/Makefile
new file mode 120000
index 0000000000..d0b0e8e008
--- /dev/null
+++ b/src/environment-d-generator/Makefile
@@ -0,0 +1 @@
+../Makefile \ No newline at end of file
diff --git a/src/environment-d-generator/environment-d-generator.c b/src/environment-d-generator/environment-d-generator.c
new file mode 100644
index 0000000000..2d4c4235e4
--- /dev/null
+++ b/src/environment-d-generator/environment-d-generator.c
@@ -0,0 +1,107 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2017 Zbigniew Jędrzejewski-Szmek
+
+ 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 "sd-path.h"
+
+#include "conf-files.h"
+#include "def.h"
+#include "escape.h"
+#include "fileio.h"
+#include "log.h"
+#include "path-lookup.h"
+
+static int environment_dirs(char ***ret) {
+ _cleanup_strv_free_ char **dirs = NULL;
+ _cleanup_free_ char *c = NULL;
+ int r;
+
+ dirs = strv_split_nulstr(CONF_PATHS_NULSTR("environment.d"));
+ if (!dirs)
+ return -ENOMEM;
+
+ /* ~/.config/systemd/environment.d */
+ r = sd_path_home(SD_PATH_USER_CONFIGURATION, "environment.d", &c);
+ if (r < 0)
+ return r;
+
+ r = strv_extend_front(&dirs, c);
+ if (r < 0)
+ return r;
+
+ *ret = dirs;
+ dirs = NULL;
+ return 0;
+}
+
+static int load_and_print(void) {
+ _cleanup_strv_free_ char **dirs = NULL, **files = NULL, **env = NULL;
+ char **i;
+ int r;
+
+ r = environment_dirs(&dirs);
+ if (r < 0)
+ return r;
+
+ r = conf_files_list_strv(&files, ".conf", NULL, (const char **) dirs);
+ if (r < 0)
+ return r;
+
+ /* This will mutate the existing environment, based on the presumption
+ * that in case of failure, a partial update is better than none. */
+
+ STRV_FOREACH(i, files) {
+ r = merge_env_file(&env, NULL, *i);
+ if (r == -ENOMEM)
+ return r;
+ }
+
+ STRV_FOREACH(i, env) {
+ char *t;
+ _cleanup_free_ char *q = NULL;
+
+ t = strchr(*i, '=');
+ assert(t);
+
+ q = shell_maybe_quote(t + 1);
+ if (!q)
+ return log_oom();
+
+ printf("%.*s=%s\n", (int) (t - *i), *i, q);
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+
+ log_parse_environment();
+ log_open();
+
+ if (argc > 1) {
+ log_error("This program takes no arguments.");
+ return EXIT_FAILURE;
+ }
+
+ r = load_and_print();
+ if (r < 0)
+ log_error_errno(r, "Failed to load environment.d: %m");
+
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index fd7051f21e..bc16290c72 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -252,7 +252,7 @@ static int process_locale(void) {
if (arg_copy_locale && arg_root) {
mkdir_parents(etc_localeconf, 0755);
- r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, 0);
+ r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, 0, COPY_REFLINK);
if (r != -ENOENT) {
if (r < 0)
return log_error_errno(r, "Failed to copy %s: %m", etc_localeconf);
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index d97bafd1fb..2677a3fb32 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -209,7 +209,8 @@ static int write_mount_timeout(FILE *f, const char *where, const char *opts) {
"x-systemd.mount-timeout\0", "TimeoutSec");
}
-static int write_requires_after(FILE *f, const char *opts) {
+static int write_dependency(FILE *f, const char *opts,
+ const char *filter, const char *format) {
_cleanup_strv_free_ char **names = NULL, **units = NULL;
_cleanup_free_ char *res = NULL;
char **s;
@@ -218,7 +219,7 @@ static int write_requires_after(FILE *f, const char *opts) {
assert(f);
assert(opts);
- r = fstab_extract_values(opts, "x-systemd.requires", &names);
+ r = fstab_extract_values(opts, filter, &names);
if (r < 0)
return log_warning_errno(r, "Failed to parse options: %m");
if (r == 0)
@@ -239,12 +240,29 @@ static int write_requires_after(FILE *f, const char *opts) {
res = strv_join(units, " ");
if (!res)
return log_oom();
- fprintf(f, "After=%1$s\nRequires=%1$s\n", res);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+ fprintf(f, format, res);
+#pragma GCC diagnostic pop
}
return 0;
}
+static int write_after(FILE *f, const char *opts) {
+ return write_dependency(f, opts, "x-systemd.after", "After=%1$s\n");
+}
+
+static int write_requires_after(FILE *f, const char *opts) {
+ return write_dependency(f, opts,
+ "x-systemd.requires", "After=%1$s\nRequires=%1$s\n");
+}
+
+static int write_before(FILE *f, const char *opts) {
+ return write_dependency(f, opts,
+ "x-systemd.before", "Before=%1$s\n");
+}
+
static int write_requires_mounts_for(FILE *f, const char *opts) {
_cleanup_strv_free_ char **paths = NULL;
_cleanup_free_ char *res = NULL;
@@ -344,9 +362,15 @@ static int add_mount(
fprintf(f, "Before=%s\n", post);
if (!automount && opts) {
+ r = write_after(f, opts);
+ if (r < 0)
+ return r;
r = write_requires_after(f, opts);
if (r < 0)
return r;
+ r = write_before(f, opts);
+ if (r < 0)
+ return r;
r = write_requires_mounts_for(f, opts);
if (r < 0)
return r;
@@ -421,9 +445,15 @@ static int add_mount(
fprintf(f, "Before=%s\n", post);
if (opts) {
+ r = write_after(f, opts);
+ if (r < 0)
+ return r;
r = write_requires_after(f, opts);
if (r < 0)
return r;
+ r = write_before(f, opts);
+ if (r < 0)
+ return r;
r = write_requires_mounts_for(f, opts);
if (r < 0)
return r;
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index b958070c90..80f676e477 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -475,7 +475,7 @@ static int add_esp(DissectedPartition *p) {
120 * USEC_PER_SEC);
}
#else
-static int add_esp(const char *what) {
+static int add_esp(DissectedPartition *p) {
return 0;
}
#endif
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 4657cf8c77..fe0aa00efb 100644
--- a/src/hostname/hostnamed.c
+++ b/src/hostname/hostnamed.c
@@ -133,6 +133,7 @@ static bool valid_chassis(const char *chassis) {
"container\0"
"desktop\0"
"laptop\0"
+ "convertible\0"
"server\0"
"tablet\0"
"handset\0"
@@ -199,6 +200,10 @@ static const char* fallback_chassis(void) {
case 0x1E: /* Tablet */
return "tablet";
+
+ case 0x1F: /* Convertible */
+ case 0x20: /* Detachable */
+ return "convertible";
}
try_acpi:
diff --git a/src/import/curl-util.c b/src/import/curl-util.c
index 734e1560e6..0e8f3fb918 100644
--- a/src/import/curl-util.c
+++ b/src/import/curl-util.c
@@ -400,7 +400,7 @@ int curl_header_strdup(const void *contents, size_t sz, const char *field, char
sz--;
}
- /* Truncate trailing whitespace*/
+ /* Truncate trailing whitespace */
while (sz > 0 && strchr(WHITESPACE, p[sz-1]))
sz--;
diff --git a/src/import/importd.c b/src/import/importd.c
index 9d31a956a5..3d379d6de9 100644
--- a/src/import/importd.c
+++ b/src/import/importd.c
@@ -449,8 +449,11 @@ static int transfer_start(Transfer *t) {
stdio_unset_cloexec();
- setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
- setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
+ if (setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1) < 0 ||
+ setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1) < 0) {
+ log_error_errno(errno, "setenv() failed: %m");
+ _exit(EXIT_FAILURE);
+ }
if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_IMPORT_RAW))
cmd[k++] = SYSTEMD_IMPORT_PATH;
diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c
index ef7fb6ac42..60a769e944 100644
--- a/src/import/pull-raw.c
+++ b/src/import/pull-raw.c
@@ -315,7 +315,7 @@ static int raw_pull_copy_auxiliary_file(
local = strjoina(i->image_root, "/", i->local, suffix);
- r = copy_file_atomic(*path, local, 0644, i->force_local, 0);
+ r = copy_file_atomic(*path, local, 0644, 0, COPY_REFLINK | (i->force_local ? COPY_REPLACE : 0));
if (r == -EEXIST)
log_warning_errno(r, "File %s already exists, not replacing.", local);
else if (r == -ENOENT)
@@ -378,7 +378,7 @@ static int raw_pull_make_local_copy(RawPull *i) {
if (r < 0)
log_warning_errno(r, "Failed to set file attributes on %s: %m", tp);
- r = copy_bytes(i->raw_job->disk_fd, dfd, (uint64_t) -1, true);
+ r = copy_bytes(i->raw_job->disk_fd, dfd, (uint64_t) -1, COPY_REFLINK);
if (r < 0) {
unlink(tp);
return log_error_errno(r, "Failed to make writable copy of image: %m");
diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c
index 375ee778e2..91833d6174 100644
--- a/src/import/pull-tar.c
+++ b/src/import/pull-tar.c
@@ -256,7 +256,7 @@ static int tar_pull_make_local_copy(TarPull *i) {
local_settings = strjoina(i->image_root, "/", i->local, ".nspawn");
- r = copy_file_atomic(i->settings_path, local_settings, 0664, i->force_local, 0);
+ r = copy_file_atomic(i->settings_path, local_settings, 0664, 0, COPY_REFLINK | (i->force_local ? COPY_REPLACE : 0));
if (r == -EEXIST)
log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings);
else if (r == -ENOENT)
diff --git a/src/journal-remote/journal-remote-parse.c b/src/journal-remote/journal-remote-parse.c
index 9ba9ee3fc0..79afe6604c 100644
--- a/src/journal-remote/journal-remote-parse.c
+++ b/src/journal-remote/journal-remote-parse.c
@@ -24,20 +24,11 @@
#include "parse-util.h"
#include "string-util.h"
-#define LINE_CHUNK 8*1024u
-
void source_free(RemoteSource *source) {
if (!source)
return;
- if (source->fd >= 0 && !source->passive_fd) {
- log_debug("Closing fd:%d (%s)", source->fd, source->name);
- safe_close(source->fd);
- }
-
- free(source->name);
- free(source->buf);
- iovw_free_contents(&source->iovw);
+ journal_importer_cleanup(&source->importer);
log_debug("Writer ref count %i", source->writer->n_ref);
writer_unref(source->writer);
@@ -65,442 +56,44 @@ RemoteSource* source_new(int fd, bool passive_fd, char *name, Writer *writer) {
if (!source)
return NULL;
- source->fd = fd;
- source->passive_fd = passive_fd;
- source->name = name;
+ source->importer.fd = fd;
+ source->importer.passive_fd = passive_fd;
+ source->importer.name = name;
+
source->writer = writer;
return source;
}
-static char* realloc_buffer(RemoteSource *source, size_t size) {
- char *b, *old = source->buf;
-
- b = GREEDY_REALLOC(source->buf, source->size, size);
- if (!b)
- return NULL;
-
- iovw_rebase(&source->iovw, old, source->buf);
-
- return b;
-}
-
-static int get_line(RemoteSource *source, char **line, size_t *size) {
- ssize_t n;
- char *c = NULL;
-
- assert(source);
- assert(source->state == STATE_LINE);
- assert(source->offset <= source->filled);
- assert(source->filled <= source->size);
- assert(source->buf == NULL || source->size > 0);
- assert(source->fd >= 0);
-
- for (;;) {
- if (source->buf) {
- size_t start = MAX(source->scanned, source->offset);
-
- c = memchr(source->buf + start, '\n',
- source->filled - start);
- if (c != NULL)
- break;
- }
-
- source->scanned = source->filled;
- if (source->scanned >= DATA_SIZE_MAX) {
- log_error("Entry is bigger than %u bytes.", DATA_SIZE_MAX);
- return -E2BIG;
- }
-
- if (source->passive_fd)
- /* we have to wait for some data to come to us */
- return -EAGAIN;
-
- /* We know that source->filled is at most DATA_SIZE_MAX, so if
- we reallocate it, we'll increase the size at least a bit. */
- assert_cc(DATA_SIZE_MAX < ENTRY_SIZE_MAX);
- if (source->size - source->filled < LINE_CHUNK &&
- !realloc_buffer(source, MIN(source->filled + LINE_CHUNK, ENTRY_SIZE_MAX)))
- return log_oom();
-
- assert(source->buf);
- assert(source->size - source->filled >= LINE_CHUNK ||
- source->size == ENTRY_SIZE_MAX);
-
- n = read(source->fd,
- source->buf + source->filled,
- source->size - source->filled);
- if (n < 0) {
- if (errno != EAGAIN)
- log_error_errno(errno, "read(%d, ..., %zu): %m",
- source->fd,
- source->size - source->filled);
- return -errno;
- } else if (n == 0)
- return 0;
-
- source->filled += n;
- }
-
- *line = source->buf + source->offset;
- *size = c + 1 - source->buf - source->offset;
- source->offset += *size;
-
- return 1;
-}
-
-int push_data(RemoteSource *source, const char *data, size_t size) {
- assert(source);
- assert(source->state != STATE_EOF);
-
- if (!realloc_buffer(source, source->filled + size)) {
- log_error("Failed to store received data of size %zu "
- "(in addition to existing %zu bytes with %zu filled): %s",
- size, source->size, source->filled, strerror(ENOMEM));
- return -ENOMEM;
- }
-
- memcpy(source->buf + source->filled, data, size);
- source->filled += size;
-
- return 0;
-}
-
-static int fill_fixed_size(RemoteSource *source, void **data, size_t size) {
-
- assert(source);
- assert(source->state == STATE_DATA_START ||
- source->state == STATE_DATA ||
- source->state == STATE_DATA_FINISH);
- assert(size <= DATA_SIZE_MAX);
- assert(source->offset <= source->filled);
- assert(source->filled <= source->size);
- assert(source->buf != NULL || source->size == 0);
- assert(source->buf == NULL || source->size > 0);
- assert(source->fd >= 0);
- assert(data);
-
- while (source->filled - source->offset < size) {
- int n;
-
- if (source->passive_fd)
- /* we have to wait for some data to come to us */
- return -EAGAIN;
-
- if (!realloc_buffer(source, source->offset + size))
- return log_oom();
-
- n = read(source->fd, source->buf + source->filled,
- source->size - source->filled);
- if (n < 0) {
- if (errno != EAGAIN)
- log_error_errno(errno, "read(%d, ..., %zu): %m", source->fd,
- source->size - source->filled);
- return -errno;
- } else if (n == 0)
- return 0;
-
- source->filled += n;
- }
-
- *data = source->buf + source->offset;
- source->offset += size;
-
- return 1;
-}
-
-static int get_data_size(RemoteSource *source) {
- int r;
- void *data;
-
- assert(source);
- assert(source->state == STATE_DATA_START);
- assert(source->data_size == 0);
-
- r = fill_fixed_size(source, &data, sizeof(uint64_t));
- if (r <= 0)
- return r;
-
- source->data_size = le64toh( *(uint64_t *) data );
- if (source->data_size > DATA_SIZE_MAX) {
- log_error("Stream declares field with size %zu > DATA_SIZE_MAX = %u",
- source->data_size, DATA_SIZE_MAX);
- return -EINVAL;
- }
- if (source->data_size == 0)
- log_warning("Binary field with zero length");
-
- return 1;
-}
-
-static int get_data_data(RemoteSource *source, void **data) {
- int r;
-
- assert(source);
- assert(data);
- assert(source->state == STATE_DATA);
-
- r = fill_fixed_size(source, data, source->data_size);
- if (r <= 0)
- return r;
-
- return 1;
-}
-
-static int get_data_newline(RemoteSource *source) {
- int r;
- char *data;
-
- assert(source);
- assert(source->state == STATE_DATA_FINISH);
-
- r = fill_fixed_size(source, (void**) &data, 1);
- if (r <= 0)
- return r;
-
- assert(data);
- if (*data != '\n') {
- log_error("expected newline, got '%c'", *data);
- return -EINVAL;
- }
-
- return 1;
-}
-
-static int process_dunder(RemoteSource *source, char *line, size_t n) {
- const char *timestamp;
- int r;
-
- assert(line);
- assert(n > 0);
- assert(line[n-1] == '\n');
-
- /* XXX: is it worth to support timestamps in extended format?
- * We don't produce them, but who knows... */
-
- timestamp = startswith(line, "__CURSOR=");
- if (timestamp)
- /* ignore __CURSOR */
- return 1;
-
- timestamp = startswith(line, "__REALTIME_TIMESTAMP=");
- if (timestamp) {
- long long unsigned x;
- line[n-1] = '\0';
- r = safe_atollu(timestamp, &x);
- if (r < 0)
- log_warning("Failed to parse __REALTIME_TIMESTAMP: '%s'", timestamp);
- else
- source->ts.realtime = x;
- return r < 0 ? r : 1;
- }
-
- timestamp = startswith(line, "__MONOTONIC_TIMESTAMP=");
- if (timestamp) {
- long long unsigned x;
- line[n-1] = '\0';
- r = safe_atollu(timestamp, &x);
- if (r < 0)
- log_warning("Failed to parse __MONOTONIC_TIMESTAMP: '%s'", timestamp);
- else
- source->ts.monotonic = x;
- return r < 0 ? r : 1;
- }
-
- timestamp = startswith(line, "__");
- if (timestamp) {
- log_notice("Unknown dunder line %s", line);
- return 1;
- }
-
- /* no dunder */
- return 0;
-}
-
-static int process_data(RemoteSource *source) {
- int r;
-
- switch(source->state) {
- case STATE_LINE: {
- char *line, *sep;
- size_t n = 0;
-
- assert(source->data_size == 0);
-
- r = get_line(source, &line, &n);
- if (r < 0)
- return r;
- if (r == 0) {
- source->state = STATE_EOF;
- return r;
- }
- assert(n > 0);
- assert(line[n-1] == '\n');
-
- if (n == 1) {
- log_trace("Received empty line, event is ready");
- return 1;
- }
-
- r = process_dunder(source, line, n);
- if (r != 0)
- return r < 0 ? r : 0;
-
- /* MESSAGE=xxx\n
- or
- COREDUMP\n
- LLLLLLLL0011223344...\n
- */
- sep = memchr(line, '=', n);
- if (sep) {
- /* chomp newline */
- n--;
-
- r = iovw_put(&source->iovw, line, n);
- if (r < 0)
- return r;
- } else {
- /* replace \n with = */
- line[n-1] = '=';
-
- source->field_len = n;
- source->state = STATE_DATA_START;
-
- /* we cannot put the field in iovec until we have all data */
- }
-
- log_trace("Received: %.*s (%s)", (int) n, line, sep ? "text" : "binary");
-
- return 0; /* continue */
- }
-
- case STATE_DATA_START:
- assert(source->data_size == 0);
-
- r = get_data_size(source);
- // log_debug("get_data_size() -> %d", r);
- if (r < 0)
- return r;
- if (r == 0) {
- source->state = STATE_EOF;
- return 0;
- }
-
- source->state = source->data_size > 0 ?
- STATE_DATA : STATE_DATA_FINISH;
-
- return 0; /* continue */
-
- case STATE_DATA: {
- void *data;
- char *field;
-
- assert(source->data_size > 0);
-
- r = get_data_data(source, &data);
- // log_debug("get_data_data() -> %d", r);
- if (r < 0)
- return r;
- if (r == 0) {
- source->state = STATE_EOF;
- return 0;
- }
-
- assert(data);
-
- field = (char*) data - sizeof(uint64_t) - source->field_len;
- memmove(field + sizeof(uint64_t), field, source->field_len);
-
- r = iovw_put(&source->iovw, field + sizeof(uint64_t), source->field_len + source->data_size);
- if (r < 0)
- return r;
-
- source->state = STATE_DATA_FINISH;
-
- return 0; /* continue */
- }
-
- case STATE_DATA_FINISH:
- r = get_data_newline(source);
- // log_debug("get_data_newline() -> %d", r);
- if (r < 0)
- return r;
- if (r == 0) {
- source->state = STATE_EOF;
- return 0;
- }
-
- source->data_size = 0;
- source->state = STATE_LINE;
-
- return 0; /* continue */
- default:
- assert_not_reached("wtf?");
- }
-}
-
int process_source(RemoteSource *source, bool compress, bool seal) {
- size_t remain, target;
int r;
assert(source);
assert(source->writer);
- r = process_data(source);
+ r = journal_importer_process_data(&source->importer);
if (r <= 0)
return r;
/* We have a full event */
log_trace("Received full event from source@%p fd:%d (%s)",
- source, source->fd, source->name);
+ source, source->importer.fd, source->importer.name);
- if (!source->iovw.count) {
+ if (source->importer.iovw.count == 0) {
log_warning("Entry with no payload, skipping");
goto freeing;
}
- assert(source->iovw.iovec);
- assert(source->iovw.count);
+ assert(source->importer.iovw.iovec);
- r = writer_write(source->writer, &source->iovw, &source->ts, compress, seal);
+ r = writer_write(source->writer, &source->importer.iovw, &source->importer.ts, compress, seal);
if (r < 0)
log_error_errno(r, "Failed to write entry of %zu bytes: %m",
- iovw_size(&source->iovw));
+ iovw_size(&source->importer.iovw));
else
r = 1;
freeing:
- iovw_free_contents(&source->iovw);
-
- /* possibly reset buffer position */
- remain = source->filled - source->offset;
-
- if (remain == 0) /* no brainer */
- source->offset = source->scanned = source->filled = 0;
- else if (source->offset > source->size - source->filled &&
- source->offset > remain) {
- memcpy(source->buf, source->buf + source->offset, remain);
- source->offset = source->scanned = 0;
- source->filled = remain;
- }
-
- target = source->size;
- while (target > 16 * LINE_CHUNK && source->filled < target / 2)
- target /= 2;
- if (target < source->size) {
- char *tmp;
-
- tmp = realloc(source->buf, target);
- if (!tmp)
- log_warning("Failed to reallocate buffer to (smaller) size %zu",
- target);
- else {
- log_debug("Reallocated buffer from %zu to %zu bytes",
- source->size, target);
- source->buf = tmp;
- source->size = target;
- }
- }
-
+ journal_importer_drop_iovw(&source->importer);
return r;
}
diff --git a/src/journal-remote/journal-remote-parse.h b/src/journal-remote/journal-remote-parse.h
index 1740a21f92..e3632528cf 100644
--- a/src/journal-remote/journal-remote-parse.h
+++ b/src/journal-remote/journal-remote-parse.h
@@ -21,34 +21,11 @@
#include "sd-event.h"
+#include "journal-importer.h"
#include "journal-remote-write.h"
-typedef enum {
- STATE_LINE = 0, /* waiting to read, or reading line */
- STATE_DATA_START, /* reading binary data header */
- STATE_DATA, /* reading binary data */
- STATE_DATA_FINISH, /* expecting newline */
- STATE_EOF, /* done */
-} source_state;
-
typedef struct RemoteSource {
- char *name;
- int fd;
- bool passive_fd;
-
- char *buf;
- size_t size; /* total size of the buffer */
- size_t offset; /* offset to the beginning of live data in the buffer */
- size_t scanned; /* number of bytes since the beginning of data without a newline */
- size_t filled; /* total number of bytes in the buffer */
-
- size_t field_len; /* used for binary fields: the field name length */
- size_t data_size; /* and the size of the binary data chunk being processed */
-
- struct iovec_wrapper iovw;
-
- source_state state;
- dual_timestamp ts;
+ JournalImporter importer;
Writer *writer;
@@ -57,13 +34,5 @@ typedef struct RemoteSource {
} RemoteSource;
RemoteSource* source_new(int fd, bool passive_fd, char *name, Writer *writer);
-
-static inline size_t source_non_empty(RemoteSource *source) {
- assert(source);
-
- return source->filled;
-}
-
void source_free(RemoteSource *source);
-int push_data(RemoteSource *source, const char *data, size_t size);
int process_source(RemoteSource *source, bool compress, bool seal);
diff --git a/src/journal-remote/journal-remote-write.c b/src/journal-remote/journal-remote-write.c
index 8729372aa3..734cad333f 100644
--- a/src/journal-remote/journal-remote-write.c
+++ b/src/journal-remote/journal-remote-write.c
@@ -20,39 +20,6 @@
#include "alloc-util.h"
#include "journal-remote.h"
-int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len) {
- if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1))
- return log_oom();
-
- iovw->iovec[iovw->count++] = (struct iovec) {data, len};
- return 0;
-}
-
-void iovw_free_contents(struct iovec_wrapper *iovw) {
- iovw->iovec = mfree(iovw->iovec);
- iovw->size_bytes = iovw->count = 0;
-}
-
-size_t iovw_size(struct iovec_wrapper *iovw) {
- size_t n = 0, i;
-
- for (i = 0; i < iovw->count; i++)
- n += iovw->iovec[i].iov_len;
-
- return n;
-}
-
-void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) {
- size_t i;
-
- for (i = 0; i < iovw->count; i++)
- iovw->iovec[i].iov_base = (char*) iovw->iovec[i].iov_base - old + new;
-}
-
-/**********************************************************************
- **********************************************************************
- **********************************************************************/
-
static int do_rotate(JournalFile **f, bool compress, bool seal) {
int r = journal_file_rotate(f, compress, seal, NULL);
if (r < 0) {
diff --git a/src/journal-remote/journal-remote-write.h b/src/journal-remote/journal-remote-write.h
index 53ba45fc04..e04af54e55 100644
--- a/src/journal-remote/journal-remote-write.h
+++ b/src/journal-remote/journal-remote-write.h
@@ -20,20 +20,10 @@
***/
#include "journal-file.h"
+#include "journal-importer.h"
typedef struct RemoteServer RemoteServer;
-struct iovec_wrapper {
- struct iovec *iovec;
- size_t size_bytes;
- size_t count;
-};
-
-int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len);
-void iovw_free_contents(struct iovec_wrapper *iovw);
-size_t iovw_size(struct iovec_wrapper *iovw);
-void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new);
-
typedef struct Writer {
JournalFile *journal;
JournalMetrics metrics;
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index d0d8d936e3..202a5a3f97 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -512,7 +512,8 @@ static int process_http_upload(
if (*upload_data_size) {
log_trace("Received %zu bytes", *upload_data_size);
- r = push_data(source, upload_data, *upload_data_size);
+ r = journal_importer_push_data(&source->importer,
+ upload_data, *upload_data_size);
if (r < 0)
return mhd_respond_oom(connection);
@@ -542,7 +543,7 @@ static int process_http_upload(
/* The upload is finished */
- remaining = source_non_empty(source);
+ remaining = journal_importer_bytes_remaining(&source->importer);
if (remaining > 0) {
log_warning("Premature EOF byte. %zu bytes lost.", remaining);
return mhd_respondf(connection,
@@ -1036,19 +1037,19 @@ static int handle_raw_source(sd_event_source *event,
assert(fd >= 0 && fd < (ssize_t) s->sources_size);
source = s->sources[fd];
- assert(source->fd == fd);
+ assert(source->importer.fd == fd);
r = process_source(source, arg_compress, arg_seal);
- if (source->state == STATE_EOF) {
+ if (journal_importer_eof(&source->importer)) {
size_t remaining;
- log_debug("EOF reached with source fd:%d (%s)",
- source->fd, source->name);
+ log_debug("EOF reached with source %s (fd=%d)",
+ source->importer.name, source->importer.fd);
- remaining = source_non_empty(source);
+ remaining = journal_importer_bytes_remaining(&source->importer);
if (remaining > 0)
log_notice("Premature EOF. %zu bytes lost.", remaining);
- remove_source(s, source->fd);
+ remove_source(s, source->importer.fd);
log_debug("%zu active sources remaining", s->active);
return 0;
} else if (r == -E2BIG) {
@@ -1072,7 +1073,7 @@ static int dispatch_raw_source_until_block(sd_event_source *event,
/* Make sure event stays around even if source is destroyed */
sd_event_source_ref(event);
- r = handle_raw_source(event, source->fd, EPOLLIN, server);
+ r = handle_raw_source(event, source->importer.fd, EPOLLIN, server);
if (r != 1)
/* No more data for now */
sd_event_source_set_enabled(event, SD_EVENT_OFF);
@@ -1105,7 +1106,7 @@ static int dispatch_blocking_source_event(sd_event_source *event,
void *userdata) {
RemoteSource *source = userdata;
- return handle_raw_source(event, source->fd, EPOLLIN, server);
+ return handle_raw_source(event, source->importer.fd, EPOLLIN, server);
}
static int accept_connection(const char* type, int fd,
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index ef87b176fa..a6ccb679a8 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -3283,7 +3283,7 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred
return -EINVAL;
/* Is this a journal file that was passed to us as fd? If so, we synthesized a path name for it, and we refuse
- * rotation, since we don't know the actual path, and couldn't rename the file hence.*/
+ * rotation, since we don't know the actual path, and couldn't rename the file hence. */
if (path_startswith(old_file->path, "/proc/self/fd"))
return -EINVAL;
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 9ad6f115a1..ad11fb314d 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -52,6 +52,7 @@
#include "journal-def.h"
#include "journal-internal.h"
#include "journal-qrcode.h"
+#include "journal-util.h"
#include "journal-vacuum.h"
#include "journal-verify.h"
#include "locale-util.h"
@@ -912,7 +913,7 @@ static int generate_new_id128(void) {
SD_ID128_FORMAT_STR "\n\n"
"As UUID:\n"
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
- "As macro:\n"
+ "As man:sd-id128(3) macro:\n"
"#define MESSAGE_XYZ SD_ID128_MAKE(",
SD_ID128_FORMAT_VAL(id),
SD_ID128_FORMAT_VAL(id));
@@ -1805,131 +1806,6 @@ static int verify(sd_journal *j) {
return r;
}
-static int access_check_var_log_journal(sd_journal *j) {
-#ifdef HAVE_ACL
- _cleanup_strv_free_ char **g = NULL;
- const char* dir;
-#endif
- int r;
-
- assert(j);
-
- if (arg_quiet)
- return 0;
-
- /* If we are root, we should have access, don't warn. */
- if (getuid() == 0)
- return 0;
-
- /* If we are in the 'systemd-journal' group, we should have
- * access too. */
- r = in_group("systemd-journal");
- if (r < 0)
- return log_error_errno(r, "Failed to check if we are in the 'systemd-journal' group: %m");
- if (r > 0)
- return 0;
-
-#ifdef HAVE_ACL
- if (laccess("/run/log/journal", F_OK) >= 0)
- dir = "/run/log/journal";
- else
- dir = "/var/log/journal";
-
- /* If we are in any of the groups listed in the journal ACLs,
- * then all is good, too. Let's enumerate all groups from the
- * default ACL of the directory, which generally should allow
- * access to most journal files too. */
- r = acl_search_groups(dir, &g);
- if (r < 0)
- return log_error_errno(r, "Failed to search journal ACL: %m");
- if (r > 0)
- return 0;
-
- /* Print a pretty list, if there were ACLs set. */
- if (!strv_isempty(g)) {
- _cleanup_free_ char *s = NULL;
-
- /* Thre are groups in the ACL, let's list them */
- r = strv_extend(&g, "systemd-journal");
- if (r < 0)
- return log_oom();
-
- strv_sort(g);
- strv_uniq(g);
-
- s = strv_join(g, "', '");
- if (!s)
- return log_oom();
-
- log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
- " Users in groups '%s' can see all messages.\n"
- " Pass -q to turn off this notice.", s);
- return 1;
- }
-#endif
-
- /* If no ACLs were found, print a short version of the message. */
- log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
- " Users in the 'systemd-journal' group can see all messages. Pass -q to\n"
- " turn off this notice.");
-
- return 1;
-}
-
-static int access_check(sd_journal *j) {
- Iterator it;
- void *code;
- char *path;
- int r = 0;
-
- assert(j);
-
- if (hashmap_isempty(j->errors)) {
- if (ordered_hashmap_isempty(j->files))
- log_notice("No journal files were found.");
-
- return 0;
- }
-
- if (hashmap_contains(j->errors, INT_TO_PTR(-EACCES))) {
- (void) access_check_var_log_journal(j);
-
- if (ordered_hashmap_isempty(j->files))
- r = log_error_errno(EACCES, "No journal files were opened due to insufficient permissions.");
- }
-
- HASHMAP_FOREACH_KEY(path, code, j->errors, it) {
- int err;
-
- err = abs(PTR_TO_INT(code));
-
- switch (err) {
- case EACCES:
- continue;
-
- case ENODATA:
- log_warning_errno(err, "Journal file %s is truncated, ignoring file.", path);
- break;
-
- case EPROTONOSUPPORT:
- 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:
- log_warning_errno(err, "Journal file %s corrupted, ignoring file.", path);
- break;
-
- default:
- log_warning_errno(err, "An error was encountered while opening journal file or directory %s, ignoring file: %m", path);
- break;
- }
- }
-
- return r;
-}
-
static int flush_to_var(void) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
@@ -2241,7 +2117,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- r = access_check(j);
+ r = journal_access_check_and_warn(j, arg_quiet);
if (r < 0)
goto finish;
diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c
index 18c8644507..8afaec0ced 100644
--- a/src/journal/journald-kmsg.c
+++ b/src/journal/journald-kmsg.c
@@ -156,7 +156,8 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
/* Did we lose any? */
if (serial > *s->kernel_seqnum)
- server_driver_message(s, SD_MESSAGE_JOURNAL_MISSED,
+ server_driver_message(s,
+ "MESSAGE_ID=" SD_MESSAGE_JOURNAL_MISSED_STR,
LOG_MESSAGE("Missed %"PRIu64" kernel messages",
serial - *s->kernel_seqnum),
NULL);
diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c
index 0a1ce205c2..3c03b83754 100644
--- a/src/journal/journald-native.c
+++ b/src/journal/journald-native.c
@@ -27,6 +27,7 @@
#include "fd-util.h"
#include "fs-util.h"
#include "io-util.h"
+#include "journal-importer.h"
#include "journald-console.h"
#include "journald-kmsg.h"
#include "journald-native.h"
diff --git a/src/journal/journald-native.h b/src/journal/journald-native.h
index c13b80aa4f..1ab415ac85 100644
--- a/src/journal/journald-native.h
+++ b/src/journal/journald-native.h
@@ -21,11 +21,6 @@
#include "journald-server.h"
-/* Make sure not to make this smaller than the maximum coredump
- * size. See COREDUMP_MAX in coredump.c */
-#define ENTRY_SIZE_MAX (1024*1024*770u)
-#define DATA_SIZE_MAX (1024*1024*768u)
-
bool valid_user_field(const char *p, size_t l, bool allow_protected);
void server_process_native_message(Server *s, const void *buffer, size_t buffer_size, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len);
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 8b92ea3def..6466e46ccc 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -214,7 +214,7 @@ void server_space_usage_message(Server *s, JournalStorage *storage) {
format_bytes(fb5, sizeof(fb5), storage->space.limit);
format_bytes(fb6, sizeof(fb6), storage->space.available);
- server_driver_message(s, SD_MESSAGE_JOURNAL_USAGE,
+ server_driver_message(s, "MESSAGE_ID=" SD_MESSAGE_JOURNAL_USAGE_STR,
LOG_MESSAGE("%s (%s) is %s, max %s, %s free.",
storage->name, storage->path, fb1, fb5, fb6),
"JOURNAL_NAME=%s", storage->name,
@@ -760,7 +760,8 @@ static void dispatch_message_real(
const char *label, size_t label_len,
const char *unit_id,
int priority,
- pid_t object_pid) {
+ pid_t object_pid,
+ char *cgroup) {
char pid[sizeof("_PID=") + DECIMAL_STR_MAX(pid_t)],
uid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)],
@@ -846,7 +847,12 @@ static void dispatch_message_real(
}
#endif
- r = cg_pid_get_path_shifted(ucred->pid, s->cgroup_root, &c);
+ r = 0;
+ if (cgroup)
+ c = cgroup;
+ else
+ r = cg_pid_get_path_shifted(ucred->pid, s->cgroup_root, &c);
+
if (r >= 0) {
_cleanup_free_ char *raw_unit = NULL, *raw_slice = NULL;
char *session = NULL;
@@ -904,7 +910,8 @@ static void dispatch_message_real(
}
}
- free(c);
+ if (!cgroup)
+ free(c);
} else if (unit_id) {
x = strjoina("_SYSTEMD_UNIT=", unit_id);
IOVEC_SET_STRING(iovec[n++], x);
@@ -1061,8 +1068,7 @@ static void dispatch_message_real(
write_to_journal(s, journal_uid, iovec, n, priority);
}
-void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) {
- char mid[11 + 32 + 1];
+void server_driver_message(Server *s, const char *message_id, const char *format, ...) {
struct iovec iovec[N_IOVEC_META_FIELDS + 5 + N_IOVEC_PAYLOAD_FIELDS];
unsigned n = 0, m;
int r;
@@ -1080,11 +1086,8 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format,
assert_cc(6 == LOG_INFO);
IOVEC_SET_STRING(iovec[n++], "PRIORITY=6");
- if (!sd_id128_is_null(message_id)) {
- snprintf(mid, sizeof(mid), LOG_MESSAGE_ID(message_id));
- IOVEC_SET_STRING(iovec[n++], mid);
- }
-
+ if (message_id)
+ IOVEC_SET_STRING(iovec[n++], message_id);
m = n;
va_start(ap, format);
@@ -1097,7 +1100,7 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format,
ucred.gid = getgid();
if (r >= 0)
- dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0);
+ dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0, NULL);
while (m < n)
free(iovec[m++].iov_base);
@@ -1111,7 +1114,7 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format,
n = 3;
IOVEC_SET_STRING(iovec[n++], "PRIORITY=4");
IOVEC_SET_STRING(iovec[n++], buf);
- dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0);
+ dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0, NULL);
}
}
@@ -1128,7 +1131,7 @@ void server_dispatch_message(
int rl, r;
_cleanup_free_ char *path = NULL;
uint64_t available = 0;
- char *c;
+ char *c = NULL;
assert(s);
assert(iovec || n == 0);
@@ -1174,12 +1177,15 @@ void server_dispatch_message(
/* Write a suppression message if we suppressed something */
if (rl > 1)
- server_driver_message(s, SD_MESSAGE_JOURNAL_DROPPED,
+ server_driver_message(s, "MESSAGE_ID=" SD_MESSAGE_JOURNAL_DROPPED_STR,
LOG_MESSAGE("Suppressed %u messages from %s", rl - 1, path),
NULL);
finish:
- dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id, priority, object_pid);
+ /* restore cgroup path for logging */
+ if (c)
+ *c = '/';
+ dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id, priority, object_pid, path);
}
int server_flush_to_var(Server *s, bool require_flag_file) {
@@ -1273,7 +1279,7 @@ finish:
sd_journal_close(j);
- server_driver_message(s, SD_ID128_NULL,
+ server_driver_message(s, NULL,
LOG_MESSAGE("Time spent on flushing to /var is %s for %u entries.",
format_timespan(ts, sizeof(ts), now(CLOCK_MONOTONIC) - start, 0),
n),
diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h
index 716e758b7c..75ac114d24 100644
--- a/src/journal/journald-server.h
+++ b/src/journal/journald-server.h
@@ -176,7 +176,7 @@ struct Server {
#define N_IOVEC_PAYLOAD_FIELDS 15
void server_dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len, const char *unit_id, int priority, pid_t object_pid);
-void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) _printf_(3,0) _sentinel_;
+void server_driver_message(Server *s, const char *message_id, const char *format, ...) _printf_(3,0) _sentinel_;
/* gperf lookup function */
const struct ConfigPerfItem* journald_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c
index 896303fb85..474369039a 100644
--- a/src/journal/journald-syslog.c
+++ b/src/journal/journald-syslog.c
@@ -444,7 +444,8 @@ void server_maybe_warn_forward_syslog_missed(Server *s) {
if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
return;
- server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED,
+ server_driver_message(s,
+ "MESSAGE_ID=" SD_MESSAGE_FORWARD_SYSLOG_MISSED_STR,
LOG_MESSAGE("Forwarding to syslog missed %u messages.",
s->n_forward_syslog_missed),
NULL);
diff --git a/src/journal/journald.c b/src/journal/journald.c
index 54fd1f999d..1aaef387b4 100644
--- a/src/journal/journald.c
+++ b/src/journal/journald.c
@@ -56,7 +56,8 @@ int main(int argc, char *argv[]) {
server_flush_dev_kmsg(&server);
log_debug("systemd-journald running as pid "PID_FMT, getpid());
- server_driver_message(&server, SD_MESSAGE_JOURNAL_START,
+ server_driver_message(&server,
+ "MESSAGE_ID=" SD_MESSAGE_JOURNAL_START_STR,
LOG_MESSAGE("Journal started"),
NULL);
@@ -114,7 +115,8 @@ int main(int argc, char *argv[]) {
}
log_debug("systemd-journald stopped as pid "PID_FMT, getpid());
- server_driver_message(&server, SD_MESSAGE_JOURNAL_STOP,
+ server_driver_message(&server,
+ "MESSAGE_ID=" SD_MESSAGE_JOURNAL_STOP_STR,
LOG_MESSAGE("Journal stopped"),
NULL);
diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c
index 0469393f3b..44a2cf5217 100644
--- a/src/journal/test-compress.c
+++ b/src/journal/test-compress.c
@@ -109,7 +109,7 @@ static void test_decompress_startswith(int compression,
size_t csize, usize = 0, len;
int r;
- log_info("/* testing decompress_startswith with %s on %.20s text*/",
+ log_info("/* testing decompress_startswith with %s on %.20s text */",
object_compressed_to_string(compression), data);
#define BUFSIZE_1 512
diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c
index 02028bf28a..2e02b3fa66 100644
--- a/src/libsystemd-network/arp-util.c
+++ b/src/libsystemd-network/arp-util.c
@@ -58,7 +58,7 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about*/
+ /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about */
BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index b4bf75a3dc..7c0317640f 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -825,6 +825,15 @@ static int client_send_request(sd_dhcp_client *client) {
return r;
}
+ if (client->vendor_class_identifier) {
+ r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
+ SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
+ strlen(client->vendor_class_identifier),
+ client->vendor_class_identifier);
+ if (r < 0)
+ return r;
+ }
+
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c
index 4dd343c101..2ebc00f247 100644
--- a/src/libsystemd-network/sd-ipv4acd.c
+++ b/src/libsystemd-network/sd-ipv4acd.c
@@ -242,8 +242,6 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata)
r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC);
if (r < 0)
goto fail;
-
- acd->n_conflict = 0;
} else {
r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC);
if (r < 0)
diff --git a/src/libsystemd-network/test-lldp.c b/src/libsystemd-network/test-lldp.c
index 6bcd65de0a..430c58ae60 100644
--- a/src/libsystemd-network/test-lldp.c
+++ b/src/libsystemd-network/test-lldp.c
@@ -98,14 +98,14 @@ static void test_receive_basic_packet(sd_event *e) {
static const uint8_t frame[] = {
/* Ethernet header */
- 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
0x88, 0xcc, /* Ethertype */
/* LLDP mandatory TLVs */
0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
0x03, 0x04, 0x05,
0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */
- 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds*/
+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
/* LLDP optional TLVs */
0x08, 0x04, 0x50, 0x6f, 0x72, 0x74, /* Port Description: "Port" */
0x0a, 0x03, 0x53, 0x59, 0x53, /* System Name: "SYS" */
@@ -162,7 +162,7 @@ static void test_receive_incomplete_packet(sd_event *e) {
sd_lldp_neighbor **neighbors;
uint8_t frame[] = {
/* Ethernet header */
- 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
0x88, 0xcc, /* Ethertype */
/* LLDP mandatory TLVs */
@@ -189,14 +189,14 @@ static void test_receive_oui_packet(sd_event *e) {
sd_lldp_neighbor **neighbors;
uint8_t frame[] = {
/* Ethernet header */
- 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
0x88, 0xcc, /* Ethertype */
/* LLDP mandatory TLVs */
0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */
0x03, 0x04, 0x05,
0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port TLV: interface name, "1/3" */
- 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds*/
+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
/* LLDP optional TLVs */
0xfe, 0x06, 0x00, 0x80, 0xc2, 0x01, /* Port VLAN ID: 0x1234 */
0x12, 0x34,
diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c
index 6fdcfa4128..64bd76a576 100644
--- a/src/libsystemd/sd-bus/test-bus-creds.c
+++ b/src/libsystemd/sd-bus/test-bus-creds.c
@@ -31,7 +31,7 @@ int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
- if (cg_all_unified() == -ENOMEDIUM) {
+ if (cg_unified_flush() == -ENOMEDIUM) {
log_info("Skipping test: /sys/fs/cgroup/ not available");
return EXIT_TEST_SKIP;
}
diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h
index 9fad388953..f4783deef8 100644
--- a/src/libsystemd/sd-device/device-internal.h
+++ b/src/libsystemd/sd-device/device-internal.h
@@ -34,7 +34,7 @@ struct sd_device {
uint64_t properties_generation; /* changes whenever the properties are changed */
uint64_t properties_iterator_generation; /* generation when iteration was started */
- /* the subset of the properties that should be written to the db*/
+ /* the subset of the properties that should be written to the db */
OrderedHashmap *properties_db;
Hashmap *sysattr_values; /* cached sysattr values */
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index efeadf0cd4..04ead29338 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -1859,8 +1859,7 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr,
_cleanup_free_ char *value = NULL;
const char *syspath;
char *path;
- struct stat statbuf;
- size_t value_len = 0;
+ size_t len = 0;
ssize_t size;
int r;
@@ -1878,8 +1877,14 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr,
return r;
path = strjoina(syspath, "/", sysattr);
- r = lstat(path, &statbuf);
- if (r < 0) {
+
+ fd = open(path, O_WRONLY | O_CLOEXEC | O_NOFOLLOW);
+ if (fd < 0) {
+ if (errno == ELOOP)
+ return -EINVAL;
+ if (errno == EISDIR)
+ return -EISDIR;
+
value = strdup("");
if (!value)
return -ENOMEM;
@@ -1891,46 +1896,30 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr,
return -ENXIO;
}
- if (S_ISLNK(statbuf.st_mode))
- return -EINVAL;
-
- /* skip directories */
- if (S_ISDIR(statbuf.st_mode))
- return -EISDIR;
-
- /* skip non-readable files */
- if ((statbuf.st_mode & S_IRUSR) == 0)
- return -EACCES;
-
- value_len = strlen(_value);
+ len = strlen(_value);
/* drop trailing newlines */
- while (value_len > 0 && _value[value_len - 1] == '\n')
- _value[--value_len] = '\0';
+ while (len > 0 && _value[len - 1] == '\n')
+ len --;
/* value length is limited to 4k */
- if (value_len > 4096)
+ if (len > 4096)
return -EINVAL;
- fd = open(path, O_WRONLY | O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- value = strdup(_value);
+ value = strndup(_value, len);
if (!value)
return -ENOMEM;
- size = write(fd, value, value_len);
+ size = write(fd, value, len);
if (size < 0)
return -errno;
- if ((size_t)size != value_len)
+ if ((size_t)size != len)
return -EIO;
r = device_add_sysattr_value(device, sysattr, value);
if (r < 0)
return r;
-
value = NULL;
return 0;
diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c
index a0fd8a3ac9..129bfd2d80 100644
--- a/src/libsystemd/sd-netlink/netlink-socket.c
+++ b/src/libsystemd/sd-netlink/netlink-socket.c
@@ -281,7 +281,7 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool
else if (errno == EAGAIN)
log_debug("rtnl: no data in socket");
- return (errno == EAGAIN || errno == EINTR) ? 0 : -errno;
+ return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
}
if (sender.nl.nl_pid != 0) {
@@ -292,7 +292,7 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool
/* drop the message */
r = recvmsg(fd, &msg, 0);
if (r < 0)
- return (errno == EAGAIN || errno == EINTR) ? 0 : -errno;
+ return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
}
return 0;
diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c
index 0f8b0cc70b..ff0e99558e 100644
--- a/src/libsystemd/sd-netlink/netlink-types.c
+++ b/src/libsystemd/sd-netlink/netlink-types.c
@@ -149,7 +149,7 @@ static const NLType rtnl_link_info_data_vxlan_types[] = {
[IFLA_VXLAN_ID] = { .type = NETLINK_TYPE_U32 },
[IFLA_VXLAN_GROUP] = { .type = NETLINK_TYPE_IN_ADDR },
[IFLA_VXLAN_LINK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VXLAN_LOCAL] = { .type = NETLINK_TYPE_U32},
+ [IFLA_VXLAN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
[IFLA_VXLAN_TTL] = { .type = NETLINK_TYPE_U8 },
[IFLA_VXLAN_TOS] = { .type = NETLINK_TYPE_U8 },
[IFLA_VXLAN_LEARNING] = { .type = NETLINK_TYPE_U8 },
diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c
index 43114eb825..68435564de 100644
--- a/src/libsystemd/sd-netlink/sd-netlink.c
+++ b/src/libsystemd/sd-netlink/sd-netlink.c
@@ -276,6 +276,10 @@ static int dispatch_rqueue(sd_netlink *rtnl, sd_netlink_message **message) {
if (rtnl->rqueue_size <= 0) {
/* Try to read a new message */
r = socket_read_message(rtnl);
+ if (r == -ENOBUFS) { /* FIXME: ignore buffer overruns for now */
+ log_debug_errno(r, "Got ENOBUFS from netlink socket, ignoring.");
+ return 1;
+ }
if (r <= 0)
return r;
}
diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c
index 0d8d99c56d..8b4af5a2c3 100644
--- a/src/libsystemd/sd-network/sd-network.c
+++ b/src/libsystemd/sd-network/sd-network.c
@@ -245,7 +245,7 @@ static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) {
}
if (ifis)
- ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice*/
+ ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice */
*ret = ifis;
ifis = NULL;
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 1aac7ae979..7dea5c0859 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -482,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");
@@ -611,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");
@@ -685,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-button.c b/src/login/logind-button.c
index 90fb93bbaf..d739af8ea2 100644
--- a/src/login/logind-button.c
+++ b/src/login/logind-button.c
@@ -155,7 +155,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
case KEY_POWER2:
log_struct(LOG_INFO,
LOG_MESSAGE("Power key pressed."),
- LOG_MESSAGE_ID(SD_MESSAGE_POWER_KEY),
+ "MESSAGE_ID=" SD_MESSAGE_POWER_KEY_STR,
NULL);
manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
@@ -170,7 +170,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
case KEY_SLEEP:
log_struct(LOG_INFO,
LOG_MESSAGE("Suspend key pressed."),
- LOG_MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY),
+ "MESSAGE_ID=" SD_MESSAGE_SUSPEND_KEY_STR,
NULL);
manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
@@ -179,7 +179,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
case KEY_SUSPEND:
log_struct(LOG_INFO,
LOG_MESSAGE("Hibernate key pressed."),
- LOG_MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY),
+ "MESSAGE_ID=" SD_MESSAGE_HIBERNATE_KEY_STR,
NULL);
manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
@@ -191,7 +191,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
if (ev.code == SW_LID) {
log_struct(LOG_INFO,
LOG_MESSAGE("Lid closed."),
- LOG_MESSAGE_ID(SD_MESSAGE_LID_CLOSED),
+ "MESSAGE_ID=" SD_MESSAGE_LID_CLOSED_STR,
NULL);
b->lid_closed = true;
@@ -201,7 +201,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
} else if (ev.code == SW_DOCK) {
log_struct(LOG_INFO,
LOG_MESSAGE("System docked."),
- LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_DOCKED),
+ "MESSAGE_ID=" SD_MESSAGE_SYSTEM_DOCKED_STR,
NULL);
b->docked = true;
@@ -212,7 +212,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
if (ev.code == SW_LID) {
log_struct(LOG_INFO,
LOG_MESSAGE("Lid opened."),
- LOG_MESSAGE_ID(SD_MESSAGE_LID_OPENED),
+ "MESSAGE_ID=" SD_MESSAGE_LID_OPENED_STR,
NULL);
b->lid_closed = false;
@@ -221,7 +221,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
} else if (ev.code == SW_DOCK) {
log_struct(LOG_INFO,
LOG_MESSAGE("System undocked."),
- LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_UNDOCKED),
+ "MESSAGE_ID=" SD_MESSAGE_SYSTEM_UNDOCKED_STR,
NULL);
b->docked = false;
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index ad44ca290e..c6be596af3 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -1430,7 +1430,7 @@ static int bus_manager_log_shutdown(
p = strjoina(p, " (", m->wall_message, ").");
return log_struct(LOG_NOTICE,
- LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN),
+ "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
p,
q,
NULL);
diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c
index bfbd07309d..30dac7997b 100644
--- a/src/login/logind-seat.c
+++ b/src/login/logind-seat.c
@@ -379,7 +379,8 @@ int seat_read_active_vt(Seat *s) {
if (!seat_has_vts(s))
return 0;
- lseek(s->manager->console_active_fd, SEEK_SET, 0);
+ if (lseek(s->manager->console_active_fd, SEEK_SET, 0) < 0)
+ return log_error_errno(errno, "lseek on console_active_fd failed: %m");
k = read(s->manager->console_active_fd, t, sizeof(t)-1);
if (k <= 0) {
@@ -396,10 +397,8 @@ int seat_read_active_vt(Seat *s) {
}
r = safe_atou(t+3, &vtnr);
- if (r < 0) {
- log_error("Failed to parse VT number %s", t+3);
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse VT number \"%s\": %m", t+3);
if (!vtnr) {
log_error("VT number invalid: %s", t+3);
@@ -416,7 +415,7 @@ int seat_start(Seat *s) {
return 0;
log_struct(LOG_INFO,
- LOG_MESSAGE_ID(SD_MESSAGE_SEAT_START),
+ "MESSAGE_ID=" SD_MESSAGE_SEAT_START_STR,
"SEAT_ID=%s", s->id,
LOG_MESSAGE("New seat %s.", s->id),
NULL);
@@ -444,7 +443,7 @@ int seat_stop(Seat *s, bool force) {
if (s->started)
log_struct(LOG_INFO,
- LOG_MESSAGE_ID(SD_MESSAGE_SEAT_STOP),
+ "MESSAGE_ID=" SD_MESSAGE_SEAT_STOP_STR,
"SEAT_ID=%s", s->id,
LOG_MESSAGE("Removed seat %s.", s->id),
NULL);
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index fd7fcf7f2c..4a168906d6 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -561,7 +561,7 @@ int session_start(Session *s) {
return r;
log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
- LOG_MESSAGE_ID(SD_MESSAGE_SESSION_START),
+ "MESSAGE_ID=" SD_MESSAGE_SESSION_START_STR,
"SESSION_ID=%s", s->id,
"USER_ID=%s", s->user->name,
"LEADER="PID_FMT, s->leader,
@@ -666,7 +666,7 @@ int session_finalize(Session *s) {
if (s->started)
log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
- LOG_MESSAGE_ID(SD_MESSAGE_SESSION_STOP),
+ "MESSAGE_ID=" SD_MESSAGE_SESSION_STOP_STR,
"SESSION_ID=%s", s->id,
"USER_ID=%s", s->user->name,
"LEADER="PID_FMT, s->leader,
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index d5051007fc..2f69e2c7b7 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -374,7 +374,7 @@ static int raw_image_get_os_release(Image *image, char ***ret, sd_bus_error *err
if (fd < 0)
_exit(EXIT_FAILURE);
- r = copy_bytes(fd, pair[1], (uint64_t) -1, false);
+ r = copy_bytes(fd, pair[1], (uint64_t) -1, 0);
if (r < 0)
_exit(EXIT_FAILURE);
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index af745b6567..36568b65ef 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -411,7 +411,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
if (fd < 0)
_exit(EXIT_FAILURE);
- r = copy_bytes(fd, pair[1], (uint64_t) -1, false);
+ r = copy_bytes(fd, pair[1], (uint64_t) -1, 0);
if (r < 0)
_exit(EXIT_FAILURE);
@@ -841,6 +841,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
int read_only, make_directory;
pid_t child;
siginfo_t si;
+ uid_t uid;
int r;
assert(message);
@@ -875,6 +876,12 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
if (r == 0)
return 1; /* Will call us back */
+ r = machine_get_uid_shift(m, &uid);
+ if (r < 0)
+ return r;
+ if (uid != 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Can't bind mount on container with user namespacing applied.");
+
/* One day, when bind mounting /proc/self/fd/n works across
* namespace boundaries we should rework this logic to make
* use of it... */
@@ -1055,10 +1062,12 @@ finish:
int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *src, *dest, *host_path, *container_path, *host_basename, *host_dirname, *container_basename, *container_dirname;
_cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
+ CopyFlags copy_flags = COPY_REFLINK|COPY_MERGE;
_cleanup_close_ int hostfd = -1;
Machine *m = userdata;
bool copy_from;
pid_t child;
+ uid_t uid_shift;
char *t;
int r;
@@ -1097,6 +1106,10 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
if (r == 0)
return 1; /* Will call us back */
+ r = machine_get_uid_shift(m, &uid_shift);
+ if (r < 0)
+ return r;
+
copy_from = strstr(sd_bus_message_get_member(message), "CopyFrom");
if (copy_from) {
@@ -1151,10 +1164,13 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
goto child_fail;
}
+ /* Run the actual copy operation. Note that when an UID shift is set we'll either clamp the UID/GID to
+ * 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy
+ * the UID/GIDs as they are. */
if (copy_from)
- r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
+ r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, uid_shift == 0 ? UID_INVALID : 0, uid_shift == 0 ? GID_INVALID : 0, copy_flags);
else
- r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
+ r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, uid_shift == 0 ? UID_INVALID : uid_shift, uid_shift == 0 ? GID_INVALID : uid_shift, copy_flags);
hostfd = safe_close(hostfd);
containerfd = safe_close(containerfd);
@@ -1276,6 +1292,32 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
return sd_bus_reply_method_return(message, "h", fd);
}
+int bus_machine_method_get_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Machine *m = userdata;
+ uid_t shift = 0;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ /* You wonder why this is a method and not a property? Well, properties are not supposed to return errors, but
+ * we kinda have to for this. */
+
+ if (m->class == MACHINE_HOST)
+ return sd_bus_reply_method_return(message, "u", UINT32_C(0));
+
+ if (m->class != MACHINE_CONTAINER)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "UID/GID shift may only be determined for container machines.");
+
+ r = machine_get_uid_shift(m, &shift);
+ if (r == -ENXIO)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Machine %s uses a complex UID/GID mapping, cannot determine shift", m->name);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, "u", (uint32_t) shift);
+}
+
const sd_bus_vtable machine_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1293,6 +1335,7 @@ const sd_bus_vtable machine_vtable[] = {
SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetUIDShift", NULL, "u", bus_machine_method_get_uid_shift, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h
index c513783480..2aa7b4ce06 100644
--- a/src/machine/machine-dbus.h
+++ b/src/machine/machine-dbus.h
@@ -39,6 +39,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_get_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error);
int machine_send_signal(Machine *m, bool new_machine);
int machine_send_create_reply(Machine *m, sd_bus_error *error);
diff --git a/src/machine/machine.c b/src/machine/machine.c
index eb4b35d52a..d3433d9b96 100644
--- a/src/machine/machine.c
+++ b/src/machine/machine.c
@@ -38,6 +38,7 @@
#include "parse-util.h"
#include "process-util.h"
#include "special.h"
+#include "stdio-util.h"
#include "string-table.h"
#include "terminal-util.h"
#include "unit-name.h"
@@ -401,7 +402,7 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
return r;
log_struct(LOG_INFO,
- LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START),
+ "MESSAGE_ID=" SD_MESSAGE_MACHINE_START_STR,
"NAME=%s", m->name,
"LEADER="PID_FMT, m->leader,
LOG_MESSAGE("New machine %s.", m->name),
@@ -464,7 +465,7 @@ int machine_finalize(Machine *m) {
if (m->started)
log_struct(LOG_INFO,
- LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
+ "MESSAGE_ID=" SD_MESSAGE_MACHINE_STOP_STR,
"NAME=%s", m->name,
"LEADER="PID_FMT, m->leader,
LOG_MESSAGE("Machine %s terminated.", m->name),
@@ -604,6 +605,96 @@ void machine_release_unit(Machine *m) {
m->unit = mfree(m->unit);
}
+int machine_get_uid_shift(Machine *m, uid_t *ret) {
+ char p[strlen("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
+ uid_t uid_base, uid_shift, uid_range;
+ gid_t gid_base, gid_shift, gid_range;
+ _cleanup_fclose_ FILE *f = NULL;
+ int k;
+
+ assert(m);
+ assert(ret);
+
+ /* Return the base UID/GID of the specified machine. Note that this only works for containers with simple
+ * mappings. In most cases setups should be simple like this, and administrators should only care about the
+ * basic offset a container has relative to the host. This is what this function exposes.
+ *
+ * If we encounter any more complex mappings we politely refuse this with ENXIO. */
+
+ if (m->class == MACHINE_HOST) {
+ *ret = 0;
+ return 0;
+ }
+
+ if (m->class != MACHINE_CONTAINER)
+ return -EOPNOTSUPP;
+
+ xsprintf(p, "/proc/" PID_FMT "/uid_map", m->leader);
+ f = fopen(p, "re");
+ if (!f) {
+ if (errno == ENOENT) {
+ /* If the file doesn't exist, user namespacing is off in the kernel, return a zero mapping hence. */
+ *ret = 0;
+ return 0;
+ }
+
+ return -errno;
+ }
+
+ /* Read the first line. There's at least one. */
+ errno = 0;
+ k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
+ if (k != 3) {
+ if (ferror(f))
+ return -errno;
+
+ return -EBADMSG;
+ }
+
+ /* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */
+ if (uid_base != 0)
+ return -ENXIO;
+ /* Insist that at least the nobody user is mapped, everything else is weird, and hence complex, and we don't support it */
+ if (uid_range < (uid_t) 65534U)
+ return -ENXIO;
+
+ /* If there's more than one line, then we don't support this mapping. */
+ if (fgetc(f) != EOF)
+ return -ENXIO;
+
+ fclose(f);
+
+ xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader);
+ f = fopen(p, "re");
+ if (!f)
+ return -errno;
+
+ /* Read the first line. There's at least one. */
+ errno = 0;
+ k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
+ if (k != 3) {
+ if (ferror(f))
+ return -errno;
+
+ return -EBADMSG;
+ }
+
+ /* If there's more than one line, then we don't support this file. */
+ if (fgetc(f) != EOF)
+ return -ENXIO;
+
+ /* If the UID and GID mapping doesn't match, we don't support this mapping. */
+ if (uid_base != (uid_t) gid_base)
+ return -ENXIO;
+ if (uid_shift != (uid_t) gid_shift)
+ return -ENXIO;
+ if (uid_range != (uid_t) gid_range)
+ return -ENXIO;
+
+ *ret = uid_shift;
+ return 0;
+}
+
static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
[MACHINE_CONTAINER] = "container",
[MACHINE_VM] = "vm",
diff --git a/src/machine/machine.h b/src/machine/machine.h
index e5d75361a9..6bdb204ed6 100644
--- a/src/machine/machine.h
+++ b/src/machine/machine.h
@@ -108,3 +108,5 @@ KillWho kill_who_from_string(const char *s) _pure_;
int machine_openpt(Machine *m, int flags);
int machine_open_terminal(Machine *m, const char *path, int mode);
+
+int machine_get_uid_shift(Machine *m, uid_t *ret);
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index 4f5f659c7c..28384286fb 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -327,8 +327,10 @@ static int list_machines(int argc, char *argv[], void *userdata) {
(int) max_version_id, strdash_if_empty(machines[j].version_id));
r = print_addresses(bus, machines[j].name, 0, "", prefix, arg_addrs);
- if (r == -EOPNOTSUPP)
- printf("-\n");
+ if (r <= 0) /* error or no addresses defined? */
+ fputs("-\n", stdout);
+ else
+ fputc('\n', stdout);
}
if (arg_legend) {
@@ -520,6 +522,7 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *addresses = NULL;
bool truncate = false;
+ unsigned n = 0;
int r;
assert(bus);
@@ -567,7 +570,7 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p
else
strcpy(buf_ifi, "");
- if(!strextend(&addresses, prefix, inet_ntop(family, a, buffer, sizeof(buffer)), buf_ifi, NULL))
+ if (!strextend(&addresses, prefix, inet_ntop(family, a, buffer, sizeof(buffer)), buf_ifi, NULL))
return log_oom();
} else
truncate = true;
@@ -581,6 +584,8 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p
if (n_addr > 0)
n_addr -= 1;
+
+ n++;
}
if (r < 0)
return bus_log_parse_error(r);
@@ -589,8 +594,10 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p
if (r < 0)
return bus_log_parse_error(r);
- fprintf(stdout, "%s%s\n", addresses, truncate ? "..." : "");
- return 0;
+ if (n > 0)
+ fprintf(stdout, "%s%s", addresses, truncate ? "..." : "");
+
+ return (int) n;
}
static int print_os_release(sd_bus *bus, const char *method, const char *name, const char *prefix) {
@@ -611,6 +618,37 @@ static int print_os_release(sd_bus *bus, const char *method, const char *name, c
return 0;
}
+static int print_uid_shift(sd_bus *bus, const char *name) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ uint32_t shift;
+ int r;
+
+ assert(bus);
+ assert(name);
+
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "GetMachineUIDShift",
+ &error,
+ &reply,
+ "s", name);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to query UID/GID shift: %s", bus_error_message(&error, r));
+
+ r = sd_bus_message_read(reply, "u", &shift);
+ if (r < 0)
+ return r;
+
+ if (shift == 0) /* Don't show trivial mappings */
+ return 0;
+
+ printf(" UID Shift: %" PRIu32 "\n", shift);
+ return 0;
+}
+
typedef struct MachineStatusInfo {
char *name;
sd_id128_t id;
@@ -707,13 +745,16 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
fputc('\n', stdout);
}
- print_addresses(bus, i->name, ifi,
- "\t Address: ",
- "\n\t ",
- ALL_IP_ADDRESSES);
+ if (print_addresses(bus, i->name, ifi,
+ "\t Address: ",
+ "\n\t ",
+ ALL_IP_ADDRESSES) > 0)
+ fputc('\n', stdout);
print_os_release(bus, "GetMachineOSRelease", i->name, "\t OS: ");
+ print_uid_shift(bus, i->name);
+
if (i->unit) {
printf("\t Unit: %s\n", i->unit);
show_unit_cgroup(bus, i->unit, i->leader);
@@ -772,6 +813,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;
@@ -784,9 +826,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");
@@ -962,6 +1005,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;
@@ -973,9 +1017,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");
@@ -1029,6 +1074,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);
@@ -1037,9 +1084,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/machine/machined-dbus.c b/src/machine/machined-dbus.c
index fd9e5b56fc..c9b92d2765 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -729,6 +729,26 @@ static int method_open_machine_root_directory(sd_bus_message *message, void *use
return bus_machine_method_open_root_directory(message, machine, error);
}
+static int method_get_machine_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ Machine *machine;
+ const char *name;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ machine = hashmap_get(m->machines, name);
+ if (!machine)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
+
+ return bus_machine_method_get_uid_shift(message, machine, error);
+}
+
static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(image_unrefp) Image* i = NULL;
const char *name;
@@ -1416,6 +1436,7 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("OpenMachineRootDirectory", "s", "h", method_open_machine_root_directory, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetMachineUIDShift", "s", "u", method_get_machine_uid_shift, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/machine/operation.c b/src/machine/operation.c
index c966d0d21c..f7d5310f44 100644
--- a/src/machine/operation.c
+++ b/src/machine/operation.c
@@ -61,8 +61,10 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat
} else {
/* The default operation when done is to simply return an error on failure or an empty success
* message on success. */
- if (r < 0)
+ if (r < 0) {
+ sd_bus_error_set_errno(&error, r);
goto fail;
+ }
r = sd_bus_reply_method_return(o->message, NULL);
if (r < 0)
diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf
index 82ebfba50c..daa365a9dd 100644
--- a/src/machine/org.freedesktop.machine1.conf
+++ b/src/machine/org.freedesktop.machine1.conf
@@ -66,6 +66,10 @@
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Manager"
+ send_member="GetMachineUIDShift"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Manager"
send_member="OpenMachineLogin"/>
<allow send_destination="org.freedesktop.machine1"
@@ -150,6 +154,10 @@
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Machine"
+ send_member="GetUIDShift"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Machine"
send_member="OpenLogin"/>
<allow send_destination="org.freedesktop.machine1"
diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c
index 4b3cac8a22..b709166aa9 100644
--- a/src/mount/mount-tool.c
+++ b/src/mount/mount-tool.c
@@ -1025,7 +1025,7 @@ static int list_devices(void) {
j = items + n++;
for (c = 0; c < _COLUMN_MAX; c++) {
- const char *x;
+ const char *x = NULL;
size_t k;
switch (c) {
diff --git a/src/network/netdev/Makefile b/src/network/netdev/Makefile
new file mode 120000
index 0000000000..94aaae2c4d
--- /dev/null
+++ b/src/network/netdev/Makefile
@@ -0,0 +1 @@
+../../Makefile \ No newline at end of file
diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf
index e74ae9eb9f..e19fa9817e 100644
--- a/src/network/netdev/netdev-gperf.gperf
+++ b/src/network/netdev/netdev-gperf.gperf
@@ -26,94 +26,96 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host)
-Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt)
-Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel)
-Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch)
-NetDev.Description, config_parse_string, 0, offsetof(NetDev, description)
-NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname)
-NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind)
-NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu)
-NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac)
-VLAN.Id, config_parse_vlanid, 0, offsetof(VLan, id)
-MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
-MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
-IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode)
-Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local)
-Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote)
-Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos)
-Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl)
-Tunnel.Key, config_parse_tunnel_key, 0, offsetof(Tunnel, key)
-Tunnel.InputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, ikey)
-Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey)
-Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc)
-Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode)
-Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel)
-Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp)
-Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit)
-Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer)
-Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer)
-VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id)
-VXLAN.Group, config_parse_vxlan_group_address, 0, offsetof(VxLan, group)
-VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos)
-VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl)
-VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning)
-VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy)
-VXLAN.ReduceARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy)
-VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss)
-VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss)
-VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit)
-VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
-VXLAN.UDPChecksum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
-VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
-VXLAN.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
-VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
-VXLAN.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
-VXLAN.RemoteChecksumTx, config_parse_bool, 0, offsetof(VxLan, remote_csum_tx)
-VXLAN.RemoteChecksumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx)
-VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing)
-VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy)
-VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb)
-VXLAN.PortRange, config_parse_port_range, 0, 0
-VXLAN.DestinationPort, config_parse_destination_port, 0, offsetof(VxLan, dest_port)
-Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
-Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
-Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
-Tun.User, config_parse_string, 0, offsetof(TunTap, user_name)
-Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name)
-Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
-Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
-Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
-Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr)
-Tap.User, config_parse_string, 0, offsetof(TunTap, user_name)
-Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name)
-Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode)
-Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy)
-Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate)
-Bond.AdSelect, config_parse_bond_ad_select, 0, offsetof(Bond, ad_select)
-Bond.FailOverMACPolicy, config_parse_bond_fail_over_mac, 0, offsetof(Bond, fail_over_mac)
-Bond.ARPIPTargets, config_parse_arp_ip_target_address, 0, 0
-Bond.ARPValidate, config_parse_bond_arp_validate, 0, offsetof(Bond, arp_validate)
-Bond.ARPAllTargets, config_parse_bond_arp_all_targets, 0, offsetof(Bond, arp_all_targets)
-Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0, offsetof(Bond, primary_reselect)
-Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp)
-Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave)
-Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp)
-Bond.AllSlavesActive, config_parse_unsigned, 0, offsetof(Bond, all_slaves_active)
-Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links)
-Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon)
-Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay)
-Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay)
-Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval)
-Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval)
-Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time)
-Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age)
-Bridge.AgeingTimeSec, config_parse_sec, 0, offsetof(Bridge, ageing_time)
-Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay)
-Bridge.Priority, config_parse_uint16, 0, offsetof(Bridge, priority)
-Bridge.DefaultPVID, config_parse_vlanid, 0, offsetof(Bridge, default_pvid)
-Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier)
-Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping)
-Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering)
-Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp)
-VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table_id)
+Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host)
+Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt)
+Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel)
+Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch)
+NetDev.Description, config_parse_string, 0, offsetof(NetDev, description)
+NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname)
+NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind)
+NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu)
+NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac)
+VLAN.Id, config_parse_vlanid, 0, offsetof(VLan, id)
+MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
+MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
+IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode)
+Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local)
+Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote)
+Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos)
+Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl)
+Tunnel.Key, config_parse_tunnel_key, 0, offsetof(Tunnel, key)
+Tunnel.InputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, ikey)
+Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey)
+Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc)
+Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode)
+Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel)
+Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp)
+Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit)
+Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer)
+Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer)
+VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id)
+VXLAN.Group, config_parse_vxlan_address, 0, offsetof(VxLan, remote)
+VXLAN.Local, config_parse_vxlan_address, 0, offsetof(VxLan, local)
+VXLAN.Remote, config_parse_vxlan_address, 0, offsetof(VxLan, remote)
+VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos)
+VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl)
+VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning)
+VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy)
+VXLAN.ReduceARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy)
+VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss)
+VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss)
+VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit)
+VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
+VXLAN.UDPChecksum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
+VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
+VXLAN.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
+VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
+VXLAN.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
+VXLAN.RemoteChecksumTx, config_parse_bool, 0, offsetof(VxLan, remote_csum_tx)
+VXLAN.RemoteChecksumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx)
+VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing)
+VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy)
+VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb)
+VXLAN.PortRange, config_parse_port_range, 0, 0
+VXLAN.DestinationPort, config_parse_destination_port, 0, offsetof(VxLan, dest_port)
+Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
+Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
+Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
+Tun.User, config_parse_string, 0, offsetof(TunTap, user_name)
+Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name)
+Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
+Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
+Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
+Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr)
+Tap.User, config_parse_string, 0, offsetof(TunTap, user_name)
+Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name)
+Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode)
+Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy)
+Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate)
+Bond.AdSelect, config_parse_bond_ad_select, 0, offsetof(Bond, ad_select)
+Bond.FailOverMACPolicy, config_parse_bond_fail_over_mac, 0, offsetof(Bond, fail_over_mac)
+Bond.ARPIPTargets, config_parse_arp_ip_target_address, 0, 0
+Bond.ARPValidate, config_parse_bond_arp_validate, 0, offsetof(Bond, arp_validate)
+Bond.ARPAllTargets, config_parse_bond_arp_all_targets, 0, offsetof(Bond, arp_all_targets)
+Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0, offsetof(Bond, primary_reselect)
+Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp)
+Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave)
+Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp)
+Bond.AllSlavesActive, config_parse_unsigned, 0, offsetof(Bond, all_slaves_active)
+Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links)
+Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon)
+Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay)
+Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay)
+Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval)
+Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval)
+Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time)
+Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age)
+Bridge.AgeingTimeSec, config_parse_sec, 0, offsetof(Bridge, ageing_time)
+Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay)
+Bridge.Priority, config_parse_uint16, 0, offsetof(Bridge, priority)
+Bridge.DefaultPVID, config_parse_vlanid, 0, offsetof(Bridge, default_pvid)
+Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier)
+Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping)
+Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering)
+Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp)
+VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table_id)
diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c
index c11ac0c539..67f4fab400 100644
--- a/src/network/netdev/tunnel.c
+++ b/src/network/netdev/tunnel.c
@@ -410,10 +410,10 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
return -EINVAL;
}
- if (netdev->kind == NETDEV_KIND_VTI6 &&
+ if (IN_SET(netdev->kind, NETDEV_KIND_VTI6, NETDEV_KIND_IP6TNL, NETDEV_KIND_IP6GRE) &&
(t->family != AF_INET6 || in_addr_is_null(t->family, &t->local))) {
log_netdev_error(netdev,
- "vti6 tunnel without a local IPv4 address configured in %s. Ignoring", filename);
+ "vti6/ip6tnl/ip6gre tunnel without a local IPv6 address configured in %s. Ignoring", filename);
return -EINVAL;
}
diff --git a/src/network/netdev/vxlan.c b/src/network/netdev/vxlan.c
index 231f5cb442..b677b000fd 100644
--- a/src/network/netdev/vxlan.c
+++ b/src/network/netdev/vxlan.c
@@ -24,6 +24,8 @@
#include "conf-parser.h"
#include "alloc-util.h"
#include "extract-word.h"
+#include "string-util.h"
+#include "strv.h"
#include "parse-util.h"
#include "missing.h"
@@ -48,9 +50,29 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_ID attribute: %m");
}
- r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_GROUP, &v->group.in);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GROUP attribute: %m");
+ if (!in_addr_is_null(v->remote_family, &v->remote)) {
+
+ if (v->remote_family == AF_INET)
+ r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_GROUP, &v->remote.in);
+ else
+ r = sd_netlink_message_append_in6_addr(m, IFLA_VXLAN_GROUP6, &v->remote.in6);
+
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GROUP attribute: %m");
+
+ }
+
+ if (!in_addr_is_null(v->local_family, &v->local)) {
+
+ if (v->local_family == AF_INET)
+ r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_LOCAL, &v->local.in);
+ else
+ r = sd_netlink_message_append_in6_addr(m, IFLA_VXLAN_LOCAL6, &v->local.in6);
+
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LOCAL attribute: %m");
+
+ }
r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LINK, link->ifindex);
if (r < 0)
@@ -144,16 +166,16 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
return r;
}
-int config_parse_vxlan_group_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) {
+int config_parse_vxlan_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) {
VxLan *v = userdata;
union in_addr_union *addr = data, buffer;
int r, f;
@@ -165,16 +187,28 @@ int config_parse_vxlan_group_address(const char *unit,
r = in_addr_from_string_auto(rvalue, &f, &buffer);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "vxlan multicast group address is invalid, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "vxlan '%s' address is invalid, ignoring assignment: %s", lvalue, rvalue);
return 0;
}
- if (v->family != AF_UNSPEC && v->family != f) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "vxlan multicast group incompatible, ignoring assignment: %s", rvalue);
- return 0;
+ r = in_addr_is_multicast(f, &buffer);
+
+ if (STR_IN_SET(lvalue, "Group", "Remote")) {
+ if (r <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "vxlan invalid multicast '%s' address, ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ v->remote_family = f;
+ } else {
+ if (r > 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "vxlan %s can not be multicast address, ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ v->local_family = f;
}
- v->family = f;
*addr = buffer;
return 0;
diff --git a/src/network/netdev/vxlan.h b/src/network/netdev/vxlan.h
index 6c3081d5fc..dca58e7fe6 100644
--- a/src/network/netdev/vxlan.h
+++ b/src/network/netdev/vxlan.h
@@ -31,8 +31,11 @@ struct VxLan {
uint64_t id;
- int family;
- union in_addr_union group;
+ int remote_family;
+ int local_family;
+
+ union in_addr_union remote;
+ union in_addr_union local;
unsigned tos;
unsigned ttl;
@@ -60,16 +63,16 @@ struct VxLan {
DEFINE_NETDEV_CAST(VXLAN, VxLan);
extern const NetDevVTable vxlan_vtable;
-int config_parse_vxlan_group_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);
+int config_parse_vxlan_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);
int config_parse_port_range(const char *unit,
const char *filename,
unsigned line,
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index ffd2e18a45..2e6c763aba 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -53,15 +53,21 @@ int address_new(Address **ret) {
return 0;
}
-int address_new_static(Network *network, unsigned section, Address **ret) {
+int address_new_static(Network *network, const char *filename, unsigned section_line, Address **ret) {
+ _cleanup_network_config_section_free_ NetworkConfigSection *n = NULL;
_cleanup_address_free_ Address *address = NULL;
int r;
assert(network);
assert(ret);
+ assert(!!filename == (section_line > 0));
- if (section) {
- address = hashmap_get(network->addresses_by_section, UINT_TO_PTR(section));
+ if (filename) {
+ r = network_config_section_new(filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ address = hashmap_get(network->addresses_by_section, n);
if (address) {
*ret = address;
address = NULL;
@@ -77,9 +83,13 @@ int address_new_static(Network *network, unsigned section, Address **ret) {
if (r < 0)
return r;
- if (section) {
- address->section = section;
- hashmap_put(network->addresses_by_section, UINT_TO_PTR(address->section), address);
+ if (filename) {
+ address->section = n;
+ n = NULL;
+
+ r = hashmap_put(network->addresses_by_section, address->section, address);
+ if (r < 0)
+ return r;
}
address->network = network;
@@ -101,8 +111,10 @@ void address_free(Address *address) {
assert(address->network->n_static_addresses > 0);
address->network->n_static_addresses--;
- if (address->section)
- hashmap_remove(address->network->addresses_by_section, UINT_TO_PTR(address->section));
+ if (address->section) {
+ hashmap_remove(address->network->addresses_by_section, address->section);
+ network_config_section_free(address->section);
+ }
}
if (address->link) {
@@ -676,7 +688,7 @@ int config_parse_broadcast(
assert(rvalue);
assert(data);
- r = address_new_static(network, section_line, &n);
+ r = address_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
@@ -723,10 +735,10 @@ int config_parse_address(const char *unit,
if (streq(section, "Network")) {
/* we are not in an Address section, so treat
* this as the special '0' section */
- section_line = 0;
- }
+ r = address_new_static(network, NULL, 0, &n);
+ } else
+ r = address_new_static(network, filename, section_line, &n);
- r = address_new_static(network, section_line, &n);
if (r < 0)
return r;
@@ -805,12 +817,12 @@ int config_parse_label(
assert(rvalue);
assert(data);
- r = address_new_static(network, section_line, &n);
+ r = address_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
- if (strlen(rvalue) >= IFNAMSIZ) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is too long, ignoring assignment: %s", rvalue);
+ if (!address_label_valid(rvalue)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is too long or invalid, ignoring assignment: %s", rvalue);
return 0;
}
@@ -844,7 +856,7 @@ int config_parse_lifetime(const char *unit,
assert(rvalue);
assert(data);
- r = address_new_static(network, section_line, &n);
+ r = address_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
@@ -891,7 +903,7 @@ int config_parse_address_flags(const char *unit,
assert(rvalue);
assert(data);
- r = address_new_static(network, section_line, &n);
+ r = address_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h
index bc3b4fc7f3..71a07ea7a3 100644
--- a/src/network/networkd-address.h
+++ b/src/network/networkd-address.h
@@ -33,10 +33,11 @@ typedef struct Address Address;
typedef struct Network Network;
typedef struct Link Link;
+typedef struct NetworkConfigSection NetworkConfigSection;
struct Address {
Network *network;
- unsigned section;
+ NetworkConfigSection *section;
Link *link;
@@ -62,7 +63,7 @@ struct Address {
LIST_FIELDS(Address, addresses);
};
-int address_new_static(Network *network, unsigned section, Address **ret);
+int address_new_static(Network *network, const char *filename, unsigned section, Address **ret);
int address_new(Address **ret);
void address_free(Address *address);
int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
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..ab372568de 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -36,6 +36,49 @@
#include "string-util.h"
#include "util.h"
+static void network_config_hash_func(const void *p, struct siphash *state) {
+ const NetworkConfigSection *c = p;
+
+ siphash24_compress(c->filename, strlen(c->filename), state);
+ siphash24_compress(&c->line, sizeof(c->line), state);
+}
+
+static int network_config_compare_func(const void *a, const void *b) {
+ const NetworkConfigSection *x = a, *y = b;
+ int r;
+
+ r = strcmp(x->filename, y->filename);
+ if (r != 0)
+ return r;
+
+ return y->line - x->line;
+}
+
+const struct hash_ops network_config_hash_ops = {
+ .hash = network_config_hash_func,
+ .compare = network_config_compare_func,
+};
+
+int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s) {
+ NetworkConfigSection *cs;
+
+ cs = malloc0(offsetof(NetworkConfigSection, filename) + strlen(filename) + 1);
+ if (!cs)
+ return -ENOMEM;
+
+ strcpy(cs->filename, filename);
+ cs->line = line;
+
+ *s = cs;
+ cs = NULL;
+
+ return 0;
+}
+
+void network_config_section_free(NetworkConfigSection *cs) {
+ free(cs);
+}
+
static int network_load_one(Manager *manager, const char *filename) {
_cleanup_network_free_ Network *network = NULL;
_cleanup_fclose_ FILE *file = NULL;
@@ -70,16 +113,17 @@ 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)
return log_oom();
- network->addresses_by_section = hashmap_new(NULL);
+ network->addresses_by_section = hashmap_new(&network_config_hash_ops);
if (!network->addresses_by_section)
return log_oom();
- network->routes_by_section = hashmap_new(NULL);
+ network->routes_by_section = hashmap_new(&network_config_hash_ops);
if (!network->routes_by_section)
return log_oom();
@@ -152,6 +196,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 +269,7 @@ void network_free(Network *network) {
Route *route;
Address *address;
FdbEntry *fdb_entry;
+ IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
Iterator i;
if (!network)
@@ -268,6 +314,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);
@@ -379,7 +428,7 @@ int network_apply(Network *network, Link *link) {
if (network->ipv4ll_route) {
Route *route;
- r = route_new_static(network, 0, &route);
+ r = route_new_static(network, "Network", 0, &route);
if (r < 0)
return r;
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index b7da9d22d4..4ce066a764 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"
@@ -81,6 +82,17 @@ typedef struct DUID {
uint8_t raw_data[MAX_DUID_LEN];
} DUID;
+typedef struct NetworkConfigSection {
+ unsigned line;
+ char filename[];
+} NetworkConfigSection;
+
+int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s);
+void network_config_section_free(NetworkConfigSection *network);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(NetworkConfigSection*, network_config_section_free);
+#define _cleanup_network_config_section_free_ _cleanup_(network_config_section_freep)
+
typedef struct Manager Manager;
struct Network {
@@ -188,10 +200,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/network/networkd-route.c b/src/network/networkd-route.c
index bde26a42d4..570083f180 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -77,15 +77,21 @@ int route_new(Route **ret) {
return 0;
}
-int route_new_static(Network *network, unsigned section, Route **ret) {
+int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
+ _cleanup_network_config_section_free_ NetworkConfigSection *n = NULL;
_cleanup_route_free_ Route *route = NULL;
int r;
assert(network);
assert(ret);
+ assert(!!filename == (section_line > 0));
- if (section) {
- route = hashmap_get(network->routes_by_section, UINT_TO_PTR(section));
+ if (filename) {
+ r = network_config_section_new(filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ route = hashmap_get(network->routes_by_section, n);
if (route) {
*ret = route;
route = NULL;
@@ -103,10 +109,11 @@ int route_new_static(Network *network, unsigned section, Route **ret) {
route->protocol = RTPROT_STATIC;
- if (section) {
- route->section = section;
+ if (filename) {
+ route->section = n;
+ n = NULL;
- r = hashmap_put(network->routes_by_section, UINT_TO_PTR(route->section), route);
+ r = hashmap_put(network->routes_by_section, route->section, route);
if (r < 0)
return r;
}
@@ -132,9 +139,11 @@ void route_free(Route *route) {
route->network->n_static_routes--;
if (route->section)
- hashmap_remove(route->network->routes_by_section, UINT_TO_PTR(route->section));
+ hashmap_remove(route->network->routes_by_section, route->section);
}
+ network_config_section_free(route->section);
+
if (route->link) {
set_remove(route->link->routes, route);
set_remove(route->link->routes_foreign, route);
@@ -673,10 +682,10 @@ int config_parse_gateway(const char *unit,
if (streq(section, "Network")) {
/* we are not in an Route section, so treat
* this as the special '0' section */
- section_line = 0;
- }
+ r = route_new_static(network, NULL, 0, &n);
+ } else
+ r = route_new_static(network, filename, section_line, &n);
- r = route_new_static(network, section_line, &n);
if (r < 0)
return r;
@@ -715,7 +724,7 @@ int config_parse_preferred_src(const char *unit,
assert(rvalue);
assert(data);
- r = route_new_static(network, section_line, &n);
+ r = route_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
@@ -757,7 +766,7 @@ int config_parse_destination(const char *unit,
assert(rvalue);
assert(data);
- r = route_new_static(network, section_line, &n);
+ r = route_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
@@ -835,7 +844,7 @@ int config_parse_route_priority(const char *unit,
assert(rvalue);
assert(data);
- r = route_new_static(network, section_line, &n);
+ r = route_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
@@ -872,7 +881,7 @@ int config_parse_route_scope(const char *unit,
assert(rvalue);
assert(data);
- r = route_new_static(network, section_line, &n);
+ r = route_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
@@ -913,7 +922,7 @@ int config_parse_route_table(const char *unit,
assert(rvalue);
assert(data);
- r = route_new_static(network, section_line, &n);
+ r = route_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h
index 02f0b27675..4ebfa0f0bd 100644
--- a/src/network/networkd-route.h
+++ b/src/network/networkd-route.h
@@ -20,12 +20,13 @@
***/
typedef struct Route Route;
+typedef struct NetworkConfigSection NetworkConfigSection;
#include "networkd-network.h"
struct Route {
Network *network;
- unsigned section;
+ NetworkConfigSection *section;
Link *link;
@@ -52,7 +53,7 @@ struct Route {
LIST_FIELDS(Route, routes);
};
-int route_new_static(Network *network, unsigned section, Route **ret);
+int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret);
int route_new(Route **ret);
void route_free(Route *route);
int route_configure(Route *route, Link *link, sd_netlink_message_handler_t callback);
diff --git a/src/network/wait-online/Makefile b/src/network/wait-online/Makefile
new file mode 120000
index 0000000000..94aaae2c4d
--- /dev/null
+++ b/src/network/wait-online/Makefile
@@ -0,0 +1 @@
+../../Makefile \ No newline at end of file
diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c
index 5274767b96..d749756437 100644
--- a/src/nspawn/nspawn-cgroup.c
+++ b/src/nspawn/nspawn-cgroup.c
@@ -78,13 +78,12 @@ int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t arg_uid_shift)
char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1];
bool undo_mount = false;
const char *fn;
- int unified, r;
+ int r, unified_controller;
- unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
- if (unified < 0)
- return log_error_errno(unified, "Failed to determine whether the unified hierarchy is used: %m");
-
- if ((unified > 0) == (unified_requested >= CGROUP_UNIFIED_SYSTEMD))
+ unified_controller = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
+ if (unified_controller < 0)
+ return log_error_errno(unified_controller, "Failed to determine whether the systemd hierarchy is unified: %m");
+ if ((unified_controller > 0) == (unified_requested >= CGROUP_UNIFIED_SYSTEMD))
return 0;
/* When the host uses the legacy cgroup setup, but the
@@ -100,7 +99,7 @@ int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t arg_uid_shift)
if (!mkdtemp(tree))
return log_error_errno(errno, "Failed to generate temporary mount point for unified hierarchy: %m");
- if (unified)
+ if (unified_controller > 0)
r = mount_verbose(LOG_ERR, "cgroup", tree, "cgroup",
MS_NOSUID|MS_NOEXEC|MS_NODEV, "none,name=systemd,xattr");
else
@@ -142,7 +141,7 @@ finish:
int create_subcgroup(pid_t pid, CGroupUnified unified_requested) {
_cleanup_free_ char *cgroup = NULL;
const char *child;
- int unified, r;
+ int r;
CGroupMask supported;
/* In the unified hierarchy inner nodes may only contain
@@ -154,10 +153,10 @@ int create_subcgroup(pid_t pid, CGroupUnified unified_requested) {
if (unified_requested == CGROUP_UNIFIED_NONE)
return 0;
- unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
- if (unified < 0)
- return log_error_errno(unified, "Failed to determine whether the unified hierarchy is used: %m");
- if (unified == 0)
+ r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether the systemd controller is unified: %m");
+ if (r == 0)
return 0;
r = cg_mask_supported(&supported);
diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c
index 4b2838b752..d276994120 100644
--- a/src/nspawn/nspawn-mount.c
+++ b/src/nspawn/nspawn-mount.c
@@ -545,10 +545,10 @@ int mount_all(const char *dest,
static const MountPoint mount_table[] = {
/* inner child mounts */
{ "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, MOUNT_FATAL|MOUNT_IN_USERNS },
- { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, MOUNT_FATAL|MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* Bind mount first ...*/
+ { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, MOUNT_FATAL|MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* Bind mount first ... */
{ "/proc/sys/net", "/proc/sys/net", NULL, NULL, MS_BIND, MOUNT_FATAL|MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO|MOUNT_APPLY_APIVFS_NETNS }, /* (except for this) */
{ NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, MOUNT_FATAL|MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* ... then, make it r/o */
- { "/proc/sysrq-trigger", "/proc/sysrq-trigger", NULL, NULL, MS_BIND, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* Bind mount first ...*/
+ { "/proc/sysrq-trigger", "/proc/sysrq-trigger", NULL, NULL, MS_BIND, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* Bind mount first ... */
{ NULL, "/proc/sysrq-trigger", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* ... then, make it r/o */
/* outer child mounts */
@@ -890,7 +890,7 @@ static int get_controllers(Set *subsystems) {
*e = 0;
- if (STR_IN_SET(l, "", "name=systemd"))
+ if (STR_IN_SET(l, "", "name=systemd", "name=unified"))
continue;
p = strdup(l);
@@ -909,7 +909,6 @@ static int mount_legacy_cgroup_hierarchy(
const char *dest,
const char *controller,
const char *hierarchy,
- CGroupUnified unified_requested,
bool read_only) {
const char *to, *fstype, *opts;
@@ -927,14 +926,12 @@ static int mount_legacy_cgroup_hierarchy(
/* The superblock mount options of the mount point need to be
* identical to the hosts', and hence writable... */
- if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
- if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
- fstype = "cgroup2";
- opts = NULL;
- } else {
- fstype = "cgroup";
- opts = "none,name=systemd,xattr";
- }
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_HYBRID)) {
+ fstype = "cgroup2";
+ opts = NULL;
+ } else if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_LEGACY)) {
+ fstype = "cgroup";
+ opts = "none,name=systemd,xattr";
} else {
fstype = "cgroup";
opts = controller;
@@ -994,7 +991,10 @@ static int mount_legacy_cgns_supported(
return r;
}
- if (cg_all_unified() > 0)
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0)
goto skip_controllers;
controllers = set_new(&string_hash_ops);
@@ -1012,7 +1012,7 @@ static int mount_legacy_cgns_supported(
if (!controller)
break;
- r = mount_legacy_cgroup_hierarchy("", controller, controller, unified_requested, !userns);
+ r = mount_legacy_cgroup_hierarchy("", controller, controller, !userns);
if (r < 0)
return r;
@@ -1046,7 +1046,13 @@ static int mount_legacy_cgns_supported(
}
skip_controllers:
- r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER, "systemd", unified_requested, false);
+ if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
+ r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
+ if (r < 0)
+ return r;
+ }
+
+ r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
if (r < 0)
return r;
@@ -1091,7 +1097,10 @@ static int mount_legacy_cgns_unsupported(
return r;
}
- if (cg_all_unified() > 0)
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0)
goto skip_controllers;
controllers = set_new(&string_hash_ops);
@@ -1117,7 +1126,7 @@ static int mount_legacy_cgns_unsupported(
if (r == -EINVAL) {
/* Not a symbolic link, but directly a single cgroup hierarchy */
- r = mount_legacy_cgroup_hierarchy(dest, controller, controller, unified_requested, true);
+ r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true);
if (r < 0)
return r;
@@ -1137,7 +1146,7 @@ static int mount_legacy_cgns_unsupported(
continue;
}
- r = mount_legacy_cgroup_hierarchy(dest, combined, combined, unified_requested, true);
+ r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true);
if (r < 0)
return r;
@@ -1150,7 +1159,13 @@ static int mount_legacy_cgns_unsupported(
}
skip_controllers:
- r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER, "systemd", unified_requested, false);
+ if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
+ r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
+ if (r < 0)
+ return r;
+ }
+
+ r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
if (r < 0)
return r;
@@ -1202,12 +1217,25 @@ int mount_cgroups(
return mount_legacy_cgns_unsupported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
}
+static int mount_systemd_cgroup_writable_one(const char *systemd_own, const char *systemd_root)
+{
+ int r;
+
+ /* Make our own cgroup a (writable) bind mount */
+ r = mount_verbose(LOG_ERR, systemd_own, systemd_own, NULL, MS_BIND, NULL);
+ if (r < 0)
+ return r;
+
+ /* And then remount the systemd cgroup root read-only */
+ return mount_verbose(LOG_ERR, NULL, systemd_root, NULL,
+ MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
+}
+
int mount_systemd_cgroup_writable(
const char *dest,
CGroupUnified unified_requested) {
_cleanup_free_ char *own_cgroup_path = NULL;
- const char *systemd_root, *systemd_own;
int r;
assert(dest);
@@ -1220,22 +1248,19 @@ int mount_systemd_cgroup_writable(
if (path_equal(own_cgroup_path, "/"))
return 0;
- if (unified_requested >= CGROUP_UNIFIED_ALL) {
- systemd_own = strjoina(dest, "/sys/fs/cgroup", own_cgroup_path);
- systemd_root = prefix_roota(dest, "/sys/fs/cgroup");
- } else {
- systemd_own = strjoina(dest, "/sys/fs/cgroup/systemd", own_cgroup_path);
- systemd_root = prefix_roota(dest, "/sys/fs/cgroup/systemd");
- }
+ if (unified_requested >= CGROUP_UNIFIED_ALL)
+ return mount_systemd_cgroup_writable_one(strjoina(dest, "/sys/fs/cgroup", own_cgroup_path),
+ prefix_roota(dest, "/sys/fs/cgroup"));
- /* Make our own cgroup a (writable) bind mount */
- r = mount_verbose(LOG_ERR, systemd_own, systemd_own, NULL, MS_BIND, NULL);
- if (r < 0)
- return r;
+ if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
+ r = mount_systemd_cgroup_writable_one(strjoina(dest, "/sys/fs/cgroup/unified", own_cgroup_path),
+ prefix_roota(dest, "/sys/fs/cgroup/unified"));
+ if (r < 0)
+ return r;
+ }
- /* And then remount the systemd cgroup root read-only */
- return mount_verbose(LOG_ERR, NULL, systemd_root, NULL,
- MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
+ return mount_systemd_cgroup_writable_one(strjoina(dest, "/sys/fs/cgroup/systemd", own_cgroup_path),
+ prefix_roota(dest, "/sys/fs/cgroup/systemd"));
}
int setup_volatile_state(
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index b172b44933..1fc0501c2e 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -41,6 +41,7 @@
#include <sys/wait.h>
#include <unistd.h>
+#include "sd-bus.h"
#include "sd-daemon.h"
#include "sd-id128.h"
@@ -49,6 +50,7 @@
#include "base-filesystem.h"
#include "blkid-util.h"
#include "btrfs-util.h"
+#include "bus-util.h"
#include "cap-list.h"
#include "capability-util.h"
#include "cgroup-util.h"
@@ -314,7 +316,7 @@ static int custom_mount_check_all(void) {
static int detect_unified_cgroup_hierarchy(const char *directory) {
const char *e;
- int r, all_unified, systemd_unified;
+ int r;
/* Allow the user to control whether the unified hierarchy is used */
e = getenv("UNIFIED_CGROUP_HIERARCHY");
@@ -330,15 +332,11 @@ static int detect_unified_cgroup_hierarchy(const char *directory) {
return 0;
}
- all_unified = cg_all_unified();
- systemd_unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
-
- if (all_unified < 0 || systemd_unified < 0)
- return log_error_errno(all_unified < 0 ? all_unified : systemd_unified,
- "Failed to determine whether the unified cgroups hierarchy is used: %m");
-
/* Otherwise inherit the default from the host system */
- if (all_unified > 0) {
+ r = cg_all_unified();
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether we are in all unified mode.");
+ if (r > 0) {
/* Unified cgroup hierarchy support was added in 230. Unfortunately the detection
* routine only detects 231, so we'll have a false negative here for 230. */
r = systemd_installation_has_version(directory, 230);
@@ -348,9 +346,9 @@ static int detect_unified_cgroup_hierarchy(const char *directory) {
arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL;
else
arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE;
- } else if (systemd_unified > 0) {
- /* Mixed cgroup hierarchy support was added in 232 */
- r = systemd_installation_has_version(directory, 232);
+ } else if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0) {
+ /* Mixed cgroup hierarchy support was added in 233 */
+ r = systemd_installation_has_version(directory, 233);
if (r < 0)
return log_error_errno(r, "Failed to determine systemd version in container: %m");
if (r > 0)
@@ -1323,6 +1321,19 @@ static int setup_timezone(const char *dest) {
return 0;
}
+static int resolved_running(void) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int r;
+
+ /* Check if resolved is running */
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ return r;
+
+ return bus_name_has_owner(bus, "org.freedesktop.resolve1", NULL);
+}
+
static int setup_resolv_conf(const char *dest) {
_cleanup_free_ char *resolved = NULL, *etc = NULL;
const char *where;
@@ -1346,8 +1357,8 @@ static int setup_resolv_conf(const char *dest) {
return 0;
}
- if (access("/run/systemd/resolve/resolv.conf", F_OK) >= 0 &&
- access("/usr/lib/systemd/resolv.conf", F_OK) >= 0) {
+ if (access("/usr/lib/systemd/resolv.conf", F_OK) >= 0 &&
+ resolved_running() > 0) {
/* resolved is enabled on the host. In this, case bind mount its static resolv.conf file into the
* container, so that the container can use the host's resolver. Given that network namespacing is
@@ -1364,7 +1375,7 @@ static int setup_resolv_conf(const char *dest) {
}
/* If that didn't work, let's copy the file */
- r = copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644, 0);
+ r = copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644, 0, COPY_REFLINK);
if (r < 0) {
/* If the file already exists as symlink, let's suppress the warning, under the assumption that
* resolved or something similar runs inside and the symlink points there.
@@ -2153,8 +2164,6 @@ static int inner_child(
assert(directory);
assert(kmsg_socket >= 0);
- cg_unified_flush();
-
if (arg_userns_mode != USER_NAMESPACE_NO) {
/* Tell the parent, that it now can write the UID map. */
(void) barrier_place(barrier); /* #1 */
@@ -2425,8 +2434,6 @@ static int outer_child(
assert(notify_socket >= 0);
assert(kmsg_socket >= 0);
- cg_unified_flush();
-
if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0)
return log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m");
@@ -2471,10 +2478,6 @@ static int outer_child(
if (r < 0)
return r;
- r = detect_unified_cgroup_hierarchy(directory);
- if (r < 0)
- return r;
-
if (arg_userns_mode != USER_NAMESPACE_NO) {
/* Let the parent know which UID shift we read from the image */
l = send(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL);
@@ -2539,7 +2542,7 @@ static int outer_child(
* inside the containter that create a new mount namespace.
* See https://github.com/systemd/systemd/issues/3860
* Further submounts (such as /dev) done after this will inherit the
- * shared propagation mode.*/
+ * shared propagation mode. */
r = mount_verbose(LOG_ERR, NULL, directory, NULL, MS_SHARED|MS_REC, NULL);
if (r < 0)
return r;
@@ -3527,6 +3530,10 @@ int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
+ r = cg_unified_flush();
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m");
+
/* Make sure rename_process() in the stub init process can work */
saved_argv = argv;
saved_argc = argc;
@@ -3700,7 +3707,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- r = copy_file(arg_image, np, O_EXCL, arg_read_only ? 0400 : 0600, FS_NOCOW_FL);
+ r = copy_file(arg_image, np, O_EXCL, arg_read_only ? 0400 : 0600, FS_NOCOW_FL, COPY_REFLINK);
if (r < 0) {
r = log_error_errno(r, "Failed to copy image file: %m");
goto finish;
@@ -3795,6 +3802,10 @@ int main(int argc, char *argv[]) {
if (r < 0)
goto finish;
+ r = detect_unified_cgroup_hierarchy(arg_directory);
+ if (r < 0)
+ goto finish;
+
interactive =
isatty(STDIN_FILENO) > 0 &&
isatty(STDOUT_FILENO) > 0;
@@ -3856,7 +3867,7 @@ finish:
/* Try to flush whatever is still queued in the pty */
if (master >= 0) {
- (void) copy_bytes(master, STDOUT_FILENO, (uint64_t) -1, false);
+ (void) copy_bytes(master, STDOUT_FILENO, (uint64_t) -1, 0);
master = safe_close(master);
}
diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c
index 11c27575c0..0570fde592 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,14 +111,11 @@ 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) {
- *errnop = ENOMEM;
- *h_errnop = NO_RECOVERY;
+ *errnop = ERANGE;
+ *h_errnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
}
@@ -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));
@@ -223,8 +220,8 @@ static enum nss_status fill_in_hostent(
(c > 0 ? c+1 : 2) * sizeof(char*);
if (buflen < ms) {
- *errnop = ENOMEM;
- *h_errnop = NO_RECOVERY;
+ *errnop = ERANGE;
+ *h_errnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
}
diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c
index fac37faea5..ea90953abb 100644
--- a/src/nss-mymachines/nss-mymachines.c
+++ b/src/nss-mymachines/nss-mymachines.c
@@ -151,8 +151,8 @@ enum nss_status _nss_mymachines_gethostbyname4_r(
l = strlen(name);
ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
if (buflen < ms) {
- *errnop = ENOMEM;
- *h_errnop = TRY_AGAIN;
+ *errnop = ERANGE;
+ *h_errnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
}
@@ -306,8 +306,8 @@ enum nss_status _nss_mymachines_gethostbyname3_r(
ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*);
if (buflen < ms) {
- *errnop = ENOMEM;
- *h_errnop = NO_RECOVERY;
+ *errnop = ERANGE;
+ *h_errnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
}
@@ -471,7 +471,7 @@ enum nss_status _nss_mymachines_getpwnam_r(
l = strlen(name);
if (buflen < l+1) {
- *errnop = ENOMEM;
+ *errnop = ERANGE;
return NSS_STATUS_TRYAGAIN;
}
@@ -550,7 +550,7 @@ enum nss_status _nss_mymachines_getpwuid_r(
goto not_found;
if (snprintf(buffer, buflen, "vu-%s-" UID_FMT, machine, (uid_t) mapped) >= (int) buflen) {
- *errnop = ENOMEM;
+ *errnop = ERANGE;
return NSS_STATUS_TRYAGAIN;
}
@@ -645,7 +645,7 @@ enum nss_status _nss_mymachines_getgrnam_r(
l = sizeof(char*) + strlen(name) + 1;
if (buflen < l) {
- *errnop = ENOMEM;
+ *errnop = ERANGE;
return NSS_STATUS_TRYAGAIN;
}
@@ -722,13 +722,13 @@ enum nss_status _nss_mymachines_getgrgid_r(
goto not_found;
if (buflen < sizeof(char*) + 1) {
- *errnop = ENOMEM;
+ *errnop = ERANGE;
return NSS_STATUS_TRYAGAIN;
}
memzero(buffer, sizeof(char*));
if (snprintf(buffer + sizeof(char*), buflen - sizeof(char*), "vg-%s-" GID_FMT, machine, (gid_t) mapped) >= (int) buflen) {
- *errnop = ENOMEM;
+ *errnop = ERANGE;
return NSS_STATUS_TRYAGAIN;
}
diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c
index d46a3afe91..d155625e11 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_INET6));
+ 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,
@@ -192,8 +206,8 @@ enum nss_status _nss_resolve_gethostbyname4_r(
l = strlen(canonical);
ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
if (buflen < ms) {
- *errnop = ENOMEM;
- *h_errnop = TRY_AGAIN;
+ *errnop = ERANGE;
+ *h_errnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
}
@@ -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));
@@ -380,8 +394,8 @@ enum nss_status _nss_resolve_gethostbyname3_r(
ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*);
if (buflen < ms) {
- *errnop = ENOMEM;
- *h_errnop = TRY_AGAIN;
+ *errnop = ERANGE;
+ *h_errnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
}
@@ -601,8 +615,8 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
c * sizeof(char*); /* pointers to aliases, plus trailing NULL */
if (buflen < ms) {
- *errnop = ENOMEM;
- *h_errnop = TRY_AGAIN;
+ *errnop = ERANGE;
+ *h_errnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
}
diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c
index fd5064c937..f404755dac 100644
--- a/src/nss-systemd/nss-systemd.c
+++ b/src/nss-systemd/nss-systemd.c
@@ -185,7 +185,7 @@ enum nss_status _nss_systemd_getpwnam_r(
l = strlen(name);
if (buflen < l+1) {
- *errnop = ENOMEM;
+ *errnop = ERANGE;
return NSS_STATUS_TRYAGAIN;
}
@@ -286,7 +286,7 @@ enum nss_status _nss_systemd_getpwuid_r(
l = strlen(translated) + 1;
if (buflen < l) {
- *errnop = ENOMEM;
+ *errnop = ERANGE;
return NSS_STATUS_TRYAGAIN;
}
@@ -386,7 +386,7 @@ enum nss_status _nss_systemd_getgrnam_r(
l = sizeof(char*) + strlen(name) + 1;
if (buflen < l) {
- *errnop = ENOMEM;
+ *errnop = ERANGE;
return NSS_STATUS_TRYAGAIN;
}
@@ -484,7 +484,7 @@ enum nss_status _nss_systemd_getgrgid_r(
l = sizeof(char*) + strlen(translated) + 1;
if (buflen < l) {
- *errnop = ENOMEM;
+ *errnop = ERANGE;
return NSS_STATUS_TRYAGAIN;
}
diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c
index 07d9582ccb..32537ce6e8 100644
--- a/src/resolve/resolve-tool.c
+++ b/src/resolve/resolve-tool.c
@@ -38,7 +38,7 @@
#include "strv.h"
#include "terminal-util.h"
-#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
+#define DNS_CALL_TIMEOUT_USEC (90*USEC_PER_SEC)
static int arg_family = AF_UNSPEC;
static int arg_ifindex = 0;
@@ -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;
}
@@ -1524,7 +1528,7 @@ static int status_all(sd_bus *bus) {
static void help_protocol_types(void) {
if (arg_legend)
puts("Known protocol types:");
- puts("dns\nllmnr\nllmnr-ipv4\nllmnr-ipv6");
+ puts("dns\nllmnr\nllmnr-ipv4\nllmnr-ipv6\nmdns\nmnds-ipv4\nmdns-ipv6");
}
static void help_dns_types(void) {
@@ -1722,6 +1726,12 @@ static int parse_argv(int argc, char *argv[]) {
arg_flags |= SD_RESOLVED_LLMNR_IPV4;
else if (streq(optarg, "llmnr-ipv6"))
arg_flags |= SD_RESOLVED_LLMNR_IPV6;
+ else if (streq(optarg, "mdns"))
+ arg_flags |= SD_RESOLVED_MDNS;
+ else if (streq(optarg, "mdns-ipv4"))
+ arg_flags |= SD_RESOLVED_MDNS_IPV4;
+ else if (streq(optarg, "mdns-ipv6"))
+ arg_flags |= SD_RESOLVED_MDNS_IPV6;
else {
log_error("Unknown protocol specifier: %s", optarg);
return -EINVAL;
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-answer.c b/src/resolve/resolved-dns-answer.c
index ab85754bf7..db86b4dcf6 100644
--- a/src/resolve/resolved-dns-answer.c
+++ b/src/resolve/resolved-dns-answer.c
@@ -148,7 +148,7 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFl
* match. We don't really care if they match
* precisely, but we do care whether one is 0
* and the other is not. See RFC 2181, Section
- * 5.2.*/
+ * 5.2. */
if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
return -EINVAL;
diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h
index 4a92bd1150..11d2e25eeb 100644
--- a/src/resolve/resolved-dns-answer.h
+++ b/src/resolve/resolved-dns-answer.h
@@ -33,9 +33,11 @@ typedef struct DnsAnswerItem DnsAnswerItem;
* Note that we usually encode the empty DnsAnswer object as a simple NULL. */
typedef enum DnsAnswerFlags {
- DNS_ANSWER_AUTHENTICATED = 1, /* Item has been authenticated */
- DNS_ANSWER_CACHEABLE = 2, /* Item is subject to caching */
- DNS_ANSWER_SHARED_OWNER = 4, /* For mDNS: RRset may be owner by multiple peers */
+ DNS_ANSWER_AUTHENTICATED = 1, /* Item has been authenticated */
+ DNS_ANSWER_CACHEABLE = 2, /* Item is subject to caching */
+ DNS_ANSWER_SHARED_OWNER = 4, /* For mDNS: RRset may be owner by multiple peers */
+ DNS_ANSWER_CACHE_FLUSH = 8, /* For mDNS: sets cache-flush bit in the rrclass of response records */
+ DNS_ANSWER_GOODBYE = 16, /* For mDNS: item is subject to disappear */
} DnsAnswerFlags;
struct DnsAnswerItem {
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index 9233fb0ac1..f8dab01308 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -34,6 +34,10 @@
/* We never keep any item longer than 2h in our cache */
#define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR)
+/* How long to cache strange rcodes, i.e. rcodes != SUCCESS and != NXDOMAIN (specifically: that's only SERVFAIL for
+ * now) */
+#define CACHE_TTL_STRANGE_RCODE_USEC (30 * USEC_PER_SEC)
+
typedef enum DnsCacheItemType DnsCacheItemType;
typedef struct DnsCacheItem DnsCacheItem;
@@ -41,12 +45,14 @@ enum DnsCacheItemType {
DNS_CACHE_POSITIVE,
DNS_CACHE_NODATA,
DNS_CACHE_NXDOMAIN,
+ DNS_CACHE_RCODE, /* "strange" RCODE (effective only SERVFAIL for now) */
};
struct DnsCacheItem {
DnsCacheItemType type;
DnsResourceKey *key;
DnsResourceRecord *rr;
+ int rcode;
usec_t until;
bool authenticated:1;
@@ -60,6 +66,27 @@ struct DnsCacheItem {
LIST_FIELDS(DnsCacheItem, by_key);
};
+static const char *dns_cache_item_type_to_string(DnsCacheItem *item) {
+ assert(item);
+
+ switch (item->type) {
+
+ case DNS_CACHE_POSITIVE:
+ return "POSITIVE";
+
+ case DNS_CACHE_NODATA:
+ return "NODATA";
+
+ case DNS_CACHE_NXDOMAIN:
+ return "NXDOMAIN";
+
+ case DNS_CACHE_RCODE:
+ return dns_rcode_to_string(item->rcode);
+ }
+
+ return NULL;
+}
+
static void dns_cache_item_free(DnsCacheItem *i) {
if (!i)
return;
@@ -406,7 +433,7 @@ static int dns_cache_put_positive(
return 0;
}
- /* Entry exists already? Update TTL, timestamp and owner*/
+ /* Entry exists already? Update TTL, timestamp and owner */
existing = dns_cache_get(c, rr);
if (existing) {
dns_cache_item_update_positive(
@@ -484,7 +511,6 @@ static int dns_cache_put_negative(
assert(c);
assert(key);
- assert(soa);
assert(owner_address);
/* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
@@ -495,13 +521,17 @@ static int dns_cache_put_negative(
if (dns_type_is_pseudo(key->type))
return 0;
- if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
- log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s",
- dns_resource_key_to_string(key, key_str, sizeof key_str));
- return 0;
- }
+ if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
+ if (!soa)
+ return 0;
- if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
+ /* For negative replies, check if we have a TTL of a SOA */
+ if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
+ log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s",
+ dns_resource_key_to_string(key, key_str, sizeof key_str));
+ return 0;
+ }
+ } else if (rcode != DNS_RCODE_SERVFAIL)
return 0;
r = dns_cache_init(c);
@@ -514,12 +544,17 @@ static int dns_cache_put_negative(
if (!i)
return -ENOMEM;
- i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN;
- i->until = calculate_until(soa, nsec_ttl, timestamp, true);
+ i->type =
+ rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA :
+ rcode == DNS_RCODE_NXDOMAIN ? DNS_CACHE_NXDOMAIN : DNS_CACHE_RCODE;
+ i->until =
+ i->type == DNS_CACHE_RCODE ? timestamp + CACHE_TTL_STRANGE_RCODE_USEC :
+ calculate_until(soa, nsec_ttl, timestamp, true);
i->authenticated = authenticated;
i->owner_family = owner_family;
i->owner_address = *owner_address;
i->prioq_idx = PRIOQ_IDX_NULL;
+ i->rcode = rcode;
if (i->type == DNS_CACHE_NXDOMAIN) {
/* NXDOMAIN entries should apply equally to all types, so we use ANY as
@@ -543,7 +578,7 @@ static int dns_cache_put_negative(
return r;
log_debug("Added %s cache entry for %s "USEC_FMT"s",
- i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN",
+ dns_cache_item_type_to_string(i),
dns_resource_key_to_string(i->key, key_str, sizeof key_str),
(i->until - timestamp) / USEC_PER_SEC);
@@ -615,6 +650,7 @@ int dns_cache_put(
const union in_addr_union *owner_address) {
DnsResourceRecord *soa = NULL, *rr;
+ bool weird_rcode = false;
DnsAnswerFlags flags;
unsigned cache_keys;
int r, ifindex;
@@ -624,18 +660,28 @@ int dns_cache_put(
dns_cache_remove_previous(c, key, answer);
- /* We only care for positive replies and NXDOMAINs, on all
- * other replies we will simply flush the respective entries,
- * and that's it */
- if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
- return 0;
+ /* We only care for positive replies and NXDOMAINs, on all other replies we will simply flush the respective
+ * entries, and that's it. (Well, with one further exception: since some DNS zones (akamai!) return SERVFAIL
+ * consistently for some lookups, and forwarders tend to propagate that we'll cache that too, but only for a
+ * short time.) */
- if (dns_answer_size(answer) <= 0) {
- char key_str[DNS_RESOURCE_KEY_STRING_MAX];
+ if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
- log_debug("Not caching negative entry without a SOA record: %s",
- dns_resource_key_to_string(key, key_str, sizeof key_str));
- return 0;
+ if (dns_answer_size(answer) <= 0) {
+ char key_str[DNS_RESOURCE_KEY_STRING_MAX];
+
+ log_debug("Not caching negative entry without a SOA record: %s",
+ dns_resource_key_to_string(key, key_str, sizeof key_str));
+ return 0;
+ }
+
+ } else {
+ /* Only cache SERVFAIL as "weird" rcode for now. We can add more later, should that turn out to be
+ * beneficial. */
+ if (rcode != DNS_RCODE_SERVFAIL)
+ return 0;
+
+ weird_rcode = true;
}
cache_keys = dns_answer_size(answer);
@@ -690,19 +736,20 @@ int dns_cache_put(
if (r > 0)
return 0;
- /* See https://tools.ietf.org/html/rfc2308, which say that a
- * matching SOA record in the packet is used to enable
- * negative caching. */
+ /* See https://tools.ietf.org/html/rfc2308, which say that a matching SOA record in the packet is used to
+ * enable negative caching. We apply one exception though: if we are about to cache a weird rcode we do so
+ * regardless of a SOA. */
r = dns_answer_find_soa(answer, key, &soa, &flags);
if (r < 0)
goto fail;
- if (r == 0)
- return 0;
-
- /* Refuse using the SOA data if it is unsigned, but the key is
- * signed */
- if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0)
+ if (r == 0 && !weird_rcode)
return 0;
+ if (r > 0) {
+ /* Refuse using the SOA data if it is unsigned, but the key is
+ * signed */
+ if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0)
+ return 0;
+ }
r = dns_cache_put_negative(
c,
@@ -799,6 +846,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
DnsCacheItem *j, *first, *nsec = NULL;
bool have_authenticated = false, have_non_authenticated = false;
usec_t current;
+ int found_rcode = -1;
assert(c);
assert(key);
@@ -817,6 +865,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
*ret = NULL;
*rcode = DNS_RCODE_SUCCESS;
+ *authenticated = false;
+
return 0;
}
@@ -831,6 +881,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
*ret = NULL;
*rcode = DNS_RCODE_SUCCESS;
+ *authenticated = false;
+
return 0;
}
@@ -842,6 +894,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
n++;
} else if (j->type == DNS_CACHE_NXDOMAIN)
nxdomain = true;
+ else if (j->type == DNS_CACHE_RCODE)
+ found_rcode = j->rcode;
if (j->authenticated)
have_authenticated = true;
@@ -849,6 +903,19 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
have_non_authenticated = true;
}
+ if (found_rcode >= 0) {
+ log_debug("RCODE %s cache hit for %s",
+ dns_rcode_to_string(found_rcode),
+ dns_resource_key_to_string(key, key_str, sizeof(key_str)));
+
+ *ret = NULL;
+ *rcode = found_rcode;
+ *authenticated = false;
+
+ c->n_hit++;
+ return 1;
+ }
+
if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) {
/* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from
* the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
@@ -980,7 +1047,7 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
if (!j->shared_owner)
continue;
- r = dns_packet_append_rr(p, j->rr, NULL, NULL);
+ r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) {
/* For mDNS, if we're unable to stuff all known answers into the given packet,
* allocate a new one, push the RR into that one and link it to the current one.
@@ -995,7 +1062,7 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
/* continue with new packet */
p = p->more;
- r = dns_packet_append_rr(p, j->rr, NULL, NULL);
+ r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
}
if (r < 0)
@@ -1042,7 +1109,7 @@ void dns_cache_dump(DnsCache *cache, FILE *f) {
fputs(dns_resource_key_to_string(j->key, key_str, sizeof key_str), f);
fputs(" -- ", f);
- fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f);
+ fputs(dns_cache_item_type_to_string(j), f);
fputc('\n', f);
}
}
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
index 51327105d0..eddab58a81 100644
--- a/src/resolve/resolved-dns-dnssec.c
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -1710,7 +1710,8 @@ static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) {
}
static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) {
- const char *common_suffix, *wc;
+ _cleanup_free_ char *wc = NULL;
+ const char *common_suffix;
int r;
assert(rr);
@@ -1734,7 +1735,10 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name)
if (r <= 0)
return r;
- wc = strjoina("*.", common_suffix);
+ r = dns_name_concat("*", common_suffix, &wc);
+ if (r < 0)
+ return r;
+
return dns_name_between(dns_resource_key_name(rr->key), wc, rr->nsec.next_domain_name);
}
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index 337a8c473f..652970284e 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -569,8 +569,9 @@ fail:
return r;
}
-int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start) {
+int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, const DnsAnswerFlags flags, size_t *start) {
size_t saved_size;
+ uint16_t class;
int r;
assert(p);
@@ -586,7 +587,8 @@ int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start)
if (r < 0)
goto fail;
- r = dns_packet_append_uint16(p, k->class, NULL);
+ class = flags & DNS_ANSWER_CACHE_FLUSH ? k->class | MDNS_RR_CACHE_FLUSH : k->class;
+ r = dns_packet_append_uint16(p, class, NULL);
if (r < 0)
goto fail;
@@ -791,9 +793,10 @@ int dns_packet_truncate_opt(DnsPacket *p) {
return 1;
}
-int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start) {
+int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAnswerFlags flags, size_t *start, size_t *rdata_start) {
size_t saved_size, rdlength_offset, end, rdlength, rds;
+ uint32_t ttl;
int r;
assert(p);
@@ -801,11 +804,12 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
saved_size = p->size;
- r = dns_packet_append_key(p, rr->key, NULL);
+ r = dns_packet_append_key(p, rr->key, flags, NULL);
if (r < 0)
goto fail;
- r = dns_packet_append_uint32(p, rr->ttl, NULL);
+ ttl = flags & DNS_ANSWER_GOODBYE ? 0 : rr->ttl;
+ r = dns_packet_append_uint32(p, ttl, NULL);
if (r < 0)
goto fail;
@@ -1143,7 +1147,7 @@ int dns_packet_append_question(DnsPacket *p, DnsQuestion *q) {
assert(p);
DNS_QUESTION_FOREACH(key, q) {
- r = dns_packet_append_key(p, key, NULL);
+ r = dns_packet_append_key(p, key, 0, NULL);
if (r < 0)
return r;
}
@@ -1153,12 +1157,13 @@ int dns_packet_append_question(DnsPacket *p, DnsQuestion *q) {
int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a) {
DnsResourceRecord *rr;
+ DnsAnswerFlags flags;
int r;
assert(p);
- DNS_ANSWER_FOREACH(rr, a) {
- r = dns_packet_append_rr(p, rr, NULL, NULL);
+ DNS_ANSWER_FOREACH_FLAGS(rr, flags, a) {
+ r = dns_packet_append_rr(p, rr, flags, NULL, NULL);
if (r < 0)
return r;
}
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index 054dc88a85..2c92392e4d 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -209,8 +209,8 @@ int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start);
int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_t *start);
int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, bool canonical_candidate, size_t *start);
int dns_packet_append_name(DnsPacket *p, const char *name, bool allow_compression, bool canonical_candidate, size_t *start);
-int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, size_t *start);
-int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start);
+int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, const DnsAnswerFlags flags, size_t *start);
+int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAnswerFlags flags, size_t *start, size_t *rdata_start);
int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, int rcode, size_t *start);
int dns_packet_append_question(DnsPacket *p, DnsQuestion *q);
int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a);
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index e03db4d003..2b091e6c45 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -28,7 +28,7 @@
#include "string-util.h"
/* How long to wait for the query in total */
-#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
+#define QUERY_TIMEOUT_USEC (60 * USEC_PER_SEC)
#define CNAME_MAX 8
#define QUERIES_MAX 2048
@@ -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. */
@@ -810,6 +811,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
q->answer = dns_answer_unref(q->answer);
q->answer_rcode = 0;
q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
+ q->answer_authenticated = false;
q->answer_errno = c->error_code;
}
@@ -846,15 +848,18 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
continue;
default:
- /* Any kind of failure? Store the data away,
- * if there's nothing stored yet. */
-
+ /* Any kind of failure? Store the data away, if there's nothing stored yet. */
if (state == DNS_TRANSACTION_SUCCESS)
continue;
+ /* If there's already an authenticated negative reply stored, then prefer that over any unauthenticated one */
+ if (q->answer_authenticated && !t->answer_authenticated)
+ continue;
+
q->answer = dns_answer_unref(q->answer);
q->answer_rcode = t->answer_rcode;
q->answer_dnssec_result = t->answer_dnssec_result;
+ q->answer_authenticated = t->answer_authenticated;
q->answer_errno = t->answer_errno;
state = t->state;
@@ -1028,6 +1033,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 +1123,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-rr.c b/src/resolve/resolved-dns-rr.c
index 209d565033..e8c05ed0da 100644
--- a/src/resolve/resolved-dns-rr.c
+++ b/src/resolve/resolved-dns-rr.c
@@ -1262,7 +1262,7 @@ int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) {
if (rr->wire_format && rr->wire_format_canonical == canonical)
return 0;
- r = dns_packet_append_rr(&packet, rr, &start, &rds);
+ r = dns_packet_append_rr(&packet, rr, 0, &start, &rds);
if (r < 0)
return r;
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index 8dbc7f623b..ffaefbe3f2 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -124,6 +124,8 @@ DnsScope* dns_scope_free(DnsScope *s) {
ordered_hashmap_free(s->conflict_queue);
sd_event_source_unref(s->conflict_event_source);
+ sd_event_source_unref(s->announce_event_source);
+
dns_cache_flush(&s->cache);
dns_zone_flush(&s->zone);
@@ -549,7 +551,11 @@ static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in
.imr_ifindex = s->link->ifindex,
};
- fd = manager_llmnr_ipv4_udp_fd(s->manager);
+ if (s->protocol == DNS_PROTOCOL_LLMNR)
+ fd = manager_llmnr_ipv4_udp_fd(s->manager);
+ else
+ fd = manager_mdns_ipv4_fd(s->manager);
+
if (fd < 0)
return fd;
@@ -568,7 +574,11 @@ static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in
.ipv6mr_interface = s->link->ifindex,
};
- fd = manager_llmnr_ipv6_udp_fd(s->manager);
+ if (s->protocol == DNS_PROTOCOL_LLMNR)
+ fd = manager_llmnr_ipv6_udp_fd(s->manager);
+ else
+ fd = manager_mdns_ipv6_fd(s->manager);
+
if (fd < 0)
return fd;
@@ -601,7 +611,7 @@ int dns_scope_mdns_membership(DnsScope *s, bool b) {
return dns_scope_multicast_membership(s, b, MDNS_MULTICAST_IPV4_ADDRESS, MDNS_MULTICAST_IPV6_ADDRESS);
}
-static int dns_scope_make_reply_packet(
+int dns_scope_make_reply_packet(
DnsScope *s,
uint16_t id,
int rcode,
@@ -830,11 +840,11 @@ static int dns_scope_make_conflict_packet(
DNS_PACKET_HEADER(p)->qdcount = htobe16(1);
DNS_PACKET_HEADER(p)->arcount = htobe16(1);
- r = dns_packet_append_key(p, rr->key, NULL);
+ r = dns_packet_append_key(p, rr->key, 0, NULL);
if (r < 0)
return r;
- r = dns_packet_append_rr(p, rr, NULL, NULL);
+ r = dns_packet_append_rr(p, rr, 0, NULL, NULL);
if (r < 0)
return r;
@@ -928,17 +938,19 @@ void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) {
assert(scope);
assert(p);
- if (p->protocol != DNS_PROTOCOL_LLMNR)
+ if (!IN_SET(p->protocol, DNS_PROTOCOL_LLMNR, DNS_PROTOCOL_MDNS))
return;
if (DNS_PACKET_RRCOUNT(p) <= 0)
return;
- if (DNS_PACKET_LLMNR_C(p) != 0)
- return;
+ if (p->protocol == DNS_PROTOCOL_LLMNR) {
+ if (DNS_PACKET_LLMNR_C(p) != 0)
+ return;
- if (DNS_PACKET_LLMNR_T(p) != 0)
- return;
+ if (DNS_PACKET_LLMNR_T(p) != 0)
+ return;
+ }
if (manager_our_packet(scope->manager, p))
return;
@@ -1041,3 +1053,77 @@ int dns_scope_ifindex(DnsScope *s) {
return 0;
}
+
+static int on_announcement_timeout(sd_event_source *s, usec_t usec, void *userdata) {
+ DnsScope *scope = userdata;
+
+ assert(s);
+
+ scope->announce_event_source = sd_event_source_unref(scope->announce_event_source);
+
+ (void) dns_scope_announce(scope, false);
+ return 0;
+}
+
+int dns_scope_announce(DnsScope *scope, bool goodbye) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+ LinkAddress *a;
+ int r;
+
+ if (!scope)
+ return 0;
+
+ if (scope->protocol != DNS_PROTOCOL_MDNS)
+ return 0;
+
+ answer = dns_answer_new(scope->link->n_addresses * 2);
+ if (!answer)
+ return log_oom();
+
+ LIST_FOREACH(addresses, a, scope->link->addresses) {
+ r = dns_answer_add(answer, a->mdns_address_rr, 0, goodbye ? DNS_ANSWER_GOODBYE : DNS_ANSWER_CACHE_FLUSH);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to add address RR to answer: %m");
+
+ r = dns_answer_add(answer, a->mdns_ptr_rr, 0, goodbye ? DNS_ANSWER_GOODBYE : DNS_ANSWER_CACHE_FLUSH);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to add PTR RR to answer: %m");
+ }
+
+ if (dns_answer_isempty(answer))
+ return 0;
+
+ r = dns_scope_make_reply_packet(scope, 0, DNS_RCODE_SUCCESS, NULL, answer, NULL, false, &p);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to build reply packet: %m");
+
+ r = dns_scope_emit_udp(scope, -1, p);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to send reply packet: %m");
+
+ /* In section 8.3 of RFC6762: "The Multicast DNS responder MUST send at least two unsolicited
+ * responses, one second apart." */
+ if (!scope->announced) {
+ usec_t ts;
+
+ scope->announced = true;
+
+ assert_se(sd_event_now(scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
+ ts += MDNS_ANNOUNCE_DELAY;
+
+ r = sd_event_add_time(
+ scope->manager->event,
+ &scope->announce_event_source,
+ clock_boottime_or_monotonic(),
+ ts,
+ MDNS_JITTER_RANGE_USEC,
+ on_announcement_timeout, scope);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to schedule second announcement: %m");
+
+ (void) sd_event_source_set_description(scope->announce_event_source, "mdns-announce");
+ }
+
+ return 0;
+}
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index 01a83a76b2..6f94b1fdcd 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -56,6 +56,9 @@ struct DnsScope {
OrderedHashmap *conflict_queue;
sd_event_source *conflict_event_source;
+ bool announced:1;
+ sd_event_source *announce_event_source;
+
RateLimit ratelimit;
usec_t resend_timeout;
@@ -96,6 +99,7 @@ void dns_scope_next_dns_server(DnsScope *s);
int dns_scope_llmnr_membership(DnsScope *s, bool b);
int dns_scope_mdns_membership(DnsScope *s, bool b);
+int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQuestion *q, DnsAnswer *answer, DnsAnswer *soa, bool tentative, DnsPacket **ret);
void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p);
DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, bool cache_ok);
@@ -112,3 +116,5 @@ bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name);
bool dns_scope_network_good(DnsScope *s);
int dns_scope_ifindex(DnsScope *s);
+
+int dns_scope_announce(DnsScope *scope, bool goodbye);
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 22c64e8491..5498f7b9cb 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -28,7 +28,7 @@
#include "string-util.h"
/* After how much time to repeat classic DNS requests */
-#define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC)
+#define DNS_TIMEOUT_MIN_USEC (750 * USEC_PER_MSEC)
#define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC)
/* The amount of time to wait before retrying with a full feature set */
@@ -399,12 +399,24 @@ static bool dns_server_grace_period_expired(DnsServer *s) {
}
DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
+ DnsServerFeatureLevel best;
+
assert(s);
- if (s->possible_feature_level != DNS_SERVER_FEATURE_LEVEL_BEST &&
- dns_server_grace_period_expired(s)) {
+ /* Determine the best feature level we care about. If DNSSEC mode is off there's no point in using anything
+ * better than EDNS0, hence don't even try. */
+ best = dns_server_get_dnssec_mode(s) == DNSSEC_NO ?
+ DNS_SERVER_FEATURE_LEVEL_EDNS0 :
+ DNS_SERVER_FEATURE_LEVEL_BEST;
+
+ /* Clamp the feature level the highest level we care about. The DNSSEC mode might have changed since the last
+ * time, hence let's downgrade if we are still at a higher level. */
+ if (s->possible_feature_level > best)
+ s->possible_feature_level = best;
+
+ if (s->possible_feature_level < best && dns_server_grace_period_expired(s)) {
- s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
+ s->possible_feature_level = best;
dns_server_reset_counters(s);
@@ -415,6 +427,8 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
dns_server_feature_level_to_string(s->possible_feature_level),
dns_server_string(s));
+ dns_server_flush_cache(s);
+
} else if (s->possible_feature_level <= s->verified_feature_level)
s->possible_feature_level = s->verified_feature_level;
else {
@@ -451,18 +465,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
@@ -566,7 +584,7 @@ void dns_server_warn_downgrade(DnsServer *server) {
return;
log_struct(LOG_NOTICE,
- LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_DOWNGRADE),
+ "MESSAGE_ID=" SD_MESSAGE_DNSSEC_DOWNGRADE_STR,
LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", dns_server_string(server)),
"DNS_SERVER=%s", dns_server_string(server),
"DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(server->possible_feature_level),
@@ -779,6 +797,34 @@ 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);
+}
+
+void dns_server_flush_cache(DnsServer *s) {
+ DnsServer *current;
+ DnsScope *scope;
+
+ assert(s);
+
+ /* Flush the cache of the scope this server belongs to */
+
+ current = s->link ? s->link->current_dns_server : s->manager->current_dns_server;
+ if (current != s)
+ return;
+
+ scope = s->link ? s->link->unicast_scope : s->manager->unicast_scope;
+ if (!scope)
+ return;
+
+ dns_cache_flush(&scope->cache);
+}
+
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..bc95d53c6a 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -144,6 +144,10 @@ 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;
+
+void dns_server_flush_cache(DnsServer *s);
diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c
index 4a3c5f612f..7afbfedfb0 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,55 @@ static int dns_stub_make_reply_packet(
continue;
add:
- r = dns_packet_append_rr(p, rr, 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 (!add_opt) {
+ /* If the client can't to EDNS0, don't do DO either */
+ edns0_do = false;
+
+ /* If the client didn't do EDNS, clamp the rcode to 4 bit */
+ if (rcode > 0xF)
+ rcode = DNS_RCODE_SERVFAIL;
+ }
+
+ /* Don't set the AD bit unless DO is on, too */
+ if (!edns0_do)
+ ad = false;
+
+ 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 +126,6 @@ static int dns_stub_make_reply_packet(
return r;
}
- *ret = p;
- p = NULL;
-
return 0;
}
@@ -148,14 +171,18 @@ static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *repl
return 0;
}
-static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode) {
+static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode, bool authenticated) {
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
int r;
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), authenticated);
if (r < 0)
return log_debug_errno(r, "Failed to build failure packet: %m");
@@ -170,33 +197,47 @@ 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_stub_make_reply_packet(
+ 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, false);
+ break;
+ }
+ if (r < 0) {
+ log_debug_errno(r, "Failed to process CNAME: %m");
+ break;
+ }
+ if (r == DNS_QUERY_RESTARTED)
+ return;
+
+ 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_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);
+ (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode, dns_query_fully_authenticated(q));
break;
case DNS_TRANSACTION_NOT_FOUND:
- (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN);
+ (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN, dns_query_fully_authenticated(q));
break;
case DNS_TRANSACTION_TIMEOUT:
@@ -212,7 +253,7 @@ static void dns_stub_query_complete(DnsQuery *q) {
case DNS_TRANSACTION_NO_TRUST_ANCHOR:
case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
case DNS_TRANSACTION_NETWORK_DOWN:
- (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL);
+ (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
break;
case DNS_TRANSACTION_NULL:
@@ -259,52 +300,52 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
if (in_addr_is_localhost(p->family, &p->sender) <= 0 ||
in_addr_is_localhost(p->family, &p->destination) <= 0) {
log_error("Got packet on unexpected IP range, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
goto fail;
}
r = dns_packet_extract(p);
if (r < 0) {
log_debug_errno(r, "Failed to extract resources from incoming packet, ignoring packet: %m");
- dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR, false);
goto fail;
}
if (!DNS_PACKET_VERSION_SUPPORTED(p)) {
log_debug("Got EDNS OPT field with unsupported version number.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS, false);
goto fail;
}
if (dns_type_is_obsolete(p->question->keys[0]->type)) {
log_debug("Got message with obsolete key type, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
goto fail;
}
if (dns_type_is_zone_transer(p->question->keys[0]->type)) {
log_debug("Got request for zone transfer, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
goto fail;
}
if (!DNS_PACKET_RD(p)) {
/* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */
log_debug("Got request with recursion disabled, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED, false);
goto fail;
}
if (DNS_PACKET_DO(p) && DNS_PACKET_CD(p)) {
log_debug("Got request with DNSSEC CD bit set, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
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);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
goto fail;
}
@@ -324,7 +365,7 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
r = dns_query_go(q);
if (r < 0) {
log_error_errno(r, "Failed to start query: %m");
- dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
goto fail;
}
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 2fce44ec8b..ff2ad9c1de 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -31,6 +31,7 @@
#include "string-table.h"
#define TRANSACTIONS_MAX 4096
+#define TRANSACTION_TCP_TIMEOUT_USEC (10U*USEC_PER_SEC)
static void dns_transaction_reset_answer(DnsTransaction *t) {
assert(t);
@@ -318,7 +319,7 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
dns_resource_key_to_string(t->key, key_str, sizeof key_str);
log_struct(LOG_NOTICE,
- LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_FAILURE),
+ "MESSAGE_ID=" SD_MESSAGE_DNSSEC_FAILURE_STR,
LOG_MESSAGE("DNSSEC validation failed for question %s: %s", key_str, dnssec_result_to_string(t->answer_dnssec_result)),
"DNS_TRANSACTION=%" PRIu16, t->id,
"DNS_QUESTION=%s", key_str,
@@ -363,6 +364,8 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
SET_FOREACH_MOVE(z, t->notify_zone_items_done, t->notify_zone_items)
dns_zone_item_notify(z);
SWAP_TWO(t->notify_zone_items, t->notify_zone_items_done);
+ if (t->probing)
+ (void) dns_scope_announce(t->scope, false);
SET_FOREACH_MOVE(d, t->notify_transactions_done, t->notify_transactions)
dns_transaction_notify(d, t);
@@ -830,7 +833,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
* should hence not attempt to access the query or transaction
* after calling this function. */
- log_debug("Processing incoming packet on transaction %" PRIu16".", t->id);
+ log_debug("Processing incoming packet on transaction %" PRIu16". (rcode=%s)", t->id, dns_rcode_to_string(DNS_PACKET_RCODE(p)));
switch (t->scope->protocol) {
@@ -908,9 +911,13 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
/* Request failed, immediately try again with reduced features */
- if (t->current_feature_level <= DNS_SERVER_FEATURE_LEVEL_WORST) {
- /* This was already at the lowest possible feature level? If so, we can't downgrade
- * this transaction anymore, hence let's process the response, and accept the rcode. */
+ if (t->current_feature_level <= DNS_SERVER_FEATURE_LEVEL_UDP) {
+ /* This was already at UDP feature level? If so, it doesn't make sense to downgrade
+ * this transaction anymore, hence let's process the response, and accept the
+ * rcode. Note that we don't retry on TCP, since that's a suitable way to mitigate
+ * packet loss, but is not going to give us better rcodes should we actually have
+ * managed to get them already at UDP level. */
+
log_debug("Server returned error: %s", dns_rcode_to_string(DNS_PACKET_RCODE(p)));
break;
}
@@ -924,7 +931,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;
@@ -1003,15 +1019,20 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
if (r > 0) /* Transaction got restarted... */
return;
- if (IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR)) {
+ if (IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR, DNS_PROTOCOL_MDNS)) {
- /* Only consider responses with equivalent query section to the request */
- r = dns_packet_is_reply_for(p, t->key);
- if (r < 0)
- goto fail;
- if (r == 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
- return;
+ /* When dealing with protocols other than mDNS only consider responses with
+ * equivalent query section to the request. For mDNS this check doesn't make
+ * sense, because the section 6 of RFC6762 states that "Multicast DNS responses MUST NOT
+ * contain any questions in the Question Section". */
+ if (t->scope->protocol != DNS_PROTOCOL_MDNS) {
+ r = dns_packet_is_reply_for(p, t->key);
+ if (r < 0)
+ goto fail;
+ if (r == 0) {
+ dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
+ return;
+ }
}
/* Install the answer as answer to the transaction */
@@ -1119,7 +1140,7 @@ static int dns_transaction_emit_udp(DnsTransaction *t) {
return r;
if (t->current_feature_level < DNS_SERVER_FEATURE_LEVEL_UDP)
- return -EAGAIN;
+ return -EAGAIN; /* Sorry, can't do UDP, try TCP! */
if (!dns_server_dnssec_supported(t->server) && dns_type_is_dnssec(t->key->type))
return -EOPNOTSUPP;
@@ -1196,15 +1217,26 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
assert(t);
assert(t->scope);
+
switch (t->scope->protocol) {
case DNS_PROTOCOL_DNS:
+
+ /* When we do TCP, grant a much longer timeout, as in this case there's no need for us to quickly
+ * resend, as the kernel does that anyway for us, and we really don't want to interrupt it in that
+ * needlessly. */
+ if (t->stream)
+ return TRANSACTION_TCP_TIMEOUT_USEC;
+
assert(t->server);
return t->server->resend_timeout;
case DNS_PROTOCOL_MDNS:
assert(t->n_attempts > 0);
- return (1 << (t->n_attempts - 1)) * USEC_PER_SEC;
+ if (t->probing)
+ return MDNS_PROBING_INTERVAL_USEC;
+ else
+ return (1 << (t->n_attempts - 1)) * USEC_PER_SEC;
case DNS_PROTOCOL_LLMNR:
return t->scope->resend_timeout;
@@ -1358,7 +1390,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
if (r < 0)
return r;
- r = dns_packet_append_key(p, t->key, NULL);
+ r = dns_packet_append_key(p, t->key, 0, NULL);
if (r < 0)
return r;
@@ -1390,7 +1422,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
if (qdcount >= UINT16_MAX)
break;
- r = dns_packet_append_key(p, other->key, NULL);
+ r = dns_packet_append_key(p, other->key, 0, NULL);
/*
* If we can't stuff more questions into the packet, just give up.
@@ -1417,7 +1449,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
if (r < 0)
return r;
- (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
+ (void) sd_event_source_set_description(other->timeout_event_source, "dns-transaction-timeout");
other->state = DNS_TRANSACTION_PENDING;
other->next_attempt_after = ts;
@@ -1459,7 +1491,7 @@ static int dns_transaction_make_packet(DnsTransaction *t) {
if (r < 0)
return r;
- r = dns_packet_append_key(p, t->key, NULL);
+ r = dns_packet_append_key(p, t->key, 0, NULL);
if (r < 0)
return r;
@@ -1560,7 +1592,7 @@ int dns_transaction_go(DnsTransaction *t) {
r = dns_transaction_emit_udp(t);
if (r == -EMSGSIZE)
log_debug("Sending query via TCP since it is too large.");
- if (r == -EAGAIN)
+ else if (r == -EAGAIN)
log_debug("Sending query via TCP since server doesn't support UDP.");
if (r == -EMSGSIZE || r == -EAGAIN)
r = dns_transaction_open_tcp(t);
@@ -1977,8 +2009,18 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
r = dns_resource_key_match_rr(t->key, rr, NULL);
if (r < 0)
return r;
- if (r == 0)
- continue;
+ if (r == 0) {
+ /* Hmm, so this SOA RR doesn't match our original question. In this case, maybe this is
+ * a negative reply, and we need the a SOA RR's TTL in order to cache a negative entry?
+ * If so, we need to validate it, too. */
+
+ r = dns_answer_match_key(t->answer, t->key, NULL);
+ if (r < 0)
+ return r;
+ if (r > 0) /* positive reply, we won't need the SOA and hence don't need to validate
+ * it. */
+ continue;
+ }
r = dnssec_has_rrsig(t->answer, rr->key);
if (r < 0)
@@ -2416,7 +2458,7 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
if (r > 0) {
/* The lookup is from a TLD that is proven not to
* exist, and we are in downgrade mode, hence ignore
- * that fact that we didn't get any NSEC RRs.*/
+ * that fact that we didn't get any NSEC RRs. */
log_info("Detected a negative query %s in a private DNS zone, permitting unsigned response.",
dns_resource_key_to_string(t->key, key_str, sizeof key_str));
@@ -2721,7 +2763,7 @@ static int dnssec_validate_records(
const char *source;
/* This RRset validated, but as a wildcard. This means we need
- * to prove via NSEC/NSEC3 that no matching non-wildcard RR exists.*/
+ * to prove via NSEC/NSEC3 that no matching non-wildcard RR exists. */
/* First step, determine the source of synthesis */
r = dns_resource_record_source(rrsig, &source);
@@ -2756,7 +2798,7 @@ static int dnssec_validate_records(
return r;
if (r == 0) {
/* Data does not require signing. In that case, just copy it over,
- * but remember that this is by no means authenticated.*/
+ * but remember that this is by no means authenticated. */
r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0);
if (r < 0)
return r;
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index 5a1df70422..a8d97738ef 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -78,6 +78,8 @@ struct DnsTransaction {
bool clamp_ttl:1;
+ bool probing:1;
+
DnsPacket *sent, *received;
DnsAnswer *answer;
@@ -172,10 +174,20 @@ DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_;
#define MDNS_JITTER_MIN_USEC (20 * USEC_PER_MSEC)
#define MDNS_JITTER_RANGE_USEC (100 * USEC_PER_MSEC)
+/* mDNS probing interval, see RFC 6762 Section 8.1 */
+#define MDNS_PROBING_INTERVAL_USEC (250 * USEC_PER_MSEC)
+
/* Maximum attempts to send DNS requests, across all DNS servers */
-#define DNS_TRANSACTION_ATTEMPTS_MAX 16
+#define DNS_TRANSACTION_ATTEMPTS_MAX 24
/* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */
#define LLMNR_TRANSACTION_ATTEMPTS_MAX 3
-#define TRANSACTION_ATTEMPTS_MAX(p) ((p) == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_ATTEMPTS_MAX : DNS_TRANSACTION_ATTEMPTS_MAX)
+/* Maximum attempts to send MDNS requests, see RFC 6762 Section 8.1 */
+#define MDNS_TRANSACTION_ATTEMPTS_MAX 3
+
+#define TRANSACTION_ATTEMPTS_MAX(p) (((p) == DNS_PROTOCOL_LLMNR) ? \
+ LLMNR_TRANSACTION_ATTEMPTS_MAX : \
+ (((p) == DNS_PROTOCOL_MDNS) ? \
+ MDNS_TRANSACTION_ATTEMPTS_MAX : \
+ DNS_TRANSACTION_ATTEMPTS_MAX))
diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c
index 9917b9e984..7e9f9e5a20 100644
--- a/src/resolve/resolved-dns-trust-anchor.c
+++ b/src/resolve/resolved-dns-trust-anchor.c
@@ -35,11 +35,16 @@
static const char trust_anchor_dirs[] = CONF_PATHS_NULSTR("dnssec-trust-anchors.d");
-/* The DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */
-static const uint8_t root_digest[] =
+/* The first DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */
+static const uint8_t root_digest1[] =
{ 0x49, 0xAA, 0xC1, 0x1D, 0x7B, 0x6F, 0x64, 0x46, 0x70, 0x2E, 0x54, 0xA1, 0x60, 0x73, 0x71, 0x60,
0x7A, 0x1A, 0x41, 0x85, 0x52, 0x00, 0xFD, 0x2C, 0xE1, 0xCD, 0xDE, 0x32, 0xF2, 0x4E, 0x8F, 0xB5 };
+/* The second DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved February 2017 */
+static const uint8_t root_digest2[] =
+ { 0xE0, 0x6D, 0x44, 0xB8, 0x0B, 0x8F, 0x1D, 0x39, 0xA9, 0x5C, 0x0B, 0x0D, 0x7C, 0x65, 0xD0, 0x84,
+ 0x58, 0xE8, 0x80, 0x40, 0x9B, 0xBC, 0x68, 0x34, 0x57, 0x10, 0x42, 0x37, 0xC7, 0xF8, 0xEC, 0x8D };
+
static bool dns_trust_anchor_knows_domain_positive(DnsTrustAnchor *d, const char *name) {
assert(d);
@@ -51,9 +56,40 @@ static bool dns_trust_anchor_knows_domain_positive(DnsTrustAnchor *d, const char
hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name));
}
-static int dns_trust_anchor_add_builtin_positive(DnsTrustAnchor *d) {
+static int add_root_ksk(
+ DnsAnswer *answer,
+ DnsResourceKey *key,
+ uint16_t key_tag,
+ uint8_t algorithm,
+ uint8_t digest_type,
+ const void *digest,
+ size_t digest_size) {
+
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ int r;
+
+ rr = dns_resource_record_new(key);
+ if (!rr)
+ return -ENOMEM;
+
+ rr->ds.key_tag = key_tag;
+ rr->ds.algorithm = algorithm;
+ rr->ds.digest_type = digest_type;
+ rr->ds.digest_size = digest_size;
+ rr->ds.digest = memdup(digest, rr->ds.digest_size);
+ if (!rr->ds.digest)
+ return -ENOMEM;
+
+ r = dns_answer_add(answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int dns_trust_anchor_add_builtin_positive(DnsTrustAnchor *d) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
int r;
assert(d);
@@ -62,35 +98,29 @@ static int dns_trust_anchor_add_builtin_positive(DnsTrustAnchor *d) {
if (r < 0)
return r;
- /* Only add the built-in trust anchor if there's neither a DS
- * nor a DNSKEY defined for the root domain. That way users
- * have an easy way to override the root domain DS/DNSKEY
- * data. */
+ /* Only add the built-in trust anchor if there's neither a DS nor a DNSKEY defined for the root domain. That
+ * way users have an easy way to override the root domain DS/DNSKEY data. */
if (dns_trust_anchor_knows_domain_positive(d, "."))
return 0;
- /* Add the RR from https://data.iana.org/root-anchors/root-anchors.xml */
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "");
- if (!rr)
+ key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_DS, "");
+ if (!key)
return -ENOMEM;
- rr->ds.key_tag = 19036;
- rr->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256;
- rr->ds.digest_type = DNSSEC_DIGEST_SHA256;
- rr->ds.digest_size = sizeof(root_digest);
- rr->ds.digest = memdup(root_digest, rr->ds.digest_size);
- if (!rr->ds.digest)
- return -ENOMEM;
-
- answer = dns_answer_new(1);
+ answer = dns_answer_new(2);
if (!answer)
return -ENOMEM;
- r = dns_answer_add(answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
+ /* Add the two RRs from https://data.iana.org/root-anchors/root-anchors.xml */
+ r = add_root_ksk(answer, key, 19036, DNSSEC_ALGORITHM_RSASHA256, DNSSEC_DIGEST_SHA256, root_digest1, sizeof(root_digest1));
if (r < 0)
return r;
- r = hashmap_put(d->positive_by_key, rr->key, answer);
+ r = add_root_ksk(answer, key, 20326, DNSSEC_ALGORITHM_RSASHA256, DNSSEC_DIGEST_SHA256, root_digest2, sizeof(root_digest2));
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(d->positive_by_key, key, answer);
if (r < 0)
return r;
@@ -547,10 +577,33 @@ int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey *ke
}
int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name) {
+ int r;
+
assert(d);
assert(name);
- return set_contains(d->negative_by_name, name);
+ for (;;) {
+ /* If the domain is listed as-is in the NTA database, then that counts */
+ if (set_contains(d->negative_by_name, name))
+ return true;
+
+ /* If the domain isn't listed as NTA, but is listed as positive trust anchor, then that counts. See RFC
+ * 7646, section 1.1 */
+ if (hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name)))
+ return false;
+
+ if (hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_KEY, name)))
+ return false;
+
+ /* And now, let's look at the parent, and check that too */
+ r = dns_name_parent(&name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+ }
+
+ return false;
}
static int dns_trust_anchor_revoked_put(DnsTrustAnchor *d, DnsResourceRecord *rr) {
@@ -594,7 +647,7 @@ static int dns_trust_anchor_remove_revoked(DnsTrustAnchor *d, DnsResourceRecord
/* We found the key! Warn the user */
log_struct(LOG_WARNING,
- LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED),
+ "MESSAGE_ID=" SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED_STR,
LOG_MESSAGE("DNSSEC Trust anchor %s has been revoked. Please update the trust anchor, or upgrade your operating system."), strna(dns_resource_record_to_string(rr)),
"TRUST_ANCHOR=%s", dns_resource_record_to_string(rr),
NULL);
diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c
index 746a979f47..ad024b54f5 100644
--- a/src/resolve/resolved-dns-zone.c
+++ b/src/resolve/resolved-dns-zone.c
@@ -196,6 +196,7 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) {
goto gc;
i->probe_transaction = t;
+ t->probing = true;
if (t->state == DNS_TRANSACTION_NULL) {
diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h
index a41df37e6b..545ec958fb 100644
--- a/src/resolve/resolved-dns-zone.h
+++ b/src/resolve/resolved-dns-zone.h
@@ -37,6 +37,9 @@ typedef enum DnsZoneItemState DnsZoneItemState;
/* RFC 4795 Section 2.8. suggests a TTL of 30s by default */
#define LLMNR_DEFAULT_TTL (30)
+/* RFC 6762 Section 10. suggests a TTL of 120s by default */
+#define MDNS_DEFAULT_TTL (120)
+
enum DnsZoneItemState {
DNS_ZONE_ITEM_PROBING,
DNS_ZONE_ITEM_ESTABLISHED,
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index e7e5c5f5a7..3f7f9035cf 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -85,6 +85,10 @@ Link *link_free(Link *l) {
if (!l)
return NULL;
+ /* Send goodbye messages. */
+ dns_scope_announce(l->mdns_ipv4_scope, true);
+ dns_scope_announce(l->mdns_ipv6_scope, true);
+
link_flush_settings(l);
while (l->addresses)
@@ -539,7 +543,7 @@ bool link_relevant(Link *l, int family, bool local_multicast) {
* beat, can do multicast and has at least one link-local (or better) IP address.
*
* A link is relevant for non-multicast traffic if it isn't a loopback device, has a link beat, and has at
- * least one routable address.*/
+ * least one routable address. */
if (l->flags & (IFF_LOOPBACK|IFF_DORMANT))
return false;
@@ -665,6 +669,7 @@ int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr
a->link = l;
LIST_PREPEND(addresses, l->addresses, a);
+ l->n_addresses++;
if (ret)
*ret = a;
@@ -679,6 +684,9 @@ LinkAddress *link_address_free(LinkAddress *a) {
if (a->link) {
LIST_REMOVE(addresses, a->link->addresses, a);
+ assert(a->link->n_addresses > 0);
+ a->link->n_addresses--;
+
if (a->llmnr_address_rr) {
if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
@@ -692,10 +700,26 @@ LinkAddress *link_address_free(LinkAddress *a) {
else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
}
+
+ if (a->mdns_address_rr) {
+ if (a->family == AF_INET && a->link->mdns_ipv4_scope)
+ dns_zone_remove_rr(&a->link->mdns_ipv4_scope->zone, a->mdns_address_rr);
+ else if (a->family == AF_INET6 && a->link->mdns_ipv6_scope)
+ dns_zone_remove_rr(&a->link->mdns_ipv6_scope->zone, a->mdns_address_rr);
+ }
+
+ if (a->mdns_ptr_rr) {
+ if (a->family == AF_INET && a->link->mdns_ipv4_scope)
+ dns_zone_remove_rr(&a->link->mdns_ipv4_scope->zone, a->mdns_ptr_rr);
+ else if (a->family == AF_INET6 && a->link->mdns_ipv6_scope)
+ dns_zone_remove_rr(&a->link->mdns_ipv6_scope->zone, a->mdns_ptr_rr);
+ }
}
dns_resource_record_unref(a->llmnr_address_rr);
dns_resource_record_unref(a->llmnr_ptr_rr);
+ dns_resource_record_unref(a->mdns_address_rr);
+ dns_resource_record_unref(a->mdns_ptr_rr);
return mfree(a);
}
@@ -746,7 +770,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
if (r < 0)
- log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
+ log_warning_errno(r, "Failed to add IPv4 PTR record to LLMNR zone: %m");
} else {
if (a->llmnr_address_rr) {
if (a->link->llmnr_ipv4_scope)
@@ -760,6 +784,59 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
}
}
+
+ if (!force_remove &&
+ link_address_relevant(a, true) &&
+ a->link->mdns_ipv4_scope &&
+ a->link->mdns_support == RESOLVE_SUPPORT_YES &&
+ a->link->manager->mdns_support == RESOLVE_SUPPORT_YES) {
+ if (!a->link->manager->mdns_host_ipv4_key) {
+ a->link->manager->mdns_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->mdns_hostname);
+ if (!a->link->manager->mdns_host_ipv4_key) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ if (!a->mdns_address_rr) {
+ a->mdns_address_rr = dns_resource_record_new(a->link->manager->mdns_host_ipv4_key);
+ if (!a->mdns_address_rr) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ a->mdns_address_rr->a.in_addr = a->in_addr.in;
+ a->mdns_address_rr->ttl = MDNS_DEFAULT_TTL;
+ }
+
+ if (!a->mdns_ptr_rr) {
+ r = dns_resource_record_new_reverse(&a->mdns_ptr_rr, a->family, &a->in_addr, a->link->manager->mdns_hostname);
+ if (r < 0)
+ goto fail;
+
+ a->mdns_ptr_rr->ttl = MDNS_DEFAULT_TTL;
+ }
+
+ r = dns_zone_put(&a->link->mdns_ipv4_scope->zone, a->link->mdns_ipv4_scope, a->mdns_address_rr, true);
+ if (r < 0)
+ log_warning_errno(r, "Failed to add A record to MDNS zone: %m");
+
+ r = dns_zone_put(&a->link->mdns_ipv4_scope->zone, a->link->mdns_ipv4_scope, a->mdns_ptr_rr, false);
+ if (r < 0)
+ log_warning_errno(r, "Failed to add IPv4 PTR record to MDNS zone: %m");
+ } else {
+ if (a->mdns_address_rr) {
+ if (a->link->mdns_ipv4_scope)
+ dns_zone_remove_rr(&a->link->mdns_ipv4_scope->zone, a->mdns_address_rr);
+ a->mdns_address_rr = dns_resource_record_unref(a->mdns_address_rr);
+ }
+
+ if (a->mdns_ptr_rr) {
+ if (a->link->mdns_ipv4_scope)
+ dns_zone_remove_rr(&a->link->mdns_ipv4_scope->zone, a->mdns_ptr_rr);
+ a->mdns_ptr_rr = dns_resource_record_unref(a->mdns_ptr_rr);
+ }
+ }
}
if (a->family == AF_INET6) {
@@ -817,6 +894,60 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
}
}
+
+ if (!force_remove &&
+ link_address_relevant(a, true) &&
+ a->link->mdns_ipv6_scope &&
+ a->link->mdns_support == RESOLVE_SUPPORT_YES &&
+ a->link->manager->mdns_support == RESOLVE_SUPPORT_YES) {
+
+ if (!a->link->manager->mdns_host_ipv6_key) {
+ a->link->manager->mdns_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->mdns_hostname);
+ if (!a->link->manager->mdns_host_ipv6_key) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ if (!a->mdns_address_rr) {
+ a->mdns_address_rr = dns_resource_record_new(a->link->manager->mdns_host_ipv6_key);
+ if (!a->mdns_address_rr) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ a->mdns_address_rr->aaaa.in6_addr = a->in_addr.in6;
+ a->mdns_address_rr->ttl = MDNS_DEFAULT_TTL;
+ }
+
+ if (!a->mdns_ptr_rr) {
+ r = dns_resource_record_new_reverse(&a->mdns_ptr_rr, a->family, &a->in_addr, a->link->manager->mdns_hostname);
+ if (r < 0)
+ goto fail;
+
+ a->mdns_ptr_rr->ttl = MDNS_DEFAULT_TTL;
+ }
+
+ r = dns_zone_put(&a->link->mdns_ipv6_scope->zone, a->link->mdns_ipv6_scope, a->mdns_address_rr, true);
+ if (r < 0)
+ log_warning_errno(r, "Failed to add AAAA record to MDNS zone: %m");
+
+ r = dns_zone_put(&a->link->mdns_ipv6_scope->zone, a->link->mdns_ipv6_scope, a->mdns_ptr_rr, false);
+ if (r < 0)
+ log_warning_errno(r, "Failed to add IPv6 PTR record to MDNS zone: %m");
+ } else {
+ if (a->mdns_address_rr) {
+ if (a->link->mdns_ipv6_scope)
+ dns_zone_remove_rr(&a->link->mdns_ipv6_scope->zone, a->mdns_address_rr);
+ a->mdns_address_rr = dns_resource_record_unref(a->mdns_address_rr);
+ }
+
+ if (a->mdns_ptr_rr) {
+ if (a->link->mdns_ipv6_scope)
+ dns_zone_remove_rr(&a->link->mdns_ipv6_scope->zone, a->mdns_ptr_rr);
+ a->mdns_ptr_rr = dns_resource_record_unref(a->mdns_ptr_rr);
+ }
+ }
}
return;
diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h
index c9b2a58c34..55a56b7906 100644
--- a/src/resolve/resolved-link.h
+++ b/src/resolve/resolved-link.h
@@ -47,6 +47,8 @@ struct LinkAddress {
DnsResourceRecord *llmnr_address_rr;
DnsResourceRecord *llmnr_ptr_rr;
+ DnsResourceRecord *mdns_address_rr;
+ DnsResourceRecord *mdns_ptr_rr;
LIST_FIELDS(LinkAddress, addresses);
};
@@ -58,6 +60,7 @@ struct Link {
unsigned flags;
LIST_HEAD(LinkAddress, addresses);
+ unsigned n_addresses;
LIST_HEAD(DnsServer, dns_servers);
DnsServer *current_dns_server;
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 667774b906..c4e4409fe3 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -322,28 +322,28 @@ 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);
- /* Extract and normalize the first label of the locally
- * configured hostname, and check it's not "localhost". */
+ /* Extract and normalize the first label of the locally configured hostname, and check it's not "localhost". */
- h = gethostname_malloc();
- if (!h)
- return log_oom();
+ r = gethostname_strict(&h);
+ if (r < 0)
+ return log_debug_errno(r, "Can't determine system hostname: %m");
p = h;
r = dns_label_unescape(&p, label, sizeof(label));
if (r < 0)
return log_error_errno(r, "Failed to unescape host name: %m");
if (r == 0) {
- log_error("Couldn't find a single label in hosntame.");
+ log_error("Couldn't find a single label in hostname.");
return -EINVAL;
}
@@ -374,32 +374,84 @@ static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) {
*llmnr_hostname = n;
n = NULL;
+ *full_hostname = h;
+ h = NULL;
+
+ return 0;
+}
+
+static const char *fallback_hostname(void) {
+
+ /* Determine the fall back hostname. For exposing this system to the outside world, we cannot have it to be
+ * "localhost" even if that's the compiled in hostname. In this case, let's revert to "linux" instead. */
+
+ if (is_localhost(FALLBACK_HOSTNAME))
+ return "linux";
+
+ return FALLBACK_HOSTNAME;
+}
+
+static int make_fallback_hostnames(char **full_hostname, char **llmnr_hostname, char **mdns_hostname) {
+ _cleanup_free_ char *n = NULL, *m = NULL;
+ char label[DNS_LABEL_MAX], *h;
+ const char *p;
+ int r;
+
+ assert(full_hostname);
+ assert(llmnr_hostname);
+ assert(mdns_hostname);
+
+ p = fallback_hostname();
+ r = dns_label_unescape(&p, label, sizeof(label));
+ if (r < 0)
+ return log_error_errno(r, "Failed to unescape fallback host name: %m");
+
+ assert(r > 0); /* The fallback hostname must have at least one label */
+
+ r = dns_label_escape_new(label, r, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to escape fallback hostname: %m");
+
+ r = dns_name_concat(n, "local", &m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to concatenate mDNS hostname: %m");
+
+ h = strdup(fallback_hostname());
+ if (!h)
+ return log_oom();
+
+ *llmnr_hostname = n;
+ n = NULL;
+
+ *mdns_hostname = m;
+ m = NULL;
+
+ *full_hostname = h;
+
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);
+ log_info("System hostname changed to '%s'.", full_hostname);
- free(m->llmnr_hostname);
- free(m->mdns_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,18 +480,15 @@ 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->llmnr_hostname = strdup("linux");
- if (!m->llmnr_hostname)
- return log_oom();
-
- m->mdns_hostname = strdup("linux.local");
- if (!m->mdns_hostname)
- return log_oom();
+ log_info("Defaulting to hostname '%s'.", fallback_hostname());
+
+ r = make_fallback_hostnames(&m->full_hostname, &m->llmnr_hostname, &m->mdns_hostname);
+ if (r < 0)
+ return r;
} else
- log_info("Using system hostname '%s'.", m->llmnr_hostname);
+ log_info("Using system hostname '%s'.", m->full_hostname);
return 0;
}
@@ -498,7 +547,7 @@ int manager_new(Manager **ret) {
m->hostname_fd = -1;
m->llmnr_support = RESOLVE_SUPPORT_YES;
- m->mdns_support = RESOLVE_SUPPORT_NO;
+ m->mdns_support = RESOLVE_SUPPORT_YES;
m->dnssec_mode = DEFAULT_DNSSEC_MODE;
m->enable_cache = true;
m->dns_stub_listener_mode = DNS_STUB_LISTENER_UDP;
@@ -621,9 +670,13 @@ Manager *manager_free(Manager *m) {
dns_resource_key_unref(m->llmnr_host_ipv4_key);
dns_resource_key_unref(m->llmnr_host_ipv6_key);
+ dns_resource_key_unref(m->mdns_host_ipv4_key);
+ dns_resource_key_unref(m->mdns_host_ipv6_key);
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);
@@ -1007,6 +1060,8 @@ void manager_refresh_rrs(Manager *m) {
m->llmnr_host_ipv4_key = dns_resource_key_unref(m->llmnr_host_ipv4_key);
m->llmnr_host_ipv6_key = dns_resource_key_unref(m->llmnr_host_ipv6_key);
+ m->mdns_host_ipv4_key = dns_resource_key_unref(m->mdns_host_ipv4_key);
+ m->mdns_host_ipv6_key = dns_resource_key_unref(m->mdns_host_ipv6_key);
HASHMAP_FOREACH(l, m->links, i) {
link_add_rrs(l, true);
@@ -1146,8 +1201,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;
}
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index 6b2208ed94..97c52b7729 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -109,10 +109,13 @@ 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;
DnsResourceKey *llmnr_host_ipv6_key;
+ DnsResourceKey *mdns_host_ipv4_key;
+ DnsResourceKey *mdns_host_ipv6_key;
/* Watch the system hostname */
int hostname_fd;
diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c
index b13b1d0144..c40e8f75f0 100644
--- a/src/resolve/resolved-mdns.c
+++ b/src/resolve/resolved-mdns.c
@@ -67,6 +67,50 @@ eaddrinuse:
return 0;
}
+static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
+ _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
+ DnsResourceKey *key = NULL;
+ bool tentative = false;
+ int r;
+
+ assert(s);
+ assert(p);
+
+ r = dns_packet_extract(p);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to extract resource records from incoming packet: %m");
+
+ /* TODO: there might be more than one question in mDNS queries. */
+ assert_return((dns_question_size(p->question) > 0), -EINVAL);
+ key = p->question->keys[0];
+
+ r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to lookup key: %m");
+ return r;
+ }
+ if (r == 0)
+ return 0;
+
+ r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, NULL, answer, NULL, false, &reply);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to build reply packet: %m");
+ return r;
+ }
+
+ if (!ratelimit_test(&s->ratelimit))
+ return 0;
+
+ r = dns_scope_emit_udp(s, -1, reply);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to send reply packet: %m");
+ return r;
+ }
+
+ return 0;
+}
+
static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
Manager *m = userdata;
@@ -77,6 +121,9 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
if (r <= 0)
return r;
+ if (manager_our_packet(m, p))
+ return 0;
+
scope = manager_find_scope(m, p);
if (!scope) {
log_warning("Got mDNS UDP packet on unknown scope. Ignoring.");
@@ -115,9 +162,28 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
dns_name_endswith(name, "local") > 0))
return 0;
+ if (rr->ttl == 0) {
+ log_debug("Got a goodbye packet");
+ /* See the section 10.1 of RFC6762 */
+ rr->ttl = 1;
+ }
+
t = dns_scope_find_transaction(scope, rr->key, false);
if (t)
dns_transaction_process_reply(t, p);
+
+ /* Also look for the various types of ANY transactions */
+ t = dns_scope_find_transaction(scope, &DNS_RESOURCE_KEY_CONST(rr->key->class, DNS_TYPE_ANY, dns_resource_key_name(rr->key)), false);
+ if (t)
+ dns_transaction_process_reply(t, p);
+
+ t = dns_scope_find_transaction(scope, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_ANY, rr->key->type, dns_resource_key_name(rr->key)), false);
+ if (t)
+ dns_transaction_process_reply(t, p);
+
+ t = dns_scope_find_transaction(scope, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_ANY, DNS_TYPE_ANY, dns_resource_key_name(rr->key)), false);
+ if (t)
+ dns_transaction_process_reply(t, p);
}
dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer, false, (uint32_t) -1, 0, p->family, &p->sender);
@@ -125,7 +191,11 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
} else if (dns_packet_validate_query(p) > 0) {
log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p));
- dns_scope_process_query(scope, NULL, p);
+ r = mdns_scope_process_query(scope, p);
+ if (r < 0) {
+ log_debug_errno(r, "mDNS query processing failed: %m");
+ return 0;
+ }
} else
log_debug("Invalid mDNS UDP packet.");
diff --git a/src/resolve/resolved-mdns.h b/src/resolve/resolved-mdns.h
index 5d274648f4..06bd3296be 100644
--- a/src/resolve/resolved-mdns.h
+++ b/src/resolve/resolved-mdns.h
@@ -22,6 +22,7 @@
#include "resolved-manager.h"
#define MDNS_PORT 5353
+#define MDNS_ANNOUNCE_DELAY (1 * USEC_PER_SEC)
int manager_mdns_ipv4_fd(Manager *m);
int manager_mdns_ipv6_fd(Manager *m);
diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c
index 13f08f8a6c..3c62550872 100644
--- a/src/resolve/resolved-resolv-conf.c
+++ b/src/resolve/resolved-resolv-conf.c
@@ -203,13 +203,13 @@ static void write_resolv_conf_search(
static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) {
Iterator i;
- fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n"
+ fputs("# This file is managed by man:systemd-resolved(8). Do not edit.\n#\n"
"# This is a dynamic resolv.conf file for connecting local clients directly to\n"
"# all known DNS servers.\n#\n"
"# Third party programs must not access this file directly, but only through the\n"
- "# symlink at /etc/resolv.conf. To manage resolv.conf(5) in a different way,\n"
+ "# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,\n"
"# replace this symlink by a static file or a different symlink.\n#\n"
- "# See systemd-resolved.service(8) for details about the supported modes of\n"
+ "# See man:systemd-resolved.service(8) for details about the supported modes of\n"
"# operation for /etc/resolv.conf.\n\n", f);
if (ordered_set_isempty(dns))
diff --git a/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts b/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts
deleted file mode 100644
index a383c6286d..0000000000
--- a/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts b/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts
deleted file mode 100644
index 15de02e997..0000000000
--- a/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/fake-caa.pkts b/src/resolve/test-data/fake-caa.pkts
deleted file mode 100644
index 1c3ecc5491..0000000000
--- a/src/resolve/test-data/fake-caa.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/fedoraproject.org.pkts b/src/resolve/test-data/fedoraproject.org.pkts
deleted file mode 100644
index 17874844d9..0000000000
--- a/src/resolve/test-data/fedoraproject.org.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/gandi.net.pkts b/src/resolve/test-data/gandi.net.pkts
deleted file mode 100644
index 5ef51e0c8e..0000000000
--- a/src/resolve/test-data/gandi.net.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/google.com.pkts b/src/resolve/test-data/google.com.pkts
deleted file mode 100644
index f98c4cd855..0000000000
--- a/src/resolve/test-data/google.com.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/kyhwana.org.pkts b/src/resolve/test-data/kyhwana.org.pkts
deleted file mode 100644
index e28a725c9a..0000000000
--- a/src/resolve/test-data/kyhwana.org.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/root.pkts b/src/resolve/test-data/root.pkts
deleted file mode 100644
index 54ba668c75..0000000000
--- a/src/resolve/test-data/root.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts b/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts
deleted file mode 100644
index a854249532..0000000000
--- a/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/teamits.com.pkts b/src/resolve/test-data/teamits.com.pkts
deleted file mode 100644
index 11deb39677..0000000000
--- a/src/resolve/test-data/teamits.com.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/zbyszek@fedoraproject.org.pkts b/src/resolve/test-data/zbyszek@fedoraproject.org.pkts
deleted file mode 100644
index f0a6f982df..0000000000
--- a/src/resolve/test-data/zbyszek@fedoraproject.org.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-dns-packet.c b/src/resolve/test-dns-packet.c
index 956b155872..8cbe492526 100644
--- a/src/resolve/test-dns-packet.c
+++ b/src/resolve/test-dns-packet.c
@@ -29,6 +29,7 @@
#include "resolved-dns-rr.h"
#include "string-util.h"
#include "strv.h"
+#include "tests.h"
#include "unaligned.h"
#define HASH_KEY SD_ID128_MAKE(d3,1e,48,90,4b,fa,4c,fe,af,9d,d5,a1,d7,2e,8a,b1)
@@ -115,7 +116,7 @@ int main(int argc, char **argv) {
N = argc - 1;
fnames = argv + 1;
} else {
- assert_se(glob(RESOLVE_TEST_DIR "/*.pkts", GLOB_NOSORT, NULL, &g) == 0);
+ assert_se(glob(get_testdata_dir("/test-resolve/*.pkts"), GLOB_NOSORT, NULL, &g) == 0);
N = g.gl_pathc;
fnames = g.gl_pathv;
}
diff --git a/src/run/run.c b/src/run/run.c
index 08f7e12336..2e6765aa18 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -497,7 +497,7 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons
if (r < 0)
return r;
- if (arg_wait) {
+ if (arg_wait || arg_pty) {
r = sd_bus_message_append(m, "(sv)", "AddRef", "b", 1);
if (r < 0)
return r;
@@ -818,16 +818,18 @@ static int run_context_update(RunContext *c, const char *path) {
{}
};
+ _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",
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);
@@ -1024,7 +1026,6 @@ static int start_transient_service(
pty_forward_set_handler(c.forward, pty_forward_handler, &c);
}
-
path = unit_dbus_path_from_name(service);
if (!path)
return log_oom();
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 20c1085697..aae69f6da5 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -554,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;
@@ -575,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")) {
@@ -585,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;
@@ -862,7 +862,7 @@ static void log_job_error_with_service_result(const char* service, const char *r
service_shell_quoted = shell_maybe_quote(service);
- if (extra_args && extra_args[1]) {
+ if (extra_args) {
_cleanup_free_ char *t;
t = strv_join((char**) extra_args, " ");
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/dissect-image.c b/src/shared/dissect-image.c
index 410a7764ed..39e724c51a 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -61,12 +61,8 @@ static int probe_filesystem(const char *node, char **ret_fstype) {
log_debug("Failed to identify any partition type on partition %s", node);
goto not_found;
}
- if (r != 0) {
- if (errno == 0)
- return -EIO;
-
- return -errno;
- }
+ if (r != 0)
+ return -errno ?: -EIO;
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
@@ -146,12 +142,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
errno = 0;
r = blkid_probe_set_device(b, fd, 0, 0);
- if (r != 0) {
- if (errno == 0)
- return -ENOMEM;
-
- return -errno;
- }
+ if (r != 0)
+ return -errno ?: -ENOMEM;
if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
/* Look for file system superblocks, unless we only shall look for GPT partition tables */
@@ -168,12 +160,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
log_debug("Failed to identify any partition table.");
return -ENOPKG;
}
- if (r != 0) {
- if (errno == 0)
- return -EIO;
-
- return -errno;
- }
+ if (r != 0)
+ return -errno ?: -EIO;
m = new0(DissectedImage, 1);
if (!m)
@@ -232,12 +220,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
errno = 0;
pl = blkid_probe_get_partitions(b);
- if (!pl) {
- if (errno == 0)
- return -ENOMEM;
-
- return -errno;
- }
+ if (!pl)
+ return -errno ?: -ENOMEM;
udev = udev_new();
if (!udev)
diff --git a/src/shared/dropin.c b/src/shared/dropin.c
index 3917eb8f23..15ccd1b6ca 100644
--- a/src/shared/dropin.c
+++ b/src/shared/dropin.c
@@ -43,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);
@@ -128,9 +127,10 @@ static int unit_file_find_dir(
assert(path);
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;
if (r < 0)
- return log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING,
- r, "Failed to canonicalize path %s: %m", path);
+ return log_full_errno(LOG_WARNING, r, "Failed to canonicalize path %s: %m", path);
r = strv_push(dirs, chased);
if (r < 0)
@@ -148,16 +148,14 @@ static int unit_file_find_dirs(
const char *suffix,
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)) {
r = unit_file_find_dir(original_root, path, dirs);
@@ -166,22 +164,15 @@ static int unit_file_find_dirs(
}
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)) {
- r = unit_file_find_dir(original_root, p, dirs);
- if (r < 0)
- return r;
- }
+ return unit_file_find_dirs(original_root, unit_path_cache, unit_path, template, suffix, dirs);
}
return 0;
@@ -194,27 +185,30 @@ int unit_file_find_dropin_paths(
const char *dir_suffix,
const char *file_suffix,
Set *names,
- char ***paths) {
+ char ***ret) {
_cleanup_strv_free_ char **dirs = NULL, **ans = NULL;
Iterator i;
char *t, **p;
int r;
- assert(paths);
+ assert(ret);
SET_FOREACH(t, names, i)
STRV_FOREACH(p, lookup_path)
unit_file_find_dirs(original_root, unit_path_cache, *p, t, dir_suffix, &dirs);
- if (strv_isempty(dirs))
+ if (strv_isempty(dirs)) {
+ *ret = NULL;
return 0;
+ }
r = conf_files_list_strv(&ans, file_suffix, NULL, (const char**) dirs);
if (r < 0)
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/firewall-util.c b/src/shared/firewall-util.c
index 9c29b0afca..952fc48c45 100644
--- a/src/shared/firewall-util.c
+++ b/src/shared/firewall-util.c
@@ -76,8 +76,11 @@ static int entry_fill_basics(
}
if (out_interface) {
+ size_t l = strlen(out_interface);
+ assert(l < sizeof entry->ip.outiface && l < sizeof entry->ip.outiface_mask);
+
strcpy(entry->ip.outiface, out_interface);
- memset(entry->ip.outiface_mask, 0xFF, strlen(out_interface)+1);
+ memset(entry->ip.outiface_mask, 0xFF, l + 1);
}
if (destination) {
entry->ip.dst = destination->in;
diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c
index 87b520b540..c3106f1ae9 100644
--- a/src/shared/fstab-util.c
+++ b/src/shared/fstab-util.c
@@ -213,7 +213,7 @@ static char *unquote(const char *s, const char* quotes) {
* trailing quotes if there is one. Doesn't care about
* escaping or anything.
*
- * DON'T USE THIS FOR NEW CODE ANYMORE!*/
+ * DON'T USE THIS FOR NEW CODE ANYMORE! */
l = strlen(s);
if (l < 2)
diff --git a/src/shared/install.c b/src/shared/install.c
index f25ed685f6..58c8e852b2 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -389,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.",
@@ -1807,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;
@@ -1833,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->type != UNIT_FILE_TYPE_REGULAR) {
- log_debug("Unit %s has type %s, ignoring.",
- i->name,
- unit_file_type_to_string(i->type) ?: "invalid");
+ 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);
+ }
+
+ } 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;
}
@@ -1878,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;
@@ -1926,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) {
@@ -2015,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;
@@ -2282,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);
@@ -2347,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,
@@ -2391,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))
@@ -2401,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;
@@ -2790,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;
@@ -2885,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)
@@ -2923,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/journal-util.c b/src/shared/journal-util.c
new file mode 100644
index 0000000000..8479221a44
--- /dev/null
+++ b/src/shared/journal-util.c
@@ -0,0 +1,151 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Zbigniew Jędrzejewski-Szmek
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "acl-util.h"
+#include "fs-util.h"
+#include "hashmap.h"
+#include "journal-internal.h"
+#include "journal-util.h"
+#include "log.h"
+#include "strv.h"
+#include "user-util.h"
+
+static int access_check_var_log_journal(sd_journal *j) {
+#ifdef HAVE_ACL
+ _cleanup_strv_free_ char **g = NULL;
+ const char* dir;
+#endif
+ int r;
+
+ assert(j);
+
+ /* If we are root, we should have access, don't warn. */
+ if (getuid() == 0)
+ return 0;
+
+ /* If we are in the 'systemd-journal' group, we should have
+ * access too. */
+ r = in_group("systemd-journal");
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if we are in the 'systemd-journal' group: %m");
+ if (r > 0)
+ return 0;
+
+#ifdef HAVE_ACL
+ if (laccess("/run/log/journal", F_OK) >= 0)
+ dir = "/run/log/journal";
+ else
+ dir = "/var/log/journal";
+
+ /* If we are in any of the groups listed in the journal ACLs,
+ * then all is good, too. Let's enumerate all groups from the
+ * default ACL of the directory, which generally should allow
+ * access to most journal files too. */
+ r = acl_search_groups(dir, &g);
+ if (r < 0)
+ return log_error_errno(r, "Failed to search journal ACL: %m");
+ if (r > 0)
+ return 0;
+
+ /* Print a pretty list, if there were ACLs set. */
+ if (!strv_isempty(g)) {
+ _cleanup_free_ char *s = NULL;
+
+ /* Thre are groups in the ACL, let's list them */
+ r = strv_extend(&g, "systemd-journal");
+ if (r < 0)
+ return log_oom();
+
+ strv_sort(g);
+ strv_uniq(g);
+
+ s = strv_join(g, "', '");
+ if (!s)
+ return log_oom();
+
+ log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
+ " Users in groups '%s' can see all messages.\n"
+ " Pass -q to turn off this notice.", s);
+ return 1;
+ }
+#endif
+
+ /* If no ACLs were found, print a short version of the message. */
+ log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
+ " Users in the 'systemd-journal' group can see all messages. Pass -q to\n"
+ " turn off this notice.");
+
+ return 1;
+}
+
+int journal_access_check_and_warn(sd_journal *j, bool quiet) {
+ Iterator it;
+ void *code;
+ char *path;
+ int r = 0;
+
+ assert(j);
+
+ if (hashmap_isempty(j->errors)) {
+ if (ordered_hashmap_isempty(j->files) && !quiet)
+ log_notice("No journal files were found.");
+
+ return 0;
+ }
+
+ if (hashmap_contains(j->errors, INT_TO_PTR(-EACCES))) {
+ if (!quiet)
+ (void) access_check_var_log_journal(j);
+
+ if (ordered_hashmap_isempty(j->files))
+ r = log_error_errno(EACCES, "No journal files were opened due to insufficient permissions.");
+ }
+
+ HASHMAP_FOREACH_KEY(path, code, j->errors, it) {
+ int err;
+
+ err = abs(PTR_TO_INT(code));
+
+ switch (err) {
+ case EACCES:
+ continue;
+
+ case ENODATA:
+ log_warning_errno(err, "Journal file %s is truncated, ignoring file.", path);
+ break;
+
+ case EPROTONOSUPPORT:
+ 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:
+ log_warning_errno(err, "Journal file %s corrupted, ignoring file.", path);
+ break;
+
+ default:
+ log_warning_errno(err, "An error was encountered while opening journal file or directory %s, ignoring file: %m", path);
+ break;
+ }
+ }
+
+ return r;
+}
diff --git a/src/shared/journal-util.h b/src/shared/journal-util.h
new file mode 100644
index 0000000000..499e6c62ec
--- /dev/null
+++ b/src/shared/journal-util.h
@@ -0,0 +1,25 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Zbigniew Jędrzejewski-Szmek
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include "sd-journal.h"
+
+int journal_access_check_and_warn(sd_journal *j, bool quiet);
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index 7bc5c0a128..32a4c67590 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -594,7 +594,7 @@ static int clone_auxiliary_file(const char *path, const char *new_name, const ch
if (!rs)
return -ENOMEM;
- return copy_file_atomic(path, rs, 0664, false, 0);
+ return copy_file_atomic(path, rs, 0664, 0, COPY_REFLINK);
}
int image_clone(Image *i, const char *new_name, bool read_only) {
@@ -636,7 +636,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
case IMAGE_SUBVOLUME:
case IMAGE_DIRECTORY:
/* If we can we'll always try to create a new btrfs subvolume here, even if the source is a plain
- * directory.*/
+ * directory. */
new_path = strjoina("/var/lib/machines/", new_name);
@@ -656,7 +656,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
case IMAGE_RAW:
new_path = strjoina("/var/lib/machines/", new_name, ".raw");
- r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, false, FS_NOCOW_FL);
+ r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, FS_NOCOW_FL, COPY_REFLINK);
break;
default:
@@ -712,7 +712,7 @@ int image_read_only(Image *i, bool b) {
use the "immutable" flag, to at least make the
top-level directory read-only. It's not as good as
a read-only subvolume, but at least something, and
- we can read the value back.*/
+ we can read the value back. */
r = chattr_path(i->path, b ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL);
if (r < 0)
diff --git a/src/shared/pager.c b/src/shared/pager.c
index 09672a4abf..f00ba9e1e7 100644
--- a/src/shared/pager.c
+++ b/src/shared/pager.c
@@ -44,7 +44,7 @@ static pid_t pager_pid = 0;
noreturn static void pager_fallback(void) {
int r;
- r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_t) -1, false);
+ r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_t) -1, 0);
if (r < 0) {
log_error_errno(r, "Internal pager failed: %m");
_exit(EXIT_FAILURE);
@@ -104,7 +104,8 @@ int pager_open(bool no_pager, bool jump_to_end) {
less_opts = "FRSXMK";
if (jump_to_end)
less_opts = strjoina(less_opts, " +G");
- setenv("LESS", less_opts, 1);
+ if (setenv("LESS", less_opts, 1) < 0)
+ _exit(EXIT_FAILURE);
/* Initialize a good charset for less. This is
* particularly important if we output UTF-8
@@ -112,8 +113,9 @@ int pager_open(bool no_pager, bool jump_to_end) {
less_charset = getenv("SYSTEMD_LESSCHARSET");
if (!less_charset && is_locale_utf8())
less_charset = "utf-8";
- if (less_charset)
- setenv("LESSCHARSET", less_charset, 1);
+ if (less_charset &&
+ setenv("LESSCHARSET", less_charset, 1) < 0)
+ _exit(EXIT_FAILURE);
/* Make sure the pager goes away when the parent dies */
if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index 586ef64e72..e2b3f8b742 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 */
@@ -503,8 +520,7 @@ int lookup_paths_init(
append = true;
}
- /* FIXME: empty components in other places should be
- * rejected. */
+ /* FIXME: empty components in other places should be rejected. */
r = path_split_and_make_absolute(e, &paths);
if (r < 0)
diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c
index e35f18471c..2631856563 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
};
@@ -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"
@@ -760,6 +802,8 @@ int seccomp_restrict_namespaces(unsigned long retain) {
case SCMP_ARCH_X86_64:
case SCMP_ARCH_X86:
case SCMP_ARCH_X32:
+ case SCMP_ARCH_PPC64:
+ case SCMP_ARCH_PPC64LE:
clone_reversed_order = 0;
break;
@@ -771,8 +815,8 @@ int seccomp_restrict_namespaces(unsigned long retain) {
/* 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!"
+#if SECCOMP_RESTRICT_NAMESPACES_BROKEN
+# warning "Consider adding the right clone() syscall definitions here!"
#endif
}
@@ -906,17 +950,42 @@ int seccomp_protect_sysctl(void) {
}
int seccomp_restrict_address_families(Set *address_families, bool whitelist) {
-
-#if !SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN
uint32_t arch;
int r;
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;
@@ -1036,7 +1105,6 @@ int seccomp_restrict_address_families(Set *address_families, bool whitelist) {
if (r < 0)
log_debug_errno(r, "Failed to install socket family rules for architecture %s, skipping: %m", seccomp_arch_to_string(arch));
}
-#endif
return 0;
}
diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h
index 61f94de638..b56ac3f763 100644
--- a/src/shared/seccomp-util.h
+++ b/src/shared/seccomp-util.h
@@ -92,7 +92,7 @@ int seccomp_memory_deny_write_execute(void);
#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__)
+#if defined(__x86_64__) || defined(__i386__) || defined(__s390x__) || defined(__s390__) || defined(__powerpc64__)
#define SECCOMP_RESTRICT_NAMESPACES_BROKEN 0
#else
#define SECCOMP_RESTRICT_NAMESPACES_BROKEN 1
diff --git a/src/shared/tests.c b/src/shared/tests.c
index 409116290d..f300bbc66f 100644
--- a/src/shared/tests.c
+++ b/src/shared/tests.c
@@ -17,10 +17,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <alloc-util.h>
+#include <fs-util.h>
+#include <libgen.h>
#include <stdlib.h>
#include <util.h>
#include "tests.h"
+#include "path-util.h"
char* setup_fake_runtime_dir(void) {
char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p;
@@ -31,3 +35,39 @@ char* setup_fake_runtime_dir(void) {
return p;
}
+
+const char* get_testdata_dir(const char *suffix) {
+ const char *env;
+ /* convenience: caller does not need to free result */
+ static char testdir[PATH_MAX];
+
+ /* if the env var is set, use that */
+ env = getenv("SYSTEMD_TEST_DATA");
+ testdir[sizeof(testdir) - 1] = '\0';
+ if (env) {
+ if (access(env, F_OK) < 0) {
+ fputs("ERROR: $SYSTEMD_TEST_DATA directory does not exist\n", stderr);
+ exit(1);
+ }
+ strncpy(testdir, env, sizeof(testdir) - 1);
+ } else {
+ _cleanup_free_ char *exedir = NULL;
+ assert_se(readlink_and_make_absolute("/proc/self/exe", &exedir) >= 0);
+
+ /* Check if we're running from the builddir. If so, use the compiled in path. */
+ if (path_startswith(exedir, ABS_BUILD_DIR))
+ assert_se(snprintf(testdir, sizeof(testdir), "%s/test", ABS_SRC_DIR) > 0);
+ else
+ /* Try relative path, according to the install-test layout */
+ assert_se(snprintf(testdir, sizeof(testdir), "%s/testdata", dirname(exedir)) > 0);
+
+ /* test this without the suffix, as it may contain a glob */
+ if (access(testdir, F_OK) < 0) {
+ fputs("ERROR: Cannot find testdata directory, set $SYSTEMD_TEST_DATA\n", stderr);
+ exit(1);
+ }
+ }
+
+ strncpy(testdir + strlen(testdir), suffix, sizeof(testdir) - strlen(testdir) - 1);
+ return testdir;
+}
diff --git a/src/shared/tests.h b/src/shared/tests.h
index 93f09013a1..7055124990 100644
--- a/src/shared/tests.h
+++ b/src/shared/tests.h
@@ -20,3 +20,4 @@
***/
char* setup_fake_runtime_dir(void);
+const char* get_testdata_dir(const char *suffix);
diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c
index c8f0742183..3bac78b3e4 100644
--- a/src/sleep/sleep.c
+++ b/src/sleep/sleep.c
@@ -25,6 +25,7 @@
#include "sd-messages.h"
#include "def.h"
+#include "exec-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "log.h"
@@ -106,10 +107,10 @@ static int execute(char **modes, char **states) {
if (r < 0)
return r;
- execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
+ execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
log_struct(LOG_INFO,
- LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START),
+ "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
LOG_MESSAGE("Suspending system..."),
"SLEEP=%s", arg_verb,
NULL);
@@ -119,13 +120,13 @@ static int execute(char **modes, char **states) {
return r;
log_struct(LOG_INFO,
- LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP),
+ "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
LOG_MESSAGE("System resumed."),
"SLEEP=%s", arg_verb,
NULL);
arguments[1] = (char*) "post";
- execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
+ execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
return r;
}
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 2809dece50..d78e56d777 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++;
}
@@ -3482,6 +3482,8 @@ static int set_exit_code(uint8_t code) {
static int start_special(int argc, char *argv[], void *userdata) {
enum action a;
int r;
+ bool termination_action; /* an action that terminates the manager,
+ * can be performed also by signal. */
assert(argv);
@@ -3521,40 +3523,43 @@ static int start_special(int argc, char *argv[], void *userdata) {
return r;
}
- if (arg_force >= 2 &&
- IN_SET(a,
- ACTION_HALT,
- ACTION_POWEROFF,
- ACTION_REBOOT))
+ termination_action = IN_SET(a,
+ ACTION_HALT,
+ ACTION_POWEROFF,
+ ACTION_REBOOT);
+ if (termination_action && arg_force >= 2)
return halt_now(a);
if (arg_force >= 1 &&
- IN_SET(a,
- ACTION_HALT,
- ACTION_POWEROFF,
- ACTION_REBOOT,
- ACTION_KEXEC,
- ACTION_EXIT))
- return trivial_method(argc, argv, userdata);
+ (termination_action || IN_SET(a, ACTION_KEXEC, ACTION_EXIT)))
+ r = trivial_method(argc, argv, userdata);
+ else {
+ /* First try logind, to allow authentication with polkit */
+ if (IN_SET(a,
+ ACTION_POWEROFF,
+ ACTION_REBOOT,
+ ACTION_SUSPEND,
+ ACTION_HIBERNATE,
+ ACTION_HYBRID_SLEEP)) {
+
+ r = logind_reboot(a);
+ if (r >= 0)
+ return r;
+ if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS))
+ /* requested operation is not supported or already in progress */
+ return r;
- /* First try logind, to allow authentication with polkit */
- if (IN_SET(a,
- ACTION_POWEROFF,
- ACTION_REBOOT,
- ACTION_SUSPEND,
- ACTION_HIBERNATE,
- ACTION_HYBRID_SLEEP)) {
- r = logind_reboot(a);
- if (r >= 0)
- return r;
- if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS))
- /* requested operation is not supported or already in progress */
- return r;
+ /* On all other errors, try low-level operation */
+ }
- /* On all other errors, try low-level operation */
+ r = start_unit(argc, argv, userdata);
}
- return start_unit(argc, argv, userdata);
+ if (termination_action && arg_force < 2 &&
+ IN_SET(r, -ENOENT, -ETIMEDOUT))
+ log_notice("It is possible to perform action directly, see discussion of --force --force in man:systemctl(1).");
+
+ return r;
}
static int start_system_special(int argc, char *argv[], void *userdata) {
@@ -4953,7 +4958,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));
@@ -5125,8 +5130,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;
@@ -5134,9 +5140,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();
@@ -5299,7 +5305,7 @@ static int cat_file(const char *filename, bool newline) {
ansi_normal());
fflush(stdout);
- return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, false);
+ return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, 0);
}
static int cat(int argc, char *argv[], void *userdata) {
@@ -5958,6 +5964,7 @@ static int mangle_names(char **original_names, char ***mangled_names) {
} else {
r = unit_name_mangle(*name, UNIT_NAME_NOGLOB, i);
if (r < 0) {
+ *i = NULL;
strv_free(l);
return log_error_errno(r, "Failed to mangle unit name: %m");
}
@@ -6028,7 +6035,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));
@@ -6581,7 +6588,7 @@ static int create_edit_temp_file(const char *new_path, const char *original_path
if (r < 0)
return log_error_errno(r, "Failed to create directories for \"%s\": %m", new_path);
- r = copy_file(original_path, t, 0, 0644, 0);
+ r = copy_file(original_path, t, 0, 0644, 0, COPY_REFLINK);
if (r == -ENOENT) {
r = touch(t);
@@ -6805,29 +6812,54 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
return r;
STRV_FOREACH(name, names) {
- _cleanup_free_ char *path = NULL, *new_path = NULL, *tmp_path = NULL;
+ _cleanup_free_ char *path = NULL, *new_path = NULL, *tmp_path = NULL, *tmp_name = NULL;
+ const char *unit_name;
r = unit_find_paths(bus, *name, &lp, &path, NULL);
if (r < 0)
return r;
- else if (!arg_force) {
- if (r == 0) {
- log_error("Run 'systemctl edit --force %s' to create a new unit.", *name);
- return -ENOENT;
- } else if (!path) {
- // FIXME: support units with path==NULL (no FragmentPath)
- log_error("No fragment exists for %s.", *name);
+
+ if (r == 0) {
+ assert(!path);
+
+ if (!arg_force) {
+ log_error("Run 'systemctl edit%s --force %s' to create a new unit.",
+ arg_scope == UNIT_FILE_GLOBAL ? " --global" :
+ arg_scope == UNIT_FILE_USER ? " --user" : "",
+ *name);
return -ENOENT;
}
- }
- if (path) {
+ /* Create a new unit from scratch */
+ unit_name = *name;
+ r = unit_file_create_new(&lp, unit_name,
+ arg_full ? NULL : ".d/override.conf",
+ &new_path, &tmp_path);
+ } else {
+ assert(path);
+
+ unit_name = basename(path);
+ /* We follow unit aliases, but we need to propagate the instance */
+ if (unit_name_is_valid(*name, UNIT_NAME_INSTANCE) &&
+ unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
+ _cleanup_free_ char *instance = NULL;
+
+ r = unit_name_to_instance(*name, &instance);
+ if (r < 0)
+ return r;
+
+ r = unit_name_replace_instance(unit_name, instance, &tmp_name);
+ if (r < 0)
+ return r;
+
+ unit_name = tmp_name;
+ }
+
if (arg_full)
- r = unit_file_create_copy(&lp, basename(path), path, &new_path, &tmp_path);
+ r = unit_file_create_copy(&lp, unit_name, path, &new_path, &tmp_path);
else
- r = unit_file_create_new(&lp, basename(path), ".d/override.conf", &new_path, &tmp_path);
- } else
- r = unit_file_create_new(&lp, *name, NULL, &new_path, &tmp_path);
+ r = unit_file_create_new(&lp, unit_name, ".d/override.conf", &new_path, &tmp_path);
+ }
if (r < 0)
return r;
@@ -7299,7 +7331,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
case 't': {
if (isempty(optarg)) {
- log_error("--type requires arguments.");
+ log_error("--type= requires arguments.");
return -EINVAL;
}
@@ -7539,7 +7571,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
case ARG_STATE: {
if (isempty(optarg)) {
- log_error("--signal requires arguments.");
+ log_error("--state= requires arguments.");
return -EINVAL;
}
@@ -7548,7 +7580,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
r = extract_first_word(&p, &s, ",", 0);
if (r < 0)
- return log_error_errno(r, "Failed to parse signal: %s", optarg);
+ return log_error_errno(r, "Failed to parse state: %s", optarg);
if (r == 0)
break;
diff --git a/src/systemd/sd-bus-vtable.h b/src/systemd/sd-bus-vtable.h
index e8f84eb545..3563a2b126 100644
--- a/src/systemd/sd-bus-vtable.h
+++ b/src/systemd/sd-bus-vtable.h
@@ -86,18 +86,26 @@ struct sd_bus_vtable {
{ \
.type = _SD_BUS_VTABLE_START, \
.flags = _flags, \
- .x.start.element_size = sizeof(sd_bus_vtable), \
+ .x = { \
+ .start = { \
+ .element_size = sizeof(sd_bus_vtable) \
+ }, \
+ }, \
}
#define SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, _offset, _flags) \
{ \
.type = _SD_BUS_VTABLE_METHOD, \
.flags = _flags, \
- .x.method.member = _member, \
- .x.method.signature = _signature, \
- .x.method.result = _result, \
- .x.method.handler = _handler, \
- .x.method.offset = _offset, \
+ .x = { \
+ .method = { \
+ .member = _member, \
+ .signature = _signature, \
+ .result = _result, \
+ .handler = _handler, \
+ .offset = _offset, \
+ }, \
+ }, \
}
#define SD_BUS_METHOD(_member, _signature, _result, _handler, _flags) \
SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, 0, _flags)
@@ -106,29 +114,41 @@ struct sd_bus_vtable {
{ \
.type = _SD_BUS_VTABLE_SIGNAL, \
.flags = _flags, \
- .x.signal.member = _member, \
- .x.signal.signature = _signature, \
+ .x = { \
+ .signal = { \
+ .member = _member, \
+ .signature = _signature, \
+ }, \
+ }, \
}
#define SD_BUS_PROPERTY(_member, _signature, _get, _offset, _flags) \
{ \
.type = _SD_BUS_VTABLE_PROPERTY, \
.flags = _flags, \
- .x.property.member = _member, \
- .x.property.signature = _signature, \
- .x.property.get = _get, \
- .x.property.offset = _offset, \
+ .x = { \
+ .property = { \
+ .member = _member, \
+ .signature = _signature, \
+ .get = _get, \
+ .offset = _offset, \
+ }, \
+ }, \
}
#define SD_BUS_WRITABLE_PROPERTY(_member, _signature, _get, _set, _offset, _flags) \
{ \
.type = _SD_BUS_VTABLE_WRITABLE_PROPERTY, \
.flags = _flags, \
- .x.property.member = _member, \
- .x.property.signature = _signature, \
- .x.property.get = _get, \
- .x.property.set = _set, \
- .x.property.offset = _offset, \
+ .x = { \
+ .property = { \
+ .member = _member, \
+ .signature = _signature, \
+ .get = _get, \
+ .set = _set, \
+ .offset = _offset, \
+ }, \
+ }, \
}
#define SD_BUS_VTABLE_END \
diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h
index 6cc8e4ac0e..9b38969b77 100644
--- a/src/systemd/sd-id128.h
+++ b/src/systemd/sd-id128.h
@@ -100,6 +100,9 @@ int sd_id128_get_invocation(sd_id128_t *ret);
((x).bytes[15] & 15) >= 10 ? 'a' + ((x).bytes[15] & 15) - 10 : '0' + ((x).bytes[15] & 15), \
0 })
+#define SD_ID128_MAKE_STR(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
+ #a #b #c #d #e #f #g #h #i #j #k #l #m #n #o #p
+
_sd_pure_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) {
return memcmp(&a, &b, 16) == 0;
}
diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h
index db1a21be05..f466d9b062 100644
--- a/src/systemd/sd-messages.h
+++ b/src/systemd/sd-messages.h
@@ -33,60 +33,109 @@ _SD_BEGIN_DECLARATIONS;
* with journalctl --new-id128. Do not use any other IDs, and do not
* count them up manually. */
-#define SD_MESSAGE_JOURNAL_START SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b)
-#define SD_MESSAGE_JOURNAL_STOP SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b)
-#define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e)
-#define SD_MESSAGE_JOURNAL_MISSED SD_ID128_MAKE(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06)
-#define SD_MESSAGE_JOURNAL_USAGE SD_ID128_MAKE(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6)
-
-#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)
-#define SD_MESSAGE_TRUNCATED_CORE SD_ID128_MAKE(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37)
-
-#define SD_MESSAGE_SESSION_START SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66)
-#define SD_MESSAGE_SESSION_STOP SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a)
-#define SD_MESSAGE_SEAT_START SD_ID128_MAKE(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b)
-#define SD_MESSAGE_SEAT_STOP SD_ID128_MAKE(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5)
-#define SD_MESSAGE_MACHINE_START SD_ID128_MAKE(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2)
-#define SD_MESSAGE_MACHINE_STOP SD_ID128_MAKE(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58)
-
-#define SD_MESSAGE_TIME_CHANGE SD_ID128_MAKE(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27)
-#define SD_MESSAGE_TIMEZONE_CHANGE SD_ID128_MAKE(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90)
-
-#define SD_MESSAGE_STARTUP_FINISHED SD_ID128_MAKE(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff)
-#define SD_MESSAGE_USER_STARTUP_FINISHED SD_ID128_MAKE(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1)
-
-#define SD_MESSAGE_SLEEP_START SD_ID128_MAKE(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28)
-#define SD_MESSAGE_SLEEP_STOP SD_ID128_MAKE(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14)
-
-#define SD_MESSAGE_SHUTDOWN SD_ID128_MAKE(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40)
-
-#define SD_MESSAGE_UNIT_STARTING SD_ID128_MAKE(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5)
-#define SD_MESSAGE_UNIT_STARTED SD_ID128_MAKE(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf)
-#define SD_MESSAGE_UNIT_STOPPING SD_ID128_MAKE(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f)
-#define SD_MESSAGE_UNIT_STOPPED SD_ID128_MAKE(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86)
-#define SD_MESSAGE_UNIT_FAILED SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d)
-#define SD_MESSAGE_UNIT_RELOADING SD_ID128_MAKE(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25)
-#define SD_MESSAGE_UNIT_RELOADED SD_ID128_MAKE(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54)
-
-#define SD_MESSAGE_SPAWN_FAILED SD_ID128_MAKE(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7)
-
-#define SD_MESSAGE_FORWARD_SYSLOG_MISSED SD_ID128_MAKE(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e)
-
-#define SD_MESSAGE_OVERMOUNTING SD_ID128_MAKE(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7)
-
-#define SD_MESSAGE_LID_OPENED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f)
-#define SD_MESSAGE_LID_CLOSED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70)
-#define SD_MESSAGE_SYSTEM_DOCKED SD_ID128_MAKE(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff)
-#define SD_MESSAGE_SYSTEM_UNDOCKED SD_ID128_MAKE(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53)
-#define SD_MESSAGE_POWER_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71)
-#define SD_MESSAGE_SUSPEND_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72)
-#define SD_MESSAGE_HIBERNATE_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73)
-
-#define SD_MESSAGE_INVALID_CONFIGURATION SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01)
-
-#define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
-#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65)
-#define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
+#define SD_MESSAGE_JOURNAL_START SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b)
+#define SD_MESSAGE_JOURNAL_START_STR SD_ID128_MAKE_STR(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b)
+#define SD_MESSAGE_JOURNAL_STOP SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b)
+#define SD_MESSAGE_JOURNAL_STOP_STR SD_ID128_MAKE_STR(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b)
+#define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e)
+#define SD_MESSAGE_JOURNAL_DROPPED_STR SD_ID128_MAKE_STR(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e)
+#define SD_MESSAGE_JOURNAL_MISSED SD_ID128_MAKE(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06)
+#define SD_MESSAGE_JOURNAL_MISSED_STR SD_ID128_MAKE_STR(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06)
+#define SD_MESSAGE_JOURNAL_USAGE SD_ID128_MAKE(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6)
+#define SD_MESSAGE_JOURNAL_USAGE_STR SD_ID128_MAKE_STR(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6)
+
+#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)
+#define SD_MESSAGE_COREDUMP_STR SD_ID128_MAKE_STR(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)
+#define SD_MESSAGE_TRUNCATED_CORE SD_ID128_MAKE(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37)
+#define SD_MESSAGE_TRUNCATED_CORE_STR SD_ID128_MAKE_STR(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37)
+#define SD_MESSAGE_BACKTRACE SD_ID128_MAKE(1f,4e,0a,44,a8,86,49,93,9a,ae,a3,4f,c6,da,8c,95)
+#define SD_MESSAGE_BACKTRACE_STR SD_ID128_MAKE_STR(1f,4e,0a,44,a8,86,49,93,9a,ae,a3,4f,c6,da,8c,95)
+
+#define SD_MESSAGE_SESSION_START SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66)
+#define SD_MESSAGE_SESSION_START_STR SD_ID128_MAKE_STR(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66)
+#define SD_MESSAGE_SESSION_STOP SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a)
+#define SD_MESSAGE_SESSION_STOP_STR SD_ID128_MAKE_STR(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a)
+#define SD_MESSAGE_SEAT_START SD_ID128_MAKE(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b)
+#define SD_MESSAGE_SEAT_START_STR SD_ID128_MAKE_STR(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b)
+#define SD_MESSAGE_SEAT_STOP SD_ID128_MAKE(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5)
+#define SD_MESSAGE_SEAT_STOP_STR SD_ID128_MAKE_STR(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5)
+#define SD_MESSAGE_MACHINE_START SD_ID128_MAKE(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2)
+#define SD_MESSAGE_MACHINE_START_STR SD_ID128_MAKE_STR(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2)
+#define SD_MESSAGE_MACHINE_STOP SD_ID128_MAKE(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58)
+#define SD_MESSAGE_MACHINE_STOP_STR SD_ID128_MAKE_STR(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58)
+
+#define SD_MESSAGE_TIME_CHANGE SD_ID128_MAKE(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27)
+#define SD_MESSAGE_TIME_CHANGE_STR SD_ID128_MAKE_STR(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27)
+#define SD_MESSAGE_TIMEZONE_CHANGE SD_ID128_MAKE(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90)
+#define SD_MESSAGE_TIMEZONE_CHANGE_STR SD_ID128_MAKE_STR(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90)
+
+#define SD_MESSAGE_STARTUP_FINISHED SD_ID128_MAKE(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff)
+#define SD_MESSAGE_STARTUP_FINISHED_STR SD_ID128_MAKE_STR(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff)
+#define SD_MESSAGE_USER_STARTUP_FINISHED \
+ SD_ID128_MAKE(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1)
+#define SD_MESSAGE_USER_STARTUP_FINISHED_STR \
+ SD_ID128_MAKE_STR(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1)
+
+#define SD_MESSAGE_SLEEP_START SD_ID128_MAKE(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28)
+#define SD_MESSAGE_SLEEP_START_STR SD_ID128_MAKE_STR(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28)
+#define SD_MESSAGE_SLEEP_STOP SD_ID128_MAKE(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14)
+#define SD_MESSAGE_SLEEP_STOP_STR SD_ID128_MAKE_STR(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14)
+
+#define SD_MESSAGE_SHUTDOWN SD_ID128_MAKE(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40)
+#define SD_MESSAGE_SHUTDOWN_STR SD_ID128_MAKE_STR(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40)
+
+#define SD_MESSAGE_UNIT_STARTING SD_ID128_MAKE(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5)
+#define SD_MESSAGE_UNIT_STARTING_STR SD_ID128_MAKE_STR(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5)
+#define SD_MESSAGE_UNIT_STARTED SD_ID128_MAKE(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf)
+#define SD_MESSAGE_UNIT_STARTED_STR SD_ID128_MAKE_STR(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf)
+#define SD_MESSAGE_UNIT_STOPPING SD_ID128_MAKE(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f)
+#define SD_MESSAGE_UNIT_STOPPING_STR SD_ID128_MAKE_STR(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f)
+#define SD_MESSAGE_UNIT_STOPPED SD_ID128_MAKE(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86)
+#define SD_MESSAGE_UNIT_STOPPED_STR SD_ID128_MAKE_STR(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86)
+#define SD_MESSAGE_UNIT_FAILED SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d)
+#define SD_MESSAGE_UNIT_FAILED_STR SD_ID128_MAKE_STR(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d)
+#define SD_MESSAGE_UNIT_RELOADING SD_ID128_MAKE(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25)
+#define SD_MESSAGE_UNIT_RELOADING_STR SD_ID128_MAKE_STR(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25)
+#define SD_MESSAGE_UNIT_RELOADED SD_ID128_MAKE(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54)
+#define SD_MESSAGE_UNIT_RELOADED_STR SD_ID128_MAKE_STR(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54)
+
+#define SD_MESSAGE_SPAWN_FAILED SD_ID128_MAKE(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7)
+#define SD_MESSAGE_SPAWN_FAILED_STR SD_ID128_MAKE_STR(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7)
+
+#define SD_MESSAGE_FORWARD_SYSLOG_MISSED SD_ID128_MAKE(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e)
+#define SD_MESSAGE_FORWARD_SYSLOG_MISSED_STR \
+ SD_ID128_MAKE_STR(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e)
+
+#define SD_MESSAGE_OVERMOUNTING SD_ID128_MAKE(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7)
+#define SD_MESSAGE_OVERMOUNTING_STR SD_ID128_MAKE_STR(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7)
+
+#define SD_MESSAGE_LID_OPENED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f)
+#define SD_MESSAGE_LID_OPENED_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f)
+#define SD_MESSAGE_LID_CLOSED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70)
+#define SD_MESSAGE_LID_CLOSED_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70)
+#define SD_MESSAGE_SYSTEM_DOCKED SD_ID128_MAKE(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff)
+#define SD_MESSAGE_SYSTEM_DOCKED_STR SD_ID128_MAKE_STR(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff)
+#define SD_MESSAGE_SYSTEM_UNDOCKED SD_ID128_MAKE(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53)
+#define SD_MESSAGE_SYSTEM_UNDOCKED_STR SD_ID128_MAKE_STR(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53)
+#define SD_MESSAGE_POWER_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71)
+#define SD_MESSAGE_POWER_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71)
+#define SD_MESSAGE_SUSPEND_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72)
+#define SD_MESSAGE_SUSPEND_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72)
+#define SD_MESSAGE_HIBERNATE_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73)
+#define SD_MESSAGE_HIBERNATE_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73)
+
+#define SD_MESSAGE_INVALID_CONFIGURATION SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01)
+#define SD_MESSAGE_INVALID_CONFIGURATION_STR \
+ SD_ID128_MAKE_STR(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01)
+
+#define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
+#define SD_MESSAGE_DNSSEC_FAILURE_STR SD_ID128_MAKE_STR(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
+#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED \
+ SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65)
+#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED_STR \
+ SD_ID128_MAKE_STR(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65)
+#define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
+#define SD_MESSAGE_DNSSEC_DOWNGRADE_STR SD_ID128_MAKE_STR(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
_SD_END_DECLARATIONS;
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index 17b966eb52..4a0a49f2bb 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -211,7 +211,7 @@ static int make_backup(const char *target, const char *x) {
if (r < 0)
return r;
- r = copy_bytes(src, fileno(dst), (uint64_t) -1, true);
+ r = copy_bytes(src, fileno(dst), (uint64_t) -1, COPY_REFLINK);
if (r < 0)
goto fail;
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-cgroup-mask.c b/src/test/test-cgroup-mask.c
index a027eb0fd2..b42088c680 100644
--- a/src/test/test-cgroup-mask.c
+++ b/src/test/test-cgroup-mask.c
@@ -27,6 +27,7 @@
#include "unit.h"
static int test_cgroup_mask(void) {
+ _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
Manager *m = NULL;
Unit *son, *daughter, *parent, *root, *grandchild, *parent_deep;
FILE *serial = NULL;
@@ -34,7 +35,8 @@ static int test_cgroup_mask(void) {
int r;
/* Prepare the manager. */
- assert_se(set_unit_path(TEST_DIR) >= 0);
+ assert_se(set_unit_path(get_testdata_dir("")) >= 0);
+ assert_se(runtime_dir = setup_fake_runtime_dir());
r = manager_new(UNIT_FILE_USER, true, &m);
if (r == -EPERM || r == -EACCES) {
puts("manager_new: Permission denied. Skipping test.");
@@ -110,10 +112,8 @@ static int test_cgroup_mask(void) {
}
int main(int argc, char* argv[]) {
- _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
int rc = 0;
- assert_se(runtime_dir = setup_fake_runtime_dir());
TEST_REQ_RUNNING_SYSTEMD(rc = test_cgroup_mask());
return rc;
diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c
index c60fb631fa..30cd463722 100644
--- a/src/test/test-cgroup-util.c
+++ b/src/test/test-cgroup-util.c
@@ -18,11 +18,13 @@
***/
#include "alloc-util.h"
+#include "build.h"
#include "cgroup-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "format-util.h"
#include "parse-util.h"
+#include "proc-cmdline.h"
#include "process-util.h"
#include "stat-util.h"
#include "string-util.h"
@@ -332,7 +334,49 @@ static void test_fd_is_cgroup_fs(void) {
fd = safe_close(fd);
}
+static void test_is_wanted_print(bool header) {
+ _cleanup_free_ char *cmdline = NULL;
+
+ log_info("-- %s --", __func__);
+ assert_se(proc_cmdline(&cmdline) >= 0);
+ log_info("cmdline: %s", cmdline);
+ if (header) {
+
+ log_info(_CGROUP_HIEARCHY_);
+ (void) system("findmnt -n /sys/fs/cgroup");
+ }
+
+ log_info("is_unified_wanted() → %s", yes_no(cg_is_unified_wanted()));
+ log_info("is_hybrid_wanted() → %s", yes_no(cg_is_hybrid_wanted()));
+ log_info("is_legacy_wanted() → %s", yes_no(cg_is_legacy_wanted()));
+ log_info(" ");
+}
+
+static void test_is_wanted(void) {
+ assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+ "systemd.unified_cgroup_hierarchy", 1) >= 0);
+ test_is_wanted_print(false);
+
+ assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+ "systemd.unified_cgroup_hierarchy=0", 1) >= 0);
+ test_is_wanted_print(false);
+
+ assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+ "systemd.unified_cgroup_hierarchy=0 "
+ "systemd.legacy_systemd_cgroup_controller", 1) >= 0);
+ test_is_wanted_print(false);
+
+ assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+ "systemd.unified_cgroup_hierarchy=0 "
+ "systemd.legacy_systemd_cgroup_controller=0", 1) >= 0);
+ test_is_wanted_print(false);
+}
+
int main(void) {
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
test_path_decode_unit();
test_path_get_unit();
test_path_get_user_unit();
@@ -349,6 +393,9 @@ int main(void) {
TEST_REQ_RUNNING_SYSTEMD(test_mask_supported());
TEST_REQ_RUNNING_SYSTEMD(test_is_cgroup_fs());
TEST_REQ_RUNNING_SYSTEMD(test_fd_is_cgroup_fs());
+ test_is_wanted_print(true);
+ test_is_wanted_print(false); /* run twice to test caching */
+ test_is_wanted();
return 0;
}
diff --git a/src/test/test-conf-files.c b/src/test/test-conf-files.c
index 03b3a9fa5c..22b7c61204 100644
--- a/src/test/test-conf-files.c
+++ b/src/test/test-conf-files.c
@@ -47,13 +47,16 @@ static void setup_test_dir(char *tmp_dir, const char *files, ...) {
static void test_conf_files_list(bool use_root) {
char tmp_dir[] = "/tmp/test-conf-files-XXXXXX";
- _cleanup_strv_free_ char **found_files = NULL;
- const char *root_dir, *search_1, *search_2, *expect_a, *expect_b;
+ _cleanup_strv_free_ char **found_files = NULL, **found_files2 = NULL;
+ const char *root_dir, *search_1, *search_2, *expect_a, *expect_b, *expect_c;
+
+ log_debug("/* %s */", __func__);
setup_test_dir(tmp_dir,
"/dir1/a.conf",
"/dir2/a.conf",
"/dir2/b.conf",
+ "/dir2/c.foo",
NULL);
if (use_root) {
@@ -68,6 +71,9 @@ static void test_conf_files_list(bool use_root) {
expect_a = strjoina(tmp_dir, "/dir1/a.conf");
expect_b = strjoina(tmp_dir, "/dir2/b.conf");
+ expect_c = strjoina(tmp_dir, "/dir2/c.foo");
+
+ log_debug("/* Check when filtered by suffix */");
assert_se(conf_files_list(&found_files, ".conf", root_dir, search_1, search_2, NULL) == 0);
strv_print(found_files);
@@ -77,10 +83,24 @@ static void test_conf_files_list(bool use_root) {
assert_se(streq_ptr(found_files[1], expect_b));
assert_se(found_files[2] == NULL);
+ log_debug("/* Check when unfiltered */");
+ assert_se(conf_files_list(&found_files2, NULL, root_dir, search_1, search_2, NULL) == 0);
+ strv_print(found_files2);
+
+ assert_se(found_files2);
+ assert_se(streq_ptr(found_files2[0], expect_a));
+ assert_se(streq_ptr(found_files2[1], expect_b));
+ assert_se(streq_ptr(found_files2[2], expect_c));
+ assert_se(found_files2[3] == NULL);
+
assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
}
int main(int argc, char **argv) {
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
test_conf_files_list(false);
test_conf_files_list(true);
return 0;
diff --git a/src/test/test-copy.c b/src/test/test-copy.c
index e65516f080..ed6725611d 100644
--- a/src/test/test-copy.c
+++ b/src/test/test-copy.c
@@ -31,6 +31,7 @@
#include "rm-rf.h"
#include "string-util.h"
#include "strv.h"
+#include "user-util.h"
#include "util.h"
static void test_copy_file(void) {
@@ -52,7 +53,7 @@ static void test_copy_file(void) {
assert_se(write_string_file(fn, "foo bar bar bar foo", WRITE_STRING_FILE_CREATE) == 0);
- assert_se(copy_file(fn, fn_copy, 0, 0644, 0) == 0);
+ assert_se(copy_file(fn, fn_copy, 0, 0644, 0, COPY_REFLINK) == 0);
assert_se(read_full_file(fn_copy, &buf, &sz) == 0);
assert_se(streq(buf, "foo bar bar bar foo\n"));
@@ -77,8 +78,8 @@ static void test_copy_file_fd(void) {
assert_se(out_fd >= 0);
assert_se(write_string_file(in_fn, text, WRITE_STRING_FILE_CREATE) == 0);
- assert_se(copy_file_fd("/a/file/which/does/not/exist/i/guess", out_fd, true) < 0);
- assert_se(copy_file_fd(in_fn, out_fd, true) >= 0);
+ assert_se(copy_file_fd("/a/file/which/does/not/exist/i/guess", out_fd, COPY_REFLINK) < 0);
+ assert_se(copy_file_fd(in_fn, out_fd, COPY_REFLINK) >= 0);
assert_se(lseek(out_fd, SEEK_SET, 0) == 0);
assert_se(read(out_fd, buf, sizeof(buf)) == sizeof(text) - 1);
@@ -125,7 +126,7 @@ static void test_copy_tree(void) {
unixsockp = strjoina(original_dir, "unixsock");
assert_se(mknod(unixsockp, S_IFSOCK|0644, 0) >= 0);
- assert_se(copy_tree(original_dir, copy_dir, true) == 0);
+ assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE) == 0);
STRV_FOREACH(p, files) {
_cleanup_free_ char *buf = NULL, *f;
@@ -152,8 +153,8 @@ static void test_copy_tree(void) {
assert_se(stat(unixsockp, &st) >= 0);
assert_se(S_ISSOCK(st.st_mode));
- assert_se(copy_tree(original_dir, copy_dir, false) < 0);
- assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, false) < 0);
+ assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0);
+ assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0);
(void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
(void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
@@ -172,7 +173,7 @@ static void test_copy_bytes(void) {
assert_se(pipe2(pipefd, O_CLOEXEC) == 0);
- r = copy_bytes(infd, pipefd[1], (uint64_t) -1, false);
+ r = copy_bytes(infd, pipefd[1], (uint64_t) -1, 0);
assert_se(r == 0);
r = read(pipefd[0], buf, sizeof(buf));
@@ -185,13 +186,13 @@ static void test_copy_bytes(void) {
assert_se(strneq(buf, buf2, r));
/* test copy_bytes with invalid descriptors */
- r = copy_bytes(pipefd[0], pipefd[0], 1, false);
+ r = copy_bytes(pipefd[0], pipefd[0], 1, 0);
assert_se(r == -EBADF);
- r = copy_bytes(pipefd[1], pipefd[1], 1, false);
+ r = copy_bytes(pipefd[1], pipefd[1], 1, 0);
assert_se(r == -EBADF);
- r = copy_bytes(pipefd[1], infd, 1, false);
+ r = copy_bytes(pipefd[1], infd, 1, 0);
assert_se(r == -EBADF);
}
@@ -213,7 +214,7 @@ static void test_copy_bytes_regular_file(const char *src, bool try_reflink, uint
fd3 = mkostemp_safe(fn3);
assert_se(fd3 >= 0);
- r = copy_bytes(fd, fd2, max_bytes, try_reflink);
+ r = copy_bytes(fd, fd2, max_bytes, try_reflink ? COPY_REFLINK : 0);
if (max_bytes == (uint64_t) -1)
assert_se(r == 0);
else
@@ -221,7 +222,7 @@ static void test_copy_bytes_regular_file(const char *src, bool try_reflink, uint
assert_se(lseek(fd2, 0, SEEK_SET) == 0);
- r = copy_bytes(fd2, fd3, max_bytes, try_reflink);
+ r = copy_bytes(fd2, fd3, max_bytes, try_reflink ? COPY_REFLINK : 0);
if (max_bytes == (uint64_t) -1)
assert_se(r == 0);
else
diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c
index b4db4a6702..a7cd8e4b51 100644
--- a/src/test/test-dns-domain.c
+++ b/src/test/test-dns-domain.c
@@ -373,16 +373,16 @@ static void test_dns_name_is_valid(void) {
test_dns_name_is_valid_one("ä", 1);
test_dns_name_is_valid_one("\n", 0);
- /* 256 characters*/
+ /* 256 characters */
test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345", 0);
- /* 255 characters*/
+ /* 255 characters */
test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a1234", 0);
- /* 254 characters*/
+ /* 254 characters */
test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a123", 0);
- /* 253 characters*/
+ /* 253 characters */
test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12", 1);
/* label of 64 chars length */
diff --git a/src/test/test-engine.c b/src/test/test-engine.c
index a651f6b683..8133343fb3 100644
--- a/src/test/test-engine.c
+++ b/src/test/test-engine.c
@@ -37,10 +37,9 @@ int main(int argc, char *argv[]) {
Job *j;
int r;
- assert_se(runtime_dir = setup_fake_runtime_dir());
-
/* prepare the test */
- assert_se(set_unit_path(TEST_DIR) >= 0);
+ assert_se(set_unit_path(get_testdata_dir("")) >= 0);
+ assert_se(runtime_dir = setup_fake_runtime_dir());
r = manager_new(UNIT_FILE_USER, true, &m);
if (MANAGER_SKIP_TEST(r)) {
log_notice_errno(r, "Skipping test: manager_new: %m");
diff --git a/src/test/test-env-util.c b/src/test/test-env-util.c
index 35bb62906e..e5cc2a2df8 100644
--- a/src/test/test-env-util.c
+++ b/src/test/test-env-util.c
@@ -45,6 +45,16 @@ static void test_strv_env_delete(void) {
assert_se(strv_length(d) == 2);
}
+static void test_strv_env_get(void) {
+ char **l;
+
+ l = STRV_MAKE("ONE_OR_TWO=1", "THREE=3", "ONE_OR_TWO=2", "FOUR=4");
+
+ assert_se(streq(strv_env_get(l, "ONE_OR_TWO"), "2"));
+ assert_se(streq(strv_env_get(l, "THREE"), "3"));
+ assert_se(streq(strv_env_get(l, "FOUR"), "4"));
+}
+
static void test_strv_env_unset(void) {
_cleanup_strv_free_ char **l = NULL;
@@ -102,7 +112,90 @@ static void test_strv_env_merge(void) {
assert_se(strv_length(r) == 5);
}
-static void test_replace_env_arg(void) {
+static void test_env_strv_get_n(void) {
+ const char *_env[] = {
+ "FOO=NO NO NO",
+ "FOO=BAR BAR",
+ "BAR=waldo",
+ "PATH=unset",
+ NULL
+ };
+ char **env = (char**) _env;
+
+ assert_se(streq(strv_env_get_n(env, "FOO__", 3, 0), "BAR BAR"));
+ assert_se(streq(strv_env_get_n(env, "FOO__", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR"));
+ assert_se(streq(strv_env_get_n(env, "FOO", 3, 0), "BAR BAR"));
+ assert_se(streq(strv_env_get_n(env, "FOO", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR"));
+
+ assert_se(streq(strv_env_get_n(env, "PATH__", 4, 0), "unset"));
+ assert_se(streq(strv_env_get_n(env, "PATH", 4, 0), "unset"));
+ assert_se(streq(strv_env_get_n(env, "PATH__", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset"));
+ assert_se(streq(strv_env_get_n(env, "PATH", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset"));
+
+ env[3] = NULL; /* kill our $PATH */
+
+ assert_se(!strv_env_get_n(env, "PATH__", 4, 0));
+ assert_se(!strv_env_get_n(env, "PATH", 4, 0));
+ assert_se(streq(strv_env_get_n(env, "PATH__", 4, REPLACE_ENV_USE_ENVIRONMENT),
+ getenv("PATH")));
+ assert_se(streq(strv_env_get_n(env, "PATH", 4, REPLACE_ENV_USE_ENVIRONMENT),
+ getenv("PATH")));
+}
+
+static void test_replace_env(bool braceless) {
+ const char *env[] = {
+ "FOO=BAR BAR",
+ "BAR=waldo",
+ NULL
+ };
+ _cleanup_free_ char *t = NULL, *s = NULL, *q = NULL, *r = NULL, *p = NULL;
+ unsigned flags = REPLACE_ENV_ALLOW_BRACELESS*braceless;
+
+ t = replace_env("FOO=$FOO=${FOO}", (char**) env, flags);
+ assert_se(streq(t, braceless ? "FOO=BAR BAR=BAR BAR" : "FOO=$FOO=BAR BAR"));
+
+ s = replace_env("BAR=$BAR=${BAR}", (char**) env, flags);
+ assert_se(streq(s, braceless ? "BAR=waldo=waldo" : "BAR=$BAR=waldo"));
+
+ q = replace_env("BARBAR=$BARBAR=${BARBAR}", (char**) env, flags);
+ assert_se(streq(q, braceless ? "BARBAR==" : "BARBAR=$BARBAR="));
+
+ r = replace_env("BAR=$BAR$BAR${BAR}${BAR}", (char**) env, flags);
+ assert_se(streq(r, braceless ? "BAR=waldowaldowaldowaldo" : "BAR=$BAR$BARwaldowaldo"));
+
+ p = replace_env("${BAR}$BAR$BAR", (char**) env, flags);
+ assert_se(streq(p, braceless ? "waldowaldowaldo" : "waldo$BAR$BAR"));
+}
+
+static void test_replace_env2(bool extended) {
+ const char *env[] = {
+ "FOO=foo",
+ "BAR=bar",
+ NULL
+ };
+ _cleanup_free_ char *t = NULL, *s = NULL, *q = NULL, *r = NULL, *p = NULL, *x = NULL;
+ unsigned flags = REPLACE_ENV_ALLOW_EXTENDED*extended;
+
+ t = replace_env("FOO=${FOO:-${BAR}}", (char**) env, flags);
+ assert_se(streq(t, extended ? "FOO=foo" : "FOO=${FOO:-bar}"));
+
+ s = replace_env("BAR=${XXX:-${BAR}}", (char**) env, flags);
+ assert_se(streq(s, extended ? "BAR=bar" : "BAR=${XXX:-bar}"));
+
+ q = replace_env("XXX=${XXX:+${BAR}}", (char**) env, flags);
+ assert_se(streq(q, extended ? "XXX=" : "XXX=${XXX:+bar}"));
+
+ r = replace_env("FOO=${FOO:+${BAR}}", (char**) env, flags);
+ assert_se(streq(r, extended ? "FOO=bar" : "FOO=${FOO:+bar}"));
+
+ p = replace_env("FOO=${FOO:-${BAR}post}", (char**) env, flags);
+ assert_se(streq(p, extended ? "FOO=foo" : "FOO=${FOO:-barpost}"));
+
+ x = replace_env("XXX=${XXX:+${BAR}post}", (char**) env, flags);
+ assert_se(streq(x, extended ? "XXX=" : "XXX=${XXX:+barpost}"));
+}
+
+static void test_replace_env_argv(void) {
const char *env[] = {
"FOO=BAR BAR",
"BAR=waldo",
@@ -120,6 +213,12 @@ static void test_replace_env_arg(void) {
"${FOO",
"FOO$$${FOO}",
"$$FOO${FOO}",
+ "${FOO:-${BAR}}",
+ "${QUUX:-${FOO}}",
+ "${FOO:+${BAR}}",
+ "${QUUX:+${BAR}}",
+ "${FOO:+|${BAR}|}}",
+ "${FOO:+|${BAR}{|}",
NULL
};
_cleanup_strv_free_ char **r = NULL;
@@ -137,7 +236,13 @@ static void test_replace_env_arg(void) {
assert_se(streq(r[8], "${FOO"));
assert_se(streq(r[9], "FOO$BAR BAR"));
assert_se(streq(r[10], "$FOOBAR BAR"));
- assert_se(strv_length(r) == 11);
+ assert_se(streq(r[11], "${FOO:-waldo}"));
+ assert_se(streq(r[12], "${QUUX:-BAR BAR}"));
+ assert_se(streq(r[13], "${FOO:+waldo}"));
+ assert_se(streq(r[14], "${QUUX:+waldo}"));
+ assert_se(streq(r[15], "${FOO:+|waldo|}}"));
+ assert_se(streq(r[16], "${FOO:+|waldo{|}"));
+ assert_se(strv_length(r) == 17);
}
static void test_env_clean(void) {
@@ -211,10 +316,16 @@ static void test_env_assignment_is_valid(void) {
int main(int argc, char *argv[]) {
test_strv_env_delete();
+ test_strv_env_get();
test_strv_env_unset();
test_strv_env_set();
test_strv_env_merge();
- test_replace_env_arg();
+ test_env_strv_get_n();
+ test_replace_env(false);
+ test_replace_env(true);
+ test_replace_env2(false);
+ test_replace_env2(true);
+ test_replace_env_argv();
test_env_clean();
test_env_name_is_valid();
test_env_value_is_valid();
diff --git a/src/test/test-exec-util.c b/src/test/test-exec-util.c
new file mode 100644
index 0000000000..482b0751b9
--- /dev/null
+++ b/src/test/test-exec-util.c
@@ -0,0 +1,348 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2013 Thomas H.P. Andersen
+
+ 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 <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "copy.h"
+#include "def.h"
+#include "env-util.h"
+#include "exec-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "log.h"
+#include "macro.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "strv.h"
+
+static int here = 0, here2 = 0, here3 = 0;
+void *ignore_stdout_args[] = {&here, &here2, &here3};
+
+/* noop handlers, just check that arguments are passed correctly */
+static int ignore_stdout_func(int fd, void *arg) {
+ assert(fd >= 0);
+ assert(arg == &here);
+ safe_close(fd);
+
+ return 0;
+}
+static int ignore_stdout_func2(int fd, void *arg) {
+ assert(fd >= 0);
+ assert(arg == &here2);
+ safe_close(fd);
+
+ return 0;
+}
+static int ignore_stdout_func3(int fd, void *arg) {
+ assert(fd >= 0);
+ assert(arg == &here3);
+ safe_close(fd);
+
+ return 0;
+}
+
+static const gather_stdout_callback_t ignore_stdout[] = {
+ ignore_stdout_func,
+ ignore_stdout_func2,
+ ignore_stdout_func3,
+};
+
+static void test_execute_directory(bool gather_stdout) {
+ char template_lo[] = "/tmp/test-exec-util.XXXXXXX";
+ char template_hi[] = "/tmp/test-exec-util.XXXXXXX";
+ const char * dirs[] = {template_hi, template_lo, NULL};
+ const char *name, *name2, *name3, *overridden, *override, *masked, *mask;
+
+ log_info("/* %s (%s) */", __func__, gather_stdout ? "gathering stdout" : "asynchronous");
+
+ assert_se(mkdtemp(template_lo));
+ assert_se(mkdtemp(template_hi));
+
+ name = strjoina(template_lo, "/script");
+ name2 = strjoina(template_hi, "/script2");
+ name3 = strjoina(template_lo, "/useless");
+ overridden = strjoina(template_lo, "/overridden");
+ override = strjoina(template_hi, "/overridden");
+ masked = strjoina(template_lo, "/masked");
+ mask = strjoina(template_hi, "/masked");
+
+ assert_se(write_string_file(name,
+ "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works",
+ WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(write_string_file(name2,
+ "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2",
+ WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(write_string_file(overridden,
+ "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
+ WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(write_string_file(override,
+ "#!/bin/sh\necho 'Executing '$0",
+ WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(write_string_file(masked,
+ "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
+ WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(symlink("/dev/null", mask) == 0);
+ assert_se(touch(name3) >= 0);
+
+ assert_se(chmod(name, 0755) == 0);
+ assert_se(chmod(name2, 0755) == 0);
+ assert_se(chmod(overridden, 0755) == 0);
+ assert_se(chmod(override, 0755) == 0);
+ assert_se(chmod(masked, 0755) == 0);
+
+ if (gather_stdout)
+ execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL);
+ else
+ execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL);
+
+ assert_se(chdir(template_lo) == 0);
+ assert_se(access("it_works", F_OK) >= 0);
+ assert_se(access("failed", F_OK) < 0);
+
+ assert_se(chdir(template_hi) == 0);
+ assert_se(access("it_works2", F_OK) >= 0);
+ assert_se(access("failed", F_OK) < 0);
+
+ (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
+ (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
+}
+
+static void test_execution_order(void) {
+ char template_lo[] = "/tmp/test-exec-util-lo.XXXXXXX";
+ char template_hi[] = "/tmp/test-exec-util-hi.XXXXXXX";
+ const char *dirs[] = {template_hi, template_lo, NULL};
+ const char *name, *name2, *name3, *overridden, *override, *masked, *mask;
+ const char *output, *t;
+ _cleanup_free_ char *contents = NULL;
+
+ assert_se(mkdtemp(template_lo));
+ assert_se(mkdtemp(template_hi));
+
+ output = strjoina(template_hi, "/output");
+
+ log_info("/* %s >>%s */", __func__, output);
+
+ /* write files in "random" order */
+ name2 = strjoina(template_lo, "/90-bar");
+ name = strjoina(template_hi, "/80-foo");
+ name3 = strjoina(template_lo, "/last");
+ overridden = strjoina(template_lo, "/30-override");
+ override = strjoina(template_hi, "/30-override");
+ masked = strjoina(template_lo, "/10-masked");
+ mask = strjoina(template_hi, "/10-masked");
+
+ t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
+ assert_se(write_string_file(name, t, WRITE_STRING_FILE_CREATE) == 0);
+
+ t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
+ assert_se(write_string_file(name2, t, WRITE_STRING_FILE_CREATE) == 0);
+
+ t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
+ assert_se(write_string_file(name3, t, WRITE_STRING_FILE_CREATE) == 0);
+
+ t = strjoina("#!/bin/sh\necho OVERRIDDEN >>", output);
+ assert_se(write_string_file(overridden, t, WRITE_STRING_FILE_CREATE) == 0);
+
+ t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
+ assert_se(write_string_file(override, t, WRITE_STRING_FILE_CREATE) == 0);
+
+ t = strjoina("#!/bin/sh\necho MASKED >>", output);
+ assert_se(write_string_file(masked, t, WRITE_STRING_FILE_CREATE) == 0);
+
+ assert_se(symlink("/dev/null", mask) == 0);
+
+ assert_se(chmod(name, 0755) == 0);
+ assert_se(chmod(name2, 0755) == 0);
+ assert_se(chmod(name3, 0755) == 0);
+ assert_se(chmod(overridden, 0755) == 0);
+ assert_se(chmod(override, 0755) == 0);
+ assert_se(chmod(masked, 0755) == 0);
+
+ execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL);
+
+ assert_se(read_full_file(output, &contents, NULL) >= 0);
+ assert_se(streq(contents, "30-override\n80-foo\n90-bar\nlast\n"));
+
+ (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
+ (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
+}
+
+static int gather_stdout_one(int fd, void *arg) {
+ char ***s = arg, *t;
+ char buf[128] = {};
+
+ assert_se(s);
+ assert_se(read(fd, buf, sizeof buf) >= 0);
+ safe_close(fd);
+
+ assert_se(t = strndup(buf, sizeof buf));
+ assert_se(strv_push(s, t) >= 0);
+
+ return 0;
+}
+static int gather_stdout_two(int fd, void *arg) {
+ char ***s = arg, **t;
+
+ STRV_FOREACH(t, *s)
+ assert_se(write(fd, *t, strlen(*t)) == (ssize_t) strlen(*t));
+ safe_close(fd);
+
+ return 0;
+}
+static int gather_stdout_three(int fd, void *arg) {
+ char **s = arg;
+ char buf[128] = {};
+
+ assert_se(read(fd, buf, sizeof buf - 1) > 0);
+ safe_close(fd);
+ assert_se(*s = strndup(buf, sizeof buf));
+
+ return 0;
+}
+
+const gather_stdout_callback_t const gather_stdout[] = {
+ gather_stdout_one,
+ gather_stdout_two,
+ gather_stdout_three,
+};
+
+
+static void test_stdout_gathering(void) {
+ char template[] = "/tmp/test-exec-util.XXXXXXX";
+ const char *dirs[] = {template, NULL};
+ const char *name, *name2, *name3;
+ int r;
+
+ char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
+ _cleanup_free_ char *output = NULL;
+
+ void* args[] = {&tmp, &tmp, &output};
+
+ assert_se(mkdtemp(template));
+
+ log_info("/* %s */", __func__);
+
+ /* write files */
+ name = strjoina(template, "/10-foo");
+ name2 = strjoina(template, "/20-bar");
+ name3 = strjoina(template, "/30-last");
+
+ assert_se(write_string_file(name,
+ "#!/bin/sh\necho a\necho b\necho c\n",
+ WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(write_string_file(name2,
+ "#!/bin/sh\necho d\n",
+ WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(write_string_file(name3,
+ "#!/bin/sh\nsleep 1",
+ WRITE_STRING_FILE_CREATE) == 0);
+
+ assert_se(chmod(name, 0755) == 0);
+ assert_se(chmod(name2, 0755) == 0);
+ assert_se(chmod(name3, 0755) == 0);
+
+ r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_stdout, args, NULL);
+ assert_se(r >= 0);
+
+ log_info("got: %s", output);
+
+ assert_se(streq(output, "a\nb\nc\nd\n"));
+}
+
+static void test_environment_gathering(void) {
+ char template[] = "/tmp/test-exec-util.XXXXXXX", **p;
+ const char *dirs[] = {template, NULL};
+ const char *name, *name2, *name3;
+ int r;
+
+ char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
+ _cleanup_strv_free_ char **env = NULL;
+
+ void* const args[] = { &tmp, &tmp, &env };
+
+ assert_se(mkdtemp(template));
+
+ log_info("/* %s */", __func__);
+
+ /* write files */
+ name = strjoina(template, "/10-foo");
+ name2 = strjoina(template, "/20-bar");
+ name3 = strjoina(template, "/30-last");
+
+ assert_se(write_string_file(name,
+ "#!/bin/sh\n"
+ "echo A=23\n",
+ WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(write_string_file(name2,
+ "#!/bin/sh\n"
+ "echo A=22:$A\n\n\n", /* substitution from previous generator */
+ WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(write_string_file(name3,
+ "#!/bin/sh\n"
+ "echo A=$A:24\n"
+ "echo B=12\n"
+ "echo C=000\n"
+ "echo C=001\n" /* variable overwriting */
+ /* various invalid entries */
+ "echo unset A\n"
+ "echo unset A=\n"
+ "echo unset A=B\n"
+ "echo unset \n"
+ "echo A B=C\n"
+ "echo A\n"
+ /* test variable assignment without newline */
+ "echo PATH=$PATH:/no/such/file", /* no newline */
+ WRITE_STRING_FILE_CREATE) == 0);
+
+ assert_se(chmod(name, 0755) == 0);
+ assert_se(chmod(name2, 0755) == 0);
+ assert_se(chmod(name3, 0755) == 0);
+
+ r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL);
+ assert_se(r >= 0);
+
+ STRV_FOREACH(p, env)
+ log_info("got env: \"%s\"", *p);
+
+ assert_se(streq(strv_env_get(env, "A"), "22:23:24"));
+ assert_se(streq(strv_env_get(env, "B"), "12"));
+ assert_se(streq(strv_env_get(env, "C"), "001"));
+ assert_se(endswith(strv_env_get(env, "PATH"), ":/no/such/file"));
+}
+
+int main(int argc, char *argv[]) {
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
+ test_execute_directory(true);
+ test_execute_directory(false);
+ test_execution_order();
+ test_stdout_gathering();
+ test_environment_gathering();
+
+ return 0;
+}
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index bc9a2021f9..90540b884b 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -35,6 +35,7 @@
#endif
#include "stat-util.h"
#include "test-helper.h"
+#include "tests.h"
#include "unit.h"
#include "util.h"
#include "virt.h"
@@ -145,11 +146,11 @@ static void test_exec_privatetmp(Manager *m) {
static void test_exec_privatedevices(Manager *m) {
if (detect_container() > 0) {
- log_notice("testing in container, skipping private device tests");
+ log_notice("testing in container, skipping %s", __func__);
return;
}
if (!is_inaccessible_available()) {
- log_notice("testing without inaccessible, skipping private device tests");
+ log_notice("testing without inaccessible, skipping %s", __func__);
return;
}
@@ -158,12 +159,22 @@ static void test_exec_privatedevices(Manager *m) {
}
static void test_exec_privatedevices_capabilities(Manager *m) {
+ int r;
+
if (detect_container() > 0) {
- log_notice("testing in container, skipping private device tests");
+ log_notice("testing in container, skipping %s", __func__);
return;
}
if (!is_inaccessible_available()) {
- log_notice("testing without inaccessible, skipping private device tests");
+ log_notice("testing without inaccessible, skipping %s", __func__);
+ return;
+ }
+
+ /* We use capsh to test if the capabilities are
+ * properly set, so be sure that it exists */
+ r = find_binary("capsh", NULL);
+ if (r < 0) {
+ log_error_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
return;
}
@@ -174,15 +185,24 @@ static void test_exec_privatedevices_capabilities(Manager *m) {
}
static void test_exec_protectkernelmodules(Manager *m) {
+ int r;
+
if (detect_container() > 0) {
- log_notice("testing in container, skipping protectkernelmodules tests");
+ log_notice("testing in container, skipping %s", __func__);
return;
}
if (!is_inaccessible_available()) {
- log_notice("testing without inaccessible, skipping protectkernelmodules tests");
+ log_notice("testing without inaccessible, skipping %s", __func__);
return;
}
+ r = find_binary("capsh", NULL);
+ if (r < 0) {
+ log_error_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
+ return;
+ }
+
+
test(m, "exec-protectkernelmodules-no-capabilities.service", 0, CLD_EXITED);
test(m, "exec-protectkernelmodules-yes-capabilities.service", 0, CLD_EXITED);
test(m, "exec-protectkernelmodules-yes-mount-propagation.service", 0, CLD_EXITED);
@@ -253,7 +273,7 @@ static void test_exec_systemcall_system_mode_with_user(Manager *m) {
else if (getpwnam("nfsnobody"))
test(m, "exec-systemcallfilter-system-user-nfsnobody.service", 0, CLD_EXITED);
else
- log_error_errno(errno, "Skipping test_exec_systemcall_system_mode_with_user, could not find nobody/nfsnobody user: %m");
+ log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody user: %m", __func__);
#endif
}
@@ -263,7 +283,7 @@ static void test_exec_user(Manager *m) {
else if (getpwnam("nfsnobody"))
test(m, "exec-user-nfsnobody.service", 0, CLD_EXITED);
else
- log_error_errno(errno, "Skipping test_exec_user, could not find nobody/nfsnobody user: %m");
+ log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody user: %m", __func__);
}
static void test_exec_group(Manager *m) {
@@ -272,7 +292,7 @@ static void test_exec_group(Manager *m) {
else if (getgrnam("nfsnobody"))
test(m, "exec-group-nfsnobody.service", 0, CLD_EXITED);
else
- log_error_errno(errno, "Skipping test_exec_group, could not find nobody/nfsnobody group: %m");
+ log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody group: %m", __func__);
}
static void test_exec_supplementary_groups(Manager *m) {
@@ -353,17 +373,15 @@ static void test_exec_runtimedirectory(Manager *m) {
else if (getgrnam("nfsnobody"))
test(m, "exec-runtimedirectory-owner-nfsnobody.service", 0, CLD_EXITED);
else
- log_error_errno(errno, "Skipping test_exec_runtimedirectory-owner, could not find nobody/nfsnobody group: %m");
+ log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody group: %m", __func__);
}
static void test_exec_capabilityboundingset(Manager *m) {
int r;
- /* We use capsh to test if the capabilities are
- * properly set, so be sure that it exists */
r = find_binary("capsh", NULL);
if (r < 0) {
- log_error_errno(r, "Skipping test_exec_capabilityboundingset, could not find capsh binary: %m");
+ log_error_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
return;
}
@@ -389,9 +407,9 @@ static void test_exec_capabilityambientset(Manager *m) {
test(m, "exec-capabilityambientset-nfsnobody.service", 0, CLD_EXITED);
test(m, "exec-capabilityambientset-merge-nfsnobody.service", 0, CLD_EXITED);
} else
- log_error_errno(errno, "Skipping test_exec_capabilityambientset, could not find nobody/nfsnobody user: %m");
+ log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody user: %m", __func__);
} else
- log_error_errno(errno, "Skipping test_exec_capabilityambientset, the kernel does not support ambient capabilities: %m");
+ log_error_errno(errno, "Skipping %s, the kernel does not support ambient capabilities: %m", __func__);
}
static void test_exec_privatenetwork(Manager *m) {
@@ -399,7 +417,7 @@ static void test_exec_privatenetwork(Manager *m) {
r = find_binary("ip", NULL);
if (r < 0) {
- log_error_errno(r, "Skipping test_exec_privatenetwork, could not find ip binary: %m");
+ log_error_errno(r, "Skipping %s, could not find ip binary: %m", __func__);
return;
}
@@ -422,6 +440,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 +497,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[] = {
@@ -494,7 +517,7 @@ int main(int argc, char *argv[]) {
}
assert_se(setenv("XDG_RUNTIME_DIR", "/tmp/", 1) == 0);
- assert_se(set_unit_path(TEST_DIR "/test-execute/") >= 0);
+ assert_se(set_unit_path(get_testdata_dir("/test-execute")) >= 0);
/* Unset VAR1, VAR2 and VAR3 which are used in the PassEnvironment test
* cases, otherwise (and if they are present in the environment),
diff --git a/src/test/test-fd-util.c b/src/test/test-fd-util.c
index f555bb976c..4425b5fe5f 100644
--- a/src/test/test-fd-util.c
+++ b/src/test/test-fd-util.c
@@ -94,10 +94,20 @@ static void test_same_fd(void) {
assert_se(same_fd(b, a) == 0);
}
+static void test_open_serialization_fd(void) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open_serialization_fd("test");
+ assert_se(fd >= 0);
+
+ write(fd, "test\n", 5);
+}
+
int main(int argc, char *argv[]) {
test_close_many();
test_close_nointr();
test_same_fd();
+ test_open_serialization_fd();
return 0;
}
diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c
index 56316904a3..b1d688c89e 100644
--- a/src/test/test-fileio.c
+++ b/src/test/test-fileio.c
@@ -71,6 +71,8 @@ static void test_parse_env_file(void) {
"seven=\"sevenval\" #nocomment\n"
"eight=eightval #nocomment\n"
"export nine=nineval\n"
+ "ten=ignored\n"
+ "ten=ignored\n"
"ten=", f);
fflush(f);
@@ -204,6 +206,113 @@ static void test_parse_multiline_env_file(void) {
unlink(p);
}
+static void test_merge_env_file(void) {
+ char t[] = "/tmp/test-fileio-XXXXXX";
+ int fd, r;
+ FILE *f;
+ _cleanup_strv_free_ char **a = NULL;
+ char **i;
+
+ fd = mkostemp_safe(t);
+ assert_se(fd >= 0);
+
+ log_info("/* %s (%s) */", __func__, t);
+
+ f = fdopen(fd, "w");
+ assert_se(f);
+
+ r = write_string_stream(f,
+ "one=1 \n"
+ "twelve=${one}2\n"
+ "twentyone=2${one}\n"
+ "one=2\n"
+ "twentytwo=2${one}\n"
+ "xxx_minus_three=$xxx - 3\n"
+ "xxx=0x$one$one$one\n"
+ "yyy=${one:-fallback}\n"
+ "zzz=${one:+replacement}\n"
+ "zzzz=${foobar:-${nothing}}\n"
+ "zzzzz=${nothing:+${nothing}}\n"
+ , false);
+ assert(r >= 0);
+
+ r = merge_env_file(&a, NULL, t);
+ assert_se(r >= 0);
+ strv_sort(a);
+
+ STRV_FOREACH(i, a)
+ log_info("Got: <%s>", *i);
+
+ assert_se(streq(a[0], "one=2"));
+ assert_se(streq(a[1], "twelve=12"));
+ assert_se(streq(a[2], "twentyone=21"));
+ assert_se(streq(a[3], "twentytwo=22"));
+ assert_se(streq(a[4], "xxx=0x222"));
+ assert_se(streq(a[5], "xxx_minus_three= - 3"));
+ assert_se(streq(a[6], "yyy=2"));
+ assert_se(streq(a[7], "zzz=replacement"));
+ assert_se(streq(a[8], "zzzz="));
+ assert_se(streq(a[9], "zzzzz="));
+ assert_se(a[10] == NULL);
+
+ r = merge_env_file(&a, NULL, t);
+ assert_se(r >= 0);
+ strv_sort(a);
+
+ STRV_FOREACH(i, a)
+ log_info("Got2: <%s>", *i);
+
+ assert_se(streq(a[0], "one=2"));
+ assert_se(streq(a[1], "twelve=12"));
+ assert_se(streq(a[2], "twentyone=21"));
+ assert_se(streq(a[3], "twentytwo=22"));
+ assert_se(streq(a[4], "xxx=0x222"));
+ assert_se(streq(a[5], "xxx_minus_three=0x222 - 3"));
+ assert_se(streq(a[6], "yyy=2"));
+ assert_se(streq(a[7], "zzz=replacement"));
+ assert_se(streq(a[8], "zzzz="));
+ assert_se(streq(a[9], "zzzzz="));
+ assert_se(a[10] == NULL);
+}
+
+static void test_merge_env_file_invalid(void) {
+ char t[] = "/tmp/test-fileio-XXXXXX";
+ int fd, r;
+ FILE *f;
+ _cleanup_strv_free_ char **a = NULL;
+ char **i;
+
+ fd = mkostemp_safe(t);
+ assert_se(fd >= 0);
+
+ log_info("/* %s (%s) */", __func__, t);
+
+ f = fdopen(fd, "w");
+ assert_se(f);
+
+ r = write_string_stream(f,
+ "unset one \n"
+ "unset one= \n"
+ "unset one=1 \n"
+ "one \n"
+ "one = \n"
+ "one two =\n"
+ "\x20two=\n"
+ "#comment=comment\n"
+ ";comment2=comment2\n"
+ "#\n"
+ "\n\n" /* empty line */
+ , false);
+ assert(r >= 0);
+
+ r = merge_env_file(&a, NULL, t);
+ assert_se(r >= 0);
+
+ STRV_FOREACH(i, a)
+ log_info("Got: <%s>", *i);
+
+ assert_se(strv_isempty(a));
+}
static void test_executable_is_script(void) {
char t[] = "/tmp/test-executable-XXXXXX";
@@ -555,11 +664,14 @@ static void test_tempfn(void) {
}
int main(int argc, char *argv[]) {
+ log_set_max_level(LOG_DEBUG);
log_parse_environment();
log_open();
test_parse_env_file();
test_parse_multiline_env_file();
+ test_merge_env_file();
+ test_merge_env_file_invalid();
test_executable_is_script();
test_status_field();
test_capeff();
diff --git a/src/test/test-ipcrm.c b/src/test/test-ipcrm.c
index 463e135e2b..ce6c7aa18a 100644
--- a/src/test/test-ipcrm.c
+++ b/src/test/test-ipcrm.c
@@ -24,7 +24,7 @@
int main(int argc, char *argv[]) {
uid_t uid;
int r;
- const char* name = argv[1] ?: "nfsnobody";
+ const char* name = argv[1] ?: NOBODY_USER_NAME;
r = get_user_creds(&name, &uid, NULL, NULL, NULL);
if (r < 0) {
diff --git a/src/test/test-journal-importer.c b/src/test/test-journal-importer.c
new file mode 100644
index 0000000000..a61212ce7b
--- /dev/null
+++ b/src/test/test-journal-importer.c
@@ -0,0 +1,90 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Zbigniew Jędrzejewski-Szmek
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "log.h"
+#include "journal-importer.h"
+#include "string-util.h"
+#include "tests.h"
+
+static void assert_iovec_entry(const struct iovec *iovec, const char* content) {
+ assert_se(strlen(content) == iovec->iov_len);
+ assert_se(memcmp(content, iovec->iov_base, iovec->iov_len) == 0);
+}
+
+#define COREDUMP_PROC_GROUP \
+ "COREDUMP_PROC_CGROUP=1:name=systemd:/\n" \
+ "0::/user.slice/user-1002.slice/user@1002.service/gnome-terminal-server.service\n"
+
+static void test_basic_parsing(void) {
+ _cleanup_(journal_importer_cleanup) JournalImporter imp = {};
+ int r;
+
+ imp.fd = open(get_testdata_dir("/journal-data/journal-1.txt"), O_RDONLY|O_CLOEXEC);
+ assert_se(imp.fd >= 0);
+
+ do
+ r = journal_importer_process_data(&imp);
+ while (r == 0 && !journal_importer_eof(&imp));
+ assert_se(r == 1);
+
+ /* We read one entry, so we should get EOF on next read, but not yet */
+ assert_se(!journal_importer_eof(&imp));
+
+ assert_se(imp.iovw.count == 6);
+ assert_iovec_entry(&imp.iovw.iovec[0], "_BOOT_ID=1531fd22ec84429e85ae888b12fadb91");
+ assert_iovec_entry(&imp.iovw.iovec[1], "_TRANSPORT=journal");
+ assert_iovec_entry(&imp.iovw.iovec[2], COREDUMP_PROC_GROUP);
+ assert_iovec_entry(&imp.iovw.iovec[3], "COREDUMP_RLIMIT=-1");
+ assert_iovec_entry(&imp.iovw.iovec[4], COREDUMP_PROC_GROUP);
+ assert_iovec_entry(&imp.iovw.iovec[5], "_SOURCE_REALTIME_TIMESTAMP=1478389147837945");
+
+ /* Let's check if we get EOF now */
+ r = journal_importer_process_data(&imp);
+ assert_se(r == 0);
+ assert_se(journal_importer_eof(&imp));
+}
+
+static void test_bad_input(void) {
+ _cleanup_(journal_importer_cleanup) JournalImporter imp = {};
+ int r;
+
+ imp.fd = open(get_testdata_dir("/journal-data/journal-2.txt"), O_RDONLY|O_CLOEXEC);
+ assert_se(imp.fd >= 0);
+
+ do
+ r = journal_importer_process_data(&imp);
+ while (!journal_importer_eof(&imp));
+ assert_se(r == 0); /* If we don't have enough input, 0 is returned */
+
+ assert_se(journal_importer_eof(&imp));
+}
+
+int main(int argc, char **argv) {
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+
+ test_basic_parsing();
+ test_bad_input();
+
+ return 0;
+}
diff --git a/src/test/test-path.c b/src/test/test-path.c
index 5e99d478ee..70ac6b3df3 100644
--- a/src/test/test-path.c
+++ b/src/test/test-path.c
@@ -262,8 +262,8 @@ int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
+ assert_se(set_unit_path(get_testdata_dir("/test-path")) >= 0);
assert_se(runtime_dir = setup_fake_runtime_dir());
- assert_se(set_unit_path(TEST_DIR "/test-path/") >= 0);
for (test = tests; test && *test; test++) {
int r;
diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c
index 7b37910c33..81d9abc2d5 100644
--- a/src/test/test-sched-prio.c
+++ b/src/test/test-sched-prio.c
@@ -34,10 +34,9 @@ int main(int argc, char *argv[]) {
FDSet *fdset = NULL;
int r;
- assert_se(runtime_dir = setup_fake_runtime_dir());
-
/* prepare the test */
- assert_se(set_unit_path(TEST_DIR) >= 0);
+ assert_se(set_unit_path(get_testdata_dir("")) >= 0);
+ assert_se(runtime_dir = setup_fake_runtime_dir());
r = manager_new(UNIT_FILE_USER, true, &m);
if (MANAGER_SKIP_TEST(r)) {
log_notice_errno(r, "Skipping test: manager_new: %m");
diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c
index d80613dc84..8ac1d7989f 100644
--- a/src/test/test-socket-util.c
+++ b/src/test/test-socket-util.c
@@ -456,6 +456,23 @@ static void test_sockaddr_un_len(void) {
assert_se(SOCKADDR_UN_LEN(abstract) == offsetof(struct sockaddr_un, sun_path) + 1 + strlen(abstract.sun_path + 1));
}
+static void test_in_addr_is_multicast(void) {
+ union in_addr_union a, b;
+ int f;
+
+ assert_se(in_addr_from_string_auto("192.168.3.11", &f, &a) >= 0);
+ assert_se(in_addr_is_multicast(f, &a) == 0);
+
+ assert_se(in_addr_from_string_auto("224.0.0.1", &f, &a) >= 0);
+ assert_se(in_addr_is_multicast(f, &a) == 1);
+
+ assert_se(in_addr_from_string_auto("FF01:0:0:0:0:0:0:1", &f, &b) >= 0);
+ assert_se(in_addr_is_multicast(f, &b) == 1);
+
+ assert_se(in_addr_from_string_auto("2001:db8::c:69b:aeff:fe53:743e", &f, &b) >= 0);
+ assert_se(in_addr_is_multicast(f, &b) == 0);
+}
+
int main(int argc, char *argv[]) {
log_set_max_level(LOG_DEBUG);
@@ -482,5 +499,7 @@ int main(int argc, char *argv[]) {
test_sockaddr_un_len();
+ test_in_addr_is_multicast();
+
return 0;
}
diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c
index a48dca99e1..3ff2aadea4 100644
--- a/src/test/test-stat-util.c
+++ b/src/test/test-stat-util.c
@@ -26,6 +26,7 @@
#include "fileio.h"
#include "macro.h"
#include "missing.h"
+#include "mount-util.h"
#include "stat-util.h"
static void test_files_same(void) {
@@ -69,8 +70,11 @@ static void test_path_is_os_tree(void) {
}
static void test_path_check_fstype(void) {
- assert_se(path_check_fstype("/run", TMPFS_MAGIC) > 0);
- assert_se(path_check_fstype("/run", BTRFS_SUPER_MAGIC) == 0);
+ /* run might not be a mount point in build chroots */
+ if (path_is_mount_point("/run", NULL, AT_SYMLINK_FOLLOW) > 0) {
+ assert_se(path_check_fstype("/run", TMPFS_MAGIC) > 0);
+ assert_se(path_check_fstype("/run", BTRFS_SUPER_MAGIC) == 0);
+ }
assert_se(path_check_fstype("/proc", PROC_SUPER_MAGIC) > 0);
assert_se(path_check_fstype("/proc", BTRFS_SUPER_MAGIC) == 0);
assert_se(path_check_fstype("/proc", BTRFS_SUPER_MAGIC) == 0);
@@ -78,7 +82,9 @@ static void test_path_check_fstype(void) {
}
static void test_path_is_temporary_fs(void) {
- assert_se(path_is_temporary_fs("/run") > 0);
+ /* run might not be a mount point in build chroots */
+ if (path_is_mount_point("/run", NULL, AT_SYMLINK_FOLLOW) > 0)
+ assert_se(path_is_temporary_fs("/run") > 0);
assert_se(path_is_temporary_fs("/proc") == 0);
assert_se(path_is_temporary_fs("/i-dont-exist") == -ENOENT);
}
diff --git a/src/test/test-util.c b/src/test/test-util.c
index 1b5cba86c1..f8bf0cb875 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -195,50 +195,6 @@ static void test_log2i(void) {
assert_se(log2i(INT_MAX) == sizeof(int)*8-2);
}
-static void test_execute_directory(void) {
- char template_lo[] = "/tmp/test-readlink_and_make_absolute-lo.XXXXXXX";
- char template_hi[] = "/tmp/test-readlink_and_make_absolute-hi.XXXXXXX";
- const char * dirs[] = {template_hi, template_lo, NULL};
- const char *name, *name2, *name3, *overridden, *override, *masked, *mask;
-
- assert_se(mkdtemp(template_lo));
- assert_se(mkdtemp(template_hi));
-
- name = strjoina(template_lo, "/script");
- name2 = strjoina(template_hi, "/script2");
- name3 = strjoina(template_lo, "/useless");
- overridden = strjoina(template_lo, "/overridden");
- override = strjoina(template_hi, "/overridden");
- masked = strjoina(template_lo, "/masked");
- mask = strjoina(template_hi, "/masked");
-
- assert_se(write_string_file(name, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works", WRITE_STRING_FILE_CREATE) == 0);
- assert_se(write_string_file(name2, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2", WRITE_STRING_FILE_CREATE) == 0);
- assert_se(write_string_file(overridden, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed", WRITE_STRING_FILE_CREATE) == 0);
- assert_se(write_string_file(override, "#!/bin/sh\necho 'Executing '$0", WRITE_STRING_FILE_CREATE) == 0);
- assert_se(write_string_file(masked, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed", WRITE_STRING_FILE_CREATE) == 0);
- assert_se(symlink("/dev/null", mask) == 0);
- assert_se(chmod(name, 0755) == 0);
- assert_se(chmod(name2, 0755) == 0);
- assert_se(chmod(overridden, 0755) == 0);
- assert_se(chmod(override, 0755) == 0);
- assert_se(chmod(masked, 0755) == 0);
- assert_se(touch(name3) >= 0);
-
- execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL);
-
- assert_se(chdir(template_lo) == 0);
- assert_se(access("it_works", F_OK) >= 0);
- assert_se(access("failed", F_OK) < 0);
-
- assert_se(chdir(template_hi) == 0);
- assert_se(access("it_works2", F_OK) >= 0);
- assert_se(access("failed", F_OK) < 0);
-
- (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
- (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
-}
-
static void test_raw_clone(void) {
pid_t parent, pid, pid2;
@@ -359,7 +315,6 @@ int main(int argc, char *argv[]) {
test_protect_errno();
test_in_set();
test_log2i();
- test_execute_directory();
test_raw_clone();
test_physical_memory();
test_physical_memory_scale();
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/timedate/timedated.c b/src/timedate/timedated.c
index 490929e93b..1061b094d3 100644
--- a/src/timedate/timedated.c
+++ b/src/timedate/timedated.c
@@ -413,7 +413,7 @@ static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *
}
log_struct(LOG_INFO,
- LOG_MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE),
+ "MESSAGE_ID=" SD_MESSAGE_TIMEZONE_CHANGE_STR,
"TIMEZONE=%s", c->zone,
LOG_MESSAGE("Changed time zone to '%s'.", c->zone),
NULL);
@@ -591,7 +591,7 @@ static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *erro
clock_set_hwclock(tm);
log_struct(LOG_INFO,
- LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE),
+ "MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR,
"REALTIME="USEC_FMT, timespec_load(&ts),
LOG_MESSAGE("Changed local time to %s", ctime(&ts.tv_sec)),
NULL);
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index c4f4d46ca1..7326597b8c 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -872,7 +872,7 @@ static int parse_attribute_from_arg(Item *item) {
{ 's', FS_SECRM_FL }, /* Secure deletion */
{ 'u', FS_UNRM_FL }, /* Undelete */
{ 't', FS_NOTAIL_FL }, /* file tail should not be merged */
- { 'T', FS_TOPDIR_FL }, /* Top of directory hierarchies*/
+ { 'T', FS_TOPDIR_FL }, /* Top of directory hierarchies */
{ 'C', FS_NOCOW_FL }, /* Do not cow file */
};
@@ -1170,7 +1170,7 @@ static int create_item(Item *i) {
return log_error_errno(r, "Failed to substitute specifiers in copy source %s: %m", i->argument);
log_debug("Copying tree \"%s\" to \"%s\".", resolved, i->path);
- r = copy_tree(resolved, i->path, false);
+ r = copy_tree(resolved, i->path, i->uid_set ? i->uid : UID_INVALID, i->gid_set ? i->gid : GID_INVALID, COPY_REFLINK);
if (r == -EROFS && stat(i->path, &st) == 0)
r = -EEXIST;
diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c
index 3c58445836..9037aa1304 100644
--- a/src/udev/udev-builtin-blkid.c
+++ b/src/udev/udev-builtin-blkid.c
@@ -122,7 +122,7 @@ static int find_gpt_root(struct udev_device *dev, blkid_probe pr, bool test) {
errno = 0;
pl = blkid_probe_get_partitions(pr);
if (!pl)
- return errno > 0 ? -errno : -ENOMEM;
+ return -errno ?: -ENOMEM;
nvals = blkid_partlist_numof_partitions(pl);
for (i = 0; i < nvals; i++) {
@@ -193,7 +193,7 @@ static int probe_superblocks(blkid_probe pr) {
int rc;
if (fstat(blkid_probe_get_fd(pr), &st))
- return -1;
+ return -errno;
blkid_probe_enable_partitions(pr, 1);
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index 5be158f527..bd7b789cad 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -34,7 +34,8 @@
*
* Type of names:
* b<number> — BCMA bus core number
- * c<bus_id> — CCW bus group name, without leading zeros [s390]
+ * c<bus_id> — bus id of a grouped CCW or CCW device,
+ * with all leading zeros stripped [s390]
* o<index>[n<phys_port_name>|d<dev_port>]
* — on-board device index number
* s<slot>[f<function>][n<phys_port_name>|d<dev_port>]
@@ -87,6 +88,11 @@
* /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2
* ID_NET_NAME_MAC=enxd626b3450fb5
* ID_NET_NAME_PATH=enp0s29u1u2
+ *
+ * s390 grouped CCW interface:
+ * /sys/devices/css0/0.0.0007/0.0.f5f0/group_device/net/encf5f0
+ * ID_NET_NAME_MAC=enx026d3c00000a
+ * ID_NET_NAME_PATH=encf5f0
*/
#include <errno.h>
@@ -115,7 +121,7 @@ enum netname_type{
NET_USB,
NET_BCMA,
NET_VIRTIO,
- NET_CCWGROUP,
+ NET_CCW,
};
struct netnames {
@@ -132,9 +138,21 @@ struct netnames {
char usb_ports[IFNAMSIZ];
char bcma_core[IFNAMSIZ];
- char ccw_group[IFNAMSIZ];
+ char ccw_busid[IFNAMSIZ];
};
+/* skip intermediate virtio devices */
+static struct udev_device *skip_virtio(struct udev_device *dev) {
+ struct udev_device *parent = dev;
+
+ /* there can only ever be one virtio bus per parent device, so we can
+ safely ignore any virtio buses. see
+ <http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html> */
+ while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent)))
+ parent = udev_device_get_parent(parent);
+ return parent;
+}
+
/* retrieve on-board index number and label from firmware */
static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
unsigned dev_port = 0;
@@ -308,12 +326,8 @@ static int names_pci(struct udev_device *dev, struct netnames *names) {
assert(names);
parent = udev_device_get_parent(dev);
-
- /* there can only ever be one virtio bus per parent device, so we can
- safely ignore any virtio buses. see
- <http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html> */
- while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent)))
- parent = udev_device_get_parent(parent);
+ /* skip virtio subsystem if present */
+ parent = skip_virtio(parent);
if (!parent)
return -ENOENT;
@@ -412,8 +426,9 @@ static int names_bcma(struct udev_device *dev, struct netnames *names) {
static int names_ccw(struct udev_device *dev, struct netnames *names) {
struct udev_device *cdev;
- const char *bus_id;
+ const char *bus_id, *subsys;
size_t bus_id_len;
+ size_t bus_id_start;
int rc;
assert(dev);
@@ -421,14 +436,17 @@ static int names_ccw(struct udev_device *dev, struct netnames *names) {
/* Retrieve the associated CCW device */
cdev = udev_device_get_parent(dev);
+ /* skip virtio subsystem if present */
+ cdev = skip_virtio(cdev);
if (!cdev)
return -ENOENT;
- /* Network devices are always grouped CCW devices */
- if (!streq_ptr("ccwgroup", udev_device_get_subsystem(cdev)))
+ /* Network devices are either single or grouped CCW devices */
+ subsys = udev_device_get_subsystem(cdev);
+ if (!STRPTR_IN_SET(subsys, "ccwgroup", "ccw"))
return -ENOENT;
- /* Retrieve bus-ID of the grouped CCW device. The bus-ID uniquely
+ /* Retrieve bus-ID of the CCW device. The bus-ID uniquely
* identifies the network device on the Linux on System z channel
* subsystem. Note that the bus-ID contains lowercase characters.
*/
@@ -447,14 +465,15 @@ static int names_ccw(struct udev_device *dev, struct netnames *names) {
/* Strip leading zeros from the bus id for aesthetic purposes. This
* keeps the ccw names stable, yet much shorter in general case of
* bus_id 0.0.0600 -> 600. This is similar to e.g. how PCI domain is
- * not prepended when it is zero.
+ * not prepended when it is zero. Preserve the last 0 for 0.0.0000.
*/
- bus_id += strspn(bus_id, ".0");
+ bus_id_start = strspn(bus_id, ".0");
+ bus_id += bus_id_start < bus_id_len ? bus_id_start : bus_id_len - 1;
/* Store the CCW bus-ID for use as network device name */
- rc = snprintf(names->ccw_group, sizeof(names->ccw_group), "c%s", bus_id);
- if (rc >= 0 && rc < (int)sizeof(names->ccw_group))
- names->type = NET_CCWGROUP;
+ rc = snprintf(names->ccw_busid, sizeof(names->ccw_busid), "c%s", bus_id);
+ if (rc >= 0 && rc < (int)sizeof(names->ccw_busid))
+ names->type = NET_CCW;
return 0;
}
@@ -564,10 +583,10 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool
/* get path names for Linux on System z network devices */
err = names_ccw(dev, &names);
- if (err >= 0 && names.type == NET_CCWGROUP) {
+ if (err >= 0 && names.type == NET_CCW) {
char str[IFNAMSIZ];
- if (snprintf(str, sizeof(str), "%s%s", prefix, names.ccw_group) < (int)sizeof(str))
+ if (snprintf(str, sizeof(str), "%s%s", prefix, names.ccw_busid) < (int)sizeof(str))
udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
goto out;
}
diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c
index 527f0bff2d..8cb330dba1 100644
--- a/src/udev/udev-builtin-path_id.c
+++ b/src/udev/udev-builtin-path_id.c
@@ -664,11 +664,8 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
parent = skip_subsystem(parent, "xen");
supported_parent = true;
} else if (streq(subsys, "virtio")) {
- while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent)))
- parent = udev_device_get_parent(parent);
- path_prepend(&path, "virtio-pci-%s", udev_device_get_sysname(parent));
+ parent = skip_subsystem(parent, "virtio");
supported_transport = true;
- supported_parent = true;
} else if (streq(subsys, "scm")) {
path_prepend(&path, "scm-%s", udev_device_get_sysname(parent));
parent = skip_subsystem(parent, "scm");
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index dd23054b0d..ce2ff89b85 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -1210,7 +1210,7 @@ static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, voi
else
log_warning("worker ["PID_FMT"] exited with return code %i", pid, WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
- log_warning("worker ["PID_FMT"] terminated by signal %i (%s)", pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
+ log_warning("worker ["PID_FMT"] terminated by signal %i (%s)", pid, WTERMSIG(status), signal_to_string(WTERMSIG(status)));
} else if (WIFSTOPPED(status)) {
log_info("worker ["PID_FMT"] stopped", pid);
continue;
diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c
index 48c2a3fff4..d466e1b759 100644
--- a/src/update-done/update-done.c
+++ b/src/update-done/update-done.c
@@ -26,7 +26,7 @@
#define MESSAGE \
"# This file was created by systemd-update-done. Its only \n" \
"# purpose is to hold a timestamp of the time this directory\n" \
- "# was updated. See systemd-update-done.service(8).\n"
+ "# was updated. See man:systemd-update-done.service(8).\n"
static int apply_timestamp(const char *path, struct timespec *ts) {
struct timespec twice[2] = {