summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--.mkosi/mkosi.debian73
-rw-r--r--.mkosi/mkosi.fedora1
-rw-r--r--CODING_STYLE5
-rw-r--r--Makefile-man.am5
-rw-r--r--Makefile.am62
-rw-r--r--TODO10
-rw-r--r--catalog/systemd.catalog.in15
-rw-r--r--hwdb/60-keyboard.hwdb24
-rw-r--r--hwdb/60-sensor.hwdb45
-rwxr-xr-xhwdb/acpi-update.py4
-rwxr-xr-xhwdb/parse_hwdb.py40
-rw-r--r--man/journalctl.xml8
-rw-r--r--man/journald.conf.xml2
-rw-r--r--man/localectl.xml2
-rw-r--r--man/loginctl.xml36
-rw-r--r--man/machine-id.xml15
-rw-r--r--man/machinectl.xml30
-rw-r--r--man/networkctl.xml6
-rw-r--r--man/sd_bus_error.xml6
-rw-r--r--man/sd_bus_error_add_map.xml2
-rw-r--r--man/sd_bus_message_append.xml4
-rw-r--r--man/sd_bus_path_encode.xml4
-rw-r--r--man/sd_event_add_defer.xml2
-rw-r--r--man/sd_event_source_set_prepare.xml9
-rw-r--r--man/sd_id128_get_machine.xml65
-rw-r--r--man/sd_is_fifo.xml4
-rw-r--r--man/sd_journal_add_match.xml2
-rw-r--r--man/sd_journal_get_data.xml4
-rw-r--r--man/sd_journal_print.xml4
-rw-r--r--man/sd_listen_fds.xml2
-rw-r--r--man/sd_notify.xml20
-rw-r--r--man/systemctl.xml82
-rw-r--r--man/systemd-analyze.xml8
-rw-r--r--man/systemd-delta.xml4
-rw-r--r--man/systemd-journal-gatewayd.service.xml4
-rw-r--r--man/systemd-notify.xml8
-rw-r--r--man/systemd-nspawn.xml53
-rw-r--r--man/systemd-resolve.xml6
-rw-r--r--man/systemd-socket-activate.xml2
-rw-r--r--man/systemd-socket-proxyd.xml8
-rw-r--r--man/systemd.exec.xml15
-rw-r--r--man/systemd.netdev.xml4
-rw-r--r--man/systemd.network.xml214
-rw-r--r--man/systemd.nspawn.xml11
-rw-r--r--man/systemd.service.xml20
-rw-r--r--man/systemd.special.xml2
-rw-r--r--man/systemd.unit.xml22
-rw-r--r--man/systemd.xml4
-rw-r--r--man/telinit.xml4
-rw-r--r--man/timedatectl.xml2
-rw-r--r--po/cs.po16
-rw-r--r--rules/60-persistent-storage.rules4
-rw-r--r--rules/60-sensor.rules10
-rw-r--r--src/activate/activate.c2
-rw-r--r--src/basic/architecture.h1
-rw-r--r--src/basic/calendarspec.c8
-rw-r--r--src/basic/extract-word.c4
-rw-r--r--src/basic/fd-util.c6
-rw-r--r--src/basic/fileio.c19
-rw-r--r--src/basic/fileio.h2
-rw-r--r--src/basic/fs-util.c102
-rw-r--r--src/basic/fs-util.h16
-rw-r--r--src/basic/khash.c275
-rw-r--r--src/basic/khash.h53
-rw-r--r--src/basic/log.c35
-rw-r--r--src/basic/missing.h4
-rw-r--r--src/basic/mount-util.c14
-rw-r--r--src/basic/mount-util.h2
-rw-r--r--src/basic/parse-util.c16
-rw-r--r--src/basic/parse-util.h2
-rw-r--r--src/basic/path-util.c45
-rw-r--r--src/basic/path-util.h4
-rw-r--r--src/basic/rm-rf.c15
-rw-r--r--src/basic/rm-rf.h2
-rw-r--r--src/basic/time-util.c2
-rw-r--r--src/basic/util.c16
-rw-r--r--src/boot/efi/boot.c2
-rw-r--r--src/core/automount.c2
-rw-r--r--src/core/cgroup.c11
-rw-r--r--src/core/device.c4
-rw-r--r--src/core/killall.c3
-rw-r--r--src/core/load-fragment-gperf.gperf.m422
-rw-r--r--src/core/load-fragment.c123
-rw-r--r--src/core/machine-id-setup.c2
-rw-r--r--src/core/manager.c16
-rw-r--r--src/core/mount-setup.c2
-rw-r--r--src/core/mount.c2
-rw-r--r--src/core/namespace.c6
-rw-r--r--src/core/scope.c7
-rw-r--r--src/core/service.c35
-rw-r--r--src/core/service.h2
-rw-r--r--src/core/socket.c8
-rw-r--r--src/core/swap.c2
-rw-r--r--src/core/umount.c19
-rw-r--r--src/core/unit-printf.c61
-rw-r--r--src/core/unit.c14
-rw-r--r--src/cryptsetup/cryptsetup.c2
-rw-r--r--src/delta/delta.c25
l---------src/dissect/Makefile1
-rw-r--r--src/dissect/dissect.c275
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c2
-rwxr-xr-xsrc/journal-remote/log-generator.py4
-rw-r--r--src/journal/compress.c4
-rw-r--r--src/journal/journalctl.c18
-rw-r--r--src/journal/journald-server.c2
-rw-r--r--src/libsystemd-network/dhcp-internal.h4
-rw-r--r--src/libsystemd-network/dhcp-network.c14
-rw-r--r--src/libsystemd-network/network-internal.c34
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c2
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c2
-rw-r--r--src/libsystemd-network/test-dhcp-client.c2
-rw-r--r--src/libsystemd/libsystemd.sym5
-rw-r--r--src/libsystemd/sd-device/sd-device.c5
-rw-r--r--src/libsystemd/sd-id128/sd-id128.c32
-rw-r--r--src/libsystemd/sd-login/sd-login.c12
-rw-r--r--src/libudev/libudev.c6
-rw-r--r--src/login/logind-dbus.c3
-rw-r--r--src/login/logind-user.c2
-rw-r--r--src/machine/image-dbus.c165
-rw-r--r--src/machine/image-dbus.h1
-rw-r--r--src/machine/machine-dbus.c40
-rw-r--r--src/machine/machine-dbus.h2
-rw-r--r--src/machine/machinectl.c33
-rw-r--r--src/machine/machined-dbus.c52
-rw-r--r--src/machine/org.freedesktop.machine1.conf8
-rw-r--r--src/network/netdev/bridge.c3
-rw-r--r--src/network/netdev/tunnel.c70
-rw-r--r--src/network/netdev/vxlan.c4
-rw-r--r--src/network/networkd-link.c16
-rw-r--r--src/network/networkd-network-gperf.gperf3
-rw-r--r--src/network/networkd-network.h3
-rw-r--r--src/nspawn/nspawn-expose-ports.c10
-rw-r--r--src/nspawn/nspawn-gperf.gperf2
-rw-r--r--src/nspawn/nspawn-mount.c317
-rw-r--r--src/nspawn/nspawn-mount.h7
-rw-r--r--src/nspawn/nspawn-register.c5
-rw-r--r--src/nspawn/nspawn-settings.c26
-rw-r--r--src/nspawn/nspawn-settings.h1
-rw-r--r--src/nspawn/nspawn.c946
-rw-r--r--src/resolve/resolve-tool.c4
-rw-r--r--src/resolve/resolved.c2
-rw-r--r--src/shared/condition.c2
-rw-r--r--src/shared/dissect-image.c1072
-rw-r--r--src/shared/dissect-image.h93
-rw-r--r--src/shared/dropin.c17
-rw-r--r--src/shared/fdset.c7
-rw-r--r--src/shared/firewall-util.c5
-rw-r--r--src/shared/gpt.h17
-rw-r--r--src/shared/loop-util.c166
-rw-r--r--src/shared/loop-util.h41
-rw-r--r--src/shared/machine-pool.c2
-rw-r--r--src/systemctl/systemctl.c4
-rw-r--r--src/systemd/sd-id128.h2
-rw-r--r--src/systemd/sd-messages.h1
-rw-r--r--src/test/test-calendarspec.c3
-rw-r--r--src/test/test-copy.c2
-rw-r--r--src/test/test-dissect-image.c66
-rw-r--r--src/test/test-execute.c8
-rw-r--r--src/test/test-fs-util.c103
-rw-r--r--src/test/test-hash.c82
-rw-r--r--src/test/test-id128.c6
-rw-r--r--src/test/test-path-util.c60
-rw-r--r--src/test/test-time.c4
-rw-r--r--src/tmpfiles/tmpfiles.c15
-rw-r--r--src/udev/udev-builtin-net_id.c3
-rw-r--r--src/udev/udev-builtin-path_id.c4
-rw-r--r--src/udev/udev-node.c9
-rw-r--r--src/udev/udev-rules.c6
-rw-r--r--src/udev/udev-watch.c4
-rw-r--r--src/udev/udevadm-info.c4
-rwxr-xr-xtest/TEST-13-NSPAWN-SMOKE/test.sh10
-rwxr-xr-xtest/networkd-test.py320
-rwxr-xr-xtest/rule-syntax-check.py8
-rwxr-xr-xtest/sysv-generator-test.py2
-rw-r--r--tools/catalog-report.py87
176 files changed, 4689 insertions, 1727 deletions
diff --git a/.gitignore b/.gitignore
index 21fcf9841c..016ba625e3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -67,6 +67,7 @@
/systemd-debug-generator
/systemd-delta
/systemd-detect-virt
+/systemd-dissect
/systemd-escape
/systemd-export
/systemd-firstboot
@@ -180,6 +181,7 @@
/test-dhcp-option
/test-dhcp-server
/test-dhcp6-client
+/test-dissect-image
/test-dns-domain
/test-dns-packet
/test-dnssec
@@ -198,6 +200,7 @@
/test-fs-util
/test-fstab-util
/test-glob-util
+/test-hash
/test-hashmap
/test-hexdecoct
/test-hostname
diff --git a/.mkosi/mkosi.debian b/.mkosi/mkosi.debian
new file mode 100644
index 0000000000..f0cedbb90e
--- /dev/null
+++ b/.mkosi/mkosi.debian
@@ -0,0 +1,73 @@
+# This file is part of systemd.
+#
+# Copyright 2016 Daniel Rusek
+#
+# 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/>.
+
+# This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi).
+# Simply invoke "mkosi" in the project directory to build an OS image.
+
+[Distribution]
+Distribution=debian
+Release=unstable
+
+[Output]
+Format=raw_btrfs
+Bootable=yes
+
+[Partitions]
+RootSize=2G
+
+[Packages]
+BuildPackages=
+ autoconf
+ automake
+ docbook-xml
+ docbook-xsl
+ gcc
+ git
+ gnu-efi
+ gperf
+ intltool
+ iptables-dev
+ libacl1-dev
+ libaudit-dev
+ libblkid-dev
+ libbz2-dev
+ libcap-dev
+ libcryptsetup-dev
+ libcurl4-gnutls-dev
+ libdbus-1-dev
+ libdw-dev
+ libfdisk-dev
+ libgcrypt20-dev
+ libgnutls28-dev
+ libidn11-dev
+ libkmod-dev
+ liblzma-dev
+ liblz4-dev
+ libmicrohttpd-dev
+ libmount-dev
+ libpam0g-dev
+ libqrencode-dev
+ libseccomp-dev
+ libsmartcols-dev
+ libtool
+ libxkbcommon-dev
+ make
+ pkg-config
+ python3
+ python3-lxml
+ uuid-dev
+ xsltproc
diff --git a/.mkosi/mkosi.fedora b/.mkosi/mkosi.fedora
index 0af20c924a..478703c41a 100644
--- a/.mkosi/mkosi.fedora
+++ b/.mkosi/mkosi.fedora
@@ -63,6 +63,7 @@ BuildPackages=
libxslt
lz4-devel
make
+ diffutils
pam-devel
pkgconfig
python3-devel
diff --git a/CODING_STYLE b/CODING_STYLE
index e89b3c67e5..ed61ea9d28 100644
--- a/CODING_STYLE
+++ b/CODING_STYLE
@@ -429,3 +429,8 @@
and Linux/GNU-specific APIs, we generally prefer the POSIX APIs. If there
aren't, we are happy to use GNU or Linux APIs, and expect non-GNU
implementations of libc to catch up with glibc.
+
+- Whenever installing a signal handler, make sure to set SA_RESTART for it, so
+ that interrupted system calls are automatically restarted, and we minimize
+ hassles with handling EINTR (in particular as EINTR handling is pretty broken
+ on Linux).
diff --git a/Makefile-man.am b/Makefile-man.am
index 013e0d7967..228e29fc4f 100644
--- a/Makefile-man.am
+++ b/Makefile-man.am
@@ -397,6 +397,7 @@ MANPAGES_ALIAS += \
man/sd_id128_from_string.3 \
man/sd_id128_get_boot.3 \
man/sd_id128_get_invocation.3 \
+ man/sd_id128_get_machine_app_specific.3 \
man/sd_id128_is_null.3 \
man/sd_id128_t.3 \
man/sd_is_mq.3 \
@@ -750,6 +751,7 @@ man/sd_id128_equal.3: man/sd-id128.3
man/sd_id128_from_string.3: man/sd_id128_to_string.3
man/sd_id128_get_boot.3: man/sd_id128_get_machine.3
man/sd_id128_get_invocation.3: man/sd_id128_get_machine.3
+man/sd_id128_get_machine_app_specific.3: man/sd_id128_get_machine.3
man/sd_id128_is_null.3: man/sd-id128.3
man/sd_id128_t.3: man/sd-id128.3
man/sd_is_mq.3: man/sd_is_fifo.3
@@ -1531,6 +1533,9 @@ man/sd_id128_get_boot.html: man/sd_id128_get_machine.html
man/sd_id128_get_invocation.html: man/sd_id128_get_machine.html
$(html-alias)
+man/sd_id128_get_machine_app_specific.html: man/sd_id128_get_machine.html
+ $(html-alias)
+
man/sd_id128_is_null.html: man/sd-id128.html
$(html-alias)
diff --git a/Makefile.am b/Makefile.am
index cad4f050af..fe3ef9116d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -404,6 +404,11 @@ rootlibexec_PROGRAMS = \
systemd-socket-proxyd \
systemd-update-done
+if HAVE_BLKID
+rootlibexec_PROGRAMS += \
+ systemd-dissect
+endif
+
if HAVE_UTMP
rootlibexec_PROGRAMS += \
systemd-update-utmp
@@ -938,7 +943,9 @@ libbasic_la_SOURCES = \
src/basic/alloc-util.h \
src/basic/alloc-util.c \
src/basic/format-util.h \
- src/basic/nss-util.h
+ src/basic/nss-util.h \
+ src/basic/khash.h \
+ src/basic/khash.c
nodist_libbasic_la_SOURCES = \
src/basic/errno-from-name.h \
@@ -1042,6 +1049,8 @@ libshared_la_SOURCES = \
src/shared/machine-image.h \
src/shared/machine-pool.c \
src/shared/machine-pool.h \
+ src/shared/loop-util.c \
+ src/shared/loop-util.h \
src/shared/resolve-util.c \
src/shared/resolve-util.h \
src/shared/bus-unit-util.c \
@@ -1053,7 +1062,9 @@ libshared_la_SOURCES = \
src/shared/fdset.c \
src/shared/fdset.h \
src/shared/nsflags.h \
- src/shared/nsflags.c
+ src/shared/nsflags.c \
+ src/shared/dissect-image.c \
+ src/shared/dissect-image.h
if HAVE_UTMP
libshared_la_SOURCES += \
@@ -1076,7 +1087,9 @@ libshared_la_CFLAGS = \
$(AM_CFLAGS) \
$(ACL_CFLAGS) \
$(LIBIDN_CFLAGS) \
- $(SECCOMP_CFLAGS)
+ $(SECCOMP_CFLAGS) \
+ $(BLKID_CFLAGS) \
+ $(LIBCRYPTSETUP_CFLAGS)
libshared_la_LIBADD = \
libsystemd-internal.la \
@@ -1085,7 +1098,9 @@ libshared_la_LIBADD = \
libudev-internal.la \
$(ACL_LIBS) \
$(LIBIDN_LIBS) \
- $(SECCOMP_LIBS)
+ $(SECCOMP_LIBS) \
+ $(BLKID_LIBS) \
+ $(LIBCRYPTSETUP_LIBS)
rootlibexec_LTLIBRARIES += \
libsystemd-shared.la
@@ -1107,6 +1122,8 @@ libsystemd_shared_la_CFLAGS = \
$(ACL_CFLAGS) \
$(LIBIDN_CFLAGS) \
$(SECCOMP_CFLAGS) \
+ $(BLKID_CFLAGS) \
+ $(LIBCRYPTSETUP_CFLAGS) \
-fvisibility=default
# We can't use libshared_la_LIBADD here because it would
@@ -1118,7 +1135,9 @@ libsystemd_shared_la_LIBADD = \
$(libudev_internal_la_LIBADD) \
$(ACL_LIBS) \
$(LIBIDN_LIBS) \
- $(SECCOMP_LIBS)
+ $(SECCOMP_LIBS) \
+ $(BLKID_LIBS) \
+ $(LIBCRYPTSETUP_LIBS)
libsystemd_shared_la_LDFLAGS = \
$(AM_LDFLAGS) \
@@ -1456,7 +1475,8 @@ manual_tests += \
test-btrfs \
test-acd \
test-ipv4ll-manual \
- test-ask-password-api
+ test-ask-password-api \
+ test-dissect-image
unsafe_tests = \
test-hostname \
@@ -2068,6 +2088,17 @@ test_ask_password_api_SOURCES = \
test_ask_password_api_LDADD = \
libsystemd-shared.la
+test_dissect_image_SOURCES = \
+ src/test/test-dissect-image.c
+
+test_dissect_image_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(BLKID_CFLAGS)
+
+test_dissect_image_LDADD = \
+ libsystemd-shared.la \
+ $(BLKID_LIBS)
+
test_signal_util_SOURCES = \
src/test/test-signal-util.c
@@ -3055,6 +3086,13 @@ systemd_notify_LDADD = \
libsystemd-shared.la
# ------------------------------------------------------------------------------
+systemd_dissect_SOURCES = \
+ src/dissect/dissect.c
+
+systemd_dissect_LDADD = \
+ libsystemd-shared.la
+
+# ------------------------------------------------------------------------------
systemd_path_SOURCES = \
src/path/path.c
@@ -3704,6 +3742,7 @@ dist_udevrules_DATA += \
rules/60-persistent-input.rules \
rules/60-persistent-alsa.rules \
rules/60-persistent-storage.rules \
+ rules/60-sensor.rules \
rules/60-serial.rules \
rules/64-btrfs.rules \
rules/70-mouse.rules \
@@ -3873,6 +3912,7 @@ dist_udevhwdb_DATA = \
hwdb/20-net-ifname.hwdb \
hwdb/60-evdev.hwdb \
hwdb/60-keyboard.hwdb \
+ hwdb/60-sensor.hwdb \
hwdb/70-mouse.hwdb \
hwdb/70-pointingstick.hwdb \
hwdb/70-touchpad.hwdb
@@ -4047,6 +4087,16 @@ tests += \
test-id128
# ------------------------------------------------------------------------------
+test_hash_SOURCES = \
+ src/test/test-hash.c
+
+test_hash_LDADD = \
+ libsystemd-shared.la
+
+tests += \
+ test-hash
+
+# ------------------------------------------------------------------------------
bin_PROGRAMS += \
systemd-socket-activate
diff --git a/TODO b/TODO
index 93f0500680..9f6f13e2c2 100644
--- a/TODO
+++ b/TODO
@@ -23,8 +23,6 @@ External:
Janitorial Clean-ups:
-* replace manual readdir() loops with FOREACH_DIRENT or FOREACH_DIRENT_ALL
-
* Rearrange tests so that the various test-xyz.c match a specific src/basic/xyz.c again
Features:
@@ -32,8 +30,6 @@ Features:
* replace all canonicalize_file_name() invocations by chase_symlinks(), in
particulr those where a rootdir is relevant.
-* add parse_ip_port() or so, that is like safe_atou16() but checks for != 0
-
* drop nss-myhostname in favour of nss-resolve?
* drop internal dlopen() based nss-dns fallback in nss-resolve, and rely on the
@@ -120,8 +116,6 @@ Features:
* journald: sigbus API via a signal-handler safe function that people may call
from the SIGBUS handler
-* move specifier expansion from service_spawn() into load-fragment.c
-
* optionally, also require WATCHDOG=1 notifications during service start-up and shutdown
* resolved: when routing queries, make sure only look for the *longest* suffix...
@@ -624,6 +618,10 @@ Features:
- maybe make copying of /etc/resolv.conf optional, and skip it if --read-only
is used
+* dissect
+ - refuse mounting over a mount point
+ - automatically discover .roothash files in dissect, similarly to nspawn
+
* machined:
- add an API so that libvirt-lxc can inform us about network interfaces being
removed or added to an existing machine
diff --git a/catalog/systemd.catalog.in b/catalog/systemd.catalog.in
index 2c72d31290..cb0ac0ca88 100644
--- a/catalog/systemd.catalog.in
+++ b/catalog/systemd.catalog.in
@@ -172,8 +172,8 @@ Defined-By: systemd
Support: %SUPPORT_URL%
All system services necessary queued for starting at boot have been
-successfully started. Note that this does not mean that the machine is
-now idle as services might still be busy with completing start-up.
+started. Note that this does not mean that the machine is now idle as services
+might still be busy with completing start-up.
Kernel start-up required @KERNEL_USEC@ microseconds.
@@ -181,6 +181,17 @@ Initial RAM disk start-up required @INITRD_USEC@ microseconds.
Userspace start-up required @USERSPACE_USEC@ microseconds.
+-- eed00a68ffd84e31882105fd973abdd1
+Subject: User manager start-up is now complete
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+The user manager instance for user @_UID@ has been started. All services queued
+for starting have been started. Note that other services might still be starting
+up or be started at any later time.
+
+Startup of the manager took @USERSPACE_USEC@ microseconds.
+
-- 6bbd95ee977941e497c48be27c254128
Subject: System sleep state @SLEEP@ entered
Defined-By: systemd
diff --git a/hwdb/60-keyboard.hwdb b/hwdb/60-keyboard.hwdb
index c4031ab587..fff3b9a6ea 100644
--- a/hwdb/60-keyboard.hwdb
+++ b/hwdb/60-keyboard.hwdb
@@ -4,7 +4,7 @@
# scan codes to add to the AT keyboard's 'force-release' list.
#
# The lookup keys are composed in:
-# 60-keyboard.rules
+# 60-evdev.rules
#
# Note: The format of the "evdev:" prefix match key is a
# contract between the rules file and the hardware data, it might
@@ -42,6 +42,13 @@
#
# To debug key presses and access scan code mapping data of
# an input device use the commonly available tool: evtest(1).
+
+# A device with a fixed keyboard layout that must not be changed by
+# the desktop environment may specify that layout as:
+# XKB_FIXED_LAYOUT="us"
+# XKB_FIXED_VARIANT=""
+# Examples of such devices: the Yubikey or other key-code generating
+# devices.
#
# To update this file, create a new file
# /etc/udev/hwdb.d/70-keyboard.hwdb
@@ -1244,3 +1251,18 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDIXONSP:pnDIXON*:pvr*
KEYBOARD_KEY_a0=! # mute
KEYBOARD_KEY_ae=! # volume down
KEYBOARD_KEY_b0=! # volume up
+
+###########################################################
+# Fixed layout devices
+###########################################################
+
+# Yubico Yubico Yubikey II"
+evdev:input:b0003v1050p0010*
+# Yubico Yubikey NEO OTP+CCID
+evdev:input:b0003v1050p0111*
+# Yubico Yubikey NEO OTP+U2F+CCID
+evdev:input:b0003v1050p0116*
+# OKE Electron Company USB barcode reader
+evdev:input:b0003v05FEp1010*
+ XKB_FIXED_LAYOUT="us"
+ XKB_FIXED_VARIANT=""
diff --git a/hwdb/60-sensor.hwdb b/hwdb/60-sensor.hwdb
new file mode 100644
index 0000000000..3160cf77e8
--- /dev/null
+++ b/hwdb/60-sensor.hwdb
@@ -0,0 +1,45 @@
+# This file is part of systemd.
+#
+# The lookup keys are composed in:
+# 60-sensor.rules
+#
+# Note: The format of the "sensor:" prefix match key is a
+# contract between the rules file and the hardware data, it might
+# change in later revisions to support more or better matches, it
+# is not necessarily expected to be a stable ABI.
+#
+# Match string formats:
+# sensor:modalias:<parent device modalias>:dmi:<dmi string>
+#
+# To add local entries, create a new file
+# /etc/udev/hwdb.d/61-sensor-local.hwdb
+# and add your rules there. To load the new rules execute (as root):
+# systemd-hwdb update
+# udevadm trigger `dirname $(udevadm info -n "/dev/iio:deviceXXX" -q path)`
+# where /dev/iio:deviceXXX is the device in question.
+#
+# If your changes are generally applicable, preferably send them as a pull
+# request to
+# https://github.com/systemd/systemd
+# or create a bug report on https://github.com/systemd/systemd/issues and
+# include your new rules, a description of the device, and the output of
+# udevadm info --export-db
+#
+# Allowed properties are:
+# ACCEL_MOUNT_MATRIX=<matrix>
+#
+# where <matrix> is a mount-matrix in the format specified in the IIO
+# subsystem[1]. The default, when unset, is equivalent to:
+# ACCEL_MOUNT_MATRIX=1, 0, 0; 0, 1, 0; 0, 0, 1
+# eg. the identity matrix.
+#
+# [1]: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfc57732ad38f93ae6232a3b4e64fd077383a0f1
+
+#
+# Sort by brand, model
+
+#########################################
+# Winbook
+#########################################
+sensor:modalias:acpi:BMA250*:dmi:*svn*WinBook*:*pn*TW100*
+ ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 0
diff --git a/hwdb/acpi-update.py b/hwdb/acpi-update.py
index 2dc8c7c064..50da531dc6 100755
--- a/hwdb/acpi-update.py
+++ b/hwdb/acpi-update.py
@@ -31,7 +31,7 @@ class PNPTableParser(HTMLParser):
elif self.state == State.AFTER_PNPID:
self.state = State.DATE
else:
- raise Error("Unexpected field")
+ raise ValueError
self.data = ""
@@ -48,7 +48,7 @@ class PNPTableParser(HTMLParser):
elif self.state == State.DATE:
self.state = State.NOWHERE
else:
- raise Error("Unexpected field")
+ raise ValueError
def handle_data(self, data):
self.data += data
diff --git a/hwdb/parse_hwdb.py b/hwdb/parse_hwdb.py
index 5d4c5ea64d..18f13edd0a 100755
--- a/hwdb/parse_hwdb.py
+++ b/hwdb/parse_hwdb.py
@@ -26,7 +26,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
-import functools
import glob
import string
import sys
@@ -35,9 +34,9 @@ import os
try:
from pyparsing import (Word, White, Literal, ParserElement, Regex,
LineStart, LineEnd,
- ZeroOrMore, OneOrMore, Combine, Or, Optional, Suppress, Group,
+ OneOrMore, Combine, Or, Optional, Suppress, Group,
nums, alphanums, printables,
- stringEnd, pythonStyleComment,
+ stringEnd, pythonStyleComment, QuotedString,
ParseBaseException)
except ImportError:
print('pyparsing is not available')
@@ -56,17 +55,20 @@ except ImportError:
lru_cache = lambda: (lambda f: f)
EOL = LineEnd().suppress()
-EMPTYLINE = LineStart() + LineEnd()
+EMPTYLINE = LineEnd()
COMMENTLINE = pythonStyleComment + EOL
INTEGER = Word(nums)
+STRING = QuotedString('"')
REAL = Combine((INTEGER + Optional('.' + Optional(INTEGER))) ^ ('.' + INTEGER))
+SIGNED_REAL = Combine(Optional(Word('-+')) + REAL)
UDEV_TAG = Word(string.ascii_uppercase, alphanums + '_')
TYPES = {'mouse': ('usb', 'bluetooth', 'ps2', '*'),
'evdev': ('name', 'atkbd', 'input'),
'touchpad': ('i8042', 'rmi', 'bluetooth', 'usb'),
'keyboard': ('name', ),
- }
+ 'sensor': ('modalias', ),
+ }
@lru_cache()
def hwdb_grammar():
@@ -76,13 +78,13 @@ def hwdb_grammar():
for category, conn in TYPES.items())
matchline = Combine(prefix + Word(printables + ' ' + '®')) + EOL
propertyline = (White(' ', exact=1).suppress() +
- Combine(UDEV_TAG - '=' - Word(alphanums + '_=:@*.! ') - Optional(pythonStyleComment)) +
+ Combine(UDEV_TAG - '=' - Word(alphanums + '_=:@*.!-;, "') - Optional(pythonStyleComment)) +
EOL)
propertycomment = White(' ', exact=1) + pythonStyleComment + EOL
group = (OneOrMore(matchline('MATCHES*') ^ COMMENTLINE.suppress()) -
OneOrMore(propertyline('PROPERTIES*') ^ propertycomment.suppress()) -
- (EMPTYLINE ^ stringEnd()).suppress() )
+ (EMPTYLINE ^ stringEnd()).suppress())
commentgroup = OneOrMore(COMMENTLINE).suppress() - EMPTYLINE.suppress()
grammar = OneOrMore(group('GROUPS*') ^ commentgroup) + stringEnd()
@@ -93,8 +95,11 @@ def hwdb_grammar():
def property_grammar():
ParserElement.setDefaultWhitespaceChars(' ')
- setting = Optional('*')('DEFAULT') + INTEGER('DPI') + Suppress('@') + INTEGER('HZ')
- props = (('MOUSE_DPI', Group(OneOrMore(setting('SETTINGS*')))),
+ dpi_setting = (Optional('*')('DEFAULT') + INTEGER('DPI') + Suppress('@') + INTEGER('HZ'))('SETTINGS*')
+ mount_matrix_row = SIGNED_REAL + ',' + SIGNED_REAL + ',' + SIGNED_REAL
+ mount_matrix = (mount_matrix_row + ';' + mount_matrix_row + ';' + mount_matrix_row)('MOUNT_MATRIX')
+
+ props = (('MOUSE_DPI', Group(OneOrMore(dpi_setting))),
('MOUSE_WHEEL_CLICK_ANGLE', INTEGER),
('MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL', INTEGER),
('MOUSE_WHEEL_CLICK_COUNT', INTEGER),
@@ -103,19 +108,22 @@ def property_grammar():
('POINTINGSTICK_SENSITIVITY', INTEGER),
('POINTINGSTICK_CONST_ACCEL', REAL),
('ID_INPUT_TOUCHPAD_INTEGRATION', Or(('internal', 'external'))),
- )
+ ('XKB_FIXED_LAYOUT', STRING),
+ ('XKB_FIXED_VARIANT', STRING),
+ ('ACCEL_MOUNT_MATRIX', mount_matrix),
+ )
fixed_props = [Literal(name)('NAME') - Suppress('=') - val('VALUE')
for name, val in props]
kbd_props = [Regex(r'KEYBOARD_KEY_[0-9a-f]+')('NAME')
- Suppress('=') -
('!' ^ (Optional('!') - Word(alphanums + '_')))('VALUE')
- ]
+ ]
abs_props = [Regex(r'EVDEV_ABS_[0-9a-f]{2}')('NAME')
- Suppress('=') -
Word(nums + ':')('VALUE')
- ]
+ ]
- grammar = Or(fixed_props + kbd_props + abs_props)
+ grammar = Or(fixed_props + kbd_props + abs_props) + EOL
return grammar
@@ -133,7 +141,8 @@ def convert_properties(group):
def parse(fname):
grammar = hwdb_grammar()
try:
- parsed = grammar.parseFile(fname)
+ with open(fname, 'r', encoding='UTF-8') as f:
+ parsed = grammar.parseFile(f)
except ParseBaseException as e:
error('Cannot parse {}: {}', fname, e)
return []
@@ -185,8 +194,7 @@ def print_summary(fname, groups):
.format(fname,
len(groups),
sum(len(matches) for matches, props in groups),
- sum(len(props) for matches, props in groups),
- ))
+ sum(len(props) for matches, props in groups)))
if __name__ == '__main__':
args = sys.argv[1:] or glob.glob(os.path.dirname(sys.argv[0]) + '/[67]0-*.hwdb')
diff --git a/man/journalctl.xml b/man/journalctl.xml
index 63b4a267b8..5e682ea9ec 100644
--- a/man/journalctl.xml
+++ b/man/journalctl.xml
@@ -412,7 +412,7 @@
<term><option>--quiet</option></term>
<listitem><para>Suppresses all info messages
- (i.e. "-- Logs begin at ...", "-- Reboot --"),
+ (i.e. "-- Logs begin at …", "-- Reboot --"),
any warning messages regarding
inaccessible system journals when run as a normal
user.</para></listitem>
@@ -573,7 +573,7 @@
<listitem><para>The cursor is shown after the last entry after
two dashes:</para>
- <programlisting>-- cursor: s=0639...</programlisting>
+ <programlisting>-- cursor: s=0639…</programlisting>
<para>The format of the cursor is private
and subject to change.</para></listitem>
</varlistentry>
@@ -731,7 +731,7 @@
<varlistentry>
<term><option>--list-catalog
- <optional><replaceable>128-bit-ID...</replaceable></optional>
+ <optional><replaceable>128-bit-ID…</replaceable></optional>
</option></term>
<listitem><para>List the contents of the message catalog as a
@@ -745,7 +745,7 @@
<varlistentry>
<term><option>--dump-catalog
- <optional><replaceable>128-bit-ID...</replaceable></optional>
+ <optional><replaceable>128-bit-ID…</replaceable></optional>
</option></term>
<listitem><para>Show the contents of the message catalog, with
diff --git a/man/journald.conf.xml b/man/journald.conf.xml
index 9daa964803..209d857234 100644
--- a/man/journald.conf.xml
+++ b/man/journald.conf.xml
@@ -227,7 +227,7 @@
rotated journal files are kept as history.</para>
<para>Specify values in bytes or use K, M, G, T, P, E as
- units for the specified sizes (equal to 1024, 1024², ... bytes).
+ units for the specified sizes (equal to 1024, 1024², … bytes).
Note that size limits are enforced synchronously when journal
files are extended, and no explicit rotation step triggered by
time is needed.</para>
diff --git a/man/localectl.xml b/man/localectl.xml
index 31238272f3..7da12c29de 100644
--- a/man/localectl.xml
+++ b/man/localectl.xml
@@ -127,7 +127,7 @@
</varlistentry>
<varlistentry>
- <term><command>set-locale LOCALE...</command></term>
+ <term><command>set-locale LOCALE…</command></term>
<listitem><para>Set the system locale. This takes one or more
assignments such as "LANG=de_DE.utf8",
diff --git a/man/loginctl.xml b/man/loginctl.xml
index fb51740503..534a0d961e 100644
--- a/man/loginctl.xml
+++ b/man/loginctl.xml
@@ -191,7 +191,7 @@
</varlistentry>
<varlistentry>
- <term><command>session-status</command> <optional><replaceable>ID</replaceable>...</optional></term>
+ <term><command>session-status</command> <optional><replaceable>ID</replaceable>…</optional></term>
<listitem><para>Show terse runtime status information about
one or more sessions, followed by the most recent log data
@@ -204,7 +204,7 @@
</varlistentry>
<varlistentry>
- <term><command>show-session</command> <optional><replaceable>ID</replaceable>...</optional></term>
+ <term><command>show-session</command> <optional><replaceable>ID</replaceable>…</optional></term>
<listitem><para>Show properties of one or more sessions or the
manager itself. If no argument is specified, properties of the
@@ -229,8 +229,8 @@
</varlistentry>
<varlistentry>
- <term><command>lock-session</command> <optional><replaceable>ID</replaceable>...</optional></term>
- <term><command>unlock-session</command> <optional><replaceable>ID</replaceable>...</optional></term>
+ <term><command>lock-session</command> <optional><replaceable>ID</replaceable>…</optional></term>
+ <term><command>unlock-session</command> <optional><replaceable>ID</replaceable>…</optional></term>
<listitem><para>Activates/deactivates the screen lock on one
or more sessions, if the session supports it. Takes one or
@@ -248,7 +248,7 @@
</varlistentry>
<varlistentry>
- <term><command>terminate-session</command> <replaceable>ID</replaceable>...</term>
+ <term><command>terminate-session</command> <replaceable>ID</replaceable>…</term>
<listitem><para>Terminates a session. This kills all processes
of the session and deallocates all resources attached to the
@@ -256,7 +256,7 @@
</varlistentry>
<varlistentry>
- <term><command>kill-session</command> <replaceable>ID</replaceable>...</term>
+ <term><command>kill-session</command> <replaceable>ID</replaceable>…</term>
<listitem><para>Send a signal to one or more processes of the
session. Use <option>--kill-who=</option> to select which
@@ -274,7 +274,7 @@
</varlistentry>
<varlistentry>
- <term><command>user-status</command> <optional><replaceable>USER</replaceable>...</optional></term>
+ <term><command>user-status</command> <optional><replaceable>USER</replaceable>…</optional></term>
<listitem><para>Show terse runtime status information about
one or more logged in users, followed by the most recent log
@@ -288,7 +288,7 @@
</varlistentry>
<varlistentry>
- <term><command>show-user</command> <optional><replaceable>USER</replaceable>...</optional></term>
+ <term><command>show-user</command> <optional><replaceable>USER</replaceable>…</optional></term>
<listitem><para>Show properties of one or more users or the
manager itself. If no argument is specified, properties of the
@@ -303,8 +303,8 @@
</varlistentry>
<varlistentry>
- <term><command>enable-linger</command> <optional><replaceable>USER</replaceable>...</optional></term>
- <term><command>disable-linger</command> <optional><replaceable>USER</replaceable>...</optional></term>
+ <term><command>enable-linger</command> <optional><replaceable>USER</replaceable>…</optional></term>
+ <term><command>disable-linger</command> <optional><replaceable>USER</replaceable>…</optional></term>
<listitem><para>Enable/disable user lingering for one or more
users. If enabled for a specific user, a user manager is
@@ -320,7 +320,7 @@
</varlistentry>
<varlistentry>
- <term><command>terminate-user</command> <replaceable>USER</replaceable>...</term>
+ <term><command>terminate-user</command> <replaceable>USER</replaceable>…</term>
<listitem><para>Terminates all sessions of a user. This kills
all processes of all sessions of the user and deallocates all
@@ -328,7 +328,7 @@
</varlistentry>
<varlistentry>
- <term><command>kill-user</command> <replaceable>USER</replaceable>...</term>
+ <term><command>kill-user</command> <replaceable>USER</replaceable>…</term>
<listitem><para>Send a signal to all processes of a user. Use
<option>--signal=</option> to select the signal to send.
@@ -345,7 +345,7 @@
</varlistentry>
<varlistentry>
- <term><command>seat-status</command> <optional><replaceable>NAME</replaceable>...</optional></term>
+ <term><command>seat-status</command> <optional><replaceable>NAME</replaceable>…</optional></term>
<listitem><para>Show terse runtime status information about
one or more seats. Takes one or more seat names as parameters.
@@ -357,7 +357,7 @@
</varlistentry>
<varlistentry>
- <term><command>show-seat</command> <optional><replaceable>NAME</replaceable>...</optional></term>
+ <term><command>show-seat</command> <optional><replaceable>NAME</replaceable>…</optional></term>
<listitem><para>Show properties of one or more seats or the
manager itself. If no argument is specified, properties of the
@@ -372,7 +372,7 @@
</varlistentry>
<varlistentry>
- <term><command>attach</command> <replaceable>NAME</replaceable> <replaceable>DEVICE</replaceable>...</term>
+ <term><command>attach</command> <replaceable>NAME</replaceable> <replaceable>DEVICE</replaceable>…</term>
<listitem><para>Persistently attach one or more devices to a
seat. The devices should be specified via device paths in the
@@ -396,7 +396,7 @@
</varlistentry>
<varlistentry>
- <term><command>terminate-seat</command> <replaceable>NAME</replaceable>...</term>
+ <term><command>terminate-seat</command> <replaceable>NAME</replaceable>…</term>
<listitem><para>Terminates all sessions on a seat. This kills
all processes of all sessions on the seat and deallocates all
@@ -426,9 +426,9 @@ fatima (1005)
Sessions: 5 *3
Unit: user-1005.slice
├─user@1005.service
- ...
+ …
├─session-3.scope
- ...
+ …
└─session-5.scope
├─3473 login -- fatima
└─3515 -zsh
diff --git a/man/machine-id.xml b/man/machine-id.xml
index a722649de4..3c261bffcc 100644
--- a/man/machine-id.xml
+++ b/man/machine-id.xml
@@ -71,13 +71,14 @@
<para>This machine ID adheres to the same format and logic as the
D-Bus machine ID.</para>
- <para>This ID uniquely identifies the host. It should be considered "confidential", and must not
- be exposed in untrusted environments, in particular on the network. If a stable unique
- identifier that is tied to the machine is needed for some application, the machine ID or any
- part of it must not be used directly. Instead the machine ID should be hashed with a
- cryptographic, keyed hash function, using a fixed, application-specific key. That way the ID
- will be properly unique, and derived in a constant way from the machine ID but there will be no
- way to retrieve the original machine ID from the application-specific one.</para>
+ <para>This ID uniquely identifies the host. It should be considered "confidential", and must not be exposed in
+ untrusted environments, in particular on the network. If a stable unique identifier that is tied to the machine is
+ needed for some application, the machine ID or any part of it must not be used directly. Instead the machine ID
+ should be hashed with a cryptographic, keyed hash function, using a fixed, application-specific key. That way the
+ ID will be properly unique, and derived in a constant way from the machine ID but there will be no way to retrieve
+ the original machine ID from the application-specific one. The
+ <citerefentry><refentrytitle>sd_id128_get_machine_app_specific</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ API provides an implementation of such an algorithm.</para>
<para>The
<citerefentry><refentrytitle>systemd-machine-id-setup</refentrytitle><manvolnum>1</manvolnum></citerefentry>
diff --git a/man/machinectl.xml b/man/machinectl.xml
index 81192417d8..8bebdcf9a1 100644
--- a/man/machinectl.xml
+++ b/man/machinectl.xml
@@ -292,7 +292,7 @@
Defaults to 1. All addresses can be requested with <literal>all</literal>
as argument to <option>--max-addresses</option> . If the argument to
<option>--max-addresses</option> is less than the actual number
- of addresses,<literal>...</literal>follows the last address.
+ of addresses, <literal>...</literal>follows the last address.
If multiple addresses are to be written for a given machine, every
address except the first one is on a new line and is followed by
<literal>,</literal> if another address will be output afterwards. </para></listitem>
@@ -327,7 +327,7 @@
</varlistentry>
<varlistentry>
- <term><command>status</command> <replaceable>NAME</replaceable>...</term>
+ <term><command>status</command> <replaceable>NAME</replaceable>…</term>
<listitem><para>Show runtime status information about
one or more virtual machines and containers, followed by the
@@ -341,7 +341,7 @@
</varlistentry>
<varlistentry>
- <term><command>show</command> [<replaceable>NAME</replaceable>...]</term>
+ <term><command>show</command> [<replaceable>NAME</replaceable>…]</term>
<listitem><para>Show properties of one or more registered virtual machines or containers or the manager
itself. If no argument is specified, properties of the manager will be shown. If a NAME is specified,
@@ -353,7 +353,7 @@
</varlistentry>
<varlistentry>
- <term><command>start</command> <replaceable>NAME</replaceable>...</term>
+ <term><command>start</command> <replaceable>NAME</replaceable>…</term>
<listitem><para>Start a container as a system service, using
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
@@ -410,7 +410,7 @@
</varlistentry>
<varlistentry>
- <term><command>shell</command> [[<replaceable>NAME</replaceable>@]<replaceable>NAME</replaceable> [<replaceable>PATH</replaceable> [<replaceable>ARGUMENTS</replaceable>...]]] </term>
+ <term><command>shell</command> [[<replaceable>NAME</replaceable>@]<replaceable>NAME</replaceable> [<replaceable>PATH</replaceable> [<replaceable>ARGUMENTS</replaceable>…]]] </term>
<listitem><para>Open an interactive shell session in a
container or on the local host. The first argument refers to
@@ -449,8 +449,8 @@
</varlistentry>
<varlistentry>
- <term><command>enable</command> <replaceable>NAME</replaceable>...</term>
- <term><command>disable</command> <replaceable>NAME</replaceable>...</term>
+ <term><command>enable</command> <replaceable>NAME</replaceable>…</term>
+ <term><command>disable</command> <replaceable>NAME</replaceable>…</term>
<listitem><para>Enable or disable a container as a system
service to start at system boot, using
@@ -463,7 +463,7 @@
</varlistentry>
<varlistentry>
- <term><command>poweroff</command> <replaceable>NAME</replaceable>...</term>
+ <term><command>poweroff</command> <replaceable>NAME</replaceable>…</term>
<listitem><para>Power off one or more containers. This will
trigger a reboot by sending SIGRTMIN+4 to the container's init
@@ -478,7 +478,7 @@
</varlistentry>
<varlistentry>
- <term><command>reboot</command> <replaceable>NAME</replaceable>...</term>
+ <term><command>reboot</command> <replaceable>NAME</replaceable>…</term>
<listitem><para>Reboot one or more containers. This will
trigger a reboot by sending SIGINT to the container's init
@@ -488,7 +488,7 @@
</varlistentry>
<varlistentry>
- <term><command>terminate</command> <replaceable>NAME</replaceable>...</term>
+ <term><command>terminate</command> <replaceable>NAME</replaceable>…</term>
<listitem><para>Immediately terminates a virtual machine or
container, without cleanly shutting it down. This kills all
@@ -499,7 +499,7 @@
</varlistentry>
<varlistentry>
- <term><command>kill</command> <replaceable>NAME</replaceable>...</term>
+ <term><command>kill</command> <replaceable>NAME</replaceable>…</term>
<listitem><para>Send a signal to one or more processes of the
virtual machine or container. This means processes as seen by
@@ -568,7 +568,7 @@
</varlistentry>
<varlistentry>
- <term><command>image-status</command> [<replaceable>NAME</replaceable>...]</term>
+ <term><command>image-status</command> [<replaceable>NAME</replaceable>…]</term>
<listitem><para>Show terse status information about one or
more container or VM images. This function is intended to
@@ -578,7 +578,7 @@
</varlistentry>
<varlistentry>
- <term><command>show-image</command> [<replaceable>NAME</replaceable>...]</term>
+ <term><command>show-image</command> [<replaceable>NAME</replaceable>…]</term>
<listitem><para>Show properties of one or more registered
virtual machine or container images, or the manager itself. If
@@ -630,7 +630,7 @@
</varlistentry>
<varlistentry>
- <term><command>remove</command> <replaceable>NAME</replaceable>...</term>
+ <term><command>remove</command> <replaceable>NAME</replaceable>…</term>
<listitem><para>Removes one or more container or VM images.
The special image <literal>.host</literal>, which refers to
@@ -849,7 +849,7 @@
</varlistentry>
<varlistentry>
- <term><command>cancel-transfers</command> <replaceable>ID</replaceable>...</term>
+ <term><command>cancel-transfers</command> <replaceable>ID</replaceable>…</term>
<listitem><para>Aborts a download, import or export of the
container or VM image with the specified ID. To list ongoing
diff --git a/man/networkctl.xml b/man/networkctl.xml
index 24e1de6986..809eb7ec6a 100644
--- a/man/networkctl.xml
+++ b/man/networkctl.xml
@@ -102,7 +102,7 @@
<varlistentry>
<term>
<command>list</command>
- <optional><replaceable>LINK...</replaceable></optional>
+ <optional><replaceable>LINK…</replaceable></optional>
</term>
<listitem>
@@ -122,7 +122,7 @@
<varlistentry>
<term>
<command>status</command>
- <optional><replaceable>LINK...</replaceable></optional>
+ <optional><replaceable>LINK…</replaceable></optional>
</term>
<listitem>
@@ -150,7 +150,7 @@
<varlistentry>
<term>
<command>lldp</command>
- <optional><replaceable>LINK...</replaceable></optional>
+ <optional><replaceable>LINK…</replaceable></optional>
</term>
<listitem>
diff --git a/man/sd_bus_error.xml b/man/sd_bus_error.xml
index c2d7ee389b..3091e1f019 100644
--- a/man/sd_bus_error.xml
+++ b/man/sd_bus_error.xml
@@ -68,7 +68,7 @@
<funcsynopsisinfo>typedef struct {
const char *name;
const char *message;
- ...
+ …
} sd_bus_error;</funcsynopsisinfo>
<para>
@@ -95,7 +95,7 @@
<paramdef>sd_bus_error *<parameter>e</parameter></paramdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>const char *<parameter>format</parameter></paramdef>
- <paramdef>...</paramdef>
+ <paramdef>…</paramdef>
</funcprototype>
<funcprototype>
@@ -116,7 +116,7 @@
<paramdef>sd_bus_error *<parameter>e</parameter></paramdef>
<paramdef>int <parameter>error</parameter></paramdef>
<paramdef>const char *<parameter>format</parameter></paramdef>
- <paramdef>...</paramdef>
+ <paramdef>…</paramdef>
</funcprototype>
<funcprototype>
diff --git a/man/sd_bus_error_add_map.xml b/man/sd_bus_error_add_map.xml
index 139bd77d8c..a1eda21ed5 100644
--- a/man/sd_bus_error_add_map.xml
+++ b/man/sd_bus_error_add_map.xml
@@ -58,7 +58,7 @@
<funcsynopsisinfo>typedef struct {
const char *name;
int code;
- ...
+ …
} sd_bus_error_map;</funcsynopsisinfo>
</funcsynopsis>
diff --git a/man/sd_bus_message_append.xml b/man/sd_bus_message_append.xml
index c222d0fd0e..132ce66434 100644
--- a/man/sd_bus_message_append.xml
+++ b/man/sd_bus_message_append.xml
@@ -58,7 +58,7 @@
<funcdef>int sd_bus_message_append</funcdef>
<paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
<paramdef>const char *<parameter>types</parameter></paramdef>
- <paramdef>...</paramdef>
+ <paramdef>…</paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
@@ -198,7 +198,7 @@ dictionary ::= "a" "{" basic_type complete_type "}"
</para>
<programlisting>sd_bus_message *m;
-...
+…
sd_bus_message_append(m, "s", "a string");</programlisting>
<para>Append all types of integers:</para>
diff --git a/man/sd_bus_path_encode.xml b/man/sd_bus_path_encode.xml
index 3088243e45..986eccc6a8 100644
--- a/man/sd_bus_path_encode.xml
+++ b/man/sd_bus_path_encode.xml
@@ -66,7 +66,7 @@
<funcdef>int <function>sd_bus_path_encode_many</function></funcdef>
<paramdef>char **<parameter>out</parameter></paramdef>
<paramdef>const char *<parameter>path_template</parameter></paramdef>
- <paramdef>...</paramdef>
+ <paramdef>…</paramdef>
</funcprototype>
<funcprototype>
@@ -80,7 +80,7 @@
<funcdef>int <function>sd_bus_path_decode_many</function></funcdef>
<paramdef>const char *<parameter>path</parameter></paramdef>
<paramdef>const char *<parameter>path_template</parameter></paramdef>
- <paramdef>...</paramdef>
+ <paramdef>…</paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
diff --git a/man/sd_event_add_defer.xml b/man/sd_event_add_defer.xml
index d9ebd3b179..ab28b330fe 100644
--- a/man/sd_event_add_defer.xml
+++ b/man/sd_event_add_defer.xml
@@ -153,7 +153,7 @@
<refsect1>
<title>Return Value</title>
- <para>On success, this functions return 0 or a positive
+ <para>On success, these functions return 0 or a positive
integer. On failure, they return a negative errno-style error
code.</para>
</refsect1>
diff --git a/man/sd_event_source_set_prepare.xml b/man/sd_event_source_set_prepare.xml
index 24861d01d9..ee61d23983 100644
--- a/man/sd_event_source_set_prepare.xml
+++ b/man/sd_event_source_set_prepare.xml
@@ -76,10 +76,11 @@
specified as <parameter>callback</parameter> will be invoked
immediately before the event loop goes to sleep to wait for
incoming events. It is invoked with the user data pointer passed
- when the event source was created. The callback function may be
- used to reconfigure the precise events to wait for. If the
- <parameter>callback</parameter> parameter is passed as NULL the
- callback function is reset. </para>
+ when the event source was created. The event source will be disabled
+ if the callback function returns a negative error code. The callback
+ function may be used to reconfigure the precise events to wait for.
+ If the <parameter>callback</parameter> parameter is passed as NULL
+ the callback function is reset. </para>
<para>Event source objects have no preparation callback associated
when they are first created with calls such as
diff --git a/man/sd_id128_get_machine.xml b/man/sd_id128_get_machine.xml
index 9a86c24aed..3938c6d836 100644
--- a/man/sd_id128_get_machine.xml
+++ b/man/sd_id128_get_machine.xml
@@ -44,6 +44,7 @@
<refnamediv>
<refname>sd_id128_get_machine</refname>
+ <refname>sd_id128_get_machine_app_specific</refname>
<refname>sd_id128_get_boot</refname>
<refname>sd_id128_get_invocation</refname>
<refpurpose>Retrieve 128-bit IDs</refpurpose>
@@ -59,6 +60,12 @@
</funcprototype>
<funcprototype>
+ <funcdef>int <function>sd_id128_get_machine_app_specific</function></funcdef>
+ <paramdef>sd_id128_t <parameter>app_id</parameter></paramdef>
+ <paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
<funcdef>int <function>sd_id128_get_boot</function></funcdef>
<paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
</funcprototype>
@@ -74,11 +81,22 @@
<refsect1>
<title>Description</title>
- <para><function>sd_id128_get_machine()</function> returns the
- machine ID of the executing host. This reads and parses the
- <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- file. This function caches the machine ID internally to make
- retrieving the machine ID a cheap operation.</para>
+ <para><function>sd_id128_get_machine()</function> returns the machine ID of the executing host. This reads and
+ parses the <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ file. This function caches the machine ID internally to make retrieving the machine ID a cheap operation. This ID
+ may be used wherever a unique identifier for the local system is needed. However, it is recommended to use this ID
+ as-is only in trusted environments. In untrusted environments it is recommended to derive an application specific
+ ID from this machine ID, in an irreversable (cryptographically secure) way. To make this easy
+ <function>sd_id128_get_machine_app_specific()</function> is provided, see below.</para>
+
+ <para><function>sd_id128_get_machine_app_specific()</function> is similar to
+ <function>sd_id128_get_machine()</function>, but retrieves a machine ID that is specific to the application that is
+ identified by the indicated application ID. It is recommended to use this function instead of
+ <function>sd_id128_get_machine()</function> when passing an ID to untrusted environments, in order to make sure
+ that the original machine ID may not be determined externally. The application-specific ID should be generated via
+ a tool like <command>journalctl --new-id128</command>, and may be compiled into the application. This function will
+ return the same application-specific ID for each combination of machine ID and application ID. Internally, this
+ function calculates HMAC-SHA256 of the application ID, keyed by the machine ID.</para>
<para><function>sd_id128_get_boot()</function> returns the boot ID
of the executing kernel. This reads and parses the
@@ -95,10 +113,10 @@
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details. The
ID is cached internally. In future a different mechanism to determine the invocation ID may be added.</para>
- <para>Note that <function>sd_id128_get_boot()</function> and <function>sd_id128_get_invocation()</function> always
- return UUID v4 compatible IDs. <function>sd_id128_get_machine()</function> will also return a UUID v4-compatible
- ID on new installations but might not on older. It is possible to convert the machine ID into a UUID v4-compatible
- one. For more information, see
+ <para>Note that <function>sd_id128_get_machine_app_specific()</function>, <function>sd_id128_get_boot()</function>
+ and <function>sd_id128_get_invocation()</function> always return UUID v4 compatible IDs.
+ <function>sd_id128_get_machine()</function> will also return a UUID v4-compatible ID on new installations but might
+ not on older. It is possible to convert the machine ID into a UUID v4-compatible one. For more information, see
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
<para>For more information about the <literal>sd_id128_t</literal>
@@ -117,13 +135,36 @@
<refsect1>
<title>Notes</title>
- <para>The <function>sd_id128_get_machine()</function>, <function>sd_id128_get_boot()</function> and
- <function>sd_id128_get_invocation()</function> interfaces are available as a shared library, which can be compiled
- and linked to with the <literal>libsystemd</literal> <citerefentry
+ <para>The <function>sd_id128_get_machine()</function>, <function>sd_id128_get_machine_app_specific()</function>
+ <function>sd_id128_get_boot()</function> and <function>sd_id128_get_invocation()</function> interfaces are
+ available as a shared library, which can be compiled and linked to with the
+ <literal>libsystemd</literal> <citerefentry
project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para>
</refsect1>
<refsect1>
+ <title>Examples</title>
+
+ <example>
+ <title>Application-specific machine ID</title>
+
+ <para>Here's a simple example for an application specific machine ID:</para>
+
+ <programlisting>#include &lt;systemd/sd-id128.h&gt;
+#include &lt;stdio.h&gt;
+
+#define OUR_APPLICATION_ID SD_ID128_MAKE(c2,73,27,73,23,db,45,4e,a6,3b,b9,6e,79,b5,3e,97)
+
+int main(int argc, char *argv[]) {
+ sd_id128_t id;
+ sd_id128_get_machine_app_specific(OUR_APPLICATION_ID, &amp;id);
+ printf("Our application ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(id));
+ return 0;
+}</programlisting>
+ </example>
+ </refsect1>
+
+ <refsect1>
<title>See Also</title>
<para>
diff --git a/man/sd_is_fifo.xml b/man/sd_is_fifo.xml
index 7ff02cbfec..991c7f8bd8 100644
--- a/man/sd_is_fifo.xml
+++ b/man/sd_is_fifo.xml
@@ -118,10 +118,10 @@
<parameter>family</parameter> parameter is not
<constant>AF_UNSPEC</constant>, it is checked whether the socket
is of the specified family (<constant>AF_UNIX</constant>,
- <constant>AF_INET</constant>, ...). If the <parameter>type</parameter>
+ <constant>AF_INET</constant>, …). If the <parameter>type</parameter>
parameter is not 0, it is checked whether the socket is of the
specified type (<constant>SOCK_STREAM</constant>,
- <constant>SOCK_DGRAM</constant>, ...). If the
+ <constant>SOCK_DGRAM</constant>, …). If the
<parameter>listening</parameter> parameter is positive, it is
checked whether the socket is in accepting mode, i.e.
<function>listen()</function> has been called for it. If
diff --git a/man/sd_journal_add_match.xml b/man/sd_journal_add_match.xml
index 7c64329aed..2294b43643 100644
--- a/man/sd_journal_add_match.xml
+++ b/man/sd_journal_add_match.xml
@@ -190,7 +190,7 @@
message ID 03bb1dab98ab4ecfbf6fff2738bdd964 coming from any
service (this example lacks the necessary error checking):</para>
- <programlisting>...
+ <programlisting>…
int add_matches(sd_journal *j) {
sd_journal_add_match(j, "_SYSTEMD_UNIT=avahi-daemon.service", 0);
sd_journal_add_match(j, "PRIORITY=0", 0);
diff --git a/man/sd_journal_get_data.xml b/man/sd_journal_get_data.xml
index 1321114de0..01e436e70d 100644
--- a/man/sd_journal_get_data.xml
+++ b/man/sd_journal_get_data.xml
@@ -207,14 +207,14 @@
iterate through all fields of the current journal
entry:</para>
- <programlisting>...
+ <programlisting>…
int print_fields(sd_journal *j) {
const void *data;
size_t length;
SD_JOURNAL_FOREACH_DATA(j, data, length)
printf("%.*s\n", (int) length, data);
}
-...</programlisting>
+…</programlisting>
</refsect1>
diff --git a/man/sd_journal_print.xml b/man/sd_journal_print.xml
index 2d8dd635aa..5538805e65 100644
--- a/man/sd_journal_print.xml
+++ b/man/sd_journal_print.xml
@@ -60,7 +60,7 @@
<funcdef>int <function>sd_journal_print</function></funcdef>
<paramdef>int <parameter>priority</parameter></paramdef>
<paramdef>const char *<parameter>format</parameter></paramdef>
- <paramdef>...</paramdef>
+ <paramdef>…</paramdef>
</funcprototype>
<funcprototype>
@@ -73,7 +73,7 @@
<funcprototype>
<funcdef>int <function>sd_journal_send</function></funcdef>
<paramdef>const char *<parameter>format</parameter></paramdef>
- <paramdef>...</paramdef>
+ <paramdef>…</paramdef>
</funcprototype>
<funcprototype>
diff --git a/man/sd_listen_fds.xml b/man/sd_listen_fds.xml
index 93bf8d853f..c1c2423884 100644
--- a/man/sd_listen_fds.xml
+++ b/man/sd_listen_fds.xml
@@ -79,7 +79,7 @@
received, zero is returned. The first file descriptor may be found
at file descriptor number 3
(i.e. <constant>SD_LISTEN_FDS_START</constant>), the remaining
- descriptors follow at 4, 5, 6, ..., if any.</para>
+ descriptors follow at 4, 5, 6, …, if any.</para>
<para>If a daemon receives more than one file descriptor, they
will be passed in the same order as configured in the systemd
diff --git a/man/sd_notify.xml b/man/sd_notify.xml
index 94542b80b8..6e98041912 100644
--- a/man/sd_notify.xml
+++ b/man/sd_notify.xml
@@ -66,7 +66,7 @@
<funcdef>int <function>sd_notifyf</function></funcdef>
<paramdef>int <parameter>unset_environment</parameter></paramdef>
<paramdef>const char *<parameter>format</parameter></paramdef>
- <paramdef>...</paramdef>
+ <paramdef>…</paramdef>
</funcprototype>
<funcprototype>
@@ -81,7 +81,7 @@
<paramdef>pid_t <parameter>pid</parameter></paramdef>
<paramdef>int <parameter>unset_environment</parameter></paramdef>
<paramdef>const char *<parameter>format</parameter></paramdef>
- <paramdef>...</paramdef>
+ <paramdef>…</paramdef>
</funcprototype>
<funcprototype>
@@ -152,7 +152,7 @@
</varlistentry>
<varlistentry>
- <term>STATUS=...</term>
+ <term>STATUS=…</term>
<listitem><para>Passes a single-line UTF-8 status string back
to the service manager that describes the service state. This
@@ -160,11 +160,11 @@
state feedback, fsck-like programs could pass completion
percentages and failing programs could pass a human-readable
error message. Example: <literal>STATUS=Completed 66% of file
- system check...</literal></para></listitem>
+ system check…</literal></para></listitem>
</varlistentry>
<varlistentry>
- <term>ERRNO=...</term>
+ <term>ERRNO=…</term>
<listitem><para>If a service fails, the errno-style error
code, formatted as string. Example: <literal>ERRNO=2</literal>
@@ -172,7 +172,7 @@
</varlistentry>
<varlistentry>
- <term>BUSERROR=...</term>
+ <term>BUSERROR=…</term>
<listitem><para>If a service fails, the D-Bus error-style
error code. Example:
@@ -180,7 +180,7 @@
</varlistentry>
<varlistentry>
- <term>MAINPID=...</term>
+ <term>MAINPID=…</term>
<listitem><para>The main process ID (PID) of the service, in
case the service manager did not fork off the process itself.
@@ -227,7 +227,7 @@
</varlistentry>
<varlistentry>
- <term>FDNAME=...</term>
+ <term>FDNAME=…</term>
<listitem><para>When used in combination with
<varname>FDSTORE=1</varname>, specifies a name for the
@@ -248,7 +248,7 @@
</varlistentry>
<varlistentry>
- <term>WATCHDOG_USEC=...</term>
+ <term>WATCHDOG_USEC=…</term>
<listitem><para>Reset <varname>watchdog_usec</varname> value during runtime.
Notice that this is not available when using <function>sd_event_set_watchdog()</function>
@@ -362,7 +362,7 @@
initialization:</para>
<programlisting>sd_notifyf(0, "READY=1\n"
- "STATUS=Processing requests...\n"
+ "STATUS=Processing requests…\n"
"MAINPID=%lu",
(unsigned long) getpid());</programlisting>
</example>
diff --git a/man/systemctl.xml b/man/systemctl.xml
index 08c3a268bd..68c8546189 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -664,7 +664,7 @@
<variablelist>
<varlistentry>
- <term><command>list-units <optional><replaceable>PATTERN</replaceable>...</optional></command></term>
+ <term><command>list-units <optional><replaceable>PATTERN</replaceable>…</optional></command></term>
<listitem>
<para>List units that <command>systemd</command> currently has in memory. This includes units that are
@@ -680,7 +680,7 @@
</varlistentry>
<varlistentry>
- <term><command>list-sockets <optional><replaceable>PATTERN</replaceable>...</optional></command></term>
+ <term><command>list-sockets <optional><replaceable>PATTERN</replaceable>…</optional></command></term>
<listitem>
<para>List socket units currently in memory, ordered by listening address. If one or more
@@ -689,7 +689,7 @@
<programlisting>
LISTEN UNIT ACTIVATES
/dev/initctl systemd-initctl.socket systemd-initctl.service
-...
+…
[::]:22 sshd.socket sshd.service
kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
@@ -703,7 +703,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>
<varlistentry>
- <term><command>list-timers <optional><replaceable>PATTERN</replaceable>...</optional></command></term>
+ <term><command>list-timers <optional><replaceable>PATTERN</replaceable>…</optional></command></term>
<listitem>
<para>List timer units currently in memory, ordered by the time they elapse next. If one or more
@@ -715,7 +715,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>
<varlistentry>
- <term><command>start <replaceable>PATTERN</replaceable>...</command></term>
+ <term><command>start <replaceable>PATTERN</replaceable>…</command></term>
<listitem>
<para>Start (activate) one or more units specified on the
@@ -729,7 +729,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</listitem>
</varlistentry>
<varlistentry>
- <term><command>stop <replaceable>PATTERN</replaceable>...</command></term>
+ <term><command>stop <replaceable>PATTERN</replaceable>…</command></term>
<listitem>
<para>Stop (deactivate) one or more units specified on the
@@ -737,7 +737,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</listitem>
</varlistentry>
<varlistentry>
- <term><command>reload <replaceable>PATTERN</replaceable>...</command></term>
+ <term><command>reload <replaceable>PATTERN</replaceable>…</command></term>
<listitem>
<para>Asks all units listed on the command line to reload
@@ -757,7 +757,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>
<varlistentry>
- <term><command>restart <replaceable>PATTERN</replaceable>...</command></term>
+ <term><command>restart <replaceable>PATTERN</replaceable>…</command></term>
<listitem>
<para>Restart one or more units specified on the command
@@ -766,7 +766,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</listitem>
</varlistentry>
<varlistentry>
- <term><command>try-restart <replaceable>PATTERN</replaceable>...</command></term>
+ <term><command>try-restart <replaceable>PATTERN</replaceable>…</command></term>
<listitem>
<para>Restart one or more units specified on the command
@@ -777,7 +777,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</listitem>
</varlistentry>
<varlistentry>
- <term><command>reload-or-restart <replaceable>PATTERN</replaceable>...</command></term>
+ <term><command>reload-or-restart <replaceable>PATTERN</replaceable>…</command></term>
<listitem>
<para>Reload one or more units if they support it. If not,
@@ -786,7 +786,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</listitem>
</varlistentry>
<varlistentry>
- <term><command>try-reload-or-restart <replaceable>PATTERN</replaceable>...</command></term>
+ <term><command>try-reload-or-restart <replaceable>PATTERN</replaceable>…</command></term>
<listitem>
<para>Reload one or more units if they support it. If not,
@@ -818,7 +818,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</listitem>
</varlistentry>
<varlistentry>
- <term><command>kill <replaceable>PATTERN</replaceable>...</command></term>
+ <term><command>kill <replaceable>PATTERN</replaceable>…</command></term>
<listitem>
<para>Send a signal to one or more processes of the
@@ -828,7 +828,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</listitem>
</varlistentry>
<varlistentry>
- <term><command>is-active <replaceable>PATTERN</replaceable>...</command></term>
+ <term><command>is-active <replaceable>PATTERN</replaceable>…</command></term>
<listitem>
<para>Check whether any of the specified units are active
@@ -840,7 +840,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</listitem>
</varlistentry>
<varlistentry>
- <term><command>is-failed <replaceable>PATTERN</replaceable>...</command></term>
+ <term><command>is-failed <replaceable>PATTERN</replaceable>…</command></term>
<listitem>
<para>Check whether any of the specified units are in a
@@ -852,7 +852,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</listitem>
</varlistentry>
<varlistentry>
- <term><command>status</command> <optional><replaceable>PATTERN</replaceable>...|<replaceable>PID</replaceable>...]</optional></term>
+ <term><command>status</command> <optional><replaceable>PATTERN</replaceable>…|<replaceable>PID</replaceable>…]</optional></term>
<listitem>
<para>Show terse runtime status information about one or
@@ -879,7 +879,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</listitem>
</varlistentry>
<varlistentry>
- <term><command>show</command> <optional><replaceable>PATTERN</replaceable>...|<replaceable>JOB</replaceable>...</optional></term>
+ <term><command>show</command> <optional><replaceable>PATTERN</replaceable>…|<replaceable>JOB</replaceable>…</optional></term>
<listitem>
<para>Show properties of one or more units, jobs, or the
@@ -896,7 +896,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</listitem>
</varlistentry>
<varlistentry>
- <term><command>cat <replaceable>PATTERN</replaceable>...</command></term>
+ <term><command>cat <replaceable>PATTERN</replaceable>…</command></term>
<listitem>
<para>Show backing files of one or more units. Prints the
@@ -910,7 +910,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</listitem>
</varlistentry>
<varlistentry>
- <term><command>set-property <replaceable>NAME</replaceable> <replaceable>ASSIGNMENT</replaceable>...</command></term>
+ <term><command>set-property <replaceable>NAME</replaceable> <replaceable>ASSIGNMENT</replaceable>…</command></term>
<listitem>
<para>Set the specified unit properties at runtime where
@@ -941,7 +941,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>
<varlistentry>
- <term><command>help <replaceable>PATTERN</replaceable>...|<replaceable>PID</replaceable>...</command></term>
+ <term><command>help <replaceable>PATTERN</replaceable>…|<replaceable>PID</replaceable>…</command></term>
<listitem>
<para>Show manual pages for one or more units, if
@@ -951,7 +951,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>
<varlistentry>
- <term><command>reset-failed [<replaceable>PATTERN</replaceable>...]</command></term>
+ <term><command>reset-failed [<replaceable>PATTERN</replaceable>…]</command></term>
<listitem>
<para>Reset the <literal>failed</literal> state of the
@@ -999,7 +999,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
<variablelist>
<varlistentry>
- <term><command>list-unit-files <optional><replaceable>PATTERN...</replaceable></optional></command></term>
+ <term><command>list-unit-files <optional><replaceable>PATTERN…</replaceable></optional></command></term>
<listitem>
<para>List unit files installed on the system, in combination with their enablement state (as reported by
@@ -1010,8 +1010,8 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>
<varlistentry>
- <term><command>enable <replaceable>NAME</replaceable>...</command></term>
- <term><command>enable <replaceable>PATH</replaceable>...</command></term>
+ <term><command>enable <replaceable>NAME</replaceable>…</command></term>
+ <term><command>enable <replaceable>PATH</replaceable>…</command></term>
<listitem>
<para>Enable one or more units or unit instances. This will create a set of symlinks, as encoded in the
@@ -1061,7 +1061,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>
<varlistentry>
- <term><command>disable <replaceable>NAME</replaceable>...</command></term>
+ <term><command>disable <replaceable>NAME</replaceable>…</command></term>
<listitem>
<para>Disables one or more units. This removes all symlinks to the unit files backing the specified units
@@ -1093,7 +1093,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>
<varlistentry>
- <term><command>reenable <replaceable>NAME</replaceable>...</command></term>
+ <term><command>reenable <replaceable>NAME</replaceable>…</command></term>
<listitem>
<para>Reenable one or more units, as specified on the command line. This is a combination of
@@ -1104,7 +1104,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>
<varlistentry>
- <term><command>preset <replaceable>NAME</replaceable>...</command></term>
+ <term><command>preset <replaceable>NAME</replaceable>…</command></term>
<listitem>
<para>Reset the enable/disable status one or more unit files, as specified on
@@ -1142,7 +1142,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>
<varlistentry>
- <term><command>is-enabled <replaceable>NAME</replaceable>...</command></term>
+ <term><command>is-enabled <replaceable>NAME</replaceable>…</command></term>
<listitem>
<para>Checks whether any of the specified unit files are
@@ -1229,7 +1229,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>
<varlistentry>
- <term><command>mask <replaceable>NAME</replaceable>...</command></term>
+ <term><command>mask <replaceable>NAME</replaceable>…</command></term>
<listitem>
<para>Mask one or more units, as specified on the command line. This will link these unit files to
@@ -1243,7 +1243,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>
<varlistentry>
- <term><command>unmask <replaceable>NAME</replaceable>...</command></term>
+ <term><command>unmask <replaceable>NAME</replaceable>…</command></term>
<listitem>
<para>Unmask one or more unit files, as specified on the command line. This will undo the effect of
@@ -1253,7 +1253,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>
<varlistentry>
- <term><command>link <replaceable>PATH</replaceable>...</command></term>
+ <term><command>link <replaceable>PATH</replaceable>…</command></term>
<listitem>
<para>Link a unit file that is not in the unit file search paths into the unit file search path. This
@@ -1264,7 +1264,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>
<varlistentry>
- <term><command>revert <replaceable>NAME</replaceable>...</command></term>
+ <term><command>revert <replaceable>NAME</replaceable>…</command></term>
<listitem>
<para>Revert one or more unit files to their vendor versions. This command removes drop-in configuration
@@ -1287,9 +1287,9 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
<varlistentry>
<term><command>add-wants <replaceable>TARGET</replaceable>
- <replaceable>NAME</replaceable>...</command></term>
+ <replaceable>NAME</replaceable>…</command></term>
<term><command>add-requires <replaceable>TARGET</replaceable>
- <replaceable>NAME</replaceable>...</command></term>
+ <replaceable>NAME</replaceable>…</command></term>
<listitem>
<para>Adds <literal>Wants=</literal> or <literal>Requires=</literal>
@@ -1305,7 +1305,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>
<varlistentry>
- <term><command>edit <replaceable>NAME</replaceable>...</command></term>
+ <term><command>edit <replaceable>NAME</replaceable>…</command></term>
<listitem>
<para>Edit a drop-in snippet or a whole replacement file if
@@ -1372,7 +1372,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
<variablelist>
<varlistentry>
- <term><command>list-machines <optional><replaceable>PATTERN</replaceable>...</optional></command></term>
+ <term><command>list-machines <optional><replaceable>PATTERN</replaceable>…</optional></command></term>
<listitem>
<para>List the host and all running local containers with
@@ -1390,7 +1390,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
<variablelist>
<varlistentry>
- <term><command>list-jobs <optional><replaceable>PATTERN...</replaceable></optional></command></term>
+ <term><command>list-jobs <optional><replaceable>PATTERN…</replaceable></optional></command></term>
<listitem>
<para>List jobs that are in progress. If one or more
@@ -1403,7 +1403,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</listitem>
</varlistentry>
<varlistentry>
- <term><command>cancel <replaceable>JOB</replaceable>...</command></term>
+ <term><command>cancel <replaceable>JOB</replaceable>…</command></term>
<listitem>
<para>Cancel one or more jobs specified on the command line
@@ -1430,7 +1430,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</listitem>
</varlistentry>
<varlistentry>
- <term><command>set-environment <replaceable>VARIABLE=VALUE</replaceable>...</command></term>
+ <term><command>set-environment <replaceable>VARIABLE=VALUE</replaceable>…</command></term>
<listitem>
<para>Set one or more systemd manager environment variables,
@@ -1438,7 +1438,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</listitem>
</varlistentry>
<varlistentry>
- <term><command>unset-environment <replaceable>VARIABLE</replaceable>...</command></term>
+ <term><command>unset-environment <replaceable>VARIABLE</replaceable>…</command></term>
<listitem>
<para>Unset one or more systemd manager environment
@@ -1451,7 +1451,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
<varlistentry>
<term>
<command>import-environment</command>
- <optional><replaceable>VARIABLE...</replaceable></optional>
+ <optional><replaceable>VARIABLE…</replaceable></optional>
</term>
<listitem>
@@ -1759,7 +1759,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
<title>Parameter Syntax</title>
<para>Unit commands listed above take either a single unit name (designated as <replaceable>NAME</replaceable>),
- or multiple unit specifications (designated as <replaceable>PATTERN</replaceable>...). In the first case, the
+ or multiple unit specifications (designated as <replaceable>PATTERN</replaceable>…). In the first case, the
unit name with or without a suffix must be given. If the suffix is not specified (unit name is "abbreviated"),
systemctl will append a suitable suffix, <literal>.service</literal> by default, and a type-specific suffix in
case of commands which operate only on specific unit types. For example,
diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml
index 634e16b5f4..7d5410d693 100644
--- a/man/systemd-analyze.xml
+++ b/man/systemd-analyze.xml
@@ -105,7 +105,7 @@
<command>systemd-analyze</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">syscall-filter</arg>
- <arg choice="opt"><replaceable>SET</replaceable>...</arg>
+ <arg choice="opt"><replaceable>SET</replaceable>…</arg>
</cmdsynopsis>
<cmdsynopsis>
<command>systemd-analyze</command>
@@ -140,7 +140,7 @@
initialization of another service to complete.</para>
<para><command>systemd-analyze critical-chain
- [<replaceable>UNIT...</replaceable>]</command> prints a tree of
+ [<replaceable>UNIT…</replaceable>]</command> prints a tree of
the time-critical chain of units (for each of the specified
<replaceable>UNIT</replaceable>s or for the default target
otherwise). The time after the unit is active or started is
@@ -187,7 +187,7 @@
<option>--log-target=</option>, described in
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>).</para>
- <para><command>systemd-analyze syscall-filter <optional><replaceable>SET</replaceable>...</optional></command>
+ <para><command>systemd-analyze syscall-filter <optional><replaceable>SET</replaceable>…</optional></command>
will list system calls contained in the specified system call set <replaceable>SET</replaceable>,
or all known sets if no sets are specified. Argument <replaceable>SET</replaceable> must include
the <literal>@</literal> prefix.</para>
@@ -333,7 +333,7 @@ $ eog targets.svg</programlisting>
</para></listitem>
<listitem><para>missing dependencies which are required to start
- the given unit, </para></listitem>
+ the given unit,</para></listitem>
<listitem><para>man pages listed in
<varname>Documentation=</varname> which are not found in the
diff --git a/man/systemd-delta.xml b/man/systemd-delta.xml
index 99709604aa..6628213209 100644
--- a/man/systemd-delta.xml
+++ b/man/systemd-delta.xml
@@ -63,7 +63,7 @@
compare configuration files that override other configuration
files. Files in <filename>/etc</filename> have highest priority,
files in <filename>/run</filename> have the second highest
- priority, ..., files in <filename>/lib</filename> have lowest
+ priority, …, files in <filename>/lib</filename> have lowest
priority. Files in a directory with higher priority override files
with the same name in directories of lower priority. In addition,
certain configuration files can have <literal>.d</literal>
@@ -82,7 +82,7 @@
suffix. Either is optional. The prefix must be one of the
directories containing configuration files
(<filename>/etc</filename>, <filename>/run</filename>,
- <filename>/usr/lib</filename>, ...). If it is given, only
+ <filename>/usr/lib</filename>, …). If it is given, only
overriding files contained in this directory will be shown.
Otherwise, all overriding files will be shown. The suffix must be
a name of a subdirectory containing configuration files like
diff --git a/man/systemd-journal-gatewayd.service.xml b/man/systemd-journal-gatewayd.service.xml
index 2cb114f6e3..22294542f3 100644
--- a/man/systemd-journal-gatewayd.service.xml
+++ b/man/systemd-journal-gatewayd.service.xml
@@ -128,7 +128,7 @@
</varlistentry>
<varlistentry>
- <term><uri>/entries[?option1&amp;option2=value...]</uri></term>
+ <term><uri>/entries[?option1&amp;option2=value…]</uri></term>
<listitem><para>Retrieval of events in various formats.</para>
@@ -157,7 +157,7 @@
"hostname" : "fedora",
"os_pretty_name" : "Fedora 19 (Rawhide)",
"virtualization" : "kvm",
- ...}</programlisting>
+ …}</programlisting>
</para>
</listitem>
</varlistentry>
diff --git a/man/systemd-notify.xml b/man/systemd-notify.xml
index a5f4077166..4a8e119eb6 100644
--- a/man/systemd-notify.xml
+++ b/man/systemd-notify.xml
@@ -112,7 +112,7 @@
<listitem><para>Send a free-form status string for the daemon
to the init systemd. This option takes the status string as
argument. This is equivalent to <command>systemd-notify
- STATUS=...</command>. For details about the semantics of this
+ STATUS=…</command>. For details about the semantics of this
option see
<citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem>
</varlistentry>
@@ -158,15 +158,15 @@
<programlisting>#!/bin/bash
mkfifo /tmp/waldo
-systemd-notify --ready --status="Waiting for data..."
+systemd-notify --ready --status="Waiting for data…"
while : ; do
read a &lt; /tmp/waldo
systemd-notify --status="Processing $a"
- # Do something with $a ...
+ # Do something with $a …
- systemd-notify --status="Waiting for data..."
+ systemd-notify --status="Waiting for data…"
done</programlisting>
</example>
</refsect1>
diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index dbbf9890c8..2bc81ea1aa 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -235,17 +235,34 @@
identified by the partition types defined by the <ulink
url="http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/">Discoverable
Partitions Specification</ulink>.</para></listitem>
+
+ <listitem><para>No partition table, and a single file system spanning the whole image.</para></listitem>
</itemizedlist>
<para>On GPT images, if an EFI System Partition (ESP) is discovered, it is automatically mounted to
<filename>/efi</filename> (or <filename>/boot</filename> as fallback) in case a directory by this name exists
and is empty.</para>
+ <para>Partitions encrypted with LUKS are automatically decrypted. Also, on GPT images dm-verity data integrity
+ hash partitions are set up if the root hash for them is specified using the <option>--root-hash=</option>
+ option.</para>
+
<para>Any other partitions, such as foreign partitions or swap partitions are not mounted. May not be specified
together with <option>--directory=</option>, <option>--template=</option>.</para></listitem>
</varlistentry>
<varlistentry>
+ <term><option>--root-hash=</option></term>
+
+ <listitem><para>Takes a data integrity (dm-verity) root hash specified in hexadecimal. This option enables data
+ integrity checks using dm-verity, if the used image contains the appropriate integrity data (see above). The
+ specified hash must match the root hash of integrity data, and is usually at least 256bits (and hence 64
+ hexadecimal characters) long (in case of SHA256 for example). If this option is not specified, but a file with
+ the <filename>.roothash</filename> suffix is found next to the image file, bearing otherwise the same name the
+ root hash is read from it and automatically used.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>-a</option></term>
<term><option>--as-pid2</option></term>
@@ -740,21 +757,19 @@
<term><option>--bind=</option></term>
<term><option>--bind-ro=</option></term>
- <listitem><para>Bind mount a file or directory from the host
- into the container. Takes one of: a path argument — in which
- case the specified path will be mounted from the host to the
- same path in the container —, or a colon-separated pair of
- paths — in which case the first specified path is the source
- in the host, and the second path is the destination in the
- container —, or a colon-separated triple of source path,
- destination path and mount options. Mount options are
- comma-separated and currently, only "rbind" and "norbind"
- are allowed. Defaults to "rbind". Backslash escapes are interpreted, so
- <literal>\:</literal> may be used to embed colons in either path.
- This option may be specified multiple times for
- creating multiple independent bind mount points. The
- <option>--bind-ro=</option> option creates read-only bind
- mounts.</para></listitem>
+ <listitem><para>Bind mount a file or directory from the host into the container. Takes one of: a path
+ argument — in which case the specified path will be mounted from the host to the same path in the container, or
+ a colon-separated pair of paths — in which case the first specified path is the source in the host, and the
+ second path is the destination in the container, or a colon-separated triple of source path, destination path
+ and mount options. The source path may optionally be prefixed with a <literal>+</literal> character. If so, the
+ source path is taken relative to the image's root directory. This permits setting up bind mounts within the
+ container image. The source path may be specified as empty string, in which case a temporary directory below
+ the host's <filename>/var/tmp</filename> directory is used. It is automatically removed when the container is
+ shut down. Mount options are comma-separated and currently, only <option>rbind</option> and
+ <option>norbind</option> are allowed, controlling whether to create a recursive or a regular bind
+ mount. Defaults to "rbind". Backslash escapes are interpreted, so <literal>\:</literal> may be used to embed
+ colons in either path. This option may be specified multiple times for creating multiple independent bind
+ mount points. The <option>--bind-ro=</option> option creates read-only bind mounts.</para></listitem>
</varlistentry>
<varlistentry>
@@ -808,6 +823,14 @@
point for the overlay file system in the container. At least
two paths have to be specified.</para>
+ <para>The source paths may optionally be prefixed with <literal>+</literal> character. If so they are taken
+ relative to the image's root directory. The uppermost source path may also be specified as empty string, in
+ which case a temporary directory below the host's <filename>/var/tmp</filename> is used. The directory is
+ removed automatically when the container is shut down. This behaviour is useful in order to make read-only
+ container directories writable while the container is running. For example, use the
+ <literal>--overlay=+/var::/var</literal> option in order to automatically overlay a writable temporary
+ directory on a read-only <filename>/var</filename> directory.</para>
+
<para>For details about overlay file systems, see <ulink
url="https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt">overlayfs.txt</ulink>. Note
that the semantics of overlay file systems are substantially
diff --git a/man/systemd-resolve.xml b/man/systemd-resolve.xml
index 2bc917ac26..bfd5a68fd9 100644
--- a/man/systemd-resolve.xml
+++ b/man/systemd-resolve.xml
@@ -120,7 +120,7 @@
originating from local, trusted sources is also reported authenticated, including resolution of the local host
name, the <literal>localhost</literal> host name or all data from <filename>/etc/hosts</filename>.</para>
- <para>The <option>--type=</option> switch may be used to specify a DNS resource record type (A, AAAA, SOA, MX, ...) in
+ <para>The <option>--type=</option> switch may be used to specify a DNS resource record type (A, AAAA, SOA, MX, …) in
order to request a specific DNS resource record, instead of the address or reverse address lookups.
The special value <literal>help</literal> may be used to list known values.</para>
@@ -356,7 +356,7 @@ _xmpp-server._tcp/gmail.com: alt1.xmpp-server.l.google.com:5269 [priority=20, we
173.194.210.125
alt4.xmpp-server.l.google.com:5269 [priority=20, weight=0]
173.194.65.125
- ...
+ …
</programlisting>
</example>
@@ -367,7 +367,7 @@ _xmpp-server._tcp/gmail.com: alt1.xmpp-server.l.google.com:5269 [priority=20, we
d08ee310438ca124a6149ea5cc21b6313b390dce485576eff96f8722._openpgpkey.fedoraproject.org. IN OPENPGPKEY
mQINBFBHPMsBEACeInGYJCb+7TurKfb6wGyTottCDtiSJB310i37/6ZYoeIay/5soJjlMyf
MFQ9T2XNT/0LM6gTa0MpC1st9LnzYTMsT6tzRly1D1UbVI6xw0g0vE5y2Cjk3xUwAynCsSs
- ...
+ …
</programlisting>
</example>
diff --git a/man/systemd-socket-activate.xml b/man/systemd-socket-activate.xml
index 1c0619a840..356bc10359 100644
--- a/man/systemd-socket-activate.xml
+++ b/man/systemd-socket-activate.xml
@@ -136,7 +136,7 @@
</varlistentry>
<varlistentry>
- <term><option>--fdname=</option><replaceable>NAME</replaceable><optional>:<replaceable>NAME</replaceable>...</optional></term>
+ <term><option>--fdname=</option><replaceable>NAME</replaceable><optional>:<replaceable>NAME</replaceable>…</optional></term>
<listitem><para>Specify names for the file descriptors passed. This is equivalent to setting
<varname>FileDescriptorName=</varname> in socket unit files, and enables use of
diff --git a/man/systemd-socket-proxyd.xml b/man/systemd-socket-proxyd.xml
index 804a8c38f1..74d9e1c124 100644
--- a/man/systemd-socket-proxyd.xml
+++ b/man/systemd-socket-proxyd.xml
@@ -127,10 +127,10 @@ PrivateNetwork=yes]]></programlisting>
<example>
<title>nginx.conf</title>
<programlisting>
-<![CDATA[[...]
+<![CDATA[[…]
server {
listen unix:/tmp/nginx.sock;
- [...]]]>
+ […]]]>
</programlisting>
</example>
<example>
@@ -169,10 +169,10 @@ PrivateNetwork=yes]]></programlisting>
</example>
<example>
<title>nginx.conf</title>
- <programlisting><![CDATA[[...]
+ <programlisting><![CDATA[[…]
server {
listen 8080;
- [...]]]></programlisting>
+ […]]]></programlisting>
</example>
<example>
<title>Enabling the proxy</title>
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index ab83876eba..f27e4a5c04 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -1806,23 +1806,32 @@
<title>Summary of possible service result variable values</title>
<tgroup cols='3'>
<colspec colname='result' />
- <colspec colname='status' />
<colspec colname='code' />
+ <colspec colname='status' />
<thead>
<row>
<entry><varname>$SERVICE_RESULT</varname></entry>
- <entry><varname>$EXIT_STATUS</varname></entry>
<entry><varname>$EXIT_CODE</varname></entry>
+ <entry><varname>$EXIT_STATUS</varname></entry>
</row>
</thead>
<tbody>
<row>
+ <entry morerows="1" valign="top"><literal>protocol</literal></entry>
+ <entry valign="top">not set</entry>
+ <entry>not set</entry>
+ </row>
+ <row>
+ <entry><literal>exited</literal></entry>
+ <entry><literal>0</literal></entry>
+ </row>
+
+ <row>
<entry morerows="1" valign="top"><literal>timeout</literal></entry>
<entry valign="top"><literal>killed</literal></entry>
<entry><literal>TERM</literal>, <literal>KILL</literal></entry>
</row>
-
<row>
<entry valign="top"><literal>exited</literal></entry>
<entry><literal>0</literal>, <literal>1</literal>, <literal>2</literal>, <literal
diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml
index ffb66e735b..a549ec83bd 100644
--- a/man/systemd.netdev.xml
+++ b/man/systemd.netdev.xml
@@ -512,7 +512,9 @@
<varlistentry>
<term><varname>ARPProxy=</varname></term>
<listitem>
- <para>A boolean. When true, enables ARP proxying.</para>
+ <para>A boolean. When true bridge-connected VXLAN tunnel endpoint
+ answers ARP requests from the local bridge on behalf of
+ remote Distributed Overlay Virtual Ethernet (DOVE) clients.</para>
</listitem>
</varlistentry>
<varlistentry>
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 99283813fd..c7083a4fe6 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -123,7 +123,10 @@
<listitem>
<para>A whitespace-separated list of shell-style globs
matching the persistent path, as exposed by the udev
- property <literal>ID_PATH</literal>.</para>
+ property <literal>ID_PATH</literal>. If the list is
+ prefixed with a "!", the test is inverted; i.e. it is
+ true when <literal>ID_PATH</literal> does not match any
+ item in the list.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -134,7 +137,8 @@
exposed by the udev property <literal>DRIVER</literal>
of its parent device, or if that is not set the driver
as exposed by <literal>ethtool -i</literal> of the
- device itself.</para>
+ device itself. If the list is prefixed with a "!", the
+ test is inverted.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -142,7 +146,8 @@
<listitem>
<para>A whitespace-separated list of shell-style globs
matching the device type, as exposed by the udev property
- <literal>DEVTYPE</literal>.</para>
+ <literal>DEVTYPE</literal>. If the list is prefixed with
+ a "!", the test is inverted.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -150,7 +155,8 @@
<listitem>
<para>A whitespace-separated list of shell-style globs
matching the device name, as exposed by the udev property
- <literal>INTERFACE</literal>.</para>
+ <literal>INTERFACE</literal>. If the list is prefixed
+ with a "!", the test is inverted.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -232,6 +238,18 @@
the network otherwise.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>Unmanaged=</varname></term>
+ <listitem>
+ <para>A boolean. When <literal>yes</literal>, no attempts are
+ made to bring up or configure matching links, equivalent to
+ when there are no matching network files. Defaults to
+ <literal>no</literal>.</para>
+ <para>This is useful for preventing later matching network
+ files from interfering with certain interfaces that are fully
+ controlled by other applications.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
@@ -596,47 +614,57 @@
<varlistentry>
<term><varname>Bridge=</varname></term>
<listitem>
- <para>The name of the bridge to add the link to.</para>
+ <para>The name of the bridge to add the link to. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ </para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Bond=</varname></term>
<listitem>
- <para>The name of the bond to add the link to.</para>
+ <para>The name of the bond to add the link to. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ </para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>VRF=</varname></term>
<listitem>
- <para>The name of the VRF to add the link to.</para>
+ <para>The name of the VRF to add the link to. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ </para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>VLAN=</varname></term>
<listitem>
- <para>The name of a VLAN to create on the link. This
- option may be specified more than once.</para>
+ <para>The name of a VLAN to create on the link. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ This option may be specified more than once.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>MACVLAN=</varname></term>
<listitem>
- <para>The name of a MACVLAN to create on the link. This
- option may be specified more than once.</para>
+ <para>The name of a MACVLAN to create on the link. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ This option may be specified more than once.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>VXLAN=</varname></term>
<listitem>
- <para>The name of a VXLAN to create on the link. This
- option may be specified more than once.</para>
+ <para>The name of a VXLAN to create on the link. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ This option may be specified more than once.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Tunnel=</varname></term>
<listitem>
- <para>The name of a Tunnel to create on the link. This
- option may be specified more than once.</para>
+ <para>The name of a Tunnel to create on the link. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ This option may be specified more than once.</para>
</listitem>
</varlistentry>
</variablelist>
@@ -1275,53 +1303,75 @@
</refsect1>
<refsect1>
- <title>Example</title>
+ <title>Examples</title>
<example>
- <title>/etc/systemd/network/50-static.network</title>
+ <title>Static network configuration</title>
- <programlisting>[Match]
+ <programlisting># /etc/systemd/network/50-static.network
+[Match]
Name=enp2s0
[Network]
Address=192.168.0.15/24
Gateway=192.168.0.1</programlisting>
+
+ <para>This brings interface <literal>enp2s0</literal> up with a static address. The
+ specified gateway will be used for a default route.</para>
</example>
<example>
- <title>/etc/systemd/network/80-dhcp.network</title>
+ <title>DHCP on ethernet links</title>
- <programlisting>[Match]
+ <programlisting># /etc/systemd/network/80-dhcp.network
+[Match]
Name=en*
[Network]
DHCP=yes</programlisting>
+
+ <para>This will enable DHCPv4 and DHCPv6 on all interfaces with names starting with
+ <literal>en</literal> (i.e. ethernet interfaces).</para>
</example>
<example>
- <title>/etc/systemd/network/25-bridge-static.network</title>
+ <title>A bridge with two enslaved links</title>
- <programlisting>[Match]
+ <programlisting># /etc/systemd/network/25-bridge-static.network
+[Match]
Name=bridge0
[Network]
Address=192.168.0.15/24
Gateway=192.168.0.1
DNS=192.168.0.1</programlisting>
- </example>
- <example>
- <title>/etc/systemd/network/25-bridge-slave-interface.network</title>
-
- <programlisting>[Match]
+ <programlisting># /etc/systemd/network/25-bridge-slave-interface-1.network
+[Match]
Name=enp2s0
[Network]
Bridge=bridge0</programlisting>
+
+ <programlisting># /etc/systemd/network/25-bridge-slave-interface-2.network
+[Match]
+Name=wlp3s0
+
+[Network]
+Bridge=bridge0</programlisting>
+
+ <para>This creates a bridge and attaches devices <literal>enp2s0</literal> and
+ <literal>wlp3s0</literal> to it. The bridge will have the specified static address
+ and network assigned, and a default route via the specified gateway will be
+ added. The specified DNS server will be added to the global list of DNS resolvers.
+ </para>
</example>
+
<example>
- <title>/etc/systemd/network/25-bridge-slave-interface-vlan.network</title>
+ <title></title>
- <programlisting>[Match]
+ <programlisting>
+# /etc/systemd/network/20-bridge-slave-interface-vlan.network
+[Match]
Name=enp2s0
[Network]
@@ -1337,66 +1387,106 @@ VLAN=100-200
[BridgeVLAN]
EgressUntagged=300-400</programlisting>
- </example>
- <example>
- <title>/etc/systemd/network/25-ipip.network</title>
- <programlisting>[Match]
-Name=em1
-
-[Network]
-Tunnel=ipip-tun</programlisting>
+ <para>This overrides the configuration specified in the previous example for the
+ interface <literal>enp2s0</literal>, and enables VLAN on that bridge port. VLAN IDs
+ 1-32, 42, 100-400 will be allowed. Packets tagged with VLAN IDs 42, 300-400 will be
+ untagged when they leave on this interface. Untagged packets which arrive on this
+ interface will be assigned VLAN ID 42.</para>
</example>
<example>
- <title>/etc/systemd/network/25-sit.network</title>
+ <title>Various tunnels</title>
- <programlisting>[Match]
-Name=em1
+ <programlisting>/etc/systemd/network/25-tunnels.network
+[Match]
+Name=ens1
[Network]
-Tunnel=sit-tun</programlisting>
+Tunnel=ipip-tun
+Tunnel=sit-tun
+Tunnel=gre-tun
+Tunnel=vti-tun
+ </programlisting>
+
+ <programlisting>/etc/systemd/network/25-tunnel-ipip.netdev
+[NetDev]
+Name=ipip-tun
+Kind=ipip
+ </programlisting>
+
+ <programlisting>/etc/systemd/network/25-tunnel-sit.netdev
+[NetDev]
+Name=sit-tun
+Kind=sit
+ </programlisting>
+
+ <programlisting>/etc/systemd/network/25-tunnel-gre.netdev
+[NetDev]
+Name=gre-tun
+Kind=gre
+ </programlisting>
+
+ <programlisting>/etc/systemd/network/25-tunnel-vti.netdev
+[NetDev]
+Name=vti-tun
+Kind=vti
+ </programlisting>
+
+ <para>This will bring interface <literal>ens1</literal> up and create an IPIP tunnel,
+ a SIT tunnel, a GRE tunnel, and a VTI tunnel using it.</para>
</example>
<example>
- <title>/etc/systemd/network/25-gre.network</title>
+ <title>A bond device</title>
- <programlisting>[Match]
-Name=em1
+ <programlisting># /etc/systemd/network/30-bond1.network
+[Match]
+Name=bond1
[Network]
-Tunnel=gre-tun</programlisting>
- </example>
+DHCP=ipv6
+</programlisting>
- <example>
- <title>/etc/systemd/network/25-vti.network</title>
+ <programlisting># /etc/systemd/network/30-bond1.netdev
+[NetDev]
+Name=bond1
+Kind=bond
+</programlisting>
- <programlisting>[Match]
-Name=em1
+ <programlisting># /etc/systemd/network/30-bond1-dev1.nework
+[Match]
+MACAddress=52:54:00:e9:64:41
[Network]
-Tunnel=vti-tun</programlisting>
- </example>
-
- <example>
- <title>/etc/systemd/network/25-bond.network</title>
+Bond=bond1
+</programlisting>
- <programlisting>[Match]
-Name=bond1
+ <programlisting># /etc/systemd/network/30-bond1-dev2.nework
+[Match]
+MACAddress=52:54:00:e9:64:42
[Network]
-DHCP=yes
+Bond=bond1
</programlisting>
+
+ <para>This will create a bond device <literal>bond1</literal> and enslave the two
+ devices with MAC addresses 52:54:00:e9:64:41 and 52:54:00:e9:64:42 to it. IPv6 DHCP
+ will be used to acquire an address.</para>
</example>
<example>
- <title>/etc/systemd/network/25-vrf.network</title>
- <para>Add the bond1 interface to the VRF master interface vrf-test. This will redirect routes generated on this interface to be within the routing table defined during VRF creation. Traffic won't be redirected towards the VRFs routing table unless specific ip-rules are added.</para>
- <programlisting>[Match]
+ <title>Virtual Routing and Forwarding (VRF)</title>
+ <para>Add the <literal>bond1</literal> interface to the VRF master interface
+ <literal>vrf1</literal>. This will redirect routes generated on this interface to be
+ within the routing table defined during VRF creation. Traffic won't be redirected
+ towards the VRFs routing table unless specific ip-rules are added.</para>
+ <programlisting># /etc/systemd/network/25-vrf.network
+[Match]
Name=bond1
[Network]
-VRF=vrf-test
+VRF=vrf1
</programlisting>
</example>
diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml
index b1344d6c10..7143188356 100644
--- a/man/systemd.nspawn.xml
+++ b/man/systemd.nspawn.xml
@@ -335,6 +335,17 @@
</varlistentry>
<varlistentry>
+ <term><varname>Overlay=</varname></term>
+ <term><varname>OverlayReadOnly=</varname></term>
+
+ <listitem><para>Adds an overlay mount point. Takes a colon-separated list of paths. This option may be used
+ multiple times to configure multiple overlay mounts. This option is equivalent to the command line switches
+ <option>--overlay=</option> and <option>--overlay-ro=</option>, see
+ <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> for details
+ about the specific options supported. This setting is privileged (see above).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>PrivateUsersChown=</varname></term>
<listitem><para>Configures whether the ownership of the files and directories in the container tree shall be
diff --git a/man/systemd.service.xml b/man/systemd.service.xml
index 67c68d2f8b..684029bdb4 100644
--- a/man/systemd.service.xml
+++ b/man/systemd.service.xml
@@ -918,18 +918,14 @@
must be passed as separate words). Lone semicolons may be escaped
as <literal>\;</literal>.</para>
- <para>Each command line is split on whitespace, with the first
- item being the command to execute, and the subsequent items being
- the arguments. Double quotes ("...") and single quotes ('...') may
- be used, in which case everything until the next matching quote
- becomes part of the same argument. C-style escapes are also
- supported. The table below contains the list of allowed escape
- patterns. Only patterns which match the syntax in the table are
- allowed; others will result in an error, and must be escaped by
- doubling the backslash. Quotes themselves are removed after
- parsing and escape sequences substituted. In addition, a trailing
- backslash (<literal>\</literal>) may be used to merge lines.
- </para>
+ <para>Each command line is split on whitespace, with the first item being the command to
+ execute, and the subsequent items being the arguments. Double quotes ("…") and single quotes
+ ('…') may be used, in which case everything until the next matching quote becomes part of the
+ same argument. Quotes themselves are removed. C-style escapes are also supported. The table
+ below contains the list of known escape patterns. Only escape patterns which match the syntax in
+ the table are allowed; other patterns may be added in the future and unknown patterns will
+ result in a warning. In particular, any backslashes should be doubled. Finally, a trailing
+ backslash (<literal>\</literal>) may be used to merge lines.</para>
<para>This syntax is intended to be very similar to shell syntax,
but only the meta-characters and expansions described in the
diff --git a/man/systemd.special.xml b/man/systemd.special.xml
index b513a13b5a..c2672511e3 100644
--- a/man/systemd.special.xml
+++ b/man/systemd.special.xml
@@ -930,7 +930,7 @@ Description=Render the desktop icons with Nautilus
PartOf=graphical-session.target
[Service]
-...
+…
</programlisting>
</example>
</refsect2>
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index 40c4cfd854..dbb0dc7bd7 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -1246,21 +1246,6 @@
<entry>This is either the unescaped instance name (if applicable) with <filename>/</filename> prepended (if applicable), or the unescaped prefix name prepended with <filename>/</filename>.</entry>
</row>
<row>
- <entry><literal>%c</literal></entry>
- <entry>Control group path of the unit</entry>
- <entry>This path does not include the <filename>/sys/fs/cgroup/systemd/</filename> prefix.</entry>
- </row>
- <row>
- <entry><literal>%r</literal></entry>
- <entry>Control group path of the slice the unit is placed in</entry>
- <entry>This usually maps to the parent control group path of <literal>%c</literal>.</entry>
- </row>
- <row>
- <entry><literal>%R</literal></entry>
- <entry>Root control group path below which slices and units are placed</entry>
- <entry>For system instances, this resolves to <filename>/</filename>, except in containers, where this maps to the container's root control group path.</entry>
- </row>
- <row>
<entry><literal>%t</literal></entry>
<entry>Runtime directory</entry>
<entry>This is either <filename>/run</filename> (for the system manager) or the path <literal>$XDG_RUNTIME_DIR</literal> resolves to (for user managers).</entry>
@@ -1314,13 +1299,6 @@
</tgroup>
</table>
- <para>Please note that specifiers <literal>%U</literal>,
- <literal>%h</literal>, <literal>%s</literal> are mostly useless
- when systemd is running in system mode. PID 1 cannot query the
- user account database for information, so the specifiers only work
- as shortcuts for things which are already specified in a different
- way in the unit file. They are fully functional when systemd is
- running in <option>--user</option> mode.</para>
</refsect1>
<refsect1>
diff --git a/man/systemd.xml b/man/systemd.xml
index fb67ba7cb3..50398e6259 100644
--- a/man/systemd.xml
+++ b/man/systemd.xml
@@ -280,9 +280,9 @@
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
however some are created automatically from other configuration,
dynamically from system state or programmatically at runtime.
- Units may be "active" (meaning started, bound, plugged in, ...,
+ Units may be "active" (meaning started, bound, plugged in, …,
depending on the unit type, see below), or "inactive" (meaning
- stopped, unbound, unplugged, ...), as well as in the process of
+ stopped, unbound, unplugged, …), as well as in the process of
being activated or deactivated, i.e. between the two states (these
states are called "activating", "deactivating"). A special
"failed" state is available as well, which is very similar to
diff --git a/man/telinit.xml b/man/telinit.xml
index 02d31fbd46..bdd22a6ce8 100644
--- a/man/telinit.xml
+++ b/man/telinit.xml
@@ -114,10 +114,10 @@
<listitem><para>Change the SysV runlevel. This is translated
into an activation request for
<filename>runlevel2.target</filename>,
- <filename>runlevel3.target</filename>, ... and is equivalent
+ <filename>runlevel3.target</filename>, … and is equivalent
to <command>systemctl isolate runlevel2.target</command>,
<command>systemctl isolate runlevel3.target</command>,
- ...</para></listitem>
+ …</para></listitem>
</varlistentry>
<varlistentry>
diff --git a/man/timedatectl.xml b/man/timedatectl.xml
index 415e2c799a..d8a83c8add 100644
--- a/man/timedatectl.xml
+++ b/man/timedatectl.xml
@@ -232,7 +232,7 @@ Password: ********
Status: "Using Time Server 216.239.38.15:123 (time4.google.com)."
CGroup: /system.slice/systemd-timesyncd.service
└─595 /usr/lib/systemd/systemd-timesyncd
-...</programlisting>
+…</programlisting>
</para>
</refsect1>
diff --git a/po/cs.po b/po/cs.po
index d5f1dcafeb..b42083beec 100644
--- a/po/cs.po
+++ b/po/cs.po
@@ -9,7 +9,7 @@ msgstr ""
"Project-Id-Version: systemd master\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-04-23 14:24+0200\n"
-"PO-Revision-Date: 2016-09-22 16:00+0200\n"
+"PO-Revision-Date: 2016-11-30 16:00+0100\n"
"Last-Translator: Daniel Rusek <mail@asciiwolf.com>\n"
"Language: cs\n"
"MIME-Version: 1.0\n"
@@ -551,32 +551,32 @@ msgid ""
"shall be enabled."
msgstr "Autentizace je vyžadována pro kontrolu synchronizace času ze sítě."
-#: ../src/core/dbus-unit.c:450
+#: ../src/core/dbus-unit.c:459
msgid "Authentication is required to start '$(unit)'."
msgstr "Autentizace je vyžadována pro spuštění „$(unit)”."
-#: ../src/core/dbus-unit.c:451
+#: ../src/core/dbus-unit.c:460
msgid "Authentication is required to stop '$(unit)'."
msgstr "Autentizace je vyžadována pro vypnutí „$(unit)”."
-#: ../src/core/dbus-unit.c:452
+#: ../src/core/dbus-unit.c:461
msgid "Authentication is required to reload '$(unit)'."
msgstr "Autentizace je vyžadována pro znovu načtení „$(unit)”."
-#: ../src/core/dbus-unit.c:453 ../src/core/dbus-unit.c:454
+#: ../src/core/dbus-unit.c:462 ../src/core/dbus-unit.c:463
msgid "Authentication is required to restart '$(unit)'."
msgstr "Autentizace je vyžadována pro restart „$(unit)”."
-#: ../src/core/dbus-unit.c:560
+#: ../src/core/dbus-unit.c:570
msgid "Authentication is required to kill '$(unit)'."
msgstr "Autentizace je vyžadována pro ukončení „$(unit)”."
-#: ../src/core/dbus-unit.c:590
+#: ../src/core/dbus-unit.c:601
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgstr ""
"Autentizace je vyžadována pro resetování chybného stavu "
"„$(unit)”."
-#: ../src/core/dbus-unit.c:622
+#: ../src/core/dbus-unit.c:634
msgid "Authentication is required to set properties on '$(unit)'."
msgstr "Autentizace je vyžadována pro nastavení vlastností na „$(unit)”."
diff --git a/rules/60-persistent-storage.rules b/rules/60-persistent-storage.rules
index c13d05cdb1..6f60ae9024 100644
--- a/rules/60-persistent-storage.rules
+++ b/rules/60-persistent-storage.rules
@@ -7,7 +7,7 @@ ACTION=="remove", GOTO="persistent_storage_end"
ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}=="1", GOTO="persistent_storage_end"
SUBSYSTEM!="block", GOTO="persistent_storage_end"
-KERNEL!="loop*|mmcblk*[0-9]|msblk*[0-9]|mspblk*[0-9]|nvme*|sd*|sr*|vd*|xvd*|bcache*|cciss*|dasd*|ubd*|scm*|pmem*", GOTO="persistent_storage_end"
+KERNEL!="loop*|mmcblk*[0-9]|msblk*[0-9]|mspblk*[0-9]|nvme*|sd*|sr*|vd*|xvd*|bcache*|cciss*|dasd*|ubd*|scm*|pmem*|nbd*", GOTO="persistent_storage_end"
# ignore partitions that span the entire disk
TEST=="whole_disk", GOTO="persistent_storage_end"
@@ -54,7 +54,7 @@ KERNEL=="sd*[0-9]", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$att
# MMC
KERNEL=="mmcblk[0-9]", SUBSYSTEMS=="mmc", ATTRS{name}=="?*", ATTRS{serial}=="?*", \
ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}"
-KERNEL=="mmcblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}-part%n"
+KERNEL=="mmcblk[0-9]p[0-9]*", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}-part%n"
# Memstick
KERNEL=="msblk[0-9]|mspblk[0-9]", SUBSYSTEMS=="memstick", ATTRS{name}=="?*", ATTRS{serial}=="?*", \
diff --git a/rules/60-sensor.rules b/rules/60-sensor.rules
new file mode 100644
index 0000000000..82e44f8843
--- /dev/null
+++ b/rules/60-sensor.rules
@@ -0,0 +1,10 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="sensor_end"
+
+# device matching the sensor's name and the machine's DMI data for IIO devices
+SUBSYSTEM=="iio", KERNEL=="iio*", SUBSYSTEMS=="usb|i2c", \
+ IMPORT{builtin}="hwdb 'sensor:modalias:$attr{modalias}:$attr{[dmi/id]modalias}'", \
+ GOTO="sensor_end"
+
+LABEL="sensor_end"
diff --git a/src/activate/activate.c b/src/activate/activate.c
index a0cfc22000..6ebd820410 100644
--- a/src/activate/activate.c
+++ b/src/activate/activate.c
@@ -339,7 +339,7 @@ static void sigchld_hdl(int sig) {
static int install_chld_handler(void) {
static const struct sigaction act = {
- .sa_flags = SA_NOCLDSTOP,
+ .sa_flags = SA_NOCLDSTOP|SA_RESTART,
.sa_handler = sigchld_hdl,
};
diff --git a/src/basic/architecture.h b/src/basic/architecture.h
index 5a77c31932..b329df2f6d 100644
--- a/src/basic/architecture.h
+++ b/src/basic/architecture.h
@@ -150,6 +150,7 @@ int uname_architecture(void);
# else
# define native_architecture() ARCHITECTURE_ARM64
# define LIB_ARCH_TUPLE "aarch64-linux-gnu"
+# define SECONDARY_ARCHITECTURE ARCHITECTURE_ARM
# endif
#elif defined(__arm__)
# if __BYTE_ORDER == __BIG_ENDIAN
diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c
index 8b57de4744..514587d237 100644
--- a/src/basic/calendarspec.c
+++ b/src/basic/calendarspec.c
@@ -752,12 +752,8 @@ static int parse_calendar_time(const char **p, CalendarSpec *c) {
goto fail;
/* Already at the end? Then it's hours and minutes, and seconds are 0 */
- if (*t == 0) {
- if (m != NULL)
- goto null_second;
-
- goto finish;
- }
+ if (*t == 0)
+ goto null_second;
if (*t != ':') {
r = -EINVAL;
diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c
index dbe64a9a58..f8cac3e911 100644
--- a/src/basic/extract-word.c
+++ b/src/basic/extract-word.c
@@ -227,8 +227,8 @@ int extract_first_word_and_warn(
*p = save;
r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
if (r >= 0) {
- /* It worked this time, hence it must have been an invalid escape sequence we could correct. */
- log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Invalid escape sequences in line, correcting: \"%s\"", rvalue);
+ /* It worked this time, hence it must have been an invalid escape sequence. */
+ log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Ignoring unknown escape sequences: \"%s\"", *ret);
return r;
}
diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c
index 5c820332a5..19ad20789b 100644
--- a/src/basic/fd-util.c
+++ b/src/basic/fd-util.c
@@ -24,6 +24,7 @@
#include <sys/stat.h>
#include <unistd.h>
+#include "dirent-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "macro.h"
@@ -234,12 +235,9 @@ int close_all_fds(const int except[], unsigned n_except) {
return r;
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, return -errno) {
int fd = -1;
- if (hidden_or_backup_file(de->d_name))
- continue;
-
if (safe_atoi(de->d_name, &fd) < 0)
/* Let's better ignore this, just in case */
continue;
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index 1615456659..c43b0583a4 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -1409,3 +1409,22 @@ int read_nul_string(FILE *f, char **ret) {
return 0;
}
+
+int mkdtemp_malloc(const char *template, char **ret) {
+ char *p;
+
+ assert(template);
+ assert(ret);
+
+ p = strdup(template);
+ if (!p)
+ return -ENOMEM;
+
+ if (!mkdtemp(p)) {
+ free(p);
+ return -errno;
+ }
+
+ *ret = p;
+ return 0;
+}
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index b58c83e64a..17b38a5d60 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -88,3 +88,5 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
int link_tmpfile(int fd, const char *path, const char *target);
int read_nul_string(FILE *f, char **ret);
+
+int mkdtemp_malloc(const char *template, char **ret);
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index d2c322a0de..5b23269109 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -17,7 +17,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <dirent.h>
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
@@ -224,25 +223,25 @@ int readlink_and_make_absolute(const char *p, char **r) {
return 0;
}
-int readlink_and_canonicalize(const char *p, char **r) {
+int readlink_and_canonicalize(const char *p, const char *root, char **ret) {
char *t, *s;
- int j;
+ int r;
assert(p);
- assert(r);
+ assert(ret);
- j = readlink_and_make_absolute(p, &t);
- if (j < 0)
- return j;
+ r = readlink_and_make_absolute(p, &t);
+ if (r < 0)
+ return r;
- s = canonicalize_file_name(t);
- if (s) {
+ r = chase_symlinks(t, root, 0, &s);
+ if (r < 0)
+ /* If we can't follow up, then let's return the original string, slightly cleaned up. */
+ *ret = path_kill_slashes(t);
+ else {
+ *ret = s;
free(t);
- *r = s;
- } else
- *r = t;
-
- path_kill_slashes(*r);
+ }
return 0;
}
@@ -446,6 +445,7 @@ int mkfifo_atomic(const char *path, mode_t mode) {
int get_files_in_directory(const char *path, char ***list) {
_cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
size_t bufsize = 0, n = 0;
_cleanup_strv_free_ char **l = NULL;
@@ -459,16 +459,7 @@ int get_files_in_directory(const char *path, char ***list) {
if (!d)
return -errno;
- for (;;) {
- struct dirent *de;
-
- errno = 0;
- de = readdir(d);
- if (!de && errno > 0)
- return -errno;
- if (!de)
- break;
-
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
dirent_ensure_type(d, de);
if (!dirent_is_file(de))
@@ -598,10 +589,11 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
return r;
}
-int chase_symlinks(const char *path, const char *_root, char **ret) {
+int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
_cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
_cleanup_close_ int fd = -1;
unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */
+ bool exists = true;
char *todo;
int r;
@@ -610,26 +602,39 @@ int chase_symlinks(const char *path, const char *_root, char **ret) {
/* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
* symlinks relative to a root directory, instead of the root of the host.
*
- * Note that "root" matters only if we encounter an absolute symlink, it's unused otherwise. Most importantly
- * this means the path parameter passed in is not prefixed by it.
+ * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following
+ * relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is
+ * assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first
+ * prefixed accordingly.
*
* Algorithmically this operates on two path buffers: "done" are the components of the path we already
* processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to
* process. On each iteration, we move one component from "todo" to "done", processing it's special meaning
* each time. The "todo" path always starts with at least one slash, the "done" path always ends in no
* slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races
- * at a minimum. */
-
- r = path_make_absolute_cwd(path, &buffer);
- if (r < 0)
- return r;
+ * at a minimum.
+ *
+ * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got
+ * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this
+ * function what to do when encountering a symlink with an absolute path as directory: prefix it by the
+ * specified path.
+ *
+ * Note: there's also chase_symlinks_prefix() (see below), which as first step prefixes the passed path by the
+ * passed root. */
- if (_root) {
- r = path_make_absolute_cwd(_root, &root);
+ if (original_root) {
+ r = path_make_absolute_cwd(original_root, &root);
if (r < 0)
return r;
+
+ if (flags & CHASE_PREFIX_ROOT)
+ path = prefix_roota(root, path);
}
+ r = path_make_absolute_cwd(path, &buffer);
+ if (r < 0)
+ return r;
+
fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
if (fd < 0)
return -errno;
@@ -665,18 +670,20 @@ int chase_symlinks(const char *path, const char *_root, char **ret) {
_cleanup_free_ char *parent = NULL;
int fd_parent = -1;
+ /* If we already are at the top, then going up will not change anything. This is in-line with
+ * how the kernel handles this. */
if (isempty(done) || path_equal(done, "/"))
- return -EINVAL;
+ continue;
parent = dirname_malloc(done);
if (!parent)
return -ENOMEM;
- /* Don't allow this to leave the root dir */
+ /* Don't allow this to leave the root dir. */
if (root &&
path_startswith(done, root) &&
!path_startswith(parent, root))
- return -EINVAL;
+ continue;
free_and_replace(done, parent);
@@ -692,8 +699,25 @@ int chase_symlinks(const char *path, const char *_root, char **ret) {
/* Otherwise let's see what this is. */
child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH);
- if (child < 0)
+ if (child < 0) {
+
+ if (errno == ENOENT &&
+ (flags & CHASE_NONEXISTENT) &&
+ (isempty(todo) || path_is_safe(todo))) {
+
+ /* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return
+ * what we got so far. But don't allow this if the remaining path contains "../ or "./"
+ * or something else weird. */
+
+ if (!strextend(&done, first, todo, NULL))
+ return -ENOMEM;
+
+ exists = false;
+ break;
+ }
+
return -errno;
+ }
if (fstat(child, &st) < 0)
return -errno;
@@ -778,5 +802,5 @@ int chase_symlinks(const char *path, const char *_root, char **ret) {
*ret = done;
done = NULL;
- return 0;
+ return exists;
}
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index 31df47cf1e..5fe5c71ff0 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -39,7 +39,7 @@ int readlinkat_malloc(int fd, const char *p, char **ret);
int readlink_malloc(const char *p, char **r);
int readlink_value(const char *p, char **ret);
int readlink_and_make_absolute(const char *p, char **r);
-int readlink_and_canonicalize(const char *p, char **r);
+int readlink_and_canonicalize(const char *p, const char *root, char **r);
int readlink_and_make_absolute_root(const char *root, const char *path, char **ret);
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
@@ -78,4 +78,16 @@ union inotify_event_buffer {
int inotify_add_watch_fd(int fd, int what, uint32_t mask);
-int chase_symlinks(const char *path, const char *_root, char **ret);
+enum {
+ CHASE_PREFIX_ROOT = 1, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
+ CHASE_NONEXISTENT = 2, /* If set, it's OK if the path doesn't actually exist. */
+};
+
+int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
+
+/* Useful for usage with _cleanup_(), removes a directory and frees the pointer */
+static inline void rmdir_and_free(char *p) {
+ (void) rmdir(p);
+ free(p);
+}
+DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free);
diff --git a/src/basic/khash.c b/src/basic/khash.c
new file mode 100644
index 0000000000..9a2a3edb75
--- /dev/null
+++ b/src/basic/khash.c
@@ -0,0 +1,275 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <linux/if_alg.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "khash.h"
+#include "macro.h"
+#include "missing.h"
+#include "string-util.h"
+#include "util.h"
+
+/* On current kernels the maximum digest (according to "grep digestsize /proc/crypto | sort -u") is actually 32, but
+ * let's add some extra room, the few wasted bytes don't really matter... */
+#define LONGEST_DIGEST 128
+
+struct khash {
+ int fd;
+ char *algorithm;
+ uint8_t digest[LONGEST_DIGEST+1];
+ size_t digest_size;
+ bool digest_valid;
+};
+
+int khash_new_with_key(khash **ret, const char *algorithm, const void *key, size_t key_size) {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_alg alg;
+ } sa = {
+ .alg.salg_family = AF_ALG,
+ .alg.salg_type = "hash",
+ };
+
+ _cleanup_(khash_unrefp) khash *h = NULL;
+ _cleanup_close_ int fd = -1;
+ ssize_t n;
+
+ assert(ret);
+ assert(key || key_size == 0);
+
+ /* Filter out an empty algorithm early, as we do not support an algorithm by that name. */
+ if (isempty(algorithm))
+ return -EINVAL;
+
+ /* Overly long hash algorithm names we definitely do not support */
+ if (strlen(algorithm) >= sizeof(sa.alg.salg_name))
+ return -EOPNOTSUPP;
+
+ fd = socket(AF_ALG, SOCK_SEQPACKET|SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -errno;
+
+ strcpy((char*) sa.alg.salg_name, algorithm);
+ if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
+ if (errno == ENOENT)
+ return -EOPNOTSUPP;
+ return -errno;
+ }
+
+ if (key) {
+ if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, key, key_size) < 0)
+ return -errno;
+ }
+
+ h = new0(khash, 1);
+ if (!h)
+ return -ENOMEM;
+
+ h->fd = accept4(fd, NULL, 0, SOCK_CLOEXEC);
+ if (h->fd < 0)
+ return -errno;
+
+ h->algorithm = strdup(algorithm);
+ if (!h->algorithm)
+ return -ENOMEM;
+
+ /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */
+ (void) send(h->fd, NULL, 0, 0);
+
+ /* Figure out the digest size */
+ n = recv(h->fd, h->digest, sizeof(h->digest), 0);
+ if (n < 0)
+ return -errno;
+ if (n >= LONGEST_DIGEST) /* longer than what we expected? If so, we don't support this */
+ return -EOPNOTSUPP;
+
+ h->digest_size = (size_t) n;
+ h->digest_valid = true;
+
+ /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */
+ (void) send(h->fd, NULL, 0, 0);
+
+ *ret = h;
+ h = NULL;
+
+ return 0;
+}
+
+int khash_new(khash **ret, const char *algorithm) {
+ return khash_new_with_key(ret, algorithm, NULL, 0);
+}
+
+khash* khash_unref(khash *h) {
+ if (!h)
+ return NULL;
+
+ safe_close(h->fd);
+ free(h->algorithm);
+ free(h);
+
+ return NULL;
+}
+
+int khash_dup(khash *h, khash **ret) {
+ _cleanup_(khash_unrefp) khash *copy = NULL;
+
+ assert(h);
+ assert(ret);
+
+ copy = newdup(khash, h, 1);
+ if (!copy)
+ return -ENOMEM;
+
+ copy->fd = -1;
+ copy->algorithm = strdup(h->algorithm);
+ if (!copy)
+ return -ENOMEM;
+
+ copy->fd = accept4(h->fd, NULL, 0, SOCK_CLOEXEC);
+ if (copy->fd < 0)
+ return -errno;
+
+ *ret = copy;
+ copy = NULL;
+
+ return 0;
+}
+
+const char *khash_get_algorithm(khash *h) {
+ assert(h);
+
+ return h->algorithm;
+}
+
+size_t khash_get_size(khash *h) {
+ assert(h);
+
+ return h->digest_size;
+}
+
+int khash_reset(khash *h) {
+ ssize_t n;
+
+ assert(h);
+
+ n = send(h->fd, NULL, 0, 0);
+ if (n < 0)
+ return -errno;
+
+ h->digest_valid = false;
+
+ return 0;
+}
+
+int khash_put(khash *h, const void *buffer, size_t size) {
+ ssize_t n;
+
+ assert(h);
+ assert(buffer || size == 0);
+
+ if (size <= 0)
+ return 0;
+
+ n = send(h->fd, buffer, size, MSG_MORE);
+ if (n < 0)
+ return -errno;
+
+ h->digest_valid = false;
+
+ return 0;
+}
+
+int khash_put_iovec(khash *h, const struct iovec *iovec, size_t n) {
+ struct msghdr mh = {
+ mh.msg_iov = (struct iovec*) iovec,
+ mh.msg_iovlen = n,
+ };
+ ssize_t k;
+
+ assert(h);
+ assert(iovec || n == 0);
+
+ if (n <= 0)
+ return 0;
+
+ k = sendmsg(h->fd, &mh, MSG_MORE);
+ if (k < 0)
+ return -errno;
+
+ h->digest_valid = false;
+
+ return 0;
+}
+
+static int retrieve_digest(khash *h) {
+ ssize_t n;
+
+ assert(h);
+
+ if (h->digest_valid)
+ return 0;
+
+ n = recv(h->fd, h->digest, h->digest_size, 0);
+ if (n < 0)
+ return n;
+ if ((size_t) n != h->digest_size) /* digest size changed? */
+ return -EIO;
+
+ h->digest_valid = true;
+
+ return 0;
+}
+
+int khash_digest_data(khash *h, const void **ret) {
+ int r;
+
+ assert(h);
+ assert(ret);
+
+ r = retrieve_digest(h);
+ if (r < 0)
+ return r;
+
+ *ret = h->digest;
+ return 0;
+}
+
+int khash_digest_string(khash *h, char **ret) {
+ int r;
+ char *p;
+
+ assert(h);
+ assert(ret);
+
+ r = retrieve_digest(h);
+ if (r < 0)
+ return r;
+
+ p = hexmem(h->digest, h->digest_size);
+ if (!p)
+ return -ENOMEM;
+
+ *ret = p;
+ return 0;
+}
diff --git a/src/basic/khash.h b/src/basic/khash.h
new file mode 100644
index 0000000000..f404a68236
--- /dev/null
+++ b/src/basic/khash.h
@@ -0,0 +1,53 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include "macro.h"
+
+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.*/
+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),
+ * cmac(des3_ede), hmac(sha3-512), hmac(sha3-384), hmac(sha3-256), hmac(sha3-224), hmac(rmd160), hmac(rmd128),
+ * hmac(sha224), hmac(sha512), hmac(sha384), hmac(sha1), hmac(md5), and more. */
+int khash_new_with_key(khash **ret, const char *algorithm, const void *key, size_t key_size);
+
+int khash_dup(khash *h, khash **ret);
+khash* khash_unref(khash *h);
+
+const char *khash_get_algorithm(khash *h);
+size_t khash_get_size(khash *h);
+
+int khash_reset(khash *h);
+
+int khash_put(khash *h, const void *buffer, size_t size);
+int khash_put_iovec(khash *h, const struct iovec *iovec, size_t n);
+
+int khash_digest_data(khash *h, const void **ret);
+int khash_digest_string(khash *h, char **ret);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(khash*, khash_unref);
diff --git a/src/basic/log.c b/src/basic/log.c
index f5cff4cc9f..71d5a0baa2 100644
--- a/src/basic/log.c
+++ b/src/basic/log.c
@@ -500,7 +500,7 @@ static int log_do_header(
line ? "CODE_LINE=" : "",
line ? 1 : 0, line, /* %.0d means no output too, special case for 0 */
line ? "\n" : "",
- isempty(func) ? "" : "CODE_FUNCTION=",
+ isempty(func) ? "" : "CODE_FUNC=",
isempty(func) ? "" : func,
isempty(func) ? "" : "\n",
error ? "ERRNO=" : "",
@@ -1134,8 +1134,8 @@ int log_syntax_internal(
PROTECT_ERRNO;
char buffer[LINE_MAX];
- int r;
va_list ap;
+ const char *unit_fmt = NULL;
if (error < 0)
error = -error;
@@ -1154,24 +1154,15 @@ int log_syntax_internal(
va_end(ap);
if (unit)
- r = log_struct_internal(
- level, error,
- file, line, func,
- getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit,
- LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION),
- "CONFIG_FILE=%s", config_file,
- "CONFIG_LINE=%u", config_line,
- LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer),
- NULL);
- else
- r = log_struct_internal(
- level, error,
- file, line, func,
- LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION),
- "CONFIG_FILE=%s", config_file,
- "CONFIG_LINE=%u", config_line,
- LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer),
- NULL);
-
- return r;
+ unit_fmt = getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s";
+
+ return log_struct_internal(
+ level, error,
+ file, line, func,
+ LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION),
+ "CONFIG_FILE=%s", config_file,
+ "CONFIG_LINE=%u", config_line,
+ LOG_MESSAGE("%s:%u: %s", config_file, config_line, buffer),
+ unit_fmt, unit,
+ NULL);
}
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 8833617dc6..1502b3f4f4 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -1109,4 +1109,8 @@ struct ethtool_link_settings {
#endif
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif
+
#include "missing_syscall.h"
diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c
index 5d37fb48be..352c3505fb 100644
--- a/src/basic/mount-util.c
+++ b/src/basic/mount-util.c
@@ -29,6 +29,7 @@
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "hashmap.h"
#include "mount-util.h"
#include "parse-util.h"
@@ -205,9 +206,10 @@ fallback_fstat:
}
/* flags can be AT_SYMLINK_FOLLOW or 0 */
-int path_is_mount_point(const char *t, int flags) {
- _cleanup_close_ int fd = -1;
+int path_is_mount_point(const char *t, const char *root, int flags) {
_cleanup_free_ char *canonical = NULL, *parent = NULL;
+ _cleanup_close_ int fd = -1;
+ int r;
assert(t);
@@ -219,9 +221,9 @@ int path_is_mount_point(const char *t, int flags) {
* /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
* look at needs to be /usr, not /. */
if (flags & AT_SYMLINK_FOLLOW) {
- canonical = canonicalize_file_name(t);
- if (!canonical)
- return -errno;
+ r = chase_symlinks(t, root, 0, &canonical);
+ if (r < 0)
+ return r;
t = canonical;
}
@@ -473,7 +475,7 @@ int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) {
return r;
/* Deal with mount points that are obstructed by a later mount */
- r = path_is_mount_point(x, 0);
+ r = path_is_mount_point(x, NULL, 0);
if (r == -ENOENT || r == 0)
continue;
if (r < 0)
diff --git a/src/basic/mount-util.h b/src/basic/mount-util.h
index 4f305df19f..b840956d63 100644
--- a/src/basic/mount-util.h
+++ b/src/basic/mount-util.h
@@ -30,7 +30,7 @@
#include "missing.h"
int fd_is_mount_point(int fd, const char *filename, int flags);
-int path_is_mount_point(const char *path, int flags);
+int path_is_mount_point(const char *path, const char *root, int flags);
int repeat_unmount(const char *path, int flags);
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
index c98815b9bc..6e58ced6f5 100644
--- a/src/basic/parse-util.c
+++ b/src/basic/parse-util.c
@@ -574,3 +574,19 @@ int parse_nice(const char *p, int *ret) {
*ret = n;
return 0;
}
+
+int parse_ip_port(const char *s, uint16_t *ret) {
+ uint16_t l;
+ int r;
+
+ r = safe_atou16(s, &l);
+ if (r < 0)
+ return r;
+
+ if (l == 0)
+ return -EINVAL;
+
+ *ret = (uint16_t) l;
+
+ return 0;
+}
diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h
index 461e1cd4d8..4d132f0de5 100644
--- a/src/basic/parse-util.h
+++ b/src/basic/parse-util.h
@@ -110,3 +110,5 @@ int parse_percent_unbounded(const char *p);
int parse_percent(const char *p);
int parse_nice(const char *p, int *ret);
+
+int parse_ip_port(const char *s, uint16_t *ret);
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index 5cdac50c68..9a51e0d8bc 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -220,10 +220,11 @@ int path_strv_make_absolute_cwd(char **l) {
return 0;
}
-char **path_strv_resolve(char **l, const char *prefix) {
+char **path_strv_resolve(char **l, const char *root) {
char **s;
unsigned k = 0;
bool enomem = false;
+ int r;
if (strv_isempty(l))
return l;
@@ -233,17 +234,17 @@ char **path_strv_resolve(char **l, const char *prefix) {
* changes on failure. */
STRV_FOREACH(s, l) {
- char *t, *u;
_cleanup_free_ char *orig = NULL;
+ char *t, *u;
if (!path_is_absolute(*s)) {
free(*s);
continue;
}
- if (prefix) {
+ if (root) {
orig = *s;
- t = strappend(prefix, orig);
+ t = prefix_root(root, orig);
if (!t) {
enomem = true;
continue;
@@ -251,28 +252,26 @@ char **path_strv_resolve(char **l, const char *prefix) {
} else
t = *s;
- errno = 0;
- u = canonicalize_file_name(t);
- if (!u) {
- if (errno == ENOENT) {
- if (prefix) {
- u = orig;
- orig = NULL;
- free(t);
- } else
- u = t;
- } else {
+ r = chase_symlinks(t, root, 0, &u);
+ if (r == -ENOENT) {
+ if (root) {
+ u = orig;
+ orig = NULL;
free(t);
- if (errno == ENOMEM || errno == 0)
- enomem = true;
+ } else
+ u = t;
+ } else if (r < 0) {
+ free(t);
- continue;
- }
- } else if (prefix) {
+ if (r == -ENOMEM)
+ enomem = true;
+
+ continue;
+ } else if (root) {
char *x;
free(t);
- x = path_startswith(u, prefix);
+ x = path_startswith(u, root);
if (x) {
/* restore the slash if it was lost */
if (!startswith(x, "/"))
@@ -304,12 +303,12 @@ char **path_strv_resolve(char **l, const char *prefix) {
return l;
}
-char **path_strv_resolve_uniq(char **l, const char *prefix) {
+char **path_strv_resolve_uniq(char **l, const char *root) {
if (strv_isempty(l))
return l;
- if (!path_strv_resolve(l, prefix))
+ if (!path_strv_resolve(l, root))
return NULL;
return strv_uniq(l);
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index 66545f52d9..d2bc0d3b8e 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -66,8 +66,8 @@ static inline bool path_equal_ptr(const char *a, const char *b) {
})
int path_strv_make_absolute_cwd(char **l);
-char** path_strv_resolve(char **l, const char *prefix);
-char** path_strv_resolve_uniq(char **l, const char *prefix);
+char** path_strv_resolve(char **l, const char *root);
+char** path_strv_resolve_uniq(char **l, const char *root);
int find_binary(const char *name, char **filename);
diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
index baa70c2c8d..07d42f78dd 100644
--- a/src/basic/rm-rf.c
+++ b/src/basic/rm-rf.c
@@ -17,7 +17,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
@@ -28,6 +27,7 @@
#include "btrfs-util.h"
#include "cgroup-util.h"
+#include "dirent-util.h"
#include "fd-util.h"
#include "log.h"
#include "macro.h"
@@ -43,6 +43,7 @@ static bool is_physical_fs(const struct statfs *sfs) {
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
_cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
int ret = 0, r;
struct statfs sfs;
@@ -78,19 +79,10 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
return errno == ENOENT ? 0 : -errno;
}
- for (;;) {
- struct dirent *de;
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
bool is_dir;
struct stat st;
- errno = 0;
- de = readdir(d);
- if (!de) {
- if (errno > 0 && ret == 0)
- ret = -errno;
- return ret;
- }
-
if (streq(de->d_name, ".") || streq(de->d_name, ".."))
continue;
@@ -178,6 +170,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
}
}
}
+ return ret;
}
int rm_rf(const char *path, RemoveFlags flags) {
diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h
index f693a5bb7c..e13f7003e3 100644
--- a/src/basic/rm-rf.h
+++ b/src/basic/rm-rf.h
@@ -33,8 +33,6 @@ int rm_rf(const char *path, RemoveFlags flags);
/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */
static inline void rm_rf_physical_and_free(char *p) {
- if (!p)
- return;
(void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
free(p);
}
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index cbdfd55ada..7a5b29d77e 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -883,6 +883,7 @@ static char* extract_multiplier(char *p, usec_t *multiplier) {
{ "y", USEC_PER_YEAR },
{ "usec", 1ULL },
{ "us", 1ULL },
+ { "µs", 1ULL },
};
unsigned i;
@@ -1016,6 +1017,7 @@ int parse_nsec(const char *t, nsec_t *nsec) {
{ "y", NSEC_PER_YEAR },
{ "usec", NSEC_PER_USEC },
{ "us", NSEC_PER_USEC },
+ { "µs", NSEC_PER_USEC },
{ "nsec", 1ULL },
{ "ns", 1ULL },
{ "", 1ULL }, /* default is nsec */
diff --git a/src/basic/util.c b/src/basic/util.c
index d0424e5392..6204906f37 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -18,7 +18,6 @@
***/
#include <alloca.h>
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
@@ -508,28 +507,17 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
int on_ac_power(void) {
bool found_offline = false, found_online = false;
_cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
d = opendir("/sys/class/power_supply");
if (!d)
return errno == ENOENT ? true : -errno;
- for (;;) {
- struct dirent *de;
+ FOREACH_DIRENT(de, d, return -errno) {
_cleanup_close_ int fd = -1, device = -1;
char contents[6];
ssize_t n;
- errno = 0;
- de = readdir(d);
- if (!de && errno > 0)
- return -errno;
-
- if (!de)
- break;
-
- if (hidden_or_backup_file(de->d_name))
- continue;
-
device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (device < 0) {
if (errno == ENOENT || errno == ENOTDIR)
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 30c1ead1aa..44ea6215dc 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -1482,7 +1482,7 @@ static VOID config_entry_add_osx(Config *config) {
root = LibOpenRoot(handles[i]);
if (!root)
continue;
- found = config_entry_add_loader_auto(config, handles[i], root, NULL, L"auto-osx", 'a', L"OS X",
+ found = config_entry_add_loader_auto(config, handles[i], root, NULL, L"auto-osx", 'a', L"macOS",
L"\\System\\Library\\CoreServices\\boot.efi");
uefi_call_wrapper(root->Close, 1, root);
if (found)
diff --git a/src/core/automount.c b/src/core/automount.c
index 5fa6eb7b18..8ff1ca90f7 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -783,7 +783,7 @@ static int automount_start(Unit *u) {
assert(a);
assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED);
- if (path_is_mount_point(a->where, 0) > 0) {
+ if (path_is_mount_point(a->where, NULL, 0) > 0) {
log_unit_error(u, "Path %s is already a mount point, refusing start.", a->where);
return -EEXIST;
}
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 6dab6e9043..5789e2aa82 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -287,14 +287,21 @@ static int lookup_block_device(const char *p, dev_t *dev) {
static int whitelist_device(const char *path, const char *node, const char *acc) {
char buf[2+DECIMAL_STR_MAX(dev_t)*2+2+4];
struct stat st;
+ bool ignore_notfound;
int r;
assert(path);
assert(acc);
+ if (node[0] == '-') {
+ /* Non-existent paths starting with "-" must be silently ignored */
+ node++;
+ ignore_notfound = true;
+ } else
+ ignore_notfound = false;
+
if (stat(node, &st) < 0) {
- /* path starting with "-" must be silently ignored */
- if (errno == ENOENT && startswith(node, "-"))
+ if (errno == ENOENT && ignore_notfound)
return 0;
return log_warning_errno(errno, "Couldn't stat device %s: %m", node);
diff --git a/src/core/device.c b/src/core/device.c
index 8e2e3c7bed..e345552f24 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -359,7 +359,7 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
fail:
log_unit_warning_errno(u, r, "Failed to set up device unit: %m");
- if (delete && u)
+ if (delete)
unit_free(u);
return r;
@@ -418,7 +418,7 @@ static int device_process_new(Manager *m, struct udev_device *dev) {
* aliases */
alias = udev_device_get_property_value(dev, "SYSTEMD_ALIAS");
for (;;) {
- _cleanup_free_ char *word = NULL, *k = NULL;
+ _cleanup_free_ char *word = NULL;
r = extract_first_word(&alias, &word, NULL, EXTRACT_QUOTES);
if (r == 0)
diff --git a/src/core/killall.c b/src/core/killall.c
index 3bc19e9c84..b3aa22adc5 100644
--- a/src/core/killall.c
+++ b/src/core/killall.c
@@ -24,6 +24,7 @@
#include "alloc-util.h"
#include "def.h"
+#include "dirent-util.h"
#include "fd-util.h"
#include "format-util.h"
#include "killall.h"
@@ -172,7 +173,7 @@ static int killall(int sig, Set *pids, bool send_sighup) {
if (!dir)
return -errno;
- while ((d = readdir(dir))) {
+ FOREACH_DIRENT_ALL(d, dir, break) {
pid_t pid;
int r;
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index f4ef5a0140..2610442b91 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -191,13 +191,13 @@ Unit.IgnoreOnIsolate, config_parse_bool, 0,
Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LEGACY, 0
Unit.JobTimeoutSec, config_parse_sec_fix_0, 0, offsetof(Unit, job_timeout)
Unit.JobTimeoutAction, config_parse_emergency_action, 0, offsetof(Unit, job_timeout_action)
-Unit.JobTimeoutRebootArgument, config_parse_string, 0, offsetof(Unit, job_timeout_reboot_arg)
+Unit.JobTimeoutRebootArgument, config_parse_unit_string_printf, 0, offsetof(Unit, job_timeout_reboot_arg)
Unit.StartLimitIntervalSec, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
m4_dnl The following is a legacy alias name for compatibility
Unit.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
Unit.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst)
Unit.StartLimitAction, config_parse_emergency_action, 0, offsetof(Unit, start_limit_action)
-Unit.RebootArgument, config_parse_string, 0, offsetof(Unit, reboot_arg)
+Unit.RebootArgument, config_parse_unit_string_printf, 0, offsetof(Unit, reboot_arg)
Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, conditions)
Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, conditions)
Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, conditions)
@@ -254,7 +254,7 @@ m4_dnl The following three only exist for compatibility, they moved into Unit, s
Service.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst)
Service.StartLimitAction, config_parse_emergency_action, 0, offsetof(Unit, start_limit_action)
-Service.RebootArgument, config_parse_string, 0, offsetof(Unit, reboot_arg)
+Service.RebootArgument, config_parse_unit_path_printf, 0, offsetof(Unit, reboot_arg)
Service.FailureAction, config_parse_emergency_action, 0, offsetof(Service, emergency_action)
Service.Type, config_parse_service_type, 0, offsetof(Service, type)
Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart)
@@ -272,8 +272,8 @@ Service.FileDescriptorStoreMax, config_parse_unsigned, 0,
Service.NotifyAccess, config_parse_notify_access, 0, offsetof(Service, notify_access)
Service.Sockets, config_parse_service_sockets, 0, 0
Service.BusPolicy, config_parse_warn_compat, DISABLED_LEGACY, 0
-Service.USBFunctionDescriptors, config_parse_path, 0, offsetof(Service, usb_function_descriptors)
-Service.USBFunctionStrings, config_parse_path, 0, offsetof(Service, usb_function_strings)
+Service.USBFunctionDescriptors, config_parse_unit_path_printf, 0, offsetof(Service, usb_function_descriptors)
+Service.USBFunctionStrings, config_parse_unit_path_printf, 0, offsetof(Service, usb_function_strings)
EXEC_CONTEXT_CONFIG_ITEMS(Service)m4_dnl
CGROUP_CONTEXT_CONFIG_ITEMS(Service)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Service)m4_dnl
@@ -332,9 +332,9 @@ Socket.Service, config_parse_socket_service, 0,
Socket.TriggerLimitIntervalSec, config_parse_sec, 0, offsetof(Socket, trigger_limit.interval)
Socket.TriggerLimitBurst, config_parse_unsigned, 0, offsetof(Socket, trigger_limit.burst)
m4_ifdef(`HAVE_SMACK',
-`Socket.SmackLabel, config_parse_string, 0, offsetof(Socket, smack)
-Socket.SmackLabelIPIn, config_parse_string, 0, offsetof(Socket, smack_ip_in)
-Socket.SmackLabelIPOut, config_parse_string, 0, offsetof(Socket, smack_ip_out)',
+`Socket.SmackLabel, config_parse_unit_string_printf, 0, offsetof(Socket, smack)
+Socket.SmackLabelIPIn, config_parse_unit_string_printf, 0, offsetof(Socket, smack_ip_in)
+Socket.SmackLabelIPOut, config_parse_unit_string_printf, 0, offsetof(Socket, smack_ip_out)',
`Socket.SmackLabel, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
Socket.SmackLabelIPIn, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
Socket.SmackLabelIPOut, config_parse_warn_compat, DISABLED_CONFIGURATION, 0')
@@ -354,9 +354,9 @@ BusName.AllowWorld, config_parse_bus_policy_world, 0,
BusName.SELinuxContext, config_parse_exec_selinux_context, 0, 0
BusName.AcceptFileDescriptors, config_parse_bool, 0, offsetof(BusName, accept_fd)
m4_dnl
-Mount.What, config_parse_string, 0, offsetof(Mount, parameters_fragment.what)
+Mount.What, config_parse_unit_string_printf, 0, offsetof(Mount, parameters_fragment.what)
Mount.Where, config_parse_path, 0, offsetof(Mount, where)
-Mount.Options, config_parse_string, 0, offsetof(Mount, parameters_fragment.options)
+Mount.Options, config_parse_unit_string_printf, 0, offsetof(Mount, parameters_fragment.options)
Mount.Type, config_parse_string, 0, offsetof(Mount, parameters_fragment.fstype)
Mount.TimeoutSec, config_parse_sec, 0, offsetof(Mount, timeout_usec)
Mount.DirectoryMode, config_parse_mode, 0, offsetof(Mount, directory_mode)
@@ -373,7 +373,7 @@ Automount.TimeoutIdleSec, config_parse_sec, 0,
m4_dnl
Swap.What, config_parse_path, 0, offsetof(Swap, parameters_fragment.what)
Swap.Priority, config_parse_int, 0, offsetof(Swap, parameters_fragment.priority)
-Swap.Options, config_parse_string, 0, offsetof(Swap, parameters_fragment.options)
+Swap.Options, config_parse_unit_string_printf, 0, offsetof(Swap, parameters_fragment.options)
Swap.TimeoutSec, config_parse_sec, 0, offsetof(Swap, timeout_usec)
EXEC_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl
CGROUP_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 970eed27c1..687cd1dd31 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -579,6 +579,7 @@ int config_parse_exec(
void *userdata) {
ExecCommand **e = data;
+ Unit *u = userdata;
const char *p;
bool semicolon;
int r;
@@ -604,8 +605,7 @@ int config_parse_exec(
_cleanup_free_ ExecCommand *nce = NULL;
_cleanup_strv_free_ char **n = NULL;
size_t nlen = 0, nbufsize = 0;
- char *f;
- int i;
+ const char *f;
semicolon = false;
@@ -614,7 +614,7 @@ int config_parse_exec(
return 0;
f = firstword;
- for (i = 0; i < 3; i++) {
+ for (;;) {
/* We accept an absolute path as first argument.
* If it's prefixed with - and the path doesn't exist,
* we ignore it instead of erroring out;
@@ -631,47 +631,47 @@ int config_parse_exec(
f++;
}
- if (isempty(f)) {
+ r = unit_full_printf(u, f, &path);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", f);
+ return 0;
+ }
+
+ if (isempty(path)) {
/* First word is either "-" or "@" with no command. */
log_syntax(unit, LOG_ERR, filename, line, 0, "Empty path in command line, ignoring: \"%s\"", rvalue);
return 0;
}
- if (!string_is_safe(f)) {
+ if (!string_is_safe(path)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path contains special characters, ignoring: %s", rvalue);
return 0;
}
- if (!path_is_absolute(f)) {
+ if (!path_is_absolute(path)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path is not absolute, ignoring: %s", rvalue);
return 0;
}
- if (endswith(f, "/")) {
+ if (endswith(path, "/")) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path specifies a directory, ignoring: %s", rvalue);
return 0;
}
- if (f == firstword) {
- path = firstword;
- firstword = NULL;
- } else {
- path = strdup(f);
- if (!path)
- return log_oom();
- }
-
if (!separate_argv0) {
+ char *w = NULL;
+
if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
return log_oom();
- f = strdup(path);
- if (!f)
+
+ w = strdup(path);
+ if (!w)
return log_oom();
- n[nlen++] = f;
+ n[nlen++] = w;
n[nlen] = NULL;
}
path_kill_slashes(path);
while (!isempty(p)) {
- _cleanup_free_ char *word = NULL;
+ _cleanup_free_ char *word = NULL, *resolved = NULL;
/* Check explicitly for an unquoted semicolon as
* command separator token. */
@@ -682,18 +682,21 @@ int config_parse_exec(
break;
}
- /* Check for \; explicitly, to not confuse it with \\;
- * or "\;" or "\\;" etc. extract_first_word would
- * return the same for all of those. */
+ /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
+ * extract_first_word() would return the same for all of those. */
if (p[0] == '\\' && p[1] == ';' && (!p[2] || strchr(WHITESPACE, p[2]))) {
+ char *w;
+
p += 2;
p += strspn(p, WHITESPACE);
+
if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
return log_oom();
- f = strdup(";");
- if (!f)
+
+ w = strdup(";");
+ if (!w)
return log_oom();
- n[nlen++] = f;
+ n[nlen++] = w;
n[nlen] = NULL;
continue;
}
@@ -701,14 +704,20 @@ int config_parse_exec(
r = extract_first_word_and_warn(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
if (r == 0)
break;
- else if (r < 0)
+ if (r < 0)
return 0;
+ r = unit_full_printf(u, word, &resolved);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to resolve unit specifiers on %s, ignoring: %m", word);
+ return 0;
+ }
+
if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
return log_oom();
- n[nlen++] = word;
+ n[nlen++] = resolved;
n[nlen] = NULL;
- word = NULL;
+ resolved = NULL;
}
if (!n || !n[0]) {
@@ -1326,7 +1335,7 @@ int config_parse_exec_selinux_context(
} else
ignore = false;
- r = unit_name_printf(u, rvalue, &k);
+ r = unit_full_printf(u, rvalue, &k);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
return 0;
@@ -1374,7 +1383,7 @@ int config_parse_exec_apparmor_profile(
} else
ignore = false;
- r = unit_name_printf(u, rvalue, &k);
+ r = unit_full_printf(u, rvalue, &k);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
return 0;
@@ -1422,7 +1431,7 @@ int config_parse_exec_smack_process_label(
} else
ignore = false;
- r = unit_name_printf(u, rvalue, &k);
+ r = unit_full_printf(u, rvalue, &k);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
return 0;
@@ -1689,7 +1698,7 @@ int config_parse_fdname(
return 0;
}
- r = unit_name_printf(UNIT(s), rvalue, &p);
+ r = unit_full_printf(UNIT(s), rvalue, &p);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
return 0;
@@ -2555,7 +2564,7 @@ int config_parse_unit_requires_mounts_for(
assert(data);
for (p = rvalue;; ) {
- _cleanup_free_ char *word = NULL;
+ _cleanup_free_ char *word = NULL, *resolved = NULL;
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
if (r == 0)
@@ -2573,9 +2582,15 @@ int config_parse_unit_requires_mounts_for(
continue;
}
- r = unit_require_mounts_for(u, word);
+ r = unit_full_printf(u, word, &resolved);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add required mount \"%s\", ignoring: %m", word);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit name \"%s\", ignoring: %m", word);
+ continue;
+ }
+
+ r = unit_require_mounts_for(u, resolved);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add required mount \"%s\", ignoring: %m", resolved);
continue;
}
}
@@ -3710,7 +3725,7 @@ int config_parse_runtime_directory(
return 0;
}
- r = unit_name_printf(u, word, &k);
+ r = unit_full_printf(u, word, &k);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to resolve specifiers in \"%s\", ignoring: %m", word);
@@ -3812,8 +3827,8 @@ int config_parse_namespace_path_strv(
void *data,
void *userdata) {
+ Unit *u = userdata;
char*** sv = data;
- const char *prev;
const char *cur;
int r;
@@ -3828,10 +3843,10 @@ int config_parse_namespace_path_strv(
return 0;
}
- prev = cur = rvalue;
+ cur = rvalue;
for (;;) {
- _cleanup_free_ char *word = NULL;
- int offset;
+ _cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL;
+ bool ignore_enoent;
r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
if (r == 0)
@@ -3839,31 +3854,37 @@ int config_parse_namespace_path_strv(
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage, ignoring: %s", prev);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
return 0;
}
if (!utf8_is_valid(word)) {
log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
- prev = cur;
continue;
}
- offset = word[0] == '-';
- if (!path_is_absolute(word + offset)) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", word);
- prev = cur;
+ ignore_enoent = word[0] == '-';
+
+ r = unit_full_printf(u, word + ignore_enoent, &resolved);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers in %s: %m", word);
+ continue;
+ }
+
+ if (!path_is_absolute(resolved)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", resolved);
continue;
}
- path_kill_slashes(word + offset);
+ path_kill_slashes(resolved);
- r = strv_push(sv, word);
+ joined = strjoin(ignore_enoent ? "-" : "", resolved);
+
+ r = strv_push(sv, joined);
if (r < 0)
return log_oom();
- prev = cur;
- word = NULL;
+ joined = NULL;
}
return 0;
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
index 76dfcfa6d7..c83bb561c7 100644
--- a/src/core/machine-id-setup.c
+++ b/src/core/machine-id-setup.c
@@ -199,7 +199,7 @@ int machine_id_commit(const char *root) {
etc_machine_id = prefix_roota(root, "/etc/machine-id");
- r = path_is_mount_point(etc_machine_id, 0);
+ r = path_is_mount_point(etc_machine_id, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id);
if (r == 0) {
diff --git a/src/core/manager.c b/src/core/manager.c
index 1f663d3c1d..1192b20b74 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -17,7 +17,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/kd.h>
@@ -233,6 +232,7 @@ static void manager_print_jobs_in_progress(Manager *m) {
static int have_ask_password(void) {
_cleanup_closedir_ DIR *dir;
+ struct dirent *de;
dir = opendir("/run/systemd/ask-password");
if (!dir) {
@@ -242,19 +242,11 @@ static int have_ask_password(void) {
return -errno;
}
- for (;;) {
- struct dirent *de;
-
- errno = 0;
- de = readdir(dir);
- if (!de && errno > 0)
- return -errno;
- if (!de)
- return false;
-
+ FOREACH_DIRENT_ALL(de, dir, return -errno) {
if (startswith(de->d_name, "ask."))
return true;
}
+ return false;
}
static int manager_dispatch_ask_password_fd(sd_event_source *source,
@@ -2958,7 +2950,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_STARTUP_FINISHED),
+ LOG_MESSAGE_ID(SD_MESSAGE_USER_STARTUP_FINISHED),
"USERSPACE_USEC="USEC_FMT, userspace_usec,
LOG_MESSAGE("Startup finished in %s.",
format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)),
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index ca63a93e8b..6338067d7e 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -159,7 +159,7 @@ static int mount_one(const MountPoint *p, bool relabel) {
if (relabel)
(void) label_fix(p->where, true, true);
- r = path_is_mount_point(p->where, AT_SYMLINK_FOLLOW);
+ r = path_is_mount_point(p->where, NULL, AT_SYMLINK_FOLLOW);
if (r < 0 && r != -ENOENT) {
log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, r, "Failed to determine whether %s is a mount point: %m", p->where);
return (p->mode & MNT_FATAL) ? r : 0;
diff --git a/src/core/mount.c b/src/core/mount.c
index 1c2be28d55..0c4d061c27 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -1509,7 +1509,7 @@ static int mount_setup_unit(
fail:
log_warning_errno(r, "Failed to set up mount unit: %m");
- if (delete && u)
+ if (delete)
unit_free(u);
return r;
diff --git a/src/core/namespace.c b/src/core/namespace.c
index e9ad26bfc3..aca47a4d2f 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -596,7 +596,7 @@ static int apply_mount(
case READONLY:
case READWRITE:
- r = path_is_mount_point(bind_mount_path(m), 0);
+ r = path_is_mount_point(bind_mount_path(m), NULL, 0);
if (r < 0)
return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", bind_mount_path(m));
if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY bit for the mount point if needed. */
@@ -665,7 +665,7 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned
_cleanup_free_ char *chased = NULL;
int k;
- k = chase_symlinks(bind_mount_path(f), root_directory, &chased);
+ k = chase_symlinks(bind_mount_path(f), root_directory, 0, &chased);
if (k < 0) {
/* Get only real errors */
if (r >= 0 && (k != -ENOENT || !f->ignore))
@@ -860,7 +860,7 @@ int setup_namespace(
if (root_directory) {
/* Turn directory into bind mount, if it isn't one yet */
- r = path_is_mount_point(root_directory, AT_SYMLINK_FOLLOW);
+ r = path_is_mount_point(root_directory, NULL, AT_SYMLINK_FOLLOW);
if (r < 0)
goto finish;
if (r == 0) {
diff --git a/src/core/scope.c b/src/core/scope.c
index d6e1f8e392..9540fb67d9 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -273,7 +273,9 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
if (state == SCOPE_STOP_SIGTERM)
skip_signal = bus_scope_send_request_stop(s) > 0;
- if (!skip_signal) {
+ if (skip_signal)
+ r = 1; /* wait */
+ else {
r = unit_kill_context(
UNIT(s),
&s->kill_context,
@@ -283,8 +285,7 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
-1, -1, false);
if (r < 0)
goto fail;
- } else
- r = 1;
+ }
if (r > 0) {
r = scope_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec));
diff --git a/src/core/service.c b/src/core/service.c
index c68a7122b6..576416ad29 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -49,7 +49,6 @@
#include "string-util.h"
#include "strv.h"
#include "unit-name.h"
-#include "unit-printf.h"
#include "unit.h"
#include "utf8.h"
#include "util.h"
@@ -1205,7 +1204,7 @@ static int service_spawn(
ExecFlags flags,
pid_t *_pid) {
- _cleanup_strv_free_ char **argv = NULL, **final_env = NULL, **our_env = NULL, **fd_names = NULL;
+ _cleanup_strv_free_ char **final_env = NULL, **our_env = NULL, **fd_names = NULL;
_cleanup_free_ int *fds = NULL;
unsigned n_fds = 0, n_env = 0;
const char *path;
@@ -1263,10 +1262,6 @@ static int service_spawn(
if (r < 0)
return r;
- r = unit_full_printf_strv(UNIT(s), c->argv, &argv);
- if (r < 0)
- return r;
-
our_env = new0(char*, 9);
if (!our_env)
return -ENOMEM;
@@ -1349,7 +1344,7 @@ static int service_spawn(
} else
path = UNIT(s)->cgroup_path;
- exec_params.argv = argv;
+ exec_params.argv = c->argv;
exec_params.environment = final_env;
exec_params.fds = fds;
exec_params.fd_names = fd_names;
@@ -1714,7 +1709,7 @@ static void service_enter_running(Service *s, ServiceResult f) {
}
} else if (f != SERVICE_SUCCESS)
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
else if (s->remain_after_exit)
service_set_state(s, SERVICE_EXITED);
else
@@ -1851,7 +1846,7 @@ static void service_enter_start(Service *s) {
fail:
log_unit_warning_errno(UNIT(s), r, "Failed to run 'start' task: %m");
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
}
static void service_enter_start_pre(Service *s) {
@@ -1997,9 +1992,7 @@ static void service_run_next_control(Service *s) {
fail:
log_unit_warning_errno(UNIT(s), r, "Failed to run next control task: %m");
- if (s->state == SERVICE_START_PRE)
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
- else if (s->state == SERVICE_STOP)
+ if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_STOP))
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
else if (s->state == SERVICE_STOP_POST)
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
@@ -2600,7 +2593,7 @@ static void service_notify_cgroup_empty_event(Unit *u) {
case SERVICE_START:
if (s->type == SERVICE_NOTIFY) {
/* No chance of getting a ready notification anymore */
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_PROTOCOL);
+ service_enter_stop_post(s, SERVICE_FAILURE_PROTOCOL);
break;
}
@@ -2613,7 +2606,7 @@ static void service_notify_cgroup_empty_event(Unit *u) {
service_unwatch_pid_file(s);
if (s->state == SERVICE_START)
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_PROTOCOL);
+ service_enter_stop_post(s, SERVICE_FAILURE_PROTOCOL);
else
service_enter_stop(s, SERVICE_FAILURE_PROTOCOL);
}
@@ -2747,17 +2740,17 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
if (f == SERVICE_SUCCESS)
service_enter_start_post(s);
else
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
break;
} else if (s->type == SERVICE_NOTIFY) {
/* Only enter running through a notification, so that the
* SERVICE_START state signifies that no ready notification
* has been received */
if (f != SERVICE_SUCCESS)
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
else if (!s->remain_after_exit)
/* The service has never been active */
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_PROTOCOL);
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_PROTOCOL);
break;
}
@@ -2837,7 +2830,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
if (f == SERVICE_SUCCESS)
service_enter_start(s);
else
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
break;
case SERVICE_START:
@@ -2846,7 +2839,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
break;
if (f != SERVICE_SUCCESS) {
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
break;
}
@@ -2863,7 +2856,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
if (!has_start_post && r < 0) {
r = service_demand_pid_file(s);
if (r < 0 || !cgroup_good(s))
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_PROTOCOL);
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_PROTOCOL);
break;
}
} else
@@ -2959,7 +2952,7 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
case SERVICE_START_PRE:
case SERVICE_START:
log_unit_warning(UNIT(s), "%s operation timed out. Terminating.", s->state == SERVICE_START ? "Start" : "Start-pre");
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT);
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
break;
case SERVICE_START_POST:
diff --git a/src/core/service.h b/src/core/service.h
index e09722a952..ff9cfaeb88 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -79,6 +79,8 @@ typedef enum NotifyState {
_NOTIFY_STATE_INVALID = -1
} NotifyState;
+/* The values of this enum are referenced in man/systemd.exec.xml and src/shared/bus-unit-util.c.
+ * Update those sources for each change to this enum. */
typedef enum ServiceResult {
SERVICE_SUCCESS,
SERVICE_FAILURE_RESOURCES, /* a bit of a misnomer, just our catch-all error for errnos we didn't expect */
diff --git a/src/core/socket.c b/src/core/socket.c
index 1a53d47f21..fee9b702e6 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -54,7 +54,6 @@
#include "string-util.h"
#include "strv.h"
#include "unit-name.h"
-#include "unit-printf.h"
#include "unit.h"
#include "user-util.h"
#include "in-addr-util.h"
@@ -1740,7 +1739,6 @@ static int socket_coldplug(Unit *u) {
}
static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
- _cleanup_free_ char **argv = NULL;
pid_t pid;
int r;
ExecParameters exec_params = {
@@ -1772,11 +1770,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
if (r < 0)
return r;
- r = unit_full_printf_strv(UNIT(s), c->argv, &argv);
- if (r < 0)
- return r;
-
- exec_params.argv = argv;
+ exec_params.argv = c->argv;
exec_params.environment = UNIT(s)->manager->environment;
exec_params.confirm_spawn = manager_get_confirm_spawn(UNIT(s)->manager);
exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported;
diff --git a/src/core/swap.c b/src/core/swap.c
index bf404db8c3..e9468e105c 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -420,7 +420,7 @@ static int swap_setup_unit(
fail:
log_unit_warning_errno(u, r, "Failed to load swap unit: %m");
- if (delete && u)
+ if (delete)
unit_free(u);
return r;
diff --git a/src/core/umount.c b/src/core/umount.c
index 1e5459ed80..2f4b12bdb9 100644
--- a/src/core/umount.c
+++ b/src/core/umount.c
@@ -344,24 +344,29 @@ static int delete_loopback(const char *device) {
}
static int delete_dm(dev_t devnum) {
- _cleanup_close_ int fd = -1;
- int r;
+
struct dm_ioctl dm = {
- .version = {DM_VERSION_MAJOR,
- DM_VERSION_MINOR,
- DM_VERSION_PATCHLEVEL},
+ .version = {
+ DM_VERSION_MAJOR,
+ DM_VERSION_MINOR,
+ DM_VERSION_PATCHLEVEL
+ },
.data_size = sizeof(dm),
.dev = devnum,
};
+ _cleanup_close_ int fd = -1;
+
assert(major(devnum) != 0);
fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
if (fd < 0)
return -errno;
- r = ioctl(fd, DM_DEV_REMOVE, &dm);
- return r >= 0 ? 0 : -errno;
+ if (ioctl(fd, DM_DEV_REMOVE, &dm) < 0)
+ return -errno;
+
+ return 0;
}
static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) {
diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c
index 1f5dc6fd88..746e1a46ef 100644
--- a/src/core/unit-printf.c
+++ b/src/core/unit-printf.c
@@ -78,12 +78,18 @@ static int specifier_filename(char specifier, void *data, void *userdata, char *
return unit_name_to_path(u->id, ret);
}
+static void bad_specifier(Unit *u, char specifier) {
+ log_unit_warning(u, "Specifier '%%%c' used in unit configuration, which is deprecated. Please update your unit file, as it does not work as intended.", specifier);
+}
+
static int specifier_cgroup(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
char *n;
assert(u);
+ bad_specifier(u, specifier);
+
if (u->cgroup_path)
n = strdup(u->cgroup_path);
else
@@ -101,6 +107,8 @@ static int specifier_cgroup_root(char specifier, void *data, void *userdata, cha
assert(u);
+ bad_specifier(u, specifier);
+
n = strdup(u->manager->cgroup_root);
if (!n)
return -ENOMEM;
@@ -115,6 +123,8 @@ static int specifier_cgroup_slice(char specifier, void *data, void *userdata, ch
assert(u);
+ bad_specifier(u, specifier);
+
if (UNIT_ISSET(u->slice)) {
Unit *slice;
@@ -194,13 +204,20 @@ static int specifier_user_shell(char specifier, void *data, void *userdata, char
int unit_name_printf(Unit *u, const char* format, char **ret) {
/*
- * This will use the passed string as format string and
- * replace the following specifiers:
+ * This will use the passed string as format string and replace the following specifiers (which should all be
+ * safe for inclusion in unit names):
*
* %n: the full id of the unit (foo@bar.waldo)
* %N: the id of the unit without the suffix (foo@bar)
* %p: the prefix (foo)
* %i: the instance (bar)
+ *
+ * %U: the UID of the running user
+ * %u: the username of the running user
+ *
+ * %m: the machine ID of the running system
+ * %H: the host name of the running system
+ * %b: the boot ID of the running system
*/
const Specifier table[] = {
@@ -208,7 +225,14 @@ int unit_name_printf(Unit *u, const char* format, char **ret) {
{ 'N', specifier_prefix_and_instance, NULL },
{ 'p', specifier_prefix, NULL },
{ 'i', specifier_string, u->instance },
- { 0, NULL, NULL }
+
+ { 'U', specifier_user_id, NULL },
+ { 'u', specifier_user_name, NULL },
+
+ { 'm', specifier_machine_id, NULL },
+ { 'H', specifier_host_name, NULL },
+ { 'b', specifier_boot_id, NULL },
+ {}
};
assert(u);
@@ -220,22 +244,23 @@ int unit_name_printf(Unit *u, const char* format, char **ret) {
int unit_full_printf(Unit *u, const char *format, char **ret) {
- /* This is similar to unit_name_printf() but also supports
- * unescaping. Also, adds a couple of additional codes:
+ /* This is similar to unit_name_printf() but also supports unescaping. Also, adds a couple of additional codes
+ * (which are likely not suitable for unescaped inclusion in unit names):
+ *
+ * %f: the unescaped instance if set, otherwise the id unescaped as path
+ * %c: cgroup path of unit (deprecated)
+ * %r: where units in this slice are placed in the cgroup tree (deprecated)
+ * %R: the root of this systemd's instance tree (deprecated)
+ * %t: the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR)
+ *
+ * %h: the homedir of the running user
+ * %s: the shell of the running user
+ *
+ * %v: `uname -r` of the running system
*
- * %f the instance if set, otherwise the id
- * %c cgroup path of unit
- * %r where units in this slice are placed in the cgroup tree
- * %R the root of this systemd's instance tree
- * %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR)
- * %U the UID of the running user
- * %u the username of the running user
- * %h the homedir of the running user
- * %s the shell of the running user
- * %m the machine ID of the running system
- * %H the host name of the running system
- * %b the boot ID of the running system
- * %v `uname -r` of the running system
+ * NOTICE: When you add new entries here, please be careful: specifiers which depend on settings of the unit
+ * file itself are broken by design, as they would resolve differently depending on whether they are used
+ * before or after the relevant configuration setting. Hence: don't add them.
*/
const Specifier table[] = {
diff --git a/src/core/unit.c b/src/core/unit.c
index cba6342eca..ab40135736 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -516,7 +516,8 @@ void unit_free(Unit *u) {
Iterator i;
char *t;
- assert(u);
+ if (!u)
+ return;
if (u->transient_file)
fclose(u->transient_file);
@@ -3754,14 +3755,14 @@ int unit_kill_context(
bool main_pid_alien) {
bool wait_for_exit = false, send_sighup;
- cg_kill_log_func_t log_func;
+ cg_kill_log_func_t log_func = NULL;
int sig, r;
assert(u);
assert(c);
- /* Kill the processes belonging to this unit, in preparation for shutting the unit down. Returns > 0 if we
- * killed something worth waiting for, 0 otherwise. */
+ /* Kill the processes belonging to this unit, in preparation for shutting the unit down.
+ * Returns > 0 if we killed something worth waiting for, 0 otherwise. */
if (c->kill_mode == KILL_NONE)
return 0;
@@ -3773,9 +3774,8 @@ int unit_kill_context(
IN_SET(k, KILL_TERMINATE, KILL_TERMINATE_AND_LOG) &&
sig != SIGHUP;
- log_func =
- k != KILL_TERMINATE ||
- IN_SET(sig, SIGKILL, SIGABRT) ? log_kill : NULL;
+ if (k != KILL_TERMINATE || IN_SET(sig, SIGKILL, SIGABRT))
+ log_func = log_kill;
if (main_pid > 0) {
if (log_func)
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 01e7ee9973..c7fec609df 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -651,7 +651,7 @@ int main(int argc, char *argv[]) {
k = crypt_init(&cd, arg_header);
} else
k = crypt_init(&cd, argv[3]);
- if (k) {
+ if (k != 0) {
log_error_errno(k, "crypt_init() failed: %m");
goto finish;
}
diff --git a/src/delta/delta.c b/src/delta/delta.c
index 04de75475d..9a44b15da7 100644
--- a/src/delta/delta.c
+++ b/src/delta/delta.c
@@ -87,14 +87,15 @@ static enum {
static int equivalent(const char *a, const char *b) {
_cleanup_free_ char *x = NULL, *y = NULL;
+ int r;
- x = canonicalize_file_name(a);
- if (!x)
- return -errno;
+ r = chase_symlinks(a, NULL, 0, &x);
+ if (r < 0)
+ return r;
- y = canonicalize_file_name(b);
- if (!y)
- return -errno;
+ r = chase_symlinks(b, NULL, 0, &y);
+ if (r < 0)
+ return r;
return path_equal(x, y);
}
@@ -296,6 +297,7 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const
static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *path, bool dropins) {
_cleanup_closedir_ DIR *d;
+ struct dirent *de;
assert(top);
assert(bottom);
@@ -312,16 +314,10 @@ static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const ch
return log_error_errno(errno, "Failed to open %s: %m", path);
}
- for (;;) {
- struct dirent *de;
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
int k;
char *p;
- errno = 0;
- de = readdir(d);
- if (!de)
- return -errno;
-
dirent_ensure_type(d, de);
if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d"))
@@ -353,6 +349,7 @@ static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const ch
return k;
}
}
+ return 0;
}
static int should_skip_prefix(const char* p) {
@@ -360,7 +357,7 @@ static int should_skip_prefix(const char* p) {
int r;
_cleanup_free_ char *target = NULL;
- r = chase_symlinks(p, NULL, &target);
+ r = chase_symlinks(p, NULL, 0, &target);
if (r < 0)
return r;
diff --git a/src/dissect/Makefile b/src/dissect/Makefile
new file mode 120000
index 0000000000..d0b0e8e008
--- /dev/null
+++ b/src/dissect/Makefile
@@ -0,0 +1 @@
+../Makefile \ No newline at end of file
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c
new file mode 100644
index 0000000000..f2f1e135ec
--- /dev/null
+++ b/src/dissect/dissect.c
@@ -0,0 +1,275 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <getopt.h>
+
+#include "architecture.h"
+#include "dissect-image.h"
+#include "hexdecoct.h"
+#include "log.h"
+#include "loop-util.h"
+#include "string-util.h"
+#include "util.h"
+
+static enum {
+ ACTION_DISSECT,
+ ACTION_MOUNT,
+} arg_action = ACTION_DISSECT;
+static const char *arg_image = NULL;
+static const char *arg_path = NULL;
+static DissectImageFlags arg_flags = DISSECT_IMAGE_DISCARD_ON_LOOP;
+static void *arg_root_hash = NULL;
+static size_t arg_root_hash_size = 0;
+
+static void help(void) {
+ printf("%s [OPTIONS...] IMAGE\n"
+ "%s [OPTIONS...] --mount IMAGE PATH\n"
+ "Dissect a file system OS image.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " -m --mount Mount the image to the specified directory\n"
+ " -r --read-only Mount read-only\n"
+ " --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
+ " --root-hash=HASH Specify root hash for verity\n",
+ program_invocation_short_name,
+ program_invocation_short_name);
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_DISCARD,
+ ARG_ROOT_HASH,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "mount", no_argument, NULL, 'm' },
+ { "read-only", no_argument, NULL, 'r' },
+ { "discard", required_argument, NULL, ARG_DISCARD },
+ { "root-hash", required_argument, NULL, ARG_ROOT_HASH },
+ {}
+ };
+
+ int c, r;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "hmr", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ help();
+ return 0;
+
+ case ARG_VERSION:
+ return version();
+
+ case 'm':
+ arg_action = ACTION_MOUNT;
+ break;
+
+ case 'r':
+ arg_flags |= DISSECT_IMAGE_READ_ONLY;
+ break;
+
+ case ARG_DISCARD: {
+ DissectImageFlags flags;
+
+ if (streq(optarg, "disabled"))
+ flags = 0;
+ else if (streq(optarg, "loop"))
+ flags = DISSECT_IMAGE_DISCARD_ON_LOOP;
+ else if (streq(optarg, "all"))
+ flags = DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD;
+ else if (streq(optarg, "crypt"))
+ flags = DISSECT_IMAGE_DISCARD_ANY;
+ else {
+ log_error("Unknown --discard= parameter: %s", optarg);
+ return -EINVAL;
+ }
+ arg_flags = (arg_flags & ~DISSECT_IMAGE_DISCARD_ANY) | flags;
+
+ break;
+ }
+
+ case ARG_ROOT_HASH: {
+ void *p;
+ size_t l;
+
+ r = unhexmem(optarg, strlen(optarg), &p, &l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse root hash: %s", optarg);
+ if (l < sizeof(sd_id128_t)) {
+ log_error("Root hash must be at least 128bit long: %s", optarg);
+ free(p);
+ return -EINVAL;
+ }
+
+ free(arg_root_hash);
+ arg_root_hash = p;
+ arg_root_hash_size = l;
+ break;
+ }
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unhandled option");
+ }
+
+ }
+
+ switch (arg_action) {
+
+ case ACTION_DISSECT:
+ if (optind + 1 != argc) {
+ log_error("Expected a file path as only argument.");
+ return -EINVAL;
+ }
+
+ arg_image = argv[optind];
+ arg_flags |= DISSECT_IMAGE_READ_ONLY;
+ break;
+
+ case ACTION_MOUNT:
+ if (optind + 2 != argc) {
+ log_error("Expected a file path and mount point path as only arguments.");
+ return -EINVAL;
+ }
+
+ arg_image = argv[optind];
+ arg_path = argv[optind + 1];
+ break;
+
+ default:
+ assert_not_reached("Unknown action.");
+ }
+
+ return 1;
+}
+
+int main(int argc, char *argv[]) {
+ _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
+ _cleanup_(decrypted_image_unrefp) DecryptedImage *di = NULL;
+ _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
+ int r;
+
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ goto finish;
+
+ r = loop_device_make_by_path(arg_image, (arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR, &d);
+ if (r < 0) {
+ log_error_errno(r, "Failed to set up loopback device: %m");
+ goto finish;
+ }
+
+ r = dissect_image(d->fd, arg_root_hash, arg_root_hash_size, &m);
+ if (r == -ENOPKG) {
+ log_error_errno(r, "Couldn't identify a suitable partition table or file system in %s.", arg_image);
+ goto finish;
+ }
+ if (r == -EADDRNOTAVAIL) {
+ log_error_errno(r, "No root partition for specified root hash found in %s.", arg_image);
+ goto finish;
+ }
+ if (r < 0) {
+ log_error_errno(r, "Failed to dissect image: %m");
+ goto finish;
+ }
+
+ switch (arg_action) {
+
+ case ACTION_DISSECT: {
+ unsigned i;
+
+ for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
+ DissectedPartition *p = m->partitions + i;
+ int k;
+
+ if (!p->found)
+ continue;
+
+ printf("Found %s '%s' partition",
+ p->rw ? "writable" : "read-only",
+ partition_designator_to_string(i));
+
+ if (p->fstype)
+ printf(" of type %s", p->fstype);
+
+ if (p->architecture != _ARCHITECTURE_INVALID)
+ printf(" for %s", architecture_to_string(p->architecture));
+
+ k = PARTITION_VERITY_OF(i);
+ if (k >= 0)
+ printf(" %s verity", m->partitions[k].found ? "with" : "without");
+
+ if (p->partno >= 0)
+ printf(" on partition #%i", p->partno);
+
+ if (p->node)
+ printf(" (%s)", p->node);
+
+ putchar('\n');
+ }
+
+ break;
+ }
+
+ case ACTION_MOUNT:
+ r = dissected_image_decrypt_interactively(m, NULL, arg_root_hash, arg_root_hash_size, arg_flags, &di);
+ if (r < 0)
+ goto finish;
+
+ r = dissected_image_mount(m, arg_path, arg_flags);
+ if (r < 0) {
+ log_error_errno(r, "Failed to mount image: %m");
+ goto finish;
+ }
+
+ if (di) {
+ r = decrypted_image_relinquish(di);
+ if (r < 0) {
+ log_error_errno(r, "Failed to relinquish DM devices: %m");
+ goto finish;
+ }
+ }
+
+ loop_device_relinquish(d);
+ break;
+
+ default:
+ assert_not_reached("Unknown action.");
+ }
+
+finish:
+ free(arg_root_hash);
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index 52cde493e5..0f95f0d813 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -252,7 +252,7 @@ static bool path_is_busy(const char *where) {
int r;
/* already a mountpoint; generators run during reload */
- r = path_is_mount_point(where, AT_SYMLINK_FOLLOW);
+ r = path_is_mount_point(where, NULL, AT_SYMLINK_FOLLOW);
if (r > 0)
return false;
diff --git a/src/journal-remote/log-generator.py b/src/journal-remote/log-generator.py
index 24874e960c..7b434b334e 100755
--- a/src/journal-remote/log-generator.py
+++ b/src/journal-remote/log-generator.py
@@ -29,7 +29,7 @@ _SOURCE_REALTIME_TIMESTAMP={source_realtime_ts}
DATA={data}
"""
-m = 0x198603b12d7
+m = 0x198603b12d7
realtime_ts = 1404101101501873
monotonic_ts = 1753961140951
source_realtime_ts = 1404101101483516
@@ -71,5 +71,5 @@ for i in range(OPTIONS.n):
print('.', file=sys.stderr, end='', flush=True)
if OPTIONS.dots:
- print(file=sys.stderr)
+ print(file=sys.stderr)
print('Wrote {} bytes'.format(bytes), file=sys.stderr)
diff --git a/src/journal/compress.c b/src/journal/compress.c
index ba734b5561..818a720ba8 100644
--- a/src/journal/compress.c
+++ b/src/journal/compress.c
@@ -112,7 +112,11 @@ int compress_blob_lz4(const void *src, uint64_t src_size,
if (src_size < 9)
return -ENOBUFS;
+#if LZ4_VERSION_NUMBER >= 10700
+ r = LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
+#else
r = LZ4_compress_limitedOutput(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
+#endif
if (r <= 0)
return -ENOBUFS;
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 22cab67824..10d3ff3b45 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -938,21 +938,21 @@ static int add_matches(sd_journal *j, char **args) {
have_term = false;
} else if (path_is_absolute(*i)) {
- _cleanup_free_ char *p, *t = NULL, *t2 = NULL, *interpreter = NULL;
- const char *path;
+ _cleanup_free_ char *p = NULL, *t = NULL, *t2 = NULL, *interpreter = NULL;
struct stat st;
- p = canonicalize_file_name(*i);
- path = p ?: *i;
+ r = chase_symlinks(*i, NULL, 0, &p);
+ if (r < 0)
+ return log_error_errno(r, "Couldn't canonicalize path: %m");
- if (lstat(path, &st) < 0)
+ if (lstat(p, &st) < 0)
return log_error_errno(errno, "Couldn't stat file: %m");
if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) {
- if (executable_is_script(path, &interpreter) > 0) {
+ if (executable_is_script(p, &interpreter) > 0) {
_cleanup_free_ char *comm;
- comm = strndup(basename(path), 15);
+ comm = strndup(basename(p), 15);
if (!comm)
return log_oom();
@@ -968,7 +968,7 @@ static int add_matches(sd_journal *j, char **args) {
return log_oom();
}
} else {
- t = strappend("_EXE=", path);
+ t = strappend("_EXE=", p);
if (!t)
return log_oom();
}
@@ -979,7 +979,7 @@ static int add_matches(sd_journal *j, char **args) {
r = sd_journal_add_match(j, t2, 0);
} else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
- r = add_matches_for_device(j, path);
+ r = add_matches_for_device(j, p);
if (r < 0)
return r;
} else {
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 28487a9451..5c6941ebd6 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -144,7 +144,7 @@ static int cache_space_refresh(Server *s, JournalStorage *storage) {
ts = now(CLOCK_MONOTONIC);
- if (space->timestamp + RECHECK_SPACE_USEC > ts)
+ if (space->timestamp != 0 && space->timestamp + RECHECK_SPACE_USEC > ts)
return 0;
r = determine_path_usage(s, storage->path, &vfs_used, &vfs_avail);
diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h
index 5aa8aca426..3fdf02da3e 100644
--- a/src/libsystemd-network/dhcp-internal.h
+++ b/src/libsystemd-network/dhcp-internal.h
@@ -30,11 +30,11 @@
#include "dhcp-protocol.h"
#include "socket-util.h"
-int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link,
+int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
uint32_t xid, const uint8_t *mac_addr,
size_t mac_addr_len, uint16_t arp_type,
uint16_t port);
-int dhcp_network_bind_udp_socket(be32_t address, uint16_t port);
+int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port);
int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
const void *packet, size_t len);
int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c
index 3c85bb0b54..65405dcce0 100644
--- a/src/libsystemd-network/dhcp-network.c
+++ b/src/libsystemd-network/dhcp-network.c
@@ -19,6 +19,7 @@
#include <errno.h>
#include <net/ethernet.h>
+#include <net/if.h>
#include <net/if_arp.h>
#include <stdio.h>
#include <string.h>
@@ -156,13 +157,14 @@ int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
bcast_addr, &eth_mac, arp_type, dhcp_hlen, port);
}
-int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) {
+int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) {
union sockaddr_union src = {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(port),
.in.sin_addr.s_addr = address,
};
_cleanup_close_ int s = -1;
+ char ifname[IF_NAMESIZE] = "";
int r, on = 1, tos = IPTOS_CLASS_CS6;
s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
@@ -177,6 +179,15 @@ int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) {
if (r < 0)
return -errno;
+ if (ifindex > 0) {
+ if (if_indextoname(ifindex, ifname) == 0)
+ return -errno;
+
+ r = setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname));
+ if (r < 0)
+ return -errno;
+ }
+
if (address == INADDR_ANY) {
r = setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
if (r < 0)
@@ -185,6 +196,7 @@ int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) {
r = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
if (r < 0)
return -errno;
+
} else {
r = setsockopt(s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on));
if (r < 0)
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index 9d78b953fc..092a1eabb0 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -86,6 +86,28 @@ int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result
return 0;
}
+static bool net_condition_test_strv(char * const *raw_patterns,
+ const char *string) {
+ if (strv_isempty(raw_patterns))
+ return true;
+
+ /* If the patterns begin with "!", edit it out and negate the test. */
+ if (raw_patterns[0][0] == '!') {
+ char **patterns;
+ unsigned i, length;
+
+ length = strv_length(raw_patterns) + 1; /* Include the NULL. */
+ patterns = newa(char*, length);
+ patterns[0] = raw_patterns[0] + 1; /* Skip the "!". */
+ for (i = 1; i < length; i++)
+ patterns[i] = raw_patterns[i];
+
+ return !string || !strv_fnmatch(patterns, string, 0);
+ }
+
+ return string && strv_fnmatch(raw_patterns, string, 0);
+}
+
bool net_match_config(const struct ether_addr *match_mac,
char * const *match_paths,
char * const *match_drivers,
@@ -117,20 +139,16 @@ bool net_match_config(const struct ether_addr *match_mac,
if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN)))
return false;
- if (!strv_isempty(match_paths) &&
- (!dev_path || !strv_fnmatch(match_paths, dev_path, 0)))
+ if (!net_condition_test_strv(match_paths, dev_path))
return false;
- if (!strv_isempty(match_drivers) &&
- (!dev_driver || !strv_fnmatch(match_drivers, dev_driver, 0)))
+ if (!net_condition_test_strv(match_drivers, dev_driver))
return false;
- if (!strv_isempty(match_types) &&
- (!dev_type || !strv_fnmatch_or_empty(match_types, dev_type, 0)))
+ if (!net_condition_test_strv(match_types, dev_type))
return false;
- if (!strv_isempty(match_names) &&
- (!dev_name || !strv_fnmatch_or_empty(match_names, dev_name, 0)))
+ if (!net_condition_test_strv(match_names, dev_name))
return false;
return true;
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 1423264806..b4bf75a3dc 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -1546,7 +1546,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
goto error;
}
- r = dhcp_network_bind_udp_socket(client->lease->address, client->port);
+ r = dhcp_network_bind_udp_socket(client->ifindex, client->lease->address, client->port);
if (r < 0) {
log_dhcp_client(client, "could not bind UDP socket");
goto error;
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index f16314a37f..0e57ab6b69 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -1022,7 +1022,7 @@ int sd_dhcp_server_start(sd_dhcp_server *server) {
}
server->fd_raw = r;
- r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
+ r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_ANY, DHCP_PORT_SERVER);
if (r < 0) {
sd_dhcp_server_stop(server);
return r;
diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c
index c10ca74b86..f5f1284e6d 100644
--- a/src/libsystemd-network/test-dhcp-client.c
+++ b/src/libsystemd-network/test-dhcp-client.c
@@ -203,7 +203,7 @@ int dhcp_network_bind_raw_socket(
return test_fd[0];
}
-int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) {
+int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) {
int fd;
fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0);
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index d48ef6bbe2..46c4dac7d7 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -511,3 +511,8 @@ global:
sd_bus_get_exit_on_disconnect;
sd_id128_get_invocation;
} LIBSYSTEMD_231;
+
+LIBSYSTEMD_233 {
+global:
+ sd_id128_get_machine_app_specific;
+} LIBSYSTEMD_232;
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index 411453e08d..bc5e92f8fe 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -28,6 +28,7 @@
#include "device-internal.h"
#include "device-private.h"
#include "device-util.h"
+#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
@@ -164,7 +165,7 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
}
if (verify) {
- r = readlink_and_canonicalize(_syspath, &syspath);
+ r = readlink_and_canonicalize(_syspath, NULL, &syspath);
if (r == -ENOENT)
/* the device does not exist (any more?) */
return -ENODEV;
@@ -1627,7 +1628,7 @@ static int device_sysattrs_read_all(sd_device *device) {
if (r < 0)
return r;
- for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+ FOREACH_DIRENT_ALL(dent, dir, return -errno) {
char *path;
struct stat statbuf;
diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c
index d4450c70a0..0d673ba655 100644
--- a/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/libsystemd/sd-id128/sd-id128.c
@@ -27,6 +27,7 @@
#include "hexdecoct.h"
#include "id128-util.h"
#include "io-util.h"
+#include "khash.h"
#include "macro.h"
#include "random-util.h"
#include "util.h"
@@ -181,3 +182,34 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
*ret = make_v4_uuid(t);
return 0;
}
+
+_public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
+ _cleanup_(khash_unrefp) khash *h = NULL;
+ sd_id128_t m, result;
+ const void *p;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ r = sd_id128_get_machine(&m);
+ if (r < 0)
+ return r;
+
+ r = khash_new_with_key(&h, "hmac(sha256)", &m, sizeof(m));
+ if (r < 0)
+ return r;
+
+ r = khash_put(h, &app_id, sizeof(app_id));
+ if (r < 0)
+ return r;
+
+ r = khash_digest_data(h, &p);
+ if (r < 0)
+ return r;
+
+ /* We chop off the trailing 16 bytes */
+ memcpy(&result, p, MIN(khash_get_size(h), sizeof(result)));
+
+ *ret = make_v4_uuid(result);
+ return 0;
+}
diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c
index 42ea0badfc..d2cfbdf5b0 100644
--- a/src/libsystemd/sd-login/sd-login.c
+++ b/src/libsystemd/sd-login/sd-login.c
@@ -793,6 +793,7 @@ _public_ int sd_get_sessions(char ***sessions) {
_public_ int sd_get_uids(uid_t **users) {
_cleanup_closedir_ DIR *d;
+ struct dirent *de;
int r = 0;
unsigned n = 0;
_cleanup_free_ uid_t *l = NULL;
@@ -801,19 +802,10 @@ _public_ int sd_get_uids(uid_t **users) {
if (!d)
return -errno;
- for (;;) {
- struct dirent *de;
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
int k;
uid_t uid;
- errno = 0;
- de = readdir(d);
- if (!de && errno > 0)
- return -errno;
-
- if (!de)
- break;
-
dirent_ensure_type(d, de);
if (!dirent_is_file(de))
diff --git a/src/libudev/libudev.c b/src/libudev/libudev.c
index 63fb05547d..d8e13288b0 100644
--- a/src/libudev/libudev.c
+++ b/src/libudev/libudev.c
@@ -97,8 +97,10 @@ _public_ struct udev *udev_new(void) {
_cleanup_fclose_ FILE *f = NULL;
udev = new0(struct udev, 1);
- if (udev == NULL)
+ if (!udev) {
+ errno = -ENOMEM;
return NULL;
+ }
udev->refcount = 1;
f = fopen("/etc/udev/udev.conf", "re");
@@ -156,7 +158,7 @@ _public_ struct udev *udev_new(void) {
/* unquote */
if (val[0] == '"' || val[0] == '\'') {
- if (val[len-1] != val[0]) {
+ if (len == 1 || val[len-1] != val[0]) {
log_debug("/etc/udev/udev.conf:%u: inconsistent quoting, skipping line.", line_nr);
continue;
}
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 23ad5d7c6a..3873bf3e96 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -1286,8 +1286,7 @@ static int flush_devices(Manager *m) {
} else {
struct dirent *de;
- while ((de = readdir(d))) {
-
+ FOREACH_DIRENT_ALL(de, d, break) {
if (!dirent_is_file(de))
continue;
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
index 0d1417ea16..888a97c2fc 100644
--- a/src/login/logind-user.c
+++ b/src/login/logind-user.c
@@ -338,7 +338,7 @@ static int user_mkdir_runtime_path(User *u) {
if (r < 0)
return log_error_errno(r, "Failed to create /run/user: %m");
- if (path_is_mount_point(u->runtime_path, 0) <= 0) {
+ if (path_is_mount_point(u->runtime_path, NULL, 0) <= 0) {
_cleanup_free_ char *t = NULL;
(void) mkdir_label(u->runtime_path, 0700);
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index 867bbc467b..e2fb882393 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -17,14 +17,23 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <sys/mount.h>
+
#include "alloc-util.h"
#include "bus-label.h"
#include "bus-util.h"
+#include "copy.h"
+#include "dissect-image.h"
#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
#include "image-dbus.h"
#include "io-util.h"
+#include "loop-util.h"
#include "machine-image.h"
+#include "mount-util.h"
#include "process-util.h"
+#include "raw-clone.h"
#include "strv.h"
#include "user-util.h"
@@ -279,6 +288,161 @@ int bus_image_method_set_limit(
return sd_bus_reply_method_return(message, NULL);
}
+#define EXIT_NOT_FOUND 2
+
+static int directory_image_get_os_release(Image *image, char ***ret, sd_bus_error *error) {
+
+ _cleanup_free_ char *path = NULL;
+ _cleanup_close_ int fd = -1;
+ int r;
+
+ assert(image);
+ assert(ret);
+
+ r = chase_symlinks("/etc/os-release", image->path, CHASE_PREFIX_ROOT, &path);
+ if (r == -ENOENT)
+ r = chase_symlinks("/usr/lib/os-release", image->path, CHASE_PREFIX_ROOT, &path);
+ if (r == -ENOENT)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Image does not contain OS release information");
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to resolve %s: %m", image->path);
+
+ r = load_env_file_pairs(NULL, path, NULL, ret);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to open %s: %m", path);
+
+ return 0;
+}
+
+static int raw_image_get_os_release(Image *image, char ***ret, sd_bus_error *error) {
+ _cleanup_(rmdir_and_freep) char *t = NULL;
+ _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
+ _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
+ _cleanup_(sigkill_waitp) pid_t child = 0;
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_strv_free_ char **v = NULL;
+ siginfo_t si;
+ int r;
+
+ assert(image);
+ assert(ret);
+
+ r = mkdtemp_malloc("/tmp/machined-root-XXXXXX", &t);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to create temporary directory: %m");
+
+ r = loop_device_make_by_path(image->path, O_RDONLY, &d);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to set up loop block device for %s: %m", image->path);
+
+ r = dissect_image(d->fd, NULL, 0, &m);
+ if (r == -ENOPKG)
+ return sd_bus_error_set_errnof(error, r, "Disk image %s not understood: %m", image->path);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to dissect image %s: %m", image->path);
+
+ if (pipe2(pair, O_CLOEXEC) < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to create communication pipe: %m");
+
+ child = raw_clone(SIGCHLD|CLONE_NEWNS);
+ if (child < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+
+ if (child == 0) {
+ int fd;
+
+ pair[0] = safe_close(pair[0]);
+
+ /* Make sure we never propagate to the host */
+ if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
+ _exit(EXIT_FAILURE);
+
+ r = dissected_image_mount(m, t, DISSECT_IMAGE_READ_ONLY);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ r = mount_move_root(t);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0 && errno == ENOENT) {
+ fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0 && errno == ENOENT)
+ _exit(EXIT_NOT_FOUND);
+ }
+ if (fd < 0)
+ _exit(EXIT_FAILURE);
+
+ r = copy_bytes(fd, pair[1], (uint64_t) -1, false);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ pair[1] = safe_close(pair[1]);
+
+ f = fdopen(pair[0], "re");
+ if (!f)
+ return -errno;
+
+ pair[0] = -1;
+
+ r = load_env_file_pairs(f, "os-release", NULL, &v);
+ if (r < 0)
+ return r;
+
+ r = wait_for_terminate(child, &si);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
+ child = 0;
+ if (si.si_code == CLD_EXITED && si.si_status == EXIT_NOT_FOUND)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Image does not contain OS release information");
+ if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+
+ *ret = v;
+ v = NULL;
+
+ return 0;
+}
+
+int bus_image_method_get_os_release(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT;
+ _cleanup_strv_free_ char **v = NULL;
+ Image *image = userdata;
+ int r;
+
+ r = image_path_lock(image->path, LOCK_SH|LOCK_NB, &tree_global_lock, &tree_local_lock);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to lock image: %m");
+
+ switch (image->type) {
+
+ case IMAGE_DIRECTORY:
+ case IMAGE_SUBVOLUME:
+ r = directory_image_get_os_release(image, &v, error);
+ break;
+
+ case IMAGE_RAW:
+ r = raw_image_get_os_release(image, &v, error);
+ break;
+
+ default:
+ assert_not_reached("Unknown image type");
+ }
+ if (r < 0)
+ return r;
+
+ return bus_reply_pair_array(message, v);
+}
+
const sd_bus_vtable image_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
@@ -296,6 +460,7 @@ const sd_bus_vtable image_vtable[] = {
SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
diff --git a/src/machine/image-dbus.h b/src/machine/image-dbus.h
index b62da996c6..bc8a6c3400 100644
--- a/src/machine/image-dbus.h
+++ b/src/machine/image-dbus.h
@@ -33,3 +33,4 @@ int bus_image_method_rename(sd_bus_message *message, void *userdata, sd_bus_erro
int bus_image_method_clone(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_image_method_mark_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_image_method_set_limit(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_image_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index 28e4867cb3..af745b6567 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -356,11 +356,11 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
return sd_bus_send(NULL, reply, NULL);
}
+#define EXIT_NOT_FOUND 2
+
int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_strv_free_ char **l = NULL;
Machine *m = userdata;
- char **k, **v;
int r;
assert(message);
@@ -394,7 +394,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
if (child == 0) {
- _cleanup_close_ int fd = -1;
+ int fd = -1;
pair[0] = safe_close(pair[0]);
@@ -402,12 +402,14 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
if (r < 0)
_exit(EXIT_FAILURE);
- fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
- if (fd < 0) {
- fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
- if (fd < 0)
- _exit(EXIT_FAILURE);
+ fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0 && errno == ENOENT) {
+ fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0 && errno == ENOENT)
+ _exit(EXIT_NOT_FOUND);
}
+ if (fd < 0)
+ _exit(EXIT_FAILURE);
r = copy_bytes(fd, pair[1], (uint64_t) -1, false);
if (r < 0)
@@ -431,6 +433,8 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
r = wait_for_terminate(child, &si);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
+ if (si.si_code == CLD_EXITED && si.si_status == EXIT_NOT_FOUND)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Machine does not contain OS release information");
if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
@@ -441,25 +445,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
}
- r = sd_bus_message_new_method_return(message, &reply);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(reply, 'a', "{ss}");
- if (r < 0)
- return r;
-
- STRV_FOREACH_PAIR(k, v, l) {
- r = sd_bus_message_append(reply, "{ss}", *k, *v);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- return sd_bus_send(NULL, reply, NULL);
+ return bus_reply_pair_array(message, l);
}
int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error) {
diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h
index 241b23c7ec..c513783480 100644
--- a/src/machine/machine-dbus.h
+++ b/src/machine/machine-dbus.h
@@ -42,3 +42,5 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
int machine_send_signal(Machine *m, bool new_machine);
int machine_send_create_reply(Machine *m, sd_bus_error *error);
+
+int bus_reply_pair_array(sd_bus_message *m, char **l);
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index 9c754b4327..3294ea7821 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -138,7 +138,7 @@ static void clean_machine_info(MachineInfo *machines, size_t n_machines) {
free(machines);
}
-static int get_os_release_property(sd_bus *bus, const char *name, const char *query, ...) {
+static int call_get_os_release(sd_bus *bus, const char *method, const char *name, const char *query, ...) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *k, *v, *iter, **query_res = NULL;
size_t count = 0, awaited_args = 0;
@@ -153,12 +153,13 @@ static int get_os_release_property(sd_bus *bus, const char *name, const char *qu
awaited_args++;
query_res = newa0(const char *, awaited_args);
- r = sd_bus_call_method(bus,
- "org.freedesktop.machine1",
- "/org/freedesktop/machine1",
- "org.freedesktop.machine1.Manager",
- "GetMachineOSRelease",
- NULL, &reply, "s", name);
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ method,
+ NULL, &reply, "s", name);
if (r < 0)
return r;
@@ -193,7 +194,7 @@ static int get_os_release_property(sd_bus *bus, const char *name, const char *qu
val = strdup(query_res[count]);
if (!val) {
va_end(ap);
- return log_oom();
+ return -ENOMEM;
}
*out = val;
}
@@ -249,8 +250,12 @@ static int list_machines(int argc, char *argv[], void *userdata) {
machines[n_machines].os = NULL;
machines[n_machines].version_id = NULL;
- r = get_os_release_property(bus, name,
- "ID\0" "VERSION_ID\0",
+ r = call_get_os_release(
+ bus,
+ "GetMachineOSRelease",
+ name,
+ "ID\0"
+ "VERSION_ID\0",
&machines[n_machines].os,
&machines[n_machines].version_id);
if (r < 0)
@@ -610,7 +615,7 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p
return 0;
}
-static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
+static int print_os_release(sd_bus *bus, const char *method, const char *name, const char *prefix) {
_cleanup_free_ char *pretty = NULL;
int r;
@@ -618,7 +623,7 @@ static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
assert(name);
assert(prefix);
- r = get_os_release_property(bus, name, "PRETTY_NAME\0", &pretty, NULL);
+ r = call_get_os_release(bus, method, name, "PRETTY_NAME\0", &pretty, NULL);
if (r < 0)
return r;
@@ -729,7 +734,7 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
"\n\t ",
ALL_IP_ADDRESSES);
- print_os_release(bus, i->name, "\t OS: ");
+ print_os_release(bus, "GetMachineOSRelease", i->name, "\t OS: ");
if (i->unit) {
printf("\t Unit: %s\n", i->unit);
@@ -927,6 +932,8 @@ static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
if (i->path)
printf("\t Path: %s\n", i->path);
+ print_os_release(bus, "GetImageOSRelease", i->name, "\t OS: ");
+
printf("\t RO: %s%s%s\n",
i->read_only ? ansi_highlight_red() : "",
i->read_only ? "read-only" : "writable",
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 3ee3938ebb..fd9e5b56fc 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -825,6 +825,30 @@ static int method_mark_image_read_only(sd_bus_message *message, void *userdata,
return bus_image_method_mark_read_only(message, i, error);
}
+static int method_get_image_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(image_unrefp) Image *i = NULL;
+ const char *name;
+ int r;
+
+ assert(message);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ if (!image_name_is_valid(name))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
+
+ r = image_find(name, &i);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
+
+ i->userdata = userdata;
+ return bus_image_method_get_os_release(message, i, error);
+}
+
static int clean_pool_done(Operation *operation, int ret, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_fclose_ FILE *f = NULL;
@@ -1396,6 +1420,7 @@ const sd_bus_vtable manager_vtable[] = {
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),
SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetImageOSRelease", "s", "a{ss}", method_get_image_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CleanPool", "s", "a(st)", method_clean_pool, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -1804,3 +1829,30 @@ int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
return 0;
}
+
+int bus_reply_pair_array(sd_bus_message *m, char **l) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ char **k, **v;
+ int r;
+
+ r = sd_bus_message_new_method_return(m, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "{ss}");
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH_PAIR(k, v, l) {
+ r = sd_bus_message_append(reply, "{ss}", *k, *v);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+
+}
diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf
index 562b9d3cc0..82ebfba50c 100644
--- a/src/machine/org.freedesktop.machine1.conf
+++ b/src/machine/org.freedesktop.machine1.conf
@@ -118,6 +118,10 @@
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Manager"
+ send_member="GetImageOSRelease"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Manager"
send_member="CleanPool"/>
<allow send_destination="org.freedesktop.machine1"
@@ -192,6 +196,10 @@
send_interface="org.freedesktop.machine1.Image"
send_member="MarkReadOnly"/>
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Image"
+ send_member="GetOSRelease"/>
+
<allow receive_sender="org.freedesktop.machine1"/>
</policy>
diff --git a/src/network/netdev/bridge.c b/src/network/netdev/bridge.c
index 08e31b974f..9fdcb55376 100644
--- a/src/network/netdev/bridge.c
+++ b/src/network/netdev/bridge.c
@@ -72,7 +72,7 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess
return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
/* convert to jiffes */
- if (b->forward_delay > 0) {
+ if (b->forward_delay != USEC_INFINITY) {
r = sd_netlink_message_append_u32(req, IFLA_BR_FORWARD_DELAY, usec_to_jiffies(b->forward_delay));
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_FORWARD_DELAY attribute: %m");
@@ -160,6 +160,7 @@ static void bridge_init(NetDev *n) {
b->mcast_snooping = -1;
b->vlan_filtering = -1;
b->stp = -1;
+ b->forward_delay = USEC_INFINITY;
}
const NetDevVTable bridge_vtable = {
diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c
index b03e770061..c11ac0c539 100644
--- a/src/network/netdev/tunnel.c
+++ b/src/network/netdev/tunnel.c
@@ -397,16 +397,31 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
assert(t);
- if (t->family != AF_INET && t->family != AF_INET6 && t->family != 0) {
- log_warning("Tunnel with invalid address family configured in %s. Ignoring", filename);
+ if (!IN_SET(t->family, AF_INET, AF_INET6, AF_UNSPEC)) {
+ log_netdev_error(netdev,
+ "Tunnel with invalid address family configured in %s. Ignoring", filename);
return -EINVAL;
}
- if (netdev->kind == NETDEV_KIND_IP6TNL) {
- if (t->ip6tnl_mode == _NETDEV_IP6_TNL_MODE_INVALID) {
- log_warning("IP6 Tunnel without mode configured in %s. Ignoring", filename);
- return -EINVAL;
- }
+ if (netdev->kind == NETDEV_KIND_VTI &&
+ (t->family != AF_INET || in_addr_is_null(t->family, &t->local))) {
+ log_netdev_error(netdev,
+ "vti tunnel without a local IPv4 address configured in %s. Ignoring", filename);
+ return -EINVAL;
+ }
+
+ if (netdev->kind == NETDEV_KIND_VTI6 &&
+ (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);
+ return -EINVAL;
+ }
+
+ if (netdev->kind == NETDEV_KIND_IP6TNL &&
+ t->ip6tnl_mode == _NETDEV_IP6_TNL_MODE_INVALID) {
+ log_netdev_error(netdev,
+ "ip6tnl without mode configured in %s. Ignoring", filename);
+ return -EINVAL;
}
return 0;
@@ -431,26 +446,40 @@ int config_parse_tunnel_address(const char *unit,
assert(rvalue);
assert(data);
+ /* This is used to parse addresses on both local and remote ends of the tunnel.
+ * Address families must match.
+ *
+ * "any" is a special value which means that the address is unspecified.
+ */
+
if (streq(rvalue, "any")) {
- t->family = 0;
+ *addr = IN_ADDR_NULL;
+
+ /* As a special case, if both the local and remote addresses are
+ * unspecified, also clear the address family.
+ */
+ if (t->family != AF_UNSPEC &&
+ in_addr_is_null(t->family, &t->local) &&
+ in_addr_is_null(t->family, &t->remote))
+ t->family = AF_UNSPEC;
return 0;
- } else {
+ }
- r = in_addr_from_string_auto(rvalue, &f, &buffer);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel address is invalid, ignoring assignment: %s", rvalue);
- return 0;
- }
+ r = in_addr_from_string_auto(rvalue, &f, &buffer);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Tunnel address \"%s\" invalid, ignoring assignment: %m", rvalue);
+ return 0;
+ }
- if (t->family != AF_UNSPEC && t->family != f) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Tunnel addresses incompatible, ignoring assignment: %s", rvalue);
- return 0;
- }
+ if (t->family != AF_UNSPEC && t->family != f) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Tunnel addresses incompatible, ignoring assignment: %s", rvalue);
+ return 0;
}
t->family = f;
*addr = buffer;
-
return 0;
}
@@ -578,7 +607,6 @@ static void ipip_init(NetDev *n) {
assert(t);
t->pmtudisc = true;
- t->family = AF_UNSPEC;
}
static void sit_init(NetDev *n) {
@@ -588,7 +616,6 @@ static void sit_init(NetDev *n) {
assert(t);
t->pmtudisc = true;
- t->family = AF_UNSPEC;
}
static void vti_init(NetDev *n) {
@@ -619,7 +646,6 @@ static void gre_init(NetDev *n) {
assert(t);
t->pmtudisc = true;
- t->family = AF_UNSPEC;
}
static void ip6gre_init(NetDev *n) {
diff --git a/src/network/netdev/vxlan.c b/src/network/netdev/vxlan.c
index 10c892b044..231f5cb442 100644
--- a/src/network/netdev/vxlan.c
+++ b/src/network/netdev/vxlan.c
@@ -252,8 +252,8 @@ int config_parse_destination_port(const char *unit,
assert(rvalue);
assert(data);
- r = safe_atou16(rvalue, &port);
- if (r < 0 || port <= 0) {
+ r = parse_ip_port(rvalue, &port);
+ if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VXLAN destination port '%s'.", rvalue);
return 0;
}
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index cb7df633b7..8d6992cee8 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -255,9 +255,10 @@ static int link_enable_ipv6(Link *link) {
r = write_string_file(p, one_zero(disabled), WRITE_STRING_FILE_VERIFY_ON_FAILURE);
if (r < 0)
- log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m", disabled ? "disable" : "enable", link->ifname);
+ log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m",
+ enable_disable(!disabled), link->ifname);
else
- log_link_info(link, "IPv6 %sd for interface: %m", enable_disable(!disabled));
+ log_link_info(link, "IPv6 successfully %sd", enable_disable(!disabled));
return 0;
}
@@ -686,18 +687,18 @@ static Address* link_find_dhcp_server_address(Link *link) {
return NULL;
}
-static int link_enter_configured(Link *link) {
+static void link_enter_configured(Link *link) {
assert(link);
assert(link->network);
- assert(link->state == LINK_STATE_SETTING_ROUTES);
+
+ if (link->state != LINK_STATE_SETTING_ROUTES)
+ return;
log_link_info(link, "Configured");
link_set_state(link, LINK_STATE_CONFIGURED);
link_dirty(link);
-
- return 0;
}
void link_check_ready(Link *link) {
@@ -2523,6 +2524,9 @@ static int link_initialized_and_synced(sd_netlink *rtnl, sd_netlink_message *m,
if (r == -ENOENT) {
link_enter_unmanaged(link);
return 1;
+ } else if (r == 0 && network->unmanaged) {
+ link_enter_unmanaged(link);
+ return 0;
} else if (r < 0)
return r;
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 463f4595c1..c9b9044a14 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -29,6 +29,7 @@ Match.Architecture, config_parse_net_condition,
Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac)
Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu)
Link.ARP, config_parse_tristate, 0, offsetof(Network, arp)
+Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged)
Network.Description, config_parse_string, 0, offsetof(Network, description)
Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge)
Network.Bond, config_parse_netdev, 0, offsetof(Network, bond)
@@ -100,7 +101,7 @@ DHCP.RouteMetric, config_parse_unsigned,
DHCP.RouteTable, config_parse_dhcp_route_table, 0, offsetof(Network, dhcp_route_table)
DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid)
-DHCP.ListenPort, config_parse_uint32, 0, offsetof(Network, dhcp_client_port)
+DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port)
IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains)
IPv6AcceptRA.RouteTable, config_parse_dhcp_route_table, 0, offsetof(Network, ipv6_accept_ra_route_table)
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 4dbc19fc3b..d13e306add 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -114,7 +114,7 @@ struct Network {
char *dhcp_hostname;
unsigned dhcp_route_metric;
uint32_t dhcp_route_table;
- uint32_t dhcp_client_port;
+ uint16_t dhcp_client_port;
bool dhcp_send_hostname;
bool dhcp_broadcast;
bool dhcp_critical;
@@ -176,6 +176,7 @@ struct Network {
struct ether_addr *mac;
size_t mtu;
int arp;
+ bool unmanaged;
uint32_t iaid;
DUID duid;
diff --git a/src/nspawn/nspawn-expose-ports.c b/src/nspawn/nspawn-expose-ports.c
index 86124b8779..bcaf0aaeaa 100644
--- a/src/nspawn/nspawn-expose-ports.c
+++ b/src/nspawn/nspawn-expose-ports.c
@@ -58,17 +58,17 @@ int expose_port_parse(ExposePort **l, const char *s) {
memcpy(v, e, split - e);
v[split - e] = 0;
- r = safe_atou16(v, &host_port);
- if (r < 0 || host_port <= 0)
+ r = parse_ip_port(v, &host_port);
+ if (r < 0)
return -EINVAL;
- r = safe_atou16(split + 1, &container_port);
+ r = parse_ip_port(split + 1, &container_port);
} else {
- r = safe_atou16(e, &container_port);
+ r = parse_ip_port(e, &container_port);
host_port = container_port;
}
- if (r < 0 || container_port <= 0)
+ if (r < 0)
return -EINVAL;
LIST_FOREACH(ports, p, *l)
diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf
index 3231a48d5a..c0fa4bfa1f 100644
--- a/src/nspawn/nspawn-gperf.gperf
+++ b/src/nspawn/nspawn-gperf.gperf
@@ -33,6 +33,8 @@ Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings,
Files.Bind, config_parse_bind, 0, 0
Files.BindReadOnly, config_parse_bind, 1, 0
Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0
+Files.Overlay, config_parse_overlay, 0, 0
+Files.OverlayReadOnly, config_parse_overlay, 1, 0
Files.PrivateUsersChown, config_parse_tristate, 0, offsetof(Settings, userns_chown)
Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network)
Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces)
diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c
index 91cb0861d3..aaa64a7ba8 100644
--- a/src/nspawn/nspawn-mount.c
+++ b/src/nspawn/nspawn-mount.c
@@ -47,7 +47,7 @@ CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t) {
assert(t >= 0);
assert(t < _CUSTOM_MOUNT_TYPE_MAX);
- c = realloc(*l, (*n + 1) * sizeof(CustomMount));
+ c = realloc_multiply(*l, (*n + 1), sizeof(CustomMount));
if (!c)
return NULL;
@@ -75,13 +75,18 @@ void custom_mount_free_all(CustomMount *l, unsigned n) {
free(m->work_dir);
}
+ if (m->rm_rf_tmpdir) {
+ (void) rm_rf(m->rm_rf_tmpdir, REMOVE_ROOT|REMOVE_PHYSICAL);
+ free(m->rm_rf_tmpdir);
+ }
+
strv_free(m->lower);
}
free(l);
}
-int custom_mount_compare(const void *a, const void *b) {
+static int custom_mount_compare(const void *a, const void *b) {
const CustomMount *x = a, *y = b;
int r;
@@ -97,6 +102,109 @@ int custom_mount_compare(const void *a, const void *b) {
return 0;
}
+static bool source_path_is_valid(const char *p) {
+ assert(p);
+
+ if (*p == '+')
+ p++;
+
+ return path_is_absolute(p);
+}
+
+static char *resolve_source_path(const char *dest, const char *source) {
+
+ if (!source)
+ return NULL;
+
+ if (source[0] == '+')
+ return prefix_root(dest, source + 1);
+
+ return strdup(source);
+}
+
+int custom_mount_prepare_all(const char *dest, CustomMount *l, unsigned n) {
+ unsigned i;
+ int r;
+
+ /* Prepare all custom mounts. This will make source we know all temporary directories. This is called in the
+ * parent process, so that we know the temporary directories to remove on exit before we fork off the
+ * children. */
+
+ assert(l || n == 0);
+
+ /* Order the custom mounts, and make sure we have a working directory */
+ qsort_safe(l, n, sizeof(CustomMount), custom_mount_compare);
+
+ for (i = 0; i < n; i++) {
+ CustomMount *m = l + i;
+
+ if (m->source) {
+ char *s;
+
+ s = resolve_source_path(dest, m->source);
+ if (!s)
+ return log_oom();
+
+ free(m->source);
+ m->source = s;
+ } else {
+ /* No source specified? In that case, use a throw-away temporary directory in /var/tmp */
+
+ m->rm_rf_tmpdir = strdup("/var/tmp/nspawn-temp-XXXXXX");
+ if (!m->rm_rf_tmpdir)
+ return log_oom();
+
+ if (!mkdtemp(m->rm_rf_tmpdir)) {
+ m->rm_rf_tmpdir = mfree(m->rm_rf_tmpdir);
+ return log_error_errno(errno, "Failed to acquire temporary directory: %m");
+ }
+
+ m->source = strjoin(m->rm_rf_tmpdir, "/src");
+ if (!m->source)
+ return log_oom();
+
+ if (mkdir(m->source, 0755) < 0)
+ return log_error_errno(errno, "Failed to create %s: %m", m->source);
+ }
+
+ if (m->type == CUSTOM_MOUNT_OVERLAY) {
+ char **j;
+
+ STRV_FOREACH(j, m->lower) {
+ char *s;
+
+ s = resolve_source_path(dest, *j);
+ if (!s)
+ return log_oom();
+
+ free(*j);
+ *j = s;
+ }
+
+ if (m->work_dir) {
+ char *s;
+
+ s = resolve_source_path(dest, m->work_dir);
+ if (!s)
+ return log_oom();
+
+ free(m->work_dir);
+ m->work_dir = s;
+ } else {
+ assert(m->source);
+
+ r = tempfn_random(m->source, NULL, &m->work_dir);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire working directory: %m");
+ }
+
+ (void) mkdir_label(m->work_dir, 0700);
+ }
+ }
+
+ return 0;
+}
+
int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only) {
_cleanup_free_ char *source = NULL, *destination = NULL, *opts = NULL;
const char *p = s;
@@ -111,20 +219,20 @@ int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only
return r;
if (r == 0)
return -EINVAL;
-
if (r == 1) {
- destination = strdup(source);
+ destination = strdup(source[0] == '+' ? source+1 : source);
if (!destination)
return -ENOMEM;
}
-
if (r == 2 && !isempty(p)) {
opts = strdup(p);
if (!opts)
return -ENOMEM;
}
- if (!path_is_absolute(source))
+ if (isempty(source))
+ source = NULL;
+ else if (!source_path_is_valid(source))
return -EINVAL;
if (!path_is_absolute(destination))
@@ -132,7 +240,7 @@ int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only
m = custom_mount_add(l, n, CUSTOM_MOUNT_BIND);
if (!m)
- return log_oom();
+ return -ENOMEM;
m->source = source;
m->destination = destination;
@@ -180,6 +288,71 @@ int tmpfs_mount_parse(CustomMount **l, unsigned *n, const char *s) {
return 0;
}
+int overlay_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only) {
+ _cleanup_free_ char *upper = NULL, *destination = NULL;
+ _cleanup_strv_free_ char **lower = NULL;
+ CustomMount *m;
+ int k;
+
+ k = strv_split_extract(&lower, s, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (k < 0)
+ return k;
+ if (k < 2)
+ return -EADDRNOTAVAIL;
+ if (k == 2) {
+ /* If two parameters are specified, the first one is the lower, the second one the upper directory. And
+ * we'll also define the destination mount point the same as the upper. */
+
+ if (!source_path_is_valid(lower[0]) ||
+ !source_path_is_valid(lower[1]))
+ return -EINVAL;
+
+ upper = lower[1];
+ lower[1] = NULL;
+
+ destination = strdup(upper[0] == '+' ? upper+1 : upper); /* take the destination without "+" prefix */
+ if (!destination)
+ return -ENOMEM;
+ } else {
+ char **i;
+
+ /* If more than two parameters are specified, the last one is the destination, the second to last one
+ * the "upper", and all before that the "lower" directories. */
+
+ destination = lower[k - 1];
+ upper = lower[k - 2];
+ lower[k - 2] = NULL;
+
+ STRV_FOREACH(i, lower)
+ if (!source_path_is_valid(*i))
+ return -EINVAL;
+
+ /* If the upper directory is unspecified, then let's create it automatically as a throw-away directory
+ * in /var/tmp */
+ if (isempty(upper))
+ upper = NULL;
+ else if (!source_path_is_valid(upper))
+ return -EINVAL;
+
+ if (!path_is_absolute(destination))
+ return -EINVAL;
+ }
+
+ m = custom_mount_add(l, n, CUSTOM_MOUNT_OVERLAY);
+ if (!m)
+ return -ENOMEM;
+
+ m->destination = destination;
+ m->source = upper;
+ m->lower = lower;
+ m->read_only = read_only;
+
+ upper = destination = NULL;
+ lower = NULL;
+
+ return 0;
+}
+
static int tmpfs_patch_options(
const char *options,
bool userns,
@@ -377,9 +550,9 @@ int mount_all(const char *dest,
{ 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 ...*/
{ 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 */
- { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, MOUNT_FATAL|MOUNT_IN_USERNS },
/* outer child mounts */
+ { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, MOUNT_FATAL },
{ "tmpfs", "/sys", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV, MOUNT_FATAL|MOUNT_APPLY_APIVFS_NETNS },
{ "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, MOUNT_FATAL|MOUNT_APPLY_APIVFS_RO }, /* skipped if above was mounted */
{ "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, MOUNT_FATAL }, /* skipped if above was mounted */
@@ -414,11 +587,11 @@ int mount_all(const char *dest,
if (!ro && (bool)(mount_table[k].mount_settings & MOUNT_APPLY_APIVFS_RO))
continue;
- where = prefix_root(dest, mount_table[k].where);
- if (!where)
- return log_oom();
+ r = chase_symlinks(mount_table[k].where, dest, CHASE_NONEXISTENT|CHASE_PREFIX_ROOT, &where);
+ if (r < 0)
+ return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, mount_table[k].where);
- r = path_is_mount_point(where, AT_SYMLINK_FOLLOW);
+ r = path_is_mount_point(where, NULL, 0);
if (r < 0 && r != -ENOENT)
return log_error_errno(r, "Failed to detect whether %s is a mount point: %m", where);
@@ -464,12 +637,14 @@ static int parse_mount_bind_options(const char *options, unsigned long *mount_fl
const char *p = options;
unsigned long flags = *mount_flags;
char *opts = NULL;
+ int r;
assert(options);
for (;;) {
_cleanup_free_ char *word = NULL;
- int r = extract_first_word(&p, &word, ",", 0);
+
+ r = extract_first_word(&p, &word, ",", 0);
if (r < 0)
return log_error_errno(r, "Failed to extract mount option: %m");
if (r == 0)
@@ -493,12 +668,13 @@ static int parse_mount_bind_options(const char *options, unsigned long *mount_fl
}
static int mount_bind(const char *dest, CustomMount *m) {
- struct stat source_st, dest_st;
- const char *where;
+
+ _cleanup_free_ char *mount_opts = NULL, *where = NULL;
unsigned long mount_flags = MS_BIND | MS_REC;
- _cleanup_free_ char *mount_opts = NULL;
+ struct stat source_st, dest_st;
int r;
+ assert(dest);
assert(m);
if (m->options) {
@@ -510,9 +686,14 @@ static int mount_bind(const char *dest, CustomMount *m) {
if (stat(m->source, &source_st) < 0)
return log_error_errno(errno, "Failed to stat %s: %m", m->source);
- where = prefix_roota(dest, m->destination);
+ r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where);
+ if (r < 0)
+ return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination);
+ if (r > 0) { /* Path exists already? */
+
+ if (stat(where, &dest_st) < 0)
+ return log_error_errno(errno, "Failed to stat %s: %m", where);
- if (stat(where, &dest_st) >= 0) {
if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode)) {
log_error("Cannot bind mount directory %s on file %s.", m->source, where);
return -EINVAL;
@@ -523,7 +704,7 @@ static int mount_bind(const char *dest, CustomMount *m) {
return -EINVAL;
}
- } else if (errno == ENOENT) {
+ } else { /* Path doesn't exist yet? */
r = mkdir_parents_label(where, 0755);
if (r < 0)
return log_error_errno(r, "Failed to make parents of %s: %m", where);
@@ -539,8 +720,7 @@ static int mount_bind(const char *dest, CustomMount *m) {
if (r < 0)
return log_error_errno(r, "Failed to create mount point %s: %m", where);
- } else
- return log_error_errno(errno, "Failed to stat %s: %m", where);
+ }
r = mount_verbose(LOG_ERR, m->source, where, NULL, mount_flags, mount_opts);
if (r < 0)
@@ -561,18 +741,21 @@ static int mount_tmpfs(
bool userns, uid_t uid_shift, uid_t uid_range,
const char *selinux_apifs_context) {
- const char *where, *options;
- _cleanup_free_ char *buf = NULL;
+ const char *options;
+ _cleanup_free_ char *buf = NULL, *where = NULL;
int r;
assert(dest);
assert(m);
- where = prefix_roota(dest, m->destination);
-
- r = mkdir_p_label(where, 0755);
- if (r < 0 && r != -EEXIST)
- return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where);
+ r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where);
+ if (r < 0)
+ return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination);
+ if (r == 0) { /* Doesn't exist yet? */
+ r = mkdir_p_label(where, 0755);
+ if (r < 0)
+ return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where);
+ }
r = tmpfs_patch_options(m->options, userns, uid_shift, uid_range, false, selinux_apifs_context, &buf);
if (r < 0)
@@ -582,7 +765,7 @@ static int mount_tmpfs(
return mount_verbose(LOG_ERR, "tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, options);
}
-static char *joined_and_escaped_lower_dirs(char * const *lower) {
+static char *joined_and_escaped_lower_dirs(char **lower) {
_cleanup_strv_free_ char **sv = NULL;
sv = strv_copy(lower);
@@ -598,18 +781,22 @@ static char *joined_and_escaped_lower_dirs(char * const *lower) {
}
static int mount_overlay(const char *dest, CustomMount *m) {
- _cleanup_free_ char *lower = NULL;
- const char *where, *options;
+
+ _cleanup_free_ char *lower = NULL, *where = NULL, *escaped_source = NULL;
+ const char *options;
int r;
assert(dest);
assert(m);
- where = prefix_roota(dest, m->destination);
-
- r = mkdir_label(where, 0755);
- if (r < 0 && r != -EEXIST)
- return log_error_errno(r, "Creating mount point for overlay %s failed: %m", where);
+ r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where);
+ if (r < 0)
+ return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination);
+ if (r == 0) { /* Doesn't exist yet? */
+ r = mkdir_label(where, 0755);
+ if (r < 0)
+ return log_error_errno(r, "Creating mount point for overlay %s failed: %m", where);
+ }
(void) mkdir_p_label(m->source, 0755);
@@ -617,23 +804,15 @@ static int mount_overlay(const char *dest, CustomMount *m) {
if (!lower)
return log_oom();
- if (m->read_only) {
- _cleanup_free_ char *escaped_source = NULL;
-
- escaped_source = shell_escape(m->source, ",:");
- if (!escaped_source)
- return log_oom();
+ escaped_source = shell_escape(m->source, ",:");
+ if (!escaped_source)
+ return log_oom();
+ if (m->read_only)
options = strjoina("lowerdir=", escaped_source, ":", lower);
- } else {
- _cleanup_free_ char *escaped_source = NULL, *escaped_work_dir = NULL;
+ else {
+ _cleanup_free_ char *escaped_work_dir = NULL;
- assert(m->work_dir);
- (void) mkdir_label(m->work_dir, 0700);
-
- escaped_source = shell_escape(m->source, ",:");
- if (!escaped_source)
- return log_oom();
escaped_work_dir = shell_escape(m->work_dir, ",:");
if (!escaped_work_dir)
return log_oom();
@@ -726,14 +905,19 @@ static int get_controllers(Set *subsystems) {
return 0;
}
-static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controller, const char *hierarchy,
- CGroupUnified unified_requested, bool read_only) {
+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;
int r;
to = strjoina(strempty(dest), "/sys/fs/cgroup/", hierarchy);
- r = path_is_mount_point(to, 0);
+ r = path_is_mount_point(to, dest, 0);
if (r < 0 && r != -ENOENT)
return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
if (r > 0)
@@ -773,8 +957,13 @@ static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controlle
/* Mount a legacy cgroup hierarchy when cgroup namespaces are supported. */
static int mount_legacy_cgns_supported(
- CGroupUnified unified_requested, bool userns, uid_t uid_shift,
- uid_t uid_range, const char *selinux_apifs_context) {
+ const char *dest,
+ CGroupUnified unified_requested,
+ bool userns,
+ uid_t uid_shift,
+ uid_t uid_range,
+ const char *selinux_apifs_context) {
+
_cleanup_set_free_free_ Set *controllers = NULL;
const char *cgroup_root = "/sys/fs/cgroup", *c;
int r;
@@ -782,7 +971,7 @@ static int mount_legacy_cgns_supported(
(void) mkdir_p(cgroup_root, 0755);
/* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
- r = path_is_mount_point(cgroup_root, AT_SYMLINK_FOLLOW);
+ r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
if (r < 0)
return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
if (r == 0) {
@@ -871,8 +1060,12 @@ skip_controllers:
/* Mount legacy cgroup hierarchy when cgroup namespaces are unsupported. */
static int mount_legacy_cgns_unsupported(
const char *dest,
- CGroupUnified unified_requested, bool userns, uid_t uid_shift, uid_t uid_range,
+ CGroupUnified unified_requested,
+ bool userns,
+ uid_t uid_shift,
+ uid_t uid_range,
const char *selinux_apifs_context) {
+
_cleanup_set_free_free_ Set *controllers = NULL;
const char *cgroup_root;
int r;
@@ -882,7 +1075,7 @@ static int mount_legacy_cgns_unsupported(
(void) mkdir_p(cgroup_root, 0755);
/* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
- r = path_is_mount_point(cgroup_root, AT_SYMLINK_FOLLOW);
+ r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
if (r < 0)
return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
if (r == 0) {
@@ -975,7 +1168,7 @@ static int mount_unified_cgroups(const char *dest) {
(void) mkdir_p(p, 0755);
- r = path_is_mount_point(p, AT_SYMLINK_FOLLOW);
+ r = path_is_mount_point(p, dest, AT_SYMLINK_FOLLOW);
if (r < 0)
return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p);
if (r > 0) {
@@ -995,14 +1188,16 @@ static int mount_unified_cgroups(const char *dest) {
int mount_cgroups(
const char *dest,
CGroupUnified unified_requested,
- bool userns, uid_t uid_shift, uid_t uid_range,
+ bool userns,
+ uid_t uid_shift,
+ uid_t uid_range,
const char *selinux_apifs_context,
bool use_cgns) {
if (unified_requested >= CGROUP_UNIFIED_ALL)
return mount_unified_cgroups(dest);
else if (use_cgns)
- return mount_legacy_cgns_supported(unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
+ return mount_legacy_cgns_supported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
return mount_legacy_cgns_unsupported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
}
diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h
index 74aee7ee7f..467082a737 100644
--- a/src/nspawn/nspawn-mount.h
+++ b/src/nspawn/nspawn-mount.h
@@ -56,15 +56,16 @@ typedef struct CustomMount {
char *options;
char *work_dir;
char **lower;
+ char *rm_rf_tmpdir;
} CustomMount;
CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t);
-
void custom_mount_free_all(CustomMount *l, unsigned n);
+int custom_mount_prepare_all(const char *dest, CustomMount *l, unsigned n);
+
int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only);
int tmpfs_mount_parse(CustomMount **l, unsigned *n, const char *s);
-
-int custom_mount_compare(const void *a, const void *b);
+int overlay_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only);
int mount_all(const char *dest, MountSettingsMask mount_settings, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
int mount_sysfs(const char *dest, MountSettingsMask mount_settings);
diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c
index 06c56d9ec8..e3ab39faea 100644
--- a/src/nspawn/nspawn-register.c
+++ b/src/nspawn/nspawn-register.c
@@ -135,6 +135,11 @@ int register_machine(
continue;
r = is_device_node(cm->source);
+ if (r == -ENOENT) {
+ /* The bind source might only appear as the image is put together, hence don't complain */
+ log_debug_errno(r, "Bind mount source %s not found, ignoring: %m", cm->source);
+ continue;
+ }
if (r < 0)
return log_error_errno(r, "Failed to stat %s: %m", cm->source);
diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c
index 09c8f070ba..22b74d88e4 100644
--- a/src/nspawn/nspawn-settings.c
+++ b/src/nspawn/nspawn-settings.c
@@ -293,6 +293,32 @@ int config_parse_tmpfs(
return 0;
}
+int config_parse_overlay(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Settings *settings = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = overlay_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
+ if (r < 0)
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid overlay file system specification %s, ignoring: %m", rvalue);
+
+ return 0;
+}
+
int config_parse_veth_extra(
const char *unit,
const char *filename,
diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h
index 231e6d7266..4bd0c642df 100644
--- a/src/nspawn/nspawn-settings.h
+++ b/src/nspawn/nspawn-settings.h
@@ -111,6 +111,7 @@ int config_parse_expose_port(const char *unit, const char *filename, unsigned li
int config_parse_volatile_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_bind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_tmpfs(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_overlay(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_veth_extra(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_network_zone(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_boot(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 2770770cd0..d701f2158d 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -53,6 +53,7 @@
#include "cgroup-util.h"
#include "copy.h"
#include "dev-setup.h"
+#include "dissect-image.h"
#include "env-util.h"
#include "fd-util.h"
#include "fdset.h"
@@ -60,9 +61,11 @@
#include "format-util.h"
#include "fs-util.h"
#include "gpt.h"
+#include "hexdecoct.h"
#include "hostname-util.h"
#include "id128-util.h"
#include "log.h"
+#include "loop-util.h"
#include "loopback-setup.h"
#include "machine-image.h"
#include "macro.h"
@@ -198,6 +201,8 @@ static bool arg_notify_ready = false;
static bool arg_use_cgns = true;
static unsigned long arg_clone_ns_flags = CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS;
static MountSettingsMask arg_mount_settings = MOUNT_APPLY_APIVFS_RO;
+static void *arg_root_hash = NULL;
+static size_t arg_root_hash_size = 0;
static void help(void) {
printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
@@ -211,6 +216,7 @@ static void help(void) {
" -x --ephemeral Run container with snapshot of root directory, and\n"
" remove it after exit\n"
" -i --image=PATH File system device or disk image for the container\n"
+ " --root-hash=HASH Specify verity root hash\n"
" -a --as-pid2 Maintain a stub init as PID1, invoke binary as PID2\n"
" -b --boot Boot up full system (i.e. invoke init)\n"
" --chdir=PATH Set working directory in the container\n"
@@ -280,14 +286,9 @@ static void help(void) {
, program_invocation_short_name);
}
-static int custom_mounts_prepare(void) {
+static int custom_mount_check_all(void) {
unsigned i;
- int r;
-
- /* Ensure the mounts are applied prefix first. */
- qsort_safe(arg_custom_mounts, arg_n_custom_mounts, sizeof(CustomMount), custom_mount_compare);
- /* Allocate working directories for the overlay file systems that need it */
for (i = 0; i < arg_n_custom_mounts; i++) {
CustomMount *m = &arg_custom_mounts[i];
@@ -301,19 +302,6 @@ static int custom_mounts_prepare(void) {
return -EINVAL;
}
}
-
- if (m->type != CUSTOM_MOUNT_OVERLAY)
- continue;
-
- if (m->work_dir)
- continue;
-
- if (m->read_only)
- continue;
-
- r = tempfn_random(m->source, NULL, &m->work_dir);
- if (r < 0)
- return log_error_errno(r, "Failed to generate work directory from %s: %m", m->source);
}
return 0;
@@ -440,6 +428,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_CHDIR,
ARG_PRIVATE_USERS_CHOWN,
ARG_NOTIFY_READY,
+ ARG_ROOT_HASH,
};
static const struct option options[] = {
@@ -489,6 +478,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "settings", required_argument, NULL, ARG_SETTINGS },
{ "chdir", required_argument, NULL, ARG_CHDIR },
{ "notify-ready", required_argument, NULL, ARG_NOTIFY_READY },
+ { "root-hash", required_argument, NULL, ARG_ROOT_HASH },
{}
};
@@ -500,7 +490,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:nU", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:nUE:", options, NULL)) >= 0)
switch (c) {
@@ -789,69 +779,15 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_OVERLAY:
- case ARG_OVERLAY_RO: {
- _cleanup_free_ char *upper = NULL, *destination = NULL;
- _cleanup_strv_free_ char **lower = NULL;
- CustomMount *m;
- unsigned n = 0;
- char **i;
-
- r = strv_split_extract(&lower, optarg, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
- if (r == -ENOMEM)
- return log_oom();
- else if (r < 0) {
- log_error("Invalid overlay specification: %s", optarg);
- return r;
- }
-
- STRV_FOREACH(i, lower) {
- if (!path_is_absolute(*i)) {
- log_error("Overlay path %s is not absolute.", *i);
- return -EINVAL;
- }
-
- n++;
- }
-
- if (n < 2) {
- log_error("--overlay= needs at least two colon-separated directories specified.");
- return -EINVAL;
- }
-
- if (n == 2) {
- /* If two parameters are specified,
- * the first one is the lower, the
- * second one the upper directory. And
- * we'll also define the destination
- * mount point the same as the upper. */
- upper = lower[1];
- lower[1] = NULL;
-
- destination = strdup(upper);
- if (!destination)
- return log_oom();
-
- } else {
- upper = lower[n - 2];
- destination = lower[n - 1];
- lower[n - 2] = NULL;
- }
-
- m = custom_mount_add(&arg_custom_mounts, &arg_n_custom_mounts, CUSTOM_MOUNT_OVERLAY);
- if (!m)
- return log_oom();
-
- m->destination = destination;
- m->source = upper;
- m->lower = lower;
- m->read_only = c == ARG_OVERLAY_RO;
-
- upper = destination = NULL;
- lower = NULL;
+ case ARG_OVERLAY_RO:
+ r = overlay_mount_parse(&arg_custom_mounts, &arg_n_custom_mounts, optarg, c == ARG_OVERLAY_RO);
+ if (r == -EADDRNOTAVAIL)
+ return log_error_errno(r, "--overlay(-ro)= needs at least two colon-separated directories specified.");
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --overlay(-ro)= argument %s: %m", optarg);
arg_settings_mask |= SETTING_CUSTOM_MOUNTS;
break;
- }
case 'E': {
char **n;
@@ -1086,6 +1022,25 @@ static int parse_argv(int argc, char *argv[]) {
arg_settings_mask |= SETTING_NOTIFY_READY;
break;
+ case ARG_ROOT_HASH: {
+ void *k;
+ size_t l;
+
+ r = unhexmem(optarg, strlen(optarg), &k, &l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse root hash: %s", optarg);
+ if (l < sizeof(sd_id128_t)) {
+ log_error("Root hash must be at least 128bit long: %s", optarg);
+ free(k);
+ return -EINVAL;
+ }
+
+ free(arg_root_hash);
+ arg_root_hash = k;
+ arg_root_hash_size = l;
+ break;
+ }
+
case '?':
return -EINVAL;
@@ -1133,6 +1088,16 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
+ if (arg_ephemeral && arg_template && !arg_directory) {
+ /* User asked for ephemeral execution but specified --template= instead of --directory=. Semantically
+ * such an invocation makes some sense, see https://github.com/systemd/systemd/issues/3667. Let's
+ * accept this here, and silently make "--ephemeral --template=" equivalent to "--ephemeral
+ * --directory=". */
+
+ arg_directory = arg_template;
+ arg_template = NULL;
+ }
+
if (arg_template && !(arg_directory || arg_machine)) {
log_error("--template= needs --directory= or --machine=.");
return -EINVAL;
@@ -1191,6 +1156,10 @@ static int parse_argv(int argc, char *argv[]) {
else
arg_use_cgns = r;
+ r = custom_mount_check_all();
+ if (r < 0)
+ return r;
+
return 1;
}
@@ -1357,6 +1326,8 @@ static int setup_resolv_conf(const char *dest) {
* advantage that the container will be able to follow the host's DNS server configuration changes
* transparently. */
+ (void) touch(where);
+
r = mount_verbose(LOG_WARNING, "/usr/lib/systemd/resolv.conf", where, NULL, MS_BIND, NULL);
if (r >= 0)
return mount_verbose(LOG_ERR, NULL, where, NULL,
@@ -1666,7 +1637,7 @@ static int setup_journal(const char *directory) {
p = strjoina("/var/log/journal/", id);
q = prefix_roota(directory, p);
- if (path_is_mount_point(p, 0) > 0) {
+ if (path_is_mount_point(p, NULL, 0) > 0) {
if (try)
return 0;
@@ -1674,7 +1645,7 @@ static int setup_journal(const char *directory) {
return -EEXIST;
}
- if (path_is_mount_point(q, 0) > 0) {
+ if (path_is_mount_point(q, NULL, 0) > 0) {
if (try)
return 0;
@@ -1827,546 +1798,6 @@ static int setup_propagate(const char *root) {
return mount_verbose(LOG_ERR, NULL, q, NULL, MS_SLAVE, NULL);
}
-static int setup_image(char **device_path, int *loop_nr) {
- struct loop_info64 info = {
- .lo_flags = LO_FLAGS_AUTOCLEAR|LO_FLAGS_PARTSCAN
- };
- _cleanup_close_ int fd = -1, control = -1, loop = -1;
- _cleanup_free_ char* loopdev = NULL;
- struct stat st;
- int r, nr;
-
- assert(device_path);
- assert(loop_nr);
- assert(arg_image);
-
- fd = open(arg_image, O_CLOEXEC|(arg_read_only ? O_RDONLY : O_RDWR)|O_NONBLOCK|O_NOCTTY);
- if (fd < 0)
- return log_error_errno(errno, "Failed to open %s: %m", arg_image);
-
- if (fstat(fd, &st) < 0)
- return log_error_errno(errno, "Failed to stat %s: %m", arg_image);
-
- if (S_ISBLK(st.st_mode)) {
- char *p;
-
- p = strdup(arg_image);
- if (!p)
- return log_oom();
-
- *device_path = p;
-
- *loop_nr = -1;
-
- r = fd;
- fd = -1;
-
- return r;
- }
-
- if (!S_ISREG(st.st_mode)) {
- log_error("%s is not a regular file or block device.", arg_image);
- return -EINVAL;
- }
-
- control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
- if (control < 0)
- return log_error_errno(errno, "Failed to open /dev/loop-control: %m");
-
- nr = ioctl(control, LOOP_CTL_GET_FREE);
- if (nr < 0)
- return log_error_errno(errno, "Failed to allocate loop device: %m");
-
- if (asprintf(&loopdev, "/dev/loop%i", nr) < 0)
- return log_oom();
-
- loop = open(loopdev, O_CLOEXEC|(arg_read_only ? O_RDONLY : O_RDWR)|O_NONBLOCK|O_NOCTTY);
- if (loop < 0)
- return log_error_errno(errno, "Failed to open loop device %s: %m", loopdev);
-
- if (ioctl(loop, LOOP_SET_FD, fd) < 0)
- return log_error_errno(errno, "Failed to set loopback file descriptor on %s: %m", loopdev);
-
- if (arg_read_only)
- info.lo_flags |= LO_FLAGS_READ_ONLY;
-
- if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0)
- return log_error_errno(errno, "Failed to set loopback settings on %s: %m", loopdev);
-
- *device_path = loopdev;
- loopdev = NULL;
-
- *loop_nr = nr;
-
- r = loop;
- loop = -1;
-
- return r;
-}
-
-#define PARTITION_TABLE_BLURB \
- "Note that the disk image needs to either contain only a single MBR partition of\n" \
- "type 0x83 that is marked bootable, or a single GPT partition of type " \
- "0FC63DAF-8483-4772-8E79-3D69D8477DE4 or follow\n" \
- " http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/\n" \
- "to be bootable with systemd-nspawn."
-
-static int dissect_image(
- int fd,
- char **root_device, bool *root_device_rw,
- char **home_device, bool *home_device_rw,
- char **srv_device, bool *srv_device_rw,
- char **esp_device,
- bool *secondary) {
-
-#ifdef HAVE_BLKID
- int home_nr = -1, srv_nr = -1, esp_nr = -1;
-#ifdef GPT_ROOT_NATIVE
- int root_nr = -1;
-#endif
-#ifdef GPT_ROOT_SECONDARY
- int secondary_root_nr = -1;
-#endif
- _cleanup_free_ char *home = NULL, *root = NULL, *secondary_root = NULL, *srv = NULL, *esp = NULL, *generic = NULL;
- _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
- _cleanup_udev_device_unref_ struct udev_device *d = NULL;
- _cleanup_blkid_free_probe_ blkid_probe b = NULL;
- _cleanup_udev_unref_ struct udev *udev = NULL;
- struct udev_list_entry *first, *item;
- bool home_rw = true, root_rw = true, secondary_root_rw = true, srv_rw = true, generic_rw = true;
- bool is_gpt, is_mbr, multiple_generic = false;
- const char *pttype = NULL;
- blkid_partlist pl;
- struct stat st;
- unsigned i;
- int r;
-
- assert(fd >= 0);
- assert(root_device);
- assert(home_device);
- assert(srv_device);
- assert(esp_device);
- assert(secondary);
- assert(arg_image);
-
- b = blkid_new_probe();
- if (!b)
- return log_oom();
-
- errno = 0;
- r = blkid_probe_set_device(b, fd, 0, 0);
- if (r != 0) {
- if (errno == 0)
- return log_oom();
-
- return log_error_errno(errno, "Failed to set device on blkid probe: %m");
- }
-
- blkid_probe_enable_partitions(b, 1);
- blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
-
- errno = 0;
- r = blkid_do_safeprobe(b);
- if (r == -2 || r == 1) {
- log_error("Failed to identify any partition table on\n"
- " %s\n"
- PARTITION_TABLE_BLURB, arg_image);
- return -EINVAL;
- } else if (r != 0) {
- if (errno == 0)
- errno = EIO;
- return log_error_errno(errno, "Failed to probe: %m");
- }
-
- (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
-
- is_gpt = streq_ptr(pttype, "gpt");
- is_mbr = streq_ptr(pttype, "dos");
-
- if (!is_gpt && !is_mbr) {
- log_error("No GPT or MBR partition table discovered on\n"
- " %s\n"
- PARTITION_TABLE_BLURB, arg_image);
- return -EINVAL;
- }
-
- errno = 0;
- pl = blkid_probe_get_partitions(b);
- if (!pl) {
- if (errno == 0)
- return log_oom();
-
- log_error("Failed to list partitions of %s", arg_image);
- return -errno;
- }
-
- udev = udev_new();
- if (!udev)
- return log_oom();
-
- if (fstat(fd, &st) < 0)
- return log_error_errno(errno, "Failed to stat block device: %m");
-
- d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
- if (!d)
- return log_oom();
-
- for (i = 0;; i++) {
- int n, m;
-
- if (i >= 10) {
- log_error("Kernel partitions never appeared.");
- return -ENXIO;
- }
-
- e = udev_enumerate_new(udev);
- if (!e)
- return log_oom();
-
- r = udev_enumerate_add_match_parent(e, d);
- if (r < 0)
- return log_oom();
-
- r = udev_enumerate_scan_devices(e);
- if (r < 0)
- return log_error_errno(r, "Failed to scan for partition devices of %s: %m", arg_image);
-
- /* Count the partitions enumerated by the kernel */
- n = 0;
- first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first)
- n++;
-
- /* Count the partitions enumerated by blkid */
- m = blkid_partlist_numof_partitions(pl);
- if (n == m + 1)
- break;
- if (n > m + 1) {
- log_error("blkid and kernel partition list do not match.");
- return -EIO;
- }
- if (n < m + 1) {
- unsigned j;
-
- /* The kernel has probed fewer partitions than
- * blkid? Maybe the kernel prober is still
- * running or it got EBUSY because udev
- * already opened the device. Let's reprobe
- * the device, which is a synchronous call
- * that waits until probing is complete. */
-
- for (j = 0; j < 20; j++) {
-
- r = ioctl(fd, BLKRRPART, 0);
- if (r < 0)
- r = -errno;
- if (r >= 0 || r != -EBUSY)
- break;
-
- /* If something else has the device
- * open, such as an udev rule, the
- * ioctl will return EBUSY. Since
- * there's no way to wait until it
- * isn't busy anymore, let's just wait
- * a bit, and try again.
- *
- * This is really something they
- * should fix in the kernel! */
-
- usleep(50 * USEC_PER_MSEC);
- }
-
- if (r < 0)
- return log_error_errno(r, "Failed to reread partition table: %m");
- }
-
- e = udev_enumerate_unref(e);
- }
-
- first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first) {
- _cleanup_udev_device_unref_ struct udev_device *q;
- const char *node;
- unsigned long long flags;
- blkid_partition pp;
- dev_t qn;
- int nr;
-
- errno = 0;
- q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
- if (!q) {
- if (!errno)
- errno = ENOMEM;
-
- return log_error_errno(errno, "Failed to get partition device of %s: %m", arg_image);
- }
-
- qn = udev_device_get_devnum(q);
- if (major(qn) == 0)
- continue;
-
- if (st.st_rdev == qn)
- continue;
-
- node = udev_device_get_devnode(q);
- if (!node)
- continue;
-
- pp = blkid_partlist_devno_to_partition(pl, qn);
- if (!pp)
- continue;
-
- flags = blkid_partition_get_flags(pp);
-
- nr = blkid_partition_get_partno(pp);
- if (nr < 0)
- continue;
-
- if (is_gpt) {
- sd_id128_t type_id;
- const char *stype;
-
- if (flags & GPT_FLAG_NO_AUTO)
- continue;
-
- stype = blkid_partition_get_type_string(pp);
- if (!stype)
- continue;
-
- if (sd_id128_from_string(stype, &type_id) < 0)
- continue;
-
- if (sd_id128_equal(type_id, GPT_HOME)) {
-
- if (home && nr >= home_nr)
- continue;
-
- home_nr = nr;
- home_rw = !(flags & GPT_FLAG_READ_ONLY);
-
- r = free_and_strdup(&home, node);
- if (r < 0)
- return log_oom();
-
- } else if (sd_id128_equal(type_id, GPT_SRV)) {
-
- if (srv && nr >= srv_nr)
- continue;
-
- srv_nr = nr;
- srv_rw = !(flags & GPT_FLAG_READ_ONLY);
-
- r = free_and_strdup(&srv, node);
- if (r < 0)
- return log_oom();
- } else if (sd_id128_equal(type_id, GPT_ESP)) {
-
- if (esp && nr >= esp_nr)
- continue;
-
- esp_nr = nr;
-
- r = free_and_strdup(&esp, node);
- if (r < 0)
- return log_oom();
- }
-#ifdef GPT_ROOT_NATIVE
- else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
-
- if (root && nr >= root_nr)
- continue;
-
- root_nr = nr;
- root_rw = !(flags & GPT_FLAG_READ_ONLY);
-
- r = free_and_strdup(&root, node);
- if (r < 0)
- return log_oom();
- }
-#endif
-#ifdef GPT_ROOT_SECONDARY
- else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
-
- if (secondary_root && nr >= secondary_root_nr)
- continue;
-
- secondary_root_nr = nr;
- secondary_root_rw = !(flags & GPT_FLAG_READ_ONLY);
-
- r = free_and_strdup(&secondary_root, node);
- if (r < 0)
- return log_oom();
- }
-#endif
- else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
-
- if (generic)
- multiple_generic = true;
- else {
- generic_rw = !(flags & GPT_FLAG_READ_ONLY);
-
- r = free_and_strdup(&generic, node);
- if (r < 0)
- return log_oom();
- }
- }
-
- } else if (is_mbr) {
- int type;
-
- if (flags != 0x80) /* Bootable flag */
- continue;
-
- type = blkid_partition_get_type(pp);
- if (type != 0x83) /* Linux partition */
- continue;
-
- if (generic)
- multiple_generic = true;
- else {
- generic_rw = true;
-
- r = free_and_strdup(&root, node);
- if (r < 0)
- return log_oom();
- }
- }
- }
-
- if (root) {
- *root_device = root;
- root = NULL;
-
- *root_device_rw = root_rw;
- *secondary = false;
- } else if (secondary_root) {
- *root_device = secondary_root;
- secondary_root = NULL;
-
- *root_device_rw = secondary_root_rw;
- *secondary = true;
- } else if (generic) {
-
- /* There were no partitions with precise meanings
- * around, but we found generic partitions. In this
- * case, if there's only one, we can go ahead and boot
- * it, otherwise we bail out, because we really cannot
- * make any sense of it. */
-
- if (multiple_generic) {
- log_error("Identified multiple bootable Linux partitions on\n"
- " %s\n"
- PARTITION_TABLE_BLURB, arg_image);
- return -EINVAL;
- }
-
- *root_device = generic;
- generic = NULL;
-
- *root_device_rw = generic_rw;
- *secondary = false;
- } else {
- log_error("Failed to identify root partition in disk image\n"
- " %s\n"
- PARTITION_TABLE_BLURB, arg_image);
- return -EINVAL;
- }
-
- if (home) {
- *home_device = home;
- home = NULL;
-
- *home_device_rw = home_rw;
- }
-
- if (srv) {
- *srv_device = srv;
- srv = NULL;
-
- *srv_device_rw = srv_rw;
- }
-
- if (esp) {
- *esp_device = esp;
- esp = NULL;
- }
-
- return 0;
-#else
- log_error("--image= is not supported, compiled without blkid support.");
- return -EOPNOTSUPP;
-#endif
-}
-
-static int mount_device(const char *what, const char *where, const char *directory, bool rw) {
-#ifdef HAVE_BLKID
- _cleanup_blkid_free_probe_ blkid_probe b = NULL;
- const char *fstype, *p, *options;
- int r;
-
- assert(what);
- assert(where);
-
- if (arg_read_only)
- rw = false;
-
- if (directory)
- p = strjoina(where, directory);
- else
- p = where;
-
- errno = 0;
- b = blkid_new_probe_from_filename(what);
- if (!b) {
- if (errno == 0)
- return log_oom();
- return log_error_errno(errno, "Failed to allocate prober for %s: %m", what);
- }
-
- blkid_probe_enable_superblocks(b, 1);
- blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
-
- errno = 0;
- r = blkid_do_safeprobe(b);
- if (r == -1 || r == 1) {
- log_error("Cannot determine file system type of %s", what);
- return -EINVAL;
- } else if (r != 0) {
- if (errno == 0)
- errno = EIO;
- return log_error_errno(errno, "Failed to probe %s: %m", what);
- }
-
- errno = 0;
- if (blkid_probe_lookup_value(b, "TYPE", &fstype, NULL) < 0) {
- if (errno == 0)
- errno = EINVAL;
- log_error("Failed to determine file system type of %s", what);
- return -errno;
- }
-
- if (streq(fstype, "crypto_LUKS")) {
- log_error("nspawn currently does not support LUKS disk images.");
- return -EOPNOTSUPP;
- }
-
- /* If this is a loopback device then let's mount the image with discard, so that the underlying file remains
- * sparse when possible. */
- if (STR_IN_SET(fstype, "btrfs", "ext4", "vfat", "xfs")) {
- const char *l;
-
- l = path_startswith(what, "/dev");
- if (l && startswith(l, "loop"))
- options = "discard";
- }
-
- return mount_verbose(LOG_ERR, what, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
-#else
- log_error("--image= is not supported, compiled without blkid support.");
- return -EOPNOTSUPP;
-#endif
-}
-
static int setup_machine_id(const char *directory) {
const char *etc_machine_id;
sd_id128_t id;
@@ -2426,83 +1857,6 @@ static int recursive_chown(const char *directory, uid_t shift, uid_t range) {
return r;
}
-static int mount_devices(
- const char *where,
- const char *root_device, bool root_device_rw,
- const char *home_device, bool home_device_rw,
- const char *srv_device, bool srv_device_rw,
- const char *esp_device) {
- int r;
-
- assert(where);
-
- if (root_device) {
- r = mount_device(root_device, arg_directory, NULL, root_device_rw);
- if (r < 0)
- return log_error_errno(r, "Failed to mount root directory: %m");
- }
-
- if (home_device) {
- r = mount_device(home_device, arg_directory, "/home", home_device_rw);
- if (r < 0)
- return log_error_errno(r, "Failed to mount home directory: %m");
- }
-
- if (srv_device) {
- r = mount_device(srv_device, arg_directory, "/srv", srv_device_rw);
- if (r < 0)
- return log_error_errno(r, "Failed to mount server data directory: %m");
- }
-
- if (esp_device) {
- const char *mp, *x;
-
- /* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */
-
- mp = "/efi";
- x = strjoina(arg_directory, mp);
- r = dir_is_empty(x);
- if (r == -ENOENT) {
- mp = "/boot";
- x = strjoina(arg_directory, mp);
- r = dir_is_empty(x);
- }
-
- if (r > 0) {
- r = mount_device(esp_device, arg_directory, mp, true);
- if (r < 0)
- return log_error_errno(r, "Failed to mount ESP: %m");
- }
- }
-
- return 0;
-}
-
-static void loop_remove(int nr, int *image_fd) {
- _cleanup_close_ int control = -1;
- int r;
-
- if (nr < 0)
- return;
-
- if (image_fd && *image_fd >= 0) {
- r = ioctl(*image_fd, LOOP_CLR_FD);
- if (r < 0)
- log_debug_errno(errno, "Failed to close loop image: %m");
- *image_fd = safe_close(*image_fd);
- }
-
- control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
- if (control < 0) {
- log_warning_errno(errno, "Failed to open /dev/loop-control: %m");
- return;
- }
-
- r = ioctl(control, LOOP_CTL_REMOVE, nr);
- if (r < 0)
- log_debug_errno(errno, "Failed to remove loop %d: %m", nr);
-}
-
/*
* Return values:
* < 0 : wait_for_terminate() failed to get the state of the
@@ -2624,10 +1978,22 @@ static int determine_names(void) {
}
if (!arg_machine) {
+
if (arg_directory && path_equal(arg_directory, "/"))
arg_machine = gethostname_malloc();
- else
- arg_machine = strdup(basename(arg_image ?: arg_directory));
+ else {
+ if (arg_image) {
+ char *e;
+
+ arg_machine = strdup(basename(arg_image));
+
+ /* Truncate suffix if there is one */
+ e = endswith(arg_machine, ".raw");
+ if (e)
+ *e = 0;
+ } else
+ arg_machine = strdup(basename(arg_directory));
+ }
if (!arg_machine)
return log_oom();
@@ -2656,6 +2022,25 @@ static int determine_names(void) {
return 0;
}
+static int chase_symlinks_and_update(char **p, unsigned flags) {
+ char *chased;
+ int r;
+
+ assert(p);
+
+ if (!*p)
+ return 0;
+
+ r = chase_symlinks(*p, NULL, flags, &chased);
+ if (r < 0)
+ return log_error_errno(r, "Failed to resolve path %s: %m", *p);
+
+ free(*p);
+ *p = chased;
+
+ return 0;
+}
+
static int determine_uid_shift(const char *directory) {
int r;
@@ -2958,10 +2343,7 @@ static int outer_child(
Barrier *barrier,
const char *directory,
const char *console,
- const char *root_device, bool root_device_rw,
- const char *home_device, bool home_device_rw,
- const char *srv_device, bool srv_device_rw,
- const char *esp_device,
+ DissectedImage *dissected_image,
bool interactive,
bool secondary,
int pid_socket,
@@ -3021,13 +2403,11 @@ static int outer_child(
if (r < 0)
return r;
- r = mount_devices(directory,
- root_device, root_device_rw,
- home_device, home_device_rw,
- srv_device, srv_device_rw,
- esp_device);
- if (r < 0)
- return r;
+ if (dissected_image) {
+ r = dissected_image_mount(dissected_image, directory, DISSECT_IMAGE_DISCARD_ON_LOOP|(arg_read_only ? DISSECT_IMAGE_READ_ONLY : 0));
+ if (r < 0)
+ return r;
+ }
r = determine_uid_shift(directory);
if (r < 0)
@@ -3644,10 +3024,7 @@ static int load_settings(void) {
static int run(int master,
const char* console,
- const char *root_device, bool root_device_rw,
- const char *home_device, bool home_device_rw,
- const char *srv_device, bool srv_device_rw,
- const char *esp_device,
+ DissectedImage *dissected_image,
bool interactive,
bool secondary,
FDSet *fds,
@@ -3657,7 +3034,7 @@ static int run(int master,
static const struct sigaction sa = {
.sa_handler = nop_signal_handler,
- .sa_flags = SA_NOCLDSTOP,
+ .sa_flags = SA_NOCLDSTOP|SA_RESTART,
};
_cleanup_release_lock_file_ LockFile uid_shift_lock = LOCK_FILE_INIT;
@@ -3754,10 +3131,7 @@ static int run(int master,
r = outer_child(&barrier,
arg_directory,
console,
- root_device, root_device_rw,
- home_device, home_device_rw,
- srv_device, srv_device_rw,
- esp_device,
+ dissected_image,
interactive,
secondary,
pid_socket_pair[1],
@@ -4062,13 +3436,59 @@ static int run(int master,
return 1; /* loop again */
}
+static int load_root_hash(const char *image) {
+ _cleanup_free_ char *text = NULL;
+ char *fn, *n, *e;
+ void *k;
+ size_t l;
+ int r;
+
+ assert_se(image);
+
+ /* Try to load the root hash from a file next to the image file if it exists. */
+
+ if (arg_root_hash)
+ return 0;
+
+ fn = new(char, strlen(image) + strlen(".roothash") + 1);
+ if (!fn)
+ return log_oom();
+
+ n = stpcpy(fn, image);
+ e = endswith(fn, ".raw");
+ if (e)
+ n = e;
+
+ strcpy(n, ".roothash");
+
+ r = read_one_line_file(fn, &text);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0) {
+ log_warning_errno(r, "Failed to read %s, ignoring: %m", fn);
+ return 0;
+ }
+
+ r = unhexmem(text, strlen(text), &k, &l);
+ if (r < 0)
+ return log_error_errno(r, "Invalid root hash: %s", text);
+ if (l < sizeof(sd_id128_t)) {
+ free(k);
+ return log_error_errno(r, "Root hash too short: %s", text);
+ }
+
+ arg_root_hash = k;
+ arg_root_hash_size = l;
+
+ return 0;
+}
+
int main(int argc, char *argv[]) {
- _cleanup_free_ char *device_path = NULL, *root_device = NULL, *home_device = NULL, *srv_device = NULL, *esp_device = NULL, *console = NULL;
- bool root_device_rw = true, home_device_rw = true, srv_device_rw = true;
- _cleanup_close_ int master = -1, image_fd = -1;
+ _cleanup_free_ char *console = NULL;
+ _cleanup_close_ int master = -1;
_cleanup_fdset_free_ FDSet *fds = NULL;
- int r, n_fd_passed, loop_nr = -1, ret = EXIT_SUCCESS;
+ int r, n_fd_passed, ret = EXIT_SUCCESS;
char veth_name[IFNAMSIZ] = "";
bool secondary = false, remove_directory = false, remove_image = false;
pid_t pid = 0;
@@ -4076,6 +3496,9 @@ int main(int argc, char *argv[]) {
_cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT;
bool interactive, veth_created = false, remove_tmprootdir = false;
char tmprootdir[] = "/tmp/nspawn-root-XXXXXX";
+ _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
+ _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
+ _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
log_parse_environment();
log_open();
@@ -4126,13 +3549,17 @@ int main(int argc, char *argv[]) {
if (arg_ephemeral) {
_cleanup_free_ char *np = NULL;
+ r = chase_symlinks_and_update(&arg_directory, 0);
+ if (r < 0)
+ goto finish;
+
/* If the specified path is a mount point we
* generate the new snapshot immediately
* inside it under a random name. However if
* the specified is not a mount point we
* create the new snapshot in the parent
* directory, just next to it. */
- r = path_is_mount_point(arg_directory, 0);
+ r = path_is_mount_point(arg_directory, NULL, 0);
if (r < 0) {
log_error_errno(r, "Failed to determine whether directory %s is mount point: %m", arg_directory);
goto finish;
@@ -4170,6 +3597,10 @@ int main(int argc, char *argv[]) {
remove_directory = true;
} else {
+ r = chase_symlinks_and_update(&arg_directory, arg_template ? CHASE_NONEXISTENT : 0);
+ if (r < 0)
+ goto finish;
+
r = image_path_lock(arg_directory, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock);
if (r == -EBUSY) {
log_error_errno(r, "Directory tree %s is currently busy.", arg_directory);
@@ -4181,6 +3612,10 @@ int main(int argc, char *argv[]) {
}
if (arg_template) {
+ r = chase_symlinks_and_update(&arg_template, 0);
+ if (r < 0)
+ goto finish;
+
r = btrfs_subvol_snapshot(arg_template, arg_directory,
(arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) |
BTRFS_SNAPSHOT_FALLBACK_COPY |
@@ -4222,6 +3657,10 @@ int main(int argc, char *argv[]) {
assert(arg_image);
assert(!arg_template);
+ r = chase_symlinks_and_update(&arg_image, 0);
+ if (r < 0)
+ goto finish;
+
if (arg_ephemeral) {
_cleanup_free_ char *np = NULL;
@@ -4258,6 +3697,10 @@ int main(int argc, char *argv[]) {
r = log_error_errno(r, "Failed to create image lock: %m");
goto finish;
}
+
+ r = load_root_hash(arg_image);
+ if (r < 0)
+ goto finish;
}
if (!mkdtemp(tmprootdir)) {
@@ -4273,18 +3716,41 @@ int main(int argc, char *argv[]) {
goto finish;
}
- image_fd = setup_image(&device_path, &loop_nr);
- if (image_fd < 0) {
- r = image_fd;
+ r = loop_device_make_by_path(arg_image, arg_read_only ? O_RDONLY : O_RDWR, &loop);
+ if (r < 0) {
+ log_error_errno(r, "Failed to set up loopback block device: %m");
+ goto finish;
+ }
+
+ r = dissect_image(loop->fd, arg_root_hash, arg_root_hash_size, &dissected_image);
+ if (r == -ENOPKG) {
+ log_error_errno(r, "Could not find a suitable file system or partition table in image: %s", arg_image);
+
+ log_notice("Note that the disk image needs to\n"
+ " a) either contain only a single MBR partition of type 0x83 that is marked bootable\n"
+ " b) or contain a single GPT partition of type 0FC63DAF-8483-4772-8E79-3D69D8477DE4\n"
+ " c) or follow http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/\n"
+ " d) or contain a file system without a partition table\n"
+ "in order to be bootable with systemd-nspawn.");
+ goto finish;
+ }
+ if (r == -EADDRNOTAVAIL) {
+ log_error_errno(r, "No root partition for specified root hash found.");
+ goto finish;
+ }
+ if (r == -EOPNOTSUPP) {
+ log_error_errno(r, "--image= is not supported, compiled without blkid support.");
+ goto finish;
+ }
+ if (r < 0) {
+ log_error_errno(r, "Failed to dissect image: %m");
goto finish;
}
- r = dissect_image(image_fd,
- &root_device, &root_device_rw,
- &home_device, &home_device_rw,
- &srv_device, &srv_device_rw,
- &esp_device,
- &secondary);
+ if (!arg_root_hash && dissected_image->can_verity)
+ log_notice("Note: image %s contains verity information, but no root hash specified! Proceeding without integrity checking.", arg_image);
+
+ r = dissected_image_decrypt_interactively(dissected_image, NULL, arg_root_hash, arg_root_hash_size, 0, &decrypted_image);
if (r < 0)
goto finish;
@@ -4293,7 +3759,7 @@ int main(int argc, char *argv[]) {
remove_image = false;
}
- r = custom_mounts_prepare();
+ r = custom_mount_prepare_all(arg_directory, arg_custom_mounts, arg_n_custom_mounts);
if (r < 0)
goto finish;
@@ -4338,10 +3804,7 @@ int main(int argc, char *argv[]) {
for (;;) {
r = run(master,
console,
- root_device, root_device_rw,
- home_device, home_device_rw,
- srv_device, srv_device_rw,
- esp_device,
+ dissected_image,
interactive, secondary,
fds,
veth_name, &veth_created,
@@ -4368,8 +3831,6 @@ finish:
if (pid > 0)
(void) wait_for_terminate(pid, NULL);
- loop_remove(loop_nr, &image_fd);
-
if (remove_directory && arg_directory) {
int k;
@@ -4416,6 +3877,7 @@ finish:
strv_free(arg_parameters);
custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts);
expose_port_free_all(arg_expose_ports);
+ free(arg_root_hash);
return r < 0 ? EXIT_FAILURE : ret;
}
diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c
index 9d4d04220c..07d9582ccb 100644
--- a/src/resolve/resolve-tool.c
+++ b/src/resolve/resolve-tool.c
@@ -881,8 +881,8 @@ static int resolve_tlsa(sd_bus *bus, const char *address) {
port = strrchr(address, ':');
if (port) {
- r = safe_atou16(port + 1, &port_num);
- if (r < 0 || port_num == 0)
+ r = parse_ip_port(port + 1, &port_num);
+ if (r < 0)
return log_error_errno(r, "Invalid port \"%s\".", port + 1);
address = strndupa(address, port - address);
diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c
index 8d5a5c6b79..74603f9311 100644
--- a/src/resolve/resolved.c
+++ b/src/resolve/resolved.c
@@ -112,7 +112,7 @@ int main(int argc, char *argv[]) {
sd_event_get_exit_code(m->event, &r);
finish:
- /* systemd-nspawn checks for private resov.conf to decide whether
+ /* systemd-nspawn checks for private resolv.conf to decide whether
or not to mount it into the container. So just delete it. */
(void) unlink(PRIVATE_RESOLV_CONF);
diff --git a/src/shared/condition.c b/src/shared/condition.c
index 525e65aedf..0b77d2c22d 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -399,7 +399,7 @@ static int condition_test_path_is_mount_point(Condition *c) {
assert(c->parameter);
assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
- return path_is_mount_point(c->parameter, AT_SYMLINK_FOLLOW) > 0;
+ return path_is_mount_point(c->parameter, NULL, AT_SYMLINK_FOLLOW) > 0;
}
static int condition_test_path_is_read_write(Condition *c) {
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
new file mode 100644
index 0000000000..d3ba9b9dde
--- /dev/null
+++ b/src/shared/dissect-image.c
@@ -0,0 +1,1072 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_LIBCRYPTSETUP
+#include <libcryptsetup.h>
+#endif
+#include <linux/dm-ioctl.h>
+#include <sys/mount.h>
+
+#include "architecture.h"
+#include "ask-password-api.h"
+#include "blkid-util.h"
+#include "dissect-image.h"
+#include "fd-util.h"
+#include "gpt.h"
+#include "mount-util.h"
+#include "path-util.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "udev-util.h"
+
+static int probe_filesystem(const char *node, char **ret_fstype) {
+#ifdef HAVE_BLKID
+ _cleanup_blkid_free_probe_ blkid_probe b = NULL;
+ const char *fstype;
+ int r;
+
+ b = blkid_new_probe_from_filename(node);
+ if (!b)
+ return -ENOMEM;
+
+ blkid_probe_enable_superblocks(b, 1);
+ blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
+
+ errno = 0;
+ r = blkid_do_safeprobe(b);
+ if (r == -2 || r == 1) {
+ 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;
+ }
+
+ (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
+
+ if (fstype) {
+ char *t;
+
+ t = strdup(fstype);
+ if (!t)
+ return -ENOMEM;
+
+ *ret_fstype = t;
+ return 1;
+ }
+
+not_found:
+ *ret_fstype = NULL;
+ return 0;
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectedImage **ret) {
+
+#ifdef HAVE_BLKID
+ sd_id128_t root_uuid = SD_ID128_NULL, verity_uuid = SD_ID128_NULL;
+ _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
+ bool is_gpt, is_mbr, generic_rw, multiple_generic = false;
+ _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+ _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
+ _cleanup_blkid_free_probe_ blkid_probe b = NULL;
+ _cleanup_udev_unref_ struct udev *udev = NULL;
+ _cleanup_free_ char *generic_node = NULL;
+ const char *pttype = NULL, *usage = NULL;
+ struct udev_list_entry *first, *item;
+ blkid_partlist pl;
+ int r, generic_nr;
+ struct stat st;
+ unsigned i;
+
+ assert(fd >= 0);
+ assert(ret);
+ assert(root_hash || root_hash_size == 0);
+
+ /* Probes a disk image, and returns information about what it found in *ret.
+ *
+ * Returns -ENOPKG if no suitable partition table or file system could be found.
+ * Returns -EADDRNOTAVAIL if a root hash was specified but no matching root/verity partitions found. */
+
+ if (root_hash) {
+ /* If a root hash is supplied, then we use the root partition that has a UUID that match the first
+ * 128bit of the root hash. And we use the verity partition that has a UUID that match the final
+ * 128bit. */
+
+ if (root_hash_size < sizeof(sd_id128_t))
+ return -EINVAL;
+
+ memcpy(&root_uuid, root_hash, sizeof(sd_id128_t));
+ memcpy(&verity_uuid, (const uint8_t*) root_hash + root_hash_size - sizeof(sd_id128_t), sizeof(sd_id128_t));
+
+ if (sd_id128_is_null(root_uuid))
+ return -EINVAL;
+ if (sd_id128_is_null(verity_uuid))
+ return -EINVAL;
+ }
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISBLK(st.st_mode))
+ return -ENOTBLK;
+
+ b = blkid_new_probe();
+ if (!b)
+ return -ENOMEM;
+
+ errno = 0;
+ r = blkid_probe_set_device(b, fd, 0, 0);
+ if (r != 0) {
+ if (errno == 0)
+ return -ENOMEM;
+
+ return -errno;
+ }
+
+ blkid_probe_enable_superblocks(b, 1);
+ blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE);
+ blkid_probe_enable_partitions(b, 1);
+ blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
+
+ errno = 0;
+ r = blkid_do_safeprobe(b);
+ if (r == -2 || r == 1) {
+ log_debug("Failed to identify any partition table.");
+ return -ENOPKG;
+ }
+ if (r != 0) {
+ if (errno == 0)
+ return -EIO;
+
+ return -errno;
+ }
+
+ m = new0(DissectedImage, 1);
+ if (!m)
+ return -ENOMEM;
+
+ (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
+ if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
+ _cleanup_free_ char *t = NULL, *n = NULL;
+ const char *fstype = NULL;
+
+ /* OK, we have found a file system, that's our root partition then. */
+ (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
+
+ if (fstype) {
+ t = strdup(fstype);
+ if (!t)
+ return -ENOMEM;
+ }
+
+ if (asprintf(&n, "/dev/block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
+ return -ENOMEM;
+
+ m->partitions[PARTITION_ROOT] = (DissectedPartition) {
+ .found = true,
+ .rw = true,
+ .partno = -1,
+ .architecture = _ARCHITECTURE_INVALID,
+ .fstype = t,
+ .node = n,
+ };
+
+ t = n = NULL;
+
+ m->encrypted = streq(fstype, "crypto_LUKS");
+
+ *ret = m;
+ m = NULL;
+
+ return 0;
+ }
+
+ (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
+ if (!pttype)
+ return -ENOPKG;
+
+ is_gpt = streq_ptr(pttype, "gpt");
+ is_mbr = streq_ptr(pttype, "dos");
+
+ if (!is_gpt && !is_mbr)
+ return -ENOPKG;
+
+ errno = 0;
+ pl = blkid_probe_get_partitions(b);
+ if (!pl) {
+ if (errno == 0)
+ return -ENOMEM;
+
+ return -errno;
+ }
+
+ udev = udev_new();
+ if (!udev)
+ return -errno;
+
+ d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
+ if (!d)
+ return -ENOMEM;
+
+ for (i = 0;; i++) {
+ int n, z;
+
+ if (i >= 10) {
+ log_debug("Kernel partitions never appeared.");
+ return -ENXIO;
+ }
+
+ e = udev_enumerate_new(udev);
+ if (!e)
+ return -errno;
+
+ r = udev_enumerate_add_match_parent(e, d);
+ if (r < 0)
+ return r;
+
+ r = udev_enumerate_scan_devices(e);
+ if (r < 0)
+ return r;
+
+ /* Count the partitions enumerated by the kernel */
+ n = 0;
+ first = udev_enumerate_get_list_entry(e);
+ udev_list_entry_foreach(item, first)
+ n++;
+
+ /* Count the partitions enumerated by blkid */
+ z = blkid_partlist_numof_partitions(pl);
+ if (n == z + 1)
+ break;
+ if (n > z + 1) {
+ log_debug("blkid and kernel partition list do not match.");
+ return -EIO;
+ }
+ if (n < z + 1) {
+ unsigned j;
+
+ /* The kernel has probed fewer partitions than blkid? Maybe the kernel prober is still running
+ * or it got EBUSY because udev already opened the device. Let's reprobe the device, which is a
+ * synchronous call that waits until probing is complete. */
+
+ for (j = 0; j < 20; j++) {
+
+ r = ioctl(fd, BLKRRPART, 0);
+ if (r < 0)
+ r = -errno;
+ if (r >= 0 || r != -EBUSY)
+ break;
+
+ /* If something else has the device open, such as an udev rule, the ioctl will return
+ * EBUSY. Since there's no way to wait until it isn't busy anymore, let's just wait a
+ * bit, and try again.
+ *
+ * This is really something they should fix in the kernel! */
+
+ usleep(50 * USEC_PER_MSEC);
+ }
+
+ if (r < 0)
+ return r;
+ }
+
+ e = udev_enumerate_unref(e);
+ }
+
+ first = udev_enumerate_get_list_entry(e);
+ udev_list_entry_foreach(item, first) {
+ _cleanup_udev_device_unref_ struct udev_device *q;
+ unsigned long long flags;
+ blkid_partition pp;
+ const char *node;
+ dev_t qn;
+ int nr;
+
+ q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
+ if (!q)
+ return -errno;
+
+ qn = udev_device_get_devnum(q);
+ if (major(qn) == 0)
+ continue;
+
+ if (st.st_rdev == qn)
+ continue;
+
+ node = udev_device_get_devnode(q);
+ if (!node)
+ continue;
+
+ pp = blkid_partlist_devno_to_partition(pl, qn);
+ if (!pp)
+ continue;
+
+ flags = blkid_partition_get_flags(pp);
+
+ nr = blkid_partition_get_partno(pp);
+ if (nr < 0)
+ continue;
+
+ if (is_gpt) {
+ int designator = _PARTITION_DESIGNATOR_INVALID, architecture = _ARCHITECTURE_INVALID;
+ const char *stype, *sid, *fstype = NULL;
+ sd_id128_t type_id, id;
+ bool rw = true;
+
+ if (flags & GPT_FLAG_NO_AUTO)
+ continue;
+
+ sid = blkid_partition_get_uuid(pp);
+ if (!sid)
+ continue;
+ if (sd_id128_from_string(sid, &id) < 0)
+ continue;
+
+ stype = blkid_partition_get_type_string(pp);
+ if (!stype)
+ continue;
+ if (sd_id128_from_string(stype, &type_id) < 0)
+ continue;
+
+ if (sd_id128_equal(type_id, GPT_HOME)) {
+ designator = PARTITION_HOME;
+ rw = !(flags & GPT_FLAG_READ_ONLY);
+ } else if (sd_id128_equal(type_id, GPT_SRV)) {
+ designator = PARTITION_SRV;
+ rw = !(flags & GPT_FLAG_READ_ONLY);
+ } else if (sd_id128_equal(type_id, GPT_ESP)) {
+ designator = PARTITION_ESP;
+ fstype = "vfat";
+ }
+#ifdef GPT_ROOT_NATIVE
+ else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
+
+ /* If a root ID is specified, ignore everything but the root id */
+ if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
+ continue;
+
+ designator = PARTITION_ROOT;
+ architecture = native_architecture();
+ rw = !(flags & GPT_FLAG_READ_ONLY);
+ } else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) {
+
+ m->can_verity = true;
+
+ /* Ignore verity unless a root hash is specified */
+ if (sd_id128_is_null(verity_uuid) || !sd_id128_equal(verity_uuid, id))
+ continue;
+
+ designator = PARTITION_ROOT_VERITY;
+ fstype = "DM_verity_hash";
+ architecture = native_architecture();
+ rw = false;
+ }
+#endif
+#ifdef GPT_ROOT_SECONDARY
+ else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
+
+ /* If a root ID is specified, ignore everything but the root id */
+ if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
+ continue;
+
+ designator = PARTITION_ROOT_SECONDARY;
+ architecture = SECONDARY_ARCHITECTURE;
+ rw = !(flags & GPT_FLAG_READ_ONLY);
+ } else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) {
+
+ m->can_verity = true;
+
+ /* Ignore verity unless root has is specified */
+ if (sd_id128_is_null(verity_uuid) || !sd_id128_equal(verity_uuid, id))
+ continue;
+
+ designator = PARTITION_ROOT_SECONDARY_VERITY;
+ fstype = "DM_verity_hash";
+ architecture = SECONDARY_ARCHITECTURE;
+ rw = false;
+ }
+#endif
+ else if (sd_id128_equal(type_id, GPT_SWAP)) {
+ designator = PARTITION_SWAP;
+ fstype = "swap";
+ } else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
+
+ if (generic_node)
+ multiple_generic = true;
+ else {
+ generic_nr = nr;
+ generic_rw = !(flags & GPT_FLAG_READ_ONLY);
+ generic_node = strdup(node);
+ if (!generic_node)
+ return -ENOMEM;
+ }
+ }
+
+ if (designator != _PARTITION_DESIGNATOR_INVALID) {
+ _cleanup_free_ char *t = NULL, *n = NULL;
+
+ /* First one wins */
+ if (m->partitions[designator].found)
+ continue;
+
+ if (fstype) {
+ t = strdup(fstype);
+ if (!t)
+ return -ENOMEM;
+ }
+
+ n = strdup(node);
+ if (!n)
+ return -ENOMEM;
+
+ m->partitions[designator] = (DissectedPartition) {
+ .found = true,
+ .partno = nr,
+ .rw = rw,
+ .architecture = architecture,
+ .node = n,
+ .fstype = t,
+ };
+
+ n = t = NULL;
+ }
+
+ } else if (is_mbr) {
+
+ if (flags != 0x80) /* Bootable flag */
+ continue;
+
+ if (blkid_partition_get_type(pp) != 0x83) /* Linux partition */
+ continue;
+
+ if (generic_node)
+ multiple_generic = true;
+ else {
+ generic_nr = nr;
+ generic_rw = true;
+ generic_node = strdup(node);
+ if (!generic_node)
+ return -ENOMEM;
+ }
+ }
+ }
+
+ if (!m->partitions[PARTITION_ROOT].found) {
+ /* No root partition found? Then let's see if ther's one for the secondary architecture. And if not
+ * either, then check if there's a single generic one, and use that. */
+
+ if (m->partitions[PARTITION_ROOT_VERITY].found)
+ return -ENXIO;
+
+ if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
+ m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
+ zero(m->partitions[PARTITION_ROOT_SECONDARY]);
+
+ m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
+ zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
+
+ } else if (generic_node && !root_hash) {
+
+ if (multiple_generic)
+ return -ENOTUNIQ;
+
+ m->partitions[PARTITION_ROOT] = (DissectedPartition) {
+ .found = true,
+ .rw = generic_rw,
+ .partno = generic_nr,
+ .architecture = _ARCHITECTURE_INVALID,
+ .node = generic_node,
+ };
+
+ generic_node = NULL;
+ } else
+ return -ENXIO;
+ }
+
+ assert(m->partitions[PARTITION_ROOT].found);
+
+ if (root_hash) {
+ if (!m->partitions[PARTITION_ROOT_VERITY].found)
+ return -EADDRNOTAVAIL;
+
+ /* If we found the primary root with the hash, then we definitely want to suppress any secondary root
+ * (which would be weird, after all the root hash should only be assigned to one pair of
+ * partitions... */
+ m->partitions[PARTITION_ROOT_SECONDARY].found = false;
+ m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found = false;
+
+ /* If we found a verity setup, then the root partition is necessarily read-only. */
+ m->partitions[PARTITION_ROOT].rw = false;
+
+ m->verity = true;
+ }
+
+ blkid_free_probe(b);
+ b = NULL;
+
+ /* Fill in file system types if we don't know them yet. */
+ for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
+ DissectedPartition *p = m->partitions + i;
+
+ if (!p->found)
+ continue;
+
+ if (!p->fstype && p->node) {
+ r = probe_filesystem(p->node, &p->fstype);
+ if (r < 0)
+ return r;
+ }
+
+ if (streq_ptr(p->fstype, "crypto_LUKS"))
+ m->encrypted = true;
+ }
+
+ *ret = m;
+ m = NULL;
+
+ return 0;
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+DissectedImage* dissected_image_unref(DissectedImage *m) {
+ unsigned i;
+
+ if (!m)
+ return NULL;
+
+ for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
+ free(m->partitions[i].fstype);
+ free(m->partitions[i].node);
+ free(m->partitions[i].decrypted_fstype);
+ free(m->partitions[i].decrypted_node);
+ }
+
+ free(m);
+ return NULL;
+}
+
+static int is_loop_device(const char *path) {
+ char s[strlen("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen("/../loop/")];
+ struct stat st;
+
+ assert(path);
+
+ if (stat(path, &st) < 0)
+ return -errno;
+
+ if (!S_ISBLK(st.st_mode))
+ return -ENOTBLK;
+
+ xsprintf(s, "/sys/dev/block/%u:%u/loop/", major(st.st_rdev), minor(st.st_rdev));
+ if (access(s, F_OK) < 0) {
+ if (errno != ENOENT)
+ return -errno;
+
+ /* The device itself isn't a loop device, but maybe it's a partition and its parent is? */
+ xsprintf(s, "/sys/dev/block/%u:%u/../loop/", major(st.st_rdev), minor(st.st_rdev));
+ if (access(s, F_OK) < 0)
+ return errno == ENOENT ? false : -errno;
+ }
+
+ return true;
+}
+
+static int mount_partition(
+ DissectedPartition *m,
+ const char *where,
+ const char *directory,
+ DissectImageFlags flags) {
+
+ const char *p, *options = NULL, *node, *fstype;
+ bool rw;
+
+ assert(m);
+ assert(where);
+
+ node = m->decrypted_node ?: m->node;
+ fstype = m->decrypted_fstype ?: m->fstype;
+
+ if (!m->found || !node || !fstype)
+ return 0;
+
+ /* Stacked encryption? Yuck */
+ if (streq_ptr(fstype, "crypto_LUKS"))
+ return -ELOOP;
+
+ rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY);
+
+ if (directory)
+ p = strjoina(where, directory);
+ else
+ p = where;
+
+ /* If requested, turn on discard support. */
+ if (STR_IN_SET(fstype, "btrfs", "ext4", "vfat", "xfs") &&
+ ((flags & DISSECT_IMAGE_DISCARD) ||
+ ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node))))
+ options = "discard";
+
+ return mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
+}
+
+int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlags flags) {
+ int r;
+
+ assert(m);
+ assert(where);
+
+ if (!m->partitions[PARTITION_ROOT].found)
+ return -ENXIO;
+
+ r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, flags);
+ if (r < 0)
+ return r;
+
+ r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", flags);
+ if (r < 0)
+ return r;
+
+ r = mount_partition(m->partitions + PARTITION_SRV, where, "/srv", flags);
+ if (r < 0)
+ return r;
+
+ if (m->partitions[PARTITION_ESP].found) {
+ const char *mp, *x;
+
+ /* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */
+
+ mp = "/efi";
+ x = strjoina(where, mp);
+ r = dir_is_empty(x);
+ if (r == -ENOENT) {
+ mp = "/boot";
+ x = strjoina(where, mp);
+ r = dir_is_empty(x);
+ }
+ if (r > 0) {
+ r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef HAVE_LIBCRYPTSETUP
+typedef struct DecryptedPartition {
+ struct crypt_device *device;
+ char *name;
+ bool relinquished;
+} DecryptedPartition;
+
+struct DecryptedImage {
+ DecryptedPartition *decrypted;
+ size_t n_decrypted;
+ size_t n_allocated;
+};
+#endif
+
+DecryptedImage* decrypted_image_unref(DecryptedImage* d) {
+#ifdef HAVE_LIBCRYPTSETUP
+ size_t i;
+ int r;
+
+ if (!d)
+ return NULL;
+
+ for (i = 0; i < d->n_decrypted; i++) {
+ DecryptedPartition *p = d->decrypted + i;
+
+ if (p->device && p->name && !p->relinquished) {
+ r = crypt_deactivate(p->device, p->name);
+ if (r < 0)
+ log_debug_errno(r, "Failed to deactivate encrypted partition %s", p->name);
+ }
+
+ if (p->device)
+ crypt_free(p->device);
+ free(p->name);
+ }
+
+ free(d);
+#endif
+ return NULL;
+}
+
+#ifdef HAVE_LIBCRYPTSETUP
+
+static int make_dm_name_and_node(const void *original_node, const char *suffix, char **ret_name, char **ret_node) {
+ _cleanup_free_ char *name = NULL, *node = NULL;
+ const char *base;
+
+ assert(original_node);
+ assert(suffix);
+ assert(ret_name);
+ assert(ret_node);
+
+ base = strrchr(original_node, '/');
+ if (!base)
+ return -EINVAL;
+ base++;
+ if (isempty(base))
+ return -EINVAL;
+
+ name = strjoin(base, suffix);
+ if (!name)
+ return -ENOMEM;
+ if (!filename_is_valid(name))
+ return -EINVAL;
+
+ node = strjoin(crypt_get_dir(), "/", name);
+ if (!node)
+ return -ENOMEM;
+
+ *ret_name = name;
+ *ret_node = node;
+
+ name = node = NULL;
+ return 0;
+}
+
+static int decrypt_partition(
+ DissectedPartition *m,
+ const char *passphrase,
+ DissectImageFlags flags,
+ DecryptedImage *d) {
+
+ _cleanup_free_ char *node = NULL, *name = NULL;
+ struct crypt_device *cd;
+ int r;
+
+ assert(m);
+ assert(d);
+
+ if (!m->found || !m->node || !m->fstype)
+ return 0;
+
+ if (!streq(m->fstype, "crypto_LUKS"))
+ return 0;
+
+ r = make_dm_name_and_node(m->node, "-decrypted", &name, &node);
+ if (r < 0)
+ return r;
+
+ if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
+ return -ENOMEM;
+
+ r = crypt_init(&cd, m->node);
+ if (r < 0)
+ return r;
+
+ r = crypt_load(cd, CRYPT_LUKS1, NULL);
+ if (r < 0)
+ goto fail;
+
+ r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase),
+ ((flags & DISSECT_IMAGE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) |
+ ((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0));
+ if (r == -EPERM) {
+ r = -EKEYREJECTED;
+ goto fail;
+ }
+ if (r < 0)
+ goto fail;
+
+ d->decrypted[d->n_decrypted].name = name;
+ name = NULL;
+
+ d->decrypted[d->n_decrypted].device = cd;
+ d->n_decrypted++;
+
+ m->decrypted_node = node;
+ node = NULL;
+
+ return 0;
+
+fail:
+ crypt_free(cd);
+ return r;
+}
+
+static int verity_partition(
+ DissectedPartition *m,
+ DissectedPartition *v,
+ const void *root_hash,
+ size_t root_hash_size,
+ DissectImageFlags flags,
+ DecryptedImage *d) {
+
+ _cleanup_free_ char *node = NULL, *name = NULL;
+ struct crypt_device *cd;
+ int r;
+
+ assert(m);
+ assert(v);
+
+ if (!root_hash)
+ return 0;
+
+ if (!m->found || !m->node || !m->fstype)
+ return 0;
+ if (!v->found || !v->node || !v->fstype)
+ return 0;
+
+ if (!streq(v->fstype, "DM_verity_hash"))
+ return 0;
+
+ r = make_dm_name_and_node(m->node, "-verity", &name, &node);
+ if (r < 0)
+ return r;
+
+ if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
+ return -ENOMEM;
+
+ r = crypt_init(&cd, v->node);
+ if (r < 0)
+ return r;
+
+ r = crypt_load(cd, CRYPT_VERITY, NULL);
+ if (r < 0)
+ goto fail;
+
+ r = crypt_set_data_device(cd, m->node);
+ if (r < 0)
+ goto fail;
+
+ r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY);
+ if (r < 0)
+ goto fail;
+
+ d->decrypted[d->n_decrypted].name = name;
+ name = NULL;
+
+ d->decrypted[d->n_decrypted].device = cd;
+ d->n_decrypted++;
+
+ m->decrypted_node = node;
+ node = NULL;
+
+ return 0;
+
+fail:
+ crypt_free(cd);
+ return r;
+}
+#endif
+
+int dissected_image_decrypt(
+ DissectedImage *m,
+ const char *passphrase,
+ const void *root_hash,
+ size_t root_hash_size,
+ DissectImageFlags flags,
+ DecryptedImage **ret) {
+
+ _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
+#ifdef HAVE_LIBCRYPTSETUP
+ unsigned i;
+ int r;
+#endif
+
+ assert(m);
+ assert(root_hash || root_hash_size == 0);
+
+ /* Returns:
+ *
+ * = 0 → There was nothing to decrypt
+ * > 0 → Decrypted successfully
+ * -ENOKEY → There's some to decrypt but no key was supplied
+ * -EKEYREJECTED → Passed key was not correct
+ */
+
+ if (root_hash && root_hash_size < sizeof(sd_id128_t))
+ return -EINVAL;
+
+ if (!m->encrypted && !m->verity) {
+ *ret = NULL;
+ return 0;
+ }
+
+#ifdef HAVE_LIBCRYPTSETUP
+ if (m->encrypted && !passphrase)
+ return -ENOKEY;
+
+ d = new0(DecryptedImage, 1);
+ if (!d)
+ return -ENOMEM;
+
+ for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
+ DissectedPartition *p = m->partitions + i;
+ int k;
+
+ if (!p->found)
+ continue;
+
+ r = decrypt_partition(p, passphrase, flags, d);
+ if (r < 0)
+ return r;
+
+ k = PARTITION_VERITY_OF(i);
+ if (k >= 0) {
+ r = verity_partition(p, m->partitions + k, root_hash, root_hash_size, flags, d);
+ if (r < 0)
+ return r;
+ }
+
+ if (!p->decrypted_fstype && p->decrypted_node) {
+ r = probe_filesystem(p->decrypted_node, &p->decrypted_fstype);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ *ret = d;
+ d = NULL;
+
+ return 1;
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+int dissected_image_decrypt_interactively(
+ DissectedImage *m,
+ const char *passphrase,
+ const void *root_hash,
+ size_t root_hash_size,
+ DissectImageFlags flags,
+ DecryptedImage **ret) {
+
+ _cleanup_strv_free_erase_ char **z = NULL;
+ int n = 3, r;
+
+ if (passphrase)
+ n--;
+
+ for (;;) {
+ r = dissected_image_decrypt(m, passphrase, root_hash, root_hash_size, flags, ret);
+ if (r >= 0)
+ return r;
+ if (r == -EKEYREJECTED)
+ log_error_errno(r, "Incorrect passphrase, try again!");
+ else if (r != -ENOKEY) {
+ log_error_errno(r, "Failed to decrypt image: %m");
+ return r;
+ }
+
+ if (--n < 0) {
+ log_error("Too many retries.");
+ return -EKEYREJECTED;
+ }
+
+ z = strv_free(z);
+
+ r = ask_password_auto("Please enter image passphrase!", NULL, "dissect", "dissect", USEC_INFINITY, 0, &z);
+ if (r < 0)
+ return log_error_errno(r, "Failed to query for passphrase: %m");
+
+ passphrase = z[0];
+ }
+}
+
+#ifdef HAVE_LIBCRYPTSETUP
+static int deferred_remove(DecryptedPartition *p) {
+
+ struct dm_ioctl dm = {
+ .version = {
+ DM_VERSION_MAJOR,
+ DM_VERSION_MINOR,
+ DM_VERSION_PATCHLEVEL
+ },
+ .data_size = sizeof(dm),
+ .flags = DM_DEFERRED_REMOVE,
+ };
+
+ _cleanup_close_ int fd = -1;
+
+ assert(p);
+
+ /* Unfortunately, libcryptsetup doesn't provide a proper API for this, hence call the ioctl() directly. */
+
+ fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ strncpy(dm.name, p->name, sizeof(dm.name));
+
+ if (ioctl(fd, DM_DEV_REMOVE, &dm))
+ return -errno;
+
+ return 0;
+}
+#endif
+
+int decrypted_image_relinquish(DecryptedImage *d) {
+
+#ifdef HAVE_LIBCRYPTSETUP
+ size_t i;
+ int r;
+#endif
+
+ assert(d);
+
+ /* Turns on automatic removal after the last use ended for all DM devices of this image, and sets a boolean so
+ * that we don't clean it up ourselves either anymore */
+
+#ifdef HAVE_LIBCRYPTSETUP
+ for (i = 0; i < d->n_decrypted; i++) {
+ DecryptedPartition *p = d->decrypted + i;
+
+ if (p->relinquished)
+ continue;
+
+ r = deferred_remove(p);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to mark %s for auto-removal: %m", p->name);
+
+ p->relinquished = true;
+ }
+#endif
+
+ return 0;
+}
+
+static const char *const partition_designator_table[] = {
+ [PARTITION_ROOT] = "root",
+ [PARTITION_ROOT_SECONDARY] = "root-secondary",
+ [PARTITION_HOME] = "home",
+ [PARTITION_SRV] = "srv",
+ [PARTITION_ESP] = "esp",
+ [PARTITION_SWAP] = "swap",
+ [PARTITION_ROOT_VERITY] = "root-verity",
+ [PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(partition_designator, int);
diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h
new file mode 100644
index 0000000000..175ddd8ea0
--- /dev/null
+++ b/src/shared/dissect-image.h
@@ -0,0 +1,93 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include "macro.h"
+
+typedef struct DissectedImage DissectedImage;
+typedef struct DissectedPartition DissectedPartition;
+typedef struct DecryptedImage DecryptedImage;
+
+struct DissectedPartition {
+ bool found:1;
+ bool rw:1;
+ int partno; /* -1 if there was no partition and the images contains a file system directly */
+ int architecture; /* Intended architecture: either native, secondary or unset (-1). */
+ char *fstype;
+ char *node;
+ char *decrypted_node;
+ char *decrypted_fstype;
+};
+
+enum {
+ PARTITION_ROOT,
+ PARTITION_ROOT_SECONDARY, /* Secondary architecture */
+ PARTITION_HOME,
+ PARTITION_SRV,
+ PARTITION_ESP,
+ PARTITION_SWAP,
+ PARTITION_ROOT_VERITY, /* verity data for the PARTITION_ROOT partition */
+ PARTITION_ROOT_SECONDARY_VERITY, /* verity data for the PARTITION_ROOT_SECONDARY partition */
+ _PARTITION_DESIGNATOR_MAX,
+ _PARTITION_DESIGNATOR_INVALID = -1
+};
+
+static inline int PARTITION_VERITY_OF(int p) {
+ if (p == PARTITION_ROOT)
+ return PARTITION_ROOT_VERITY;
+ if (p == PARTITION_ROOT_SECONDARY)
+ return PARTITION_ROOT_SECONDARY_VERITY;
+ return _PARTITION_DESIGNATOR_INVALID;
+}
+
+typedef enum DissectImageFlags {
+ DISSECT_IMAGE_READ_ONLY = 1,
+ DISSECT_IMAGE_DISCARD_ON_LOOP = 2, /* Turn on "discard" if on a loop device and file system supports it */
+ DISSECT_IMAGE_DISCARD = 4, /* Turn on "discard" if file system supports it, on all block devices */
+ DISSECT_IMAGE_DISCARD_ON_CRYPTO = 8, /* Turn on "discard" also on crypto devices */
+ DISSECT_IMAGE_DISCARD_ANY = DISSECT_IMAGE_DISCARD_ON_LOOP |
+ DISSECT_IMAGE_DISCARD |
+ DISSECT_IMAGE_DISCARD_ON_CRYPTO,
+} DissectImageFlags;
+
+struct DissectedImage {
+ bool encrypted:1;
+ bool verity:1; /* verity available and usable */
+ bool can_verity:1; /* verity available, but not necessarily used */
+ DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX];
+};
+
+int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectedImage **ret);
+
+DissectedImage* dissected_image_unref(DissectedImage *m);
+DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
+
+int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret);
+int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret);
+int dissected_image_mount(DissectedImage *m, const char *dest, DissectImageFlags flags);
+
+DecryptedImage* decrypted_image_unref(DecryptedImage *p);
+DEFINE_TRIVIAL_CLEANUP_FUNC(DecryptedImage*, decrypted_image_unref);
+int decrypted_image_relinquish(DecryptedImage *d);
+
+const char* partition_designator_to_string(int i) _const_;
+int partition_designator_from_string(const char *name) _pure_;
diff --git a/src/shared/dropin.c b/src/shared/dropin.c
index 2c1cd84df5..3cbfe13f4c 100644
--- a/src/shared/dropin.c
+++ b/src/shared/dropin.c
@@ -17,7 +17,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <dirent.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
@@ -25,6 +24,7 @@
#include "alloc-util.h"
#include "conf-files.h"
+#include "dirent-util.h"
#include "dropin.h"
#include "escape.h"
#include "fd-util.h"
@@ -124,6 +124,7 @@ static int iterate_dir(
char ***strv) {
_cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
int r;
assert(path);
@@ -148,21 +149,9 @@ static int iterate_dir(
return log_error_errno(errno, "Failed to open directory %s: %m", path);
}
- for (;;) {
- struct dirent *de;
+ FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read directory %s: %m", path)) {
_cleanup_free_ char *f = NULL;
- errno = 0;
- de = readdir(d);
- if (!de && errno > 0)
- return log_error_errno(errno, "Failed to read directory %s: %m", path);
-
- if (!de)
- break;
-
- if (hidden_or_backup_file(de->d_name))
- continue;
-
f = strjoin(path, "/", de->d_name);
if (!f)
return log_oom();
diff --git a/src/shared/fdset.c b/src/shared/fdset.c
index 527f27bc67..090f3fdcdd 100644
--- a/src/shared/fdset.c
+++ b/src/shared/fdset.c
@@ -18,13 +18,13 @@
***/
#include <alloca.h>
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include "sd-daemon.h"
+#include "dirent-util.h"
#include "fd-util.h"
#include "fdset.h"
#include "log.h"
@@ -148,12 +148,9 @@ int fdset_new_fill(FDSet **_s) {
goto finish;
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, return -errno) {
int fd = -1;
- if (hidden_or_backup_file(de->d_name))
- continue;
-
r = safe_atoi(de->d_name, &fd);
if (r < 0)
goto finish;
diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c
index f73108eaa3..9c29b0afca 100644
--- a/src/shared/firewall-util.c
+++ b/src/shared/firewall-util.c
@@ -17,8 +17,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#warning "Temporary work-around for broken glibc vs. linux kernel header definitions"
-#warning "This really should be removed sooner rather than later, when this is fixed upstream"
+/* Temporary work-around for broken glibc vs. linux kernel header definitions
+ * This is already fixed upstream, remove this when distributions have updated.
+ */
#define _NET_IF_H 1
#include <alloca.h>
diff --git a/src/shared/gpt.h b/src/shared/gpt.h
index 55b41bbcd8..13d80d611c 100644
--- a/src/shared/gpt.h
+++ b/src/shared/gpt.h
@@ -32,28 +32,43 @@
#define GPT_ROOT_ARM SD_ID128_MAKE(69,da,d7,10,2c,e4,4e,3c,b1,6c,21,a1,d4,9a,be,d3)
#define GPT_ROOT_ARM_64 SD_ID128_MAKE(b9,21,b0,45,1d,f0,41,c3,af,44,4c,6f,28,0d,3f,ae)
#define GPT_ROOT_IA64 SD_ID128_MAKE(99,3d,8d,3d,f8,0e,42,25,85,5a,9d,af,8e,d7,ea,97)
-
#define GPT_ESP SD_ID128_MAKE(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b)
#define GPT_SWAP SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)
#define GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15)
#define GPT_SRV SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8)
+/* Verity partitions for the root partitions above (we only define them for the root partitions, because only they are
+ * are commonly read-only and hence suitable for verity). */
+#define GPT_ROOT_X86_VERITY SD_ID128_MAKE(d1,3c,5d,3b,b5,d1,42,2a,b2,9f,94,54,fd,c8,9d,76)
+#define GPT_ROOT_X86_64_VERITY SD_ID128_MAKE(2c,73,57,ed,eb,d2,46,d9,ae,c1,23,d4,37,ec,2b,f5)
+#define GPT_ROOT_ARM_VERITY SD_ID128_MAKE(73,86,cd,f2,20,3c,47,a9,a4,98,f2,ec,ce,45,a2,d6)
+#define GPT_ROOT_ARM_64_VERITY SD_ID128_MAKE(df,33,00,ce,d6,9f,4c,92,97,8c,9b,fb,0f,38,d8,20)
+#define GPT_ROOT_IA64_VERITY SD_ID128_MAKE(86,ed,10,d5,b6,07,45,bb,89,57,d3,50,f2,3d,05,71)
+
+
#if defined(__x86_64__)
# define GPT_ROOT_NATIVE GPT_ROOT_X86_64
# define GPT_ROOT_SECONDARY GPT_ROOT_X86
+# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_X86_64_VERITY
+# define GPT_ROOT_SECONDARY_VERITY GPT_ROOT_X86_VERITY
#elif defined(__i386__)
# define GPT_ROOT_NATIVE GPT_ROOT_X86
+# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_X86_VERITY
#endif
#if defined(__ia64__)
# define GPT_ROOT_NATIVE GPT_ROOT_IA64
+# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_IA64_VERITY
#endif
#if defined(__aarch64__) && (__BYTE_ORDER != __BIG_ENDIAN)
# define GPT_ROOT_NATIVE GPT_ROOT_ARM_64
# define GPT_ROOT_SECONDARY GPT_ROOT_ARM
+# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_ARM_64_VERITY
+# define GPT_ROOT_SECONDARY_VERITY GPT_ROOT_ARM_VERITY
#elif defined(__arm__) && (__BYTE_ORDER != __BIG_ENDIAN)
# define GPT_ROOT_NATIVE GPT_ROOT_ARM
+# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_ARM_VERITY
#endif
/* Flags we recognize on the root, swap, home and srv partitions when
diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c
new file mode 100644
index 0000000000..047e213634
--- /dev/null
+++ b/src/shared/loop-util.c
@@ -0,0 +1,166 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+#include <linux/loop.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "loop-util.h"
+
+int loop_device_make(int fd, int open_flags, LoopDevice **ret) {
+ const struct loop_info64 info = {
+ .lo_flags = LO_FLAGS_AUTOCLEAR|LO_FLAGS_PARTSCAN|(open_flags == O_RDONLY ? LO_FLAGS_READ_ONLY : 0),
+ };
+
+ _cleanup_close_ int control = -1, loop = -1;
+ _cleanup_free_ char *loopdev = NULL;
+ struct stat st;
+ LoopDevice *d;
+ int nr;
+
+ assert(fd >= 0);
+ assert(ret);
+ assert(IN_SET(open_flags, O_RDWR, O_RDONLY));
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (S_ISBLK(st.st_mode)) {
+ int copy;
+
+ /* If this is already a block device, store a copy of the fd as it is */
+
+ copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (copy < 0)
+ return -errno;
+
+ d = new0(LoopDevice, 1);
+ if (!d)
+ return -ENOMEM;
+
+ *d = (LoopDevice) {
+ .fd = copy,
+ .nr = -1,
+ };
+
+ *ret = d;
+
+ return 0;
+ }
+
+ if (!S_ISREG(st.st_mode))
+ return -EINVAL;
+
+ control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (control < 0)
+ return -errno;
+
+ nr = ioctl(control, LOOP_CTL_GET_FREE);
+ if (nr < 0)
+ return -errno;
+
+ if (asprintf(&loopdev, "/dev/loop%i", nr) < 0)
+ return -ENOMEM;
+
+ loop = open(loopdev, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
+ if (loop < 0)
+ return -errno;
+
+ if (ioctl(loop, LOOP_SET_FD, fd) < 0)
+ return -errno;
+
+ if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0)
+ return -errno;
+
+ d = new(LoopDevice, 1);
+ if (!d)
+ return -ENOMEM;
+
+ *d = (LoopDevice) {
+ .fd = loop,
+ .node = loopdev,
+ .nr = nr,
+ };
+
+ loop = -1;
+ loopdev = NULL;
+
+ *ret = d;
+
+ return (*ret)->fd;
+}
+
+int loop_device_make_by_path(const char *path, int open_flags, LoopDevice **ret) {
+ _cleanup_close_ int fd = -1;
+
+ assert(path);
+ assert(ret);
+ assert(IN_SET(open_flags, O_RDWR, O_RDONLY));
+
+ fd = open(path, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
+ if (fd < 0)
+ return -errno;
+
+ return loop_device_make(fd, open_flags, ret);
+}
+
+LoopDevice* loop_device_unref(LoopDevice *d) {
+ if (!d)
+ return NULL;
+
+ if (d->fd >= 0) {
+
+ if (d->nr >= 0 && !d->relinquished) {
+ if (ioctl(d->fd, LOOP_CLR_FD) < 0)
+ log_debug_errno(errno, "Failed to clear loop device: %m");
+
+ }
+
+ safe_close(d->fd);
+ }
+
+ if (d->nr >= 0 && !d->relinquished) {
+ _cleanup_close_ int control = -1;
+
+ control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (control < 0)
+ log_debug_errno(errno, "Failed to open loop control device: %m");
+ else {
+ if (ioctl(control, LOOP_CTL_REMOVE, d->nr) < 0)
+ log_debug_errno(errno, "Failed to remove loop device: %m");
+ }
+ }
+
+ free(d->node);
+ free(d);
+
+ return NULL;
+}
+
+void loop_device_relinquish(LoopDevice *d) {
+ assert(d);
+
+ /* Don't attempt to clean up the loop device anymore from this point on. Leave the clean-ing up to the kernel
+ * itself, using the loop device "auto-clear" logic we already turned on when creating the device. */
+
+ d->relinquished = true;
+}
diff --git a/src/shared/loop-util.h b/src/shared/loop-util.h
new file mode 100644
index 0000000000..45fead5f18
--- /dev/null
+++ b/src/shared/loop-util.h
@@ -0,0 +1,41 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "macro.h"
+
+typedef struct LoopDevice LoopDevice;
+
+/* Some helpers for setting up loopback block devices */
+
+struct LoopDevice {
+ int fd;
+ int nr;
+ char *node;
+ bool relinquished;
+};
+
+int loop_device_make(int fd, int open_flags, LoopDevice **ret);
+int loop_device_make_by_path(const char *path, int open_flags, LoopDevice **ret);
+
+LoopDevice* loop_device_unref(LoopDevice *d);
+DEFINE_TRIVIAL_CLEANUP_FUNC(LoopDevice*, loop_device_unref);
+
+void loop_device_relinquish(LoopDevice *d);
diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c
index 23890c63a0..c581bdeb79 100644
--- a/src/shared/machine-pool.c
+++ b/src/shared/machine-pool.c
@@ -225,7 +225,7 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) {
return 1;
}
- if (path_is_mount_point("/var/lib/machines", AT_SYMLINK_FOLLOW) > 0) {
+ if (path_is_mount_point("/var/lib/machines", NULL, AT_SYMLINK_FOLLOW) > 0) {
log_debug("/var/lib/machines is already a mount point, not creating loopback file for it.");
return 0;
}
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index ed1c7178b5..67516cf5fb 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -2487,7 +2487,7 @@ static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **un
if (!path)
return log_oom();
- r = chase_symlinks(path, arg_root, &lpath);
+ r = chase_symlinks(path, arg_root, 0, &lpath);
if (r == -ENOENT)
continue;
if (r == -ENOMEM)
@@ -6438,7 +6438,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) {
r = unit_file_get_state(arg_scope, arg_root, *name, &state);
if (r < 0)
- return log_error_errno(state, "Failed to get unit file state for %s: %m", *name);
+ return log_error_errno(r, "Failed to get unit file state for %s: %m", *name);
if (IN_SET(state,
UNIT_FILE_ENABLED,
diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h
index ee011b1861..6cc8e4ac0e 100644
--- a/src/systemd/sd-id128.h
+++ b/src/systemd/sd-id128.h
@@ -39,12 +39,12 @@ union sd_id128 {
#define SD_ID128_STRING_MAX 33
char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]);
-
int sd_id128_from_string(const char *s, sd_id128_t *ret);
int sd_id128_randomize(sd_id128_t *ret);
int sd_id128_get_machine(sd_id128_t *ret);
+int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_boot(sd_id128_t *ret);
int sd_id128_get_invocation(sd_id128_t *ret);
diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h
index 79246ae060..db1a21be05 100644
--- a/src/systemd/sd-messages.h
+++ b/src/systemd/sd-messages.h
@@ -53,6 +53,7 @@ _SD_BEGIN_DECLARATIONS;
#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)
diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c
index b3d1160ea7..b8320b081b 100644
--- a/src/test/test-calendarspec.c
+++ b/src/test/test-calendarspec.c
@@ -186,6 +186,9 @@ int main(int argc, char* argv[]) {
test_one("Monday *-*-*", "Mon *-*-* 00:00:00");
test_one("*-*-*", "*-*-* 00:00:00");
test_one("*:*:*", "*-*-* *:*:*");
+ test_one("*:*", "*-*-* *:*:00");
+ test_one("12:*", "*-*-* 12:*:00");
+ test_one("*:30", "*-*-* *:30: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-copy.c b/src/test/test-copy.c
index 91d2a0bcd4..e65516f080 100644
--- a/src/test/test-copy.c
+++ b/src/test/test-copy.c
@@ -144,7 +144,7 @@ static void test_copy_tree(void) {
assert_se((f = strjoin(original_dir, *p)));
assert_se((l = strjoin(copy_dir, *link)));
- assert_se(readlink_and_canonicalize(l, &target) == 0);
+ assert_se(readlink_and_canonicalize(l, NULL, &target) == 0);
assert_se(path_equal(f, target));
}
diff --git a/src/test/test-dissect-image.c b/src/test/test-dissect-image.c
new file mode 100644
index 0000000000..0512a15e88
--- /dev/null
+++ b/src/test/test-dissect-image.c
@@ -0,0 +1,66 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "dissect-image.h"
+#include "log.h"
+#include "loop-util.h"
+#include "string-util.h"
+
+int main(int argc, char *argv[]) {
+ _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
+ _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
+ int r, i;
+
+ log_set_max_level(LOG_DEBUG);
+
+ if (argc < 2) {
+ log_error("Requires one command line argument.");
+ return EXIT_FAILURE;
+ }
+
+ r = loop_device_make_by_path(argv[1], O_RDONLY, &d);
+ if (r < 0) {
+ log_error_errno(r, "Failed to set up loopback device: %m");
+ return EXIT_FAILURE;
+ }
+
+ r = dissect_image(d->fd, NULL, 0, &m);
+ if (r < 0) {
+ log_error_errno(r, "Failed to dissect image: %m");
+ return EXIT_FAILURE;
+ }
+
+ for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
+
+ if (!m->partitions[i].found)
+ continue;
+
+ printf("Found %s partition, %s of type %s at #%i (%s)\n",
+ partition_designator_to_string(i),
+ m->partitions[i].rw ? "writable" : "read-only",
+ strna(m->partitions[i].fstype),
+ m->partitions[i].partno,
+ strna(m->partitions[i].node));
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index b2ea358b8c..4670458ffb 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -409,8 +409,8 @@ static void test_exec_spec_interpolation(Manager *m) {
test(m, "exec-spec-interpolation.service", 0, CLD_EXITED);
}
-static int run_tests(UnitFileScope scope, test_function_t *tests) {
- test_function_t *test = NULL;
+static int run_tests(UnitFileScope scope, const test_function_t *tests) {
+ const test_function_t *test = NULL;
Manager *m = NULL;
int r;
@@ -433,7 +433,7 @@ static int run_tests(UnitFileScope scope, test_function_t *tests) {
}
int main(int argc, char *argv[]) {
- test_function_t user_tests[] = {
+ static const test_function_t user_tests[] = {
test_exec_workingdirectory,
test_exec_personality,
test_exec_ignoresigpipe,
@@ -464,7 +464,7 @@ int main(int argc, char *argv[]) {
test_exec_spec_interpolation,
NULL,
};
- test_function_t system_tests[] = {
+ static const test_function_t system_tests[] = {
test_exec_systemcall_system_mode_with_user,
NULL,
};
diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c
index 53a3cdc663..b502cd0ad1 100644
--- a/src/test/test-fs-util.c
+++ b/src/test/test-fs-util.c
@@ -60,66 +60,131 @@ static void test_chase_symlinks(void) {
p = strjoina(temp, "/start");
assert_se(symlink("top/dot/dotdota", p) >= 0);
- r = chase_symlinks(p, NULL, &result);
- assert_se(r >= 0);
+ /* Paths that use symlinks underneath the "root" */
+
+ r = chase_symlinks(p, NULL, 0, &result);
+ assert_se(r > 0);
assert_se(path_equal(result, "/usr"));
result = mfree(result);
- r = chase_symlinks(p, temp, &result);
+ r = chase_symlinks(p, temp, 0, &result);
assert_se(r == -ENOENT);
q = strjoina(temp, "/usr");
+
+ r = chase_symlinks(p, temp, CHASE_NONEXISTENT, &result);
+ assert_se(r == 0);
+ assert_se(path_equal(result, q));
+
assert_se(mkdir(q, 0700) >= 0);
- r = chase_symlinks(p, temp, &result);
- assert_se(r >= 0);
+ r = chase_symlinks(p, temp, 0, &result);
+ assert_se(r > 0);
assert_se(path_equal(result, q));
p = strjoina(temp, "/slash");
assert_se(symlink("/", p) >= 0);
result = mfree(result);
- r = chase_symlinks(p, NULL, &result);
- assert_se(r >= 0);
+ r = chase_symlinks(p, NULL, 0, &result);
+ assert_se(r > 0);
assert_se(path_equal(result, "/"));
result = mfree(result);
- r = chase_symlinks(p, temp, &result);
- assert_se(r >= 0);
+ r = chase_symlinks(p, temp, 0, &result);
+ assert_se(r > 0);
assert_se(path_equal(result, temp));
+ /* Paths that would "escape" outside of the "root" */
+
+ p = strjoina(temp, "/6dots");
+ assert_se(symlink("../../..", p) >= 0);
+
+ result = mfree(result);
+ r = chase_symlinks(p, temp, 0, &result);
+ assert_se(r > 0 && path_equal(result, temp));
+
+ p = strjoina(temp, "/6dotsusr");
+ assert_se(symlink("../../../usr", p) >= 0);
+
+ result = mfree(result);
+ r = chase_symlinks(p, temp, 0, &result);
+ assert_se(r > 0 && path_equal(result, q));
+
+ p = strjoina(temp, "/top/8dotsusr");
+ assert_se(symlink("../../../../usr", p) >= 0);
+
+ result = mfree(result);
+ r = chase_symlinks(p, temp, 0, &result);
+ assert_se(r > 0 && path_equal(result, q));
+
+ /* Paths that contain repeated slashes */
+
p = strjoina(temp, "/slashslash");
assert_se(symlink("///usr///", p) >= 0);
result = mfree(result);
- r = chase_symlinks(p, NULL, &result);
- assert_se(r >= 0);
+ r = chase_symlinks(p, NULL, 0, &result);
+ assert_se(r > 0);
assert_se(path_equal(result, "/usr"));
result = mfree(result);
- r = chase_symlinks(p, temp, &result);
- assert_se(r >= 0);
+ r = chase_symlinks(p, temp, 0, &result);
+ assert_se(r > 0);
assert_se(path_equal(result, q));
+ /* Paths using . */
+
result = mfree(result);
- r = chase_symlinks("/etc/./.././", NULL, &result);
- assert_se(r >= 0);
+ r = chase_symlinks("/etc/./.././", NULL, 0, &result);
+ assert_se(r > 0);
assert_se(path_equal(result, "/"));
result = mfree(result);
- r = chase_symlinks("/etc/./.././", "/etc", &result);
- assert_se(r == -EINVAL);
+ r = chase_symlinks("/etc/./.././", "/etc", 0, &result);
+ assert_se(r > 0 && path_equal(result, "/etc"));
result = mfree(result);
- r = chase_symlinks("/etc/machine-id/foo", NULL, &result);
+ r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result);
assert_se(r == -ENOTDIR);
+ /* Path that loops back to self */
+
result = mfree(result);
p = strjoina(temp, "/recursive-symlink");
assert_se(symlink("recursive-symlink", p) >= 0);
- r = chase_symlinks(p, NULL, &result);
+ r = chase_symlinks(p, NULL, 0, &result);
assert_se(r == -ELOOP);
+ /* Path which doesn't exist */
+
+ p = strjoina(temp, "/idontexist");
+ r = chase_symlinks(p, NULL, 0, &result);
+ assert_se(r == -ENOENT);
+
+ r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
+ assert_se(r == 0);
+ assert_se(path_equal(result, p));
+ result = mfree(result);
+
+ p = strjoina(temp, "/idontexist/meneither");
+ r = chase_symlinks(p, NULL, 0, &result);
+ assert_se(r == -ENOENT);
+
+ r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
+ assert_se(r == 0);
+ assert_se(path_equal(result, p));
+ result = mfree(result);
+
+ /* Path which doesn't exist, but contains weird stuff */
+
+ p = strjoina(temp, "/idontexist/..");
+ r = chase_symlinks(p, NULL, 0, &result);
+ assert_se(r == -ENOENT);
+
+ r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
+ assert_se(r == -ENOENT);
+
assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
diff --git a/src/test/test-hash.c b/src/test/test-hash.c
new file mode 100644
index 0000000000..1972b94cfe
--- /dev/null
+++ b/src/test/test-hash.c
@@ -0,0 +1,82 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "log.h"
+#include "string-util.h"
+#include "khash.h"
+
+int main(int argc, char *argv[]) {
+ _cleanup_(khash_unrefp) khash *h = NULL, *copy = NULL;
+ _cleanup_free_ char *s = NULL;
+
+ log_set_max_level(LOG_DEBUG);
+
+ assert_se(khash_new(&h, NULL) == -EINVAL);
+ assert_se(khash_new(&h, "") == -EINVAL);
+ assert_se(khash_new(&h, "foobar") == -EOPNOTSUPP);
+
+ assert_se(khash_new(&h, "sha256") >= 0);
+ assert_se(khash_get_size(h) == 32);
+ assert_se(streq(khash_get_algorithm(h), "sha256"));
+
+ assert_se(khash_digest_string(h, &s) >= 0);
+ assert_se(streq(s, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"));
+ s = mfree(s);
+
+ assert_se(khash_put(h, "foobar", 6) >= 0);
+ assert_se(khash_digest_string(h, &s) >= 0);
+ assert_se(streq(s, "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"));
+ s = mfree(s);
+
+ assert_se(khash_put(h, "piep", 4) >= 0);
+ assert_se(khash_digest_string(h, &s) >= 0);
+ assert_se(streq(s, "f114d872b5ea075d3be9040d0b7a429514b3f9324a8e8e3dc3fb24c34ee56bea"));
+ s = mfree(s);
+
+ assert_se(khash_put(h, "foo", 3) >= 0);
+ assert_se(khash_dup(h, &copy) >= 0);
+
+ assert_se(khash_put(h, "bar", 3) >= 0);
+ assert_se(khash_put(copy, "bar", 3) >= 0);
+
+ assert_se(khash_digest_string(h, &s) >= 0);
+ assert_se(streq(s, "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"));
+ s = mfree(s);
+
+ assert_se(khash_digest_string(copy, &s) >= 0);
+ assert_se(streq(s, "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"));
+ s = mfree(s);
+
+ h = khash_unref(h);
+
+ assert_se(khash_new_with_key(&h, "hmac(sha256)", "quux", 4) >= 0);
+ assert_se(khash_get_size(h) == 32);
+ assert_se(streq(khash_get_algorithm(h), "hmac(sha256)"));
+
+ assert_se(khash_digest_string(h, &s) >= 0);
+ assert_se(streq(s, "abed9f8218ab473f77218a6a7d39abf1d21fa46d0700c4898e330ba88309d5ae"));
+ s = mfree(s);
+
+ assert_se(khash_put(h, "foobar", 6) >= 0);
+ assert_se(khash_digest_string(h, &s) >= 0);
+ assert_se(streq(s, "33f6c70a60db66007d5325d5d1dea37c371354e5b83347a59ad339ce9f4ba3dc"));
+
+ return 0;
+}
diff --git a/src/test/test-id128.c b/src/test/test-id128.c
index 1c8e5549da..ab5a111ba9 100644
--- a/src/test/test-id128.c
+++ b/src/test/test-id128.c
@@ -153,5 +153,11 @@ int main(int argc, char *argv[]) {
assert_se(id128_read_fd(fd, ID128_UUID, &id2) >= 0);
assert_se(sd_id128_equal(id, id2));
+ assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id) >= 0);
+ assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id2) >= 0);
+ assert_se(sd_id128_equal(id, id2));
+ assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(51,df,0b,4b,c3,b0,4c,97,80,e2,99,b9,8c,a3,73,b8), &id2) >= 0);
+ assert_se(!sd_id128_equal(id, id2));
+
return 0;
}
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
index a6a09a0031..22df20a1eb 100644
--- a/src/test/test-path-util.c
+++ b/src/test/test-path-util.c
@@ -337,17 +337,17 @@ static void test_path_is_mount_point(void) {
_cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL;
_cleanup_free_ char *dir2 = NULL, *dir2file = NULL;
- assert_se(path_is_mount_point("/", AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/", 0) > 0);
+ assert_se(path_is_mount_point("/", NULL, AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/", NULL, 0) > 0);
- assert_se(path_is_mount_point("/proc", AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/proc", 0) > 0);
+ assert_se(path_is_mount_point("/proc", NULL, AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/proc", NULL, 0) > 0);
- assert_se(path_is_mount_point("/proc/1", AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point("/proc/1", 0) == 0);
+ assert_se(path_is_mount_point("/proc/1", NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point("/proc/1", NULL, 0) == 0);
- assert_se(path_is_mount_point("/sys", AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/sys", 0) > 0);
+ assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/sys", NULL, 0) > 0);
/* we'll create a hierarchy of different kinds of dir/file/link
* layouts:
@@ -381,10 +381,10 @@ static void test_path_is_mount_point(void) {
assert_se(link1);
assert_se(symlink("file2", link2) == 0);
- assert_se(path_is_mount_point(file1, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(file1, 0) == 0);
- assert_se(path_is_mount_point(link1, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(link1, 0) == 0);
+ assert_se(path_is_mount_point(file1, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(file1, NULL, 0) == 0);
+ assert_se(path_is_mount_point(link1, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(link1, NULL, 0) == 0);
/* directory mountpoints */
dir1 = path_join(NULL, tmp_dir, "dir1");
@@ -400,10 +400,10 @@ static void test_path_is_mount_point(void) {
assert_se(dir2);
assert_se(mkdir(dir2, 0755) == 0);
- assert_se(path_is_mount_point(dir1, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(dir1, 0) == 0);
- assert_se(path_is_mount_point(dirlink1, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(dirlink1, 0) == 0);
+ assert_se(path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(dir1, NULL, 0) == 0);
+ assert_se(path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(dirlink1, NULL, 0) == 0);
/* file in subdirectory mountpoints */
dir1file = path_join(NULL, dir1, "file");
@@ -412,10 +412,10 @@ static void test_path_is_mount_point(void) {
assert_se(fd > 0);
close(fd);
- assert_se(path_is_mount_point(dir1file, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(dir1file, 0) == 0);
- assert_se(path_is_mount_point(dirlink1file, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(dirlink1file, 0) == 0);
+ assert_se(path_is_mount_point(dir1file, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(dir1file, NULL, 0) == 0);
+ assert_se(path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(dirlink1file, NULL, 0) == 0);
/* these tests will only work as root */
if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
@@ -423,10 +423,10 @@ static void test_path_is_mount_point(void) {
/* files */
/* capture results in vars, to avoid dangling mounts on failure */
- rf = path_is_mount_point(file2, 0);
- rt = path_is_mount_point(file2, AT_SYMLINK_FOLLOW);
- rlf = path_is_mount_point(link2, 0);
- rlt = path_is_mount_point(link2, AT_SYMLINK_FOLLOW);
+ rf = path_is_mount_point(file2, NULL, 0);
+ rt = path_is_mount_point(file2, NULL, AT_SYMLINK_FOLLOW);
+ rlf = path_is_mount_point(link2, NULL, 0);
+ rlt = path_is_mount_point(link2, NULL, AT_SYMLINK_FOLLOW);
assert_se(umount(file2) == 0);
@@ -444,13 +444,13 @@ static void test_path_is_mount_point(void) {
assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0);
- rf = path_is_mount_point(dir1, 0);
- rt = path_is_mount_point(dir1, AT_SYMLINK_FOLLOW);
- rlf = path_is_mount_point(dirlink1, 0);
- rlt = path_is_mount_point(dirlink1, AT_SYMLINK_FOLLOW);
+ rf = path_is_mount_point(dir1, NULL, 0);
+ rt = path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW);
+ rlf = path_is_mount_point(dirlink1, NULL, 0);
+ rlt = path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW);
/* its parent is a mount point, but not /file itself */
- rl1f = path_is_mount_point(dirlink1file, 0);
- rl1t = path_is_mount_point(dirlink1file, AT_SYMLINK_FOLLOW);
+ rl1f = path_is_mount_point(dirlink1file, NULL, 0);
+ rl1t = path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW);
assert_se(umount(dir1) == 0);
diff --git a/src/test/test-time.c b/src/test/test-time.c
index 7078a0374d..8259491813 100644
--- a/src/test/test-time.c
+++ b/src/test/test-time.c
@@ -42,6 +42,10 @@ static void test_parse_sec(void) {
assert_se(u == 2500 * USEC_PER_MSEC);
assert_se(parse_sec(".7", &u) >= 0);
assert_se(u == 700 * USEC_PER_MSEC);
+ assert_se(parse_sec("23us", &u) >= 0);
+ assert_se(u == 23);
+ assert_se(parse_sec("23µs", &u) >= 0);
+ assert_se(u == 23);
assert_se(parse_sec("infinity", &u) >= 0);
assert_se(u == USEC_INFINITY);
assert_se(parse_sec(" infinity ", &u) >= 0);
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index b881d774a0..79f75e165b 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -18,7 +18,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
@@ -44,6 +43,7 @@
#include "conf-files.h"
#include "copy.h"
#include "def.h"
+#include "dirent-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
@@ -380,7 +380,7 @@ static int dir_cleanup(
bool deleted = false;
int r = 0;
- while ((dent = readdir(d))) {
+ FOREACH_DIRENT_ALL(dent, d, break) {
struct stat s;
usec_t age;
_cleanup_free_ char *sub_path = NULL;
@@ -1053,6 +1053,7 @@ typedef int (*action_t)(Item *, const char *);
static int item_do_children(Item *i, const char *path, action_t action) {
_cleanup_closedir_ DIR *d;
+ struct dirent *de;
int r = 0;
assert(i);
@@ -1065,19 +1066,11 @@ static int item_do_children(Item *i, const char *path, action_t action) {
if (!d)
return errno == ENOENT || errno == ENOTDIR ? 0 : -errno;
- for (;;) {
+ FOREACH_DIRENT_ALL(de, d, r = -errno) {
_cleanup_free_ char *p = NULL;
- struct dirent *de;
int q;
errno = 0;
- de = readdir(d);
- if (!de) {
- if (errno > 0 && r == 0)
- r = -errno;
-
- break;
- }
if (STR_IN_SET(de->d_name, ".", ".."))
continue;
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index fe9d6f4482..5be158f527 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -100,6 +100,7 @@
#include <unistd.h>
#include <linux/pci_regs.h>
+#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "stdio-util.h"
@@ -256,7 +257,7 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
goto out;
}
- for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+ FOREACH_DIRENT_ALL(dent, dir, break) {
int i;
char *rest, *address, str[PATH_MAX];
diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c
index 1825ee75a7..527f0bff2d 100644
--- a/src/udev/udev-builtin-path_id.c
+++ b/src/udev/udev-builtin-path_id.c
@@ -20,7 +20,6 @@
*/
#include <ctype.h>
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
@@ -31,6 +30,7 @@
#include <unistd.h>
#include "alloc-util.h"
+#include "dirent-util.h"
#include "string-util.h"
#include "udev.h"
@@ -405,7 +405,7 @@ static struct udev_device *handle_scsi_default(struct udev_device *parent, char
parent = NULL;
goto out;
}
- for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+ FOREACH_DIRENT_ALL(dent, dir, break) {
char *rest;
int i;
diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
index e94a814388..53cfd9c053 100644
--- a/src/udev/udev-node.c
+++ b/src/udev/udev-node.c
@@ -15,7 +15,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
@@ -25,6 +24,7 @@
#include <sys/stat.h>
#include <unistd.h>
+#include "dirent-util.h"
#include "format-util.h"
#include "fs-util.h"
#include "selinux-util.h"
@@ -129,6 +129,7 @@ exit:
static const char *link_find_prioritized(struct udev_device *dev, bool add, const char *stackdir, char *buf, size_t bufsize) {
struct udev *udev = udev_device_get_udev(dev);
DIR *dir;
+ struct dirent *dent;
int priority = 0;
const char *target = NULL;
@@ -141,12 +142,10 @@ static const char *link_find_prioritized(struct udev_device *dev, bool add, cons
dir = opendir(stackdir);
if (dir == NULL)
return target;
- for (;;) {
+ FOREACH_DIRENT_ALL(dent, dir, break) {
struct udev_device *dev_db;
- struct dirent *dent;
- dent = readdir(dir);
- if (dent == NULL || dent->d_name[0] == '\0')
+ if (dent->d_name[0] == '\0')
break;
if (dent->d_name[0] == '.')
continue;
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index f6c416bf70..b0238220e4 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -16,7 +16,6 @@
*/
#include <ctype.h>
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
@@ -31,6 +30,7 @@
#include "alloc-util.h"
#include "conf-files.h"
+#include "dirent-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fs-util.h"
@@ -614,7 +614,7 @@ static int import_property_from_string(struct udev_device *dev, char *line) {
/* unquote */
if (val[0] == '"' || val[0] == '\'') {
- if (val[len-1] != val[0]) {
+ if (len == 1 || val[len-1] != val[0]) {
log_debug("inconsistent quoting: '%s', skip", line);
return -1;
}
@@ -703,7 +703,7 @@ static void attr_subst_subdir(char *attr, size_t len) {
if (dir == NULL)
return;
- for (dent = readdir(dir); dent != NULL; dent = readdir(dir))
+ FOREACH_DIRENT_ALL(dent, dir, break)
if (dent->d_name[0] != '.') {
char n[strlen(dent->d_name) + strlen(tail) + 1];
diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c
index bc9096ed0c..aa432bb90a 100644
--- a/src/udev/udev-watch.c
+++ b/src/udev/udev-watch.c
@@ -17,13 +17,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <dirent.h>
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/inotify.h>
#include <unistd.h>
+#include "dirent-util.h"
#include "stdio-util.h"
#include "udev.h"
@@ -57,7 +57,7 @@ void udev_watch_restore(struct udev *udev) {
return;
}
- for (ent = readdir(dir); ent != NULL; ent = readdir(dir)) {
+ FOREACH_DIRENT_ALL(ent, dir, break) {
char device[UTIL_PATH_SIZE];
ssize_t len;
struct udev_device *dev;
diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c
index 6753c52005..90cdfa16c7 100644
--- a/src/udev/udevadm-info.c
+++ b/src/udev/udevadm-info.c
@@ -16,7 +16,6 @@
*/
#include <ctype.h>
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
@@ -26,6 +25,7 @@
#include <sys/stat.h>
#include <unistd.h>
+#include "dirent-util.h"
#include "fd-util.h"
#include "string-util.h"
#include "udev-util.h"
@@ -196,7 +196,7 @@ static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
if (depth <= 0)
return;
- for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+ FOREACH_DIRENT_ALL(dent, dir, break) {
struct stat stats;
if (dent->d_name[0] == '.')
diff --git a/test/TEST-13-NSPAWN-SMOKE/test.sh b/test/TEST-13-NSPAWN-SMOKE/test.sh
index 305866ae38..b8b8ec34bd 100755
--- a/test/TEST-13-NSPAWN-SMOKE/test.sh
+++ b/test/TEST-13-NSPAWN-SMOKE/test.sh
@@ -83,6 +83,14 @@ if unshare -U sh -c :; then
is_user_ns_supported=yes
fi
+function check_bind_tmp_path {
+ # https://github.com/systemd/systemd/issues/4789
+ local _root="/var/lib/machines/bind-tmp-path"
+ /create-busybox-container "$_root"
+ >/tmp/bind
+ systemd-nspawn --register=no -D "$_root" --bind=/tmp/bind /bin/sh -c 'test -e /tmp/bind'
+}
+
function run {
if [[ "$1" = "yes" && "$is_v2_supported" = "no" ]]; then
printf "Unified cgroup hierarchy is not supported. Skipping.\n" >&2
@@ -113,6 +121,8 @@ function run {
return 0
}
+check_bind_tmp_path
+
for api_vfs_writable in yes no network; do
run no no $api_vfs_writable
run yes no $api_vfs_writable
diff --git a/test/networkd-test.py b/test/networkd-test.py
index f8914a7895..39bd4f5b1b 100755
--- a/test/networkd-test.py
+++ b/test/networkd-test.py
@@ -30,6 +30,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with systemd; If not, see <http://www.gnu.org/licenses/>.
+import errno
import os
import sys
import time
@@ -39,16 +40,109 @@ import subprocess
import shutil
import socket
-networkd_active = subprocess.call(['systemctl', 'is-active', '--quiet',
- 'systemd-networkd']) == 0
-have_dnsmasq = shutil.which('dnsmasq')
+HAVE_DNSMASQ = shutil.which('dnsmasq') is not None
+
+NETWORK_UNITDIR = '/run/systemd/network'
+
+NETWORKD_WAIT_ONLINE = shutil.which('systemd-networkd-wait-online',
+ path='/usr/lib/systemd:/lib/systemd')
RESOLV_CONF = '/run/systemd/resolve/resolv.conf'
-@unittest.skipIf(networkd_active,
- 'networkd is already active')
-class ClientTestBase:
+def setUpModule():
+ """Initialize the environment, and perform sanity checks on it."""
+ if NETWORKD_WAIT_ONLINE is None:
+ raise OSError(errno.ENOENT, 'systemd-networkd-wait-online not found')
+
+ # Do not run any tests if the system is using networkd already.
+ if subprocess.call(['systemctl', 'is-active', '--quiet',
+ 'systemd-networkd.service']) == 0:
+ raise unittest.SkipTest('networkd is already active')
+
+ # Avoid "Failed to open /dev/tty" errors in containers.
+ os.environ['SYSTEMD_LOG_TARGET'] = 'journal'
+
+ # Ensure the unit directory exists so tests can dump files into it.
+ os.makedirs(NETWORK_UNITDIR, exist_ok=True)
+
+
+class NetworkdTestingUtilities:
+ """Provide a set of utility functions to facilitate networkd tests.
+
+ This class must be inherited along with unittest.TestCase to define
+ some required methods.
+ """
+
+ def add_veth_pair(self, veth, peer, veth_options=(), peer_options=()):
+ """Add a veth interface pair, and queue them to be removed."""
+ subprocess.check_call(['ip', 'link', 'add', 'name', veth] +
+ list(veth_options) +
+ ['type', 'veth', 'peer', 'name', peer] +
+ list(peer_options))
+ self.addCleanup(subprocess.call, ['ip', 'link', 'del', 'dev', peer])
+
+ def write_network(self, unit_name, contents):
+ """Write a network unit file, and queue it to be removed."""
+ unit_path = os.path.join(NETWORK_UNITDIR, unit_name)
+
+ with open(unit_path, 'w') as unit:
+ unit.write(contents)
+ self.addCleanup(os.remove, unit_path)
+
+ def write_network_dropin(self, unit_name, dropin_name, contents):
+ """Write a network unit drop-in, and queue it to be removed."""
+ dropin_dir = os.path.join(NETWORK_UNITDIR, "%s.d" % unit_name)
+ dropin_path = os.path.join(dropin_dir, "%s.conf" % dropin_name)
+
+ os.makedirs(dropin_dir, exist_ok=True)
+ with open(dropin_path, 'w') as dropin:
+ dropin.write(contents)
+ self.addCleanup(os.remove, dropin_path)
+
+ def assert_link_states(self, **kwargs):
+ """Match networkctl link states to the given ones.
+
+ Each keyword argument should be the name of a network interface
+ with its expected value of the "SETUP" column in output from
+ networkctl. The interfaces have five seconds to come online
+ before the check is performed. Every specified interface must
+ be present in the output, and any other interfaces found in the
+ output are ignored.
+
+ A special interface state "managed" is supported, which matches
+ any value in the "SETUP" column other than "unmanaged".
+ """
+ if not kwargs:
+ return
+ interfaces = set(kwargs)
+
+ # Wait for the requested interfaces, but don't fail for them.
+ subprocess.call([NETWORKD_WAIT_ONLINE, '--timeout=5'] +
+ ['--interface=%s' % iface for iface in kwargs])
+
+ # Validate each link state found in the networkctl output.
+ out = subprocess.check_output(['networkctl', '--no-legend']).rstrip()
+ for line in out.decode('utf-8').split('\n'):
+ fields = line.split()
+ if len(fields) >= 5 and fields[1] in kwargs:
+ iface = fields[1]
+ expected = kwargs[iface]
+ actual = fields[-1]
+ if (actual != expected and
+ not (expected == 'managed' and actual != 'unmanaged')):
+ self.fail("Link %s expects state %s, found %s" %
+ (iface, expected, actual))
+ interfaces.remove(iface)
+
+ # Ensure that all requested interfaces have been covered.
+ if interfaces:
+ self.fail("Missing links in status output: %s" % interfaces)
+
+
+class ClientTestBase(NetworkdTestingUtilities):
+ """Provide common methods for testing networkd against servers."""
+
@classmethod
def setUpClass(klass):
klass.orig_log_level = subprocess.check_output(
@@ -65,19 +159,7 @@ class ClientTestBase:
self.if_router = 'router_eth42'
self.workdir_obj = tempfile.TemporaryDirectory()
self.workdir = self.workdir_obj.name
- self.config = '/run/systemd/network/test_eth42.network'
-
- # avoid "Failed to open /dev/tty" errors in containers
- os.environ['SYSTEMD_LOG_TARGET'] = 'journal'
-
- # determine path to systemd-networkd-wait-online
- for p in ['/usr/lib/systemd/systemd-networkd-wait-online',
- '/lib/systemd/systemd-networkd-wait-online']:
- if os.path.exists(p):
- self.networkd_wait_online = p
- break
- else:
- self.fail('systemd-networkd-wait-online not found')
+ self.config = 'test_eth42.network'
# get current journal cursor
subprocess.check_output(['journalctl', '--sync'])
@@ -93,12 +175,6 @@ class ClientTestBase:
subprocess.call(['ip', 'link', 'del', 'dummy0'],
stderr=subprocess.DEVNULL)
- def writeConfig(self, fname, contents):
- os.makedirs(os.path.dirname(fname), exist_ok=True)
- with open(fname, 'w') as f:
- f.write(contents)
- self.addCleanup(os.remove, fname)
-
def show_journal(self, unit):
'''Show journal of given unit since start of the test'''
@@ -126,7 +202,7 @@ class ClientTestBase:
def do_test(self, coldplug=True, ipv6=False, extra_opts='',
online_timeout=10, dhcp_mode='yes'):
subprocess.check_call(['systemctl', 'start', 'systemd-resolved'])
- self.writeConfig(self.config, '''\
+ self.write_network(self.config, '''\
[Match]
Name=%s
[Network]
@@ -146,14 +222,14 @@ DHCP=%s
subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
try:
- subprocess.check_call([self.networkd_wait_online, '--interface',
+ subprocess.check_call([NETWORKD_WAIT_ONLINE, '--interface',
self.iface, '--timeout=%i' % online_timeout])
if ipv6:
# check iface state and IP 6 address; FIXME: we need to wait a bit
# longer, as the iface is "configured" already with IPv4 *or*
# IPv6, but we want to wait for both
- for timeout in range(10):
+ for _ in range(10):
out = subprocess.check_output(['ip', 'a', 'show', 'dev', self.iface])
if b'state UP' in out and b'inet6 2600' in out and b'inet 192.168' in out:
break
@@ -166,33 +242,33 @@ DHCP=%s
else:
# should have link-local address on IPv6 only
out = subprocess.check_output(['ip', '-6', 'a', 'show', 'dev', self.iface])
- self.assertRegex(out, b'inet6 fe80::.* scope link')
+ self.assertRegex(out, br'inet6 fe80::.* scope link')
self.assertNotIn(b'scope global', out)
# should have IPv4 address
out = subprocess.check_output(['ip', '-4', 'a', 'show', 'dev', self.iface])
self.assertIn(b'state UP', out)
- self.assertRegex(out, b'inet 192.168.5.\d+/.* scope global dynamic')
+ self.assertRegex(out, br'inet 192.168.5.\d+/.* scope global dynamic')
# check networkctl state
out = subprocess.check_output(['networkctl'])
- self.assertRegex(out, ('%s\s+ether\s+routable\s+unmanaged' % self.if_router).encode())
- self.assertRegex(out, ('%s\s+ether\s+routable\s+configured' % self.iface).encode())
+ self.assertRegex(out, (r'%s\s+ether\s+routable\s+unmanaged' % self.if_router).encode())
+ self.assertRegex(out, (r'%s\s+ether\s+routable\s+configured' % self.iface).encode())
out = subprocess.check_output(['networkctl', 'status', self.iface])
- self.assertRegex(out, b'Type:\s+ether')
- self.assertRegex(out, b'State:\s+routable.*configured')
- self.assertRegex(out, b'Address:\s+192.168.5.\d+')
+ self.assertRegex(out, br'Type:\s+ether')
+ self.assertRegex(out, br'State:\s+routable.*configured')
+ self.assertRegex(out, br'Address:\s+192.168.5.\d+')
if ipv6:
- self.assertRegex(out, b'2600::')
+ self.assertRegex(out, br'2600::')
else:
- self.assertNotIn(b'2600::', out)
- self.assertRegex(out, b'fe80::')
- self.assertRegex(out, b'Gateway:\s+192.168.5.1')
- self.assertRegex(out, b'DNS:\s+192.168.5.1')
+ self.assertNotIn(br'2600::', out)
+ self.assertRegex(out, br'fe80::')
+ self.assertRegex(out, br'Gateway:\s+192.168.5.1')
+ self.assertRegex(out, br'DNS:\s+192.168.5.1')
except (AssertionError, subprocess.CalledProcessError):
# show networkd status, journal, and DHCP server log on failure
- with open(self.config) as f:
+ with open(os.path.join(NETWORK_UNITDIR, self.config)) as f:
print('\n---- %s ----\n%s' % (self.config, f.read()))
print('---- interface status ----')
sys.stdout.flush()
@@ -247,12 +323,12 @@ DHCP=%s
self.do_test(coldplug=False, ipv6=True)
def test_route_only_dns(self):
- self.writeConfig('/run/systemd/network/myvpn.netdev', '''\
+ self.write_network('myvpn.netdev', '''\
[NetDev]
Name=dummy0
Kind=dummy
MACAddress=12:34:56:78:9a:bc''')
- self.writeConfig('/run/systemd/network/myvpn.network', '''\
+ self.write_network('myvpn.network', '''\
[Match]
Name=dummy0
[Network]
@@ -273,20 +349,16 @@ Domains= ~company''')
self.assertNotIn('nameserver 192.168.42.1\n', contents)
def test_route_only_dns_all_domains(self):
- with open('/run/systemd/network/myvpn.netdev', 'w') as f:
- f.write('''[NetDev]
+ self.write_network('myvpn.netdev', '''[NetDev]
Name=dummy0
Kind=dummy
MACAddress=12:34:56:78:9a:bc''')
- with open('/run/systemd/network/myvpn.network', 'w') as f:
- f.write('''[Match]
+ self.write_network('myvpn.network', '''[Match]
Name=dummy0
[Network]
Address=192.168.42.100
DNS=192.168.42.1
Domains= ~company ~.''')
- self.addCleanup(os.remove, '/run/systemd/network/myvpn.netdev')
- self.addCleanup(os.remove, '/run/systemd/network/myvpn.network')
self.do_test(coldplug=True, ipv6=False,
extra_opts='IPv6AcceptRouterAdvertisements=False')
@@ -303,7 +375,7 @@ Domains= ~company ~.''')
self.assertIn('nameserver 192.168.42.1\n', contents)
-@unittest.skipUnless(have_dnsmasq, 'dnsmasq not installed')
+@unittest.skipUnless(HAVE_DNSMASQ, 'dnsmasq not installed')
class DnsmasqClientTest(ClientTestBase, unittest.TestCase):
'''Test networkd client against dnsmasq'''
@@ -366,7 +438,7 @@ class DnsmasqClientTest(ClientTestBase, unittest.TestCase):
# create interface for generic connections; this will map all DNS names
# to 192.168.42.1
self.create_iface(dnsmasq_opts=['--address=/#/192.168.42.1'])
- self.writeConfig('/run/systemd/network/general.network', '''\
+ self.write_network('general.network', '''\
[Match]
Name=%s
[Network]
@@ -375,9 +447,7 @@ IPv6AcceptRA=False''' % self.iface)
# create second device/dnsmasq for a .company/.lab VPN interface
# static IPs for simplicity
- subprocess.check_call(['ip', 'link', 'add', 'name', 'testvpnclient', 'type',
- 'veth', 'peer', 'name', 'testvpnrouter'])
- self.addCleanup(subprocess.call, ['ip', 'link', 'del', 'dev', 'testvpnrouter'])
+ self.add_veth_pair('testvpnclient', 'testvpnrouter')
subprocess.check_call(['ip', 'a', 'flush', 'dev', 'testvpnrouter'])
subprocess.check_call(['ip', 'a', 'add', '10.241.3.1/24', 'dev', 'testvpnrouter'])
subprocess.check_call(['ip', 'link', 'set', 'testvpnrouter', 'up'])
@@ -392,7 +462,7 @@ IPv6AcceptRA=False''' % self.iface)
self.addCleanup(vpn_dnsmasq.wait)
self.addCleanup(vpn_dnsmasq.kill)
- self.writeConfig('/run/systemd/network/vpn.network', '''\
+ self.write_network('vpn.network', '''\
[Match]
Name=testvpnclient
[Network]
@@ -402,7 +472,7 @@ DNS=10.241.3.1
Domains= ~company ~lab''')
subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
- subprocess.check_call([self.networkd_wait_online, '--interface', self.iface,
+ subprocess.check_call([NETWORKD_WAIT_ONLINE, '--interface', self.iface,
'--interface=testvpnclient', '--timeout=20'])
# ensure we start fresh with every test
@@ -452,8 +522,17 @@ Domains= ~company ~lab''')
# should have received the fixed IP above
out = subprocess.check_output(['ip', '-4', 'a', 'show', 'dev', self.iface])
self.assertRegex(out, b'inet 192.168.5.210/24 .* scope global dynamic')
- # should have set transient hostname in hostnamed
- self.assertIn(b'testgreen', subprocess.check_output(['hostnamectl']))
+ # should have set transient hostname in hostnamed; this is
+ # sometimes a bit lagging (issue #4753), so retry a few times
+ for retry in range(1, 6):
+ out = subprocess.check_output(['hostnamectl'])
+ if b'testgreen' in out:
+ break
+ time.sleep(5)
+ sys.stdout.write('[retry %i] ' % retry)
+ sys.stdout.flush()
+ else:
+ self.fail('Transient hostname not found in hostnamectl:\n%s' % out.decode())
# and also applied to the system
self.assertEqual(socket.gethostname(), 'testgreen')
except AssertionError:
@@ -549,7 +628,7 @@ exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ { s/^.*=//
'--service-type=notify', script])
# wait until devices got created
- for timeout in range(50):
+ for _ in range(50):
out = subprocess.check_output(['ip', 'a', 'show', 'dev', self.if_router])
if b'state UP' in out and b'scope global' in out:
break
@@ -583,12 +662,12 @@ exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ { s/^.*=//
# we don't use this interface for this test
self.if_router = None
- self.writeConfig('/run/systemd/network/test.netdev', '''\
+ self.write_network('test.netdev', '''\
[NetDev]
Name=dummy0
Kind=dummy
MACAddress=12:34:56:78:9a:bc''')
- self.writeConfig('/run/systemd/network/test.network', '''\
+ self.write_network('test.network', '''\
[Match]
Name=dummy0
[Network]
@@ -615,12 +694,12 @@ Domains= one two three four five six seven eight nine ten''')
name_prefix = 'a' * 60
- self.writeConfig('/run/systemd/network/test.netdev', '''\
+ self.write_network('test.netdev', '''\
[NetDev]
Name=dummy0
Kind=dummy
MACAddress=12:34:56:78:9a:bc''')
- self.writeConfig('/run/systemd/network/test.network', '''\
+ self.write_network('test.network', '''\
[Match]
Name=dummy0
[Network]
@@ -643,18 +722,18 @@ Domains={p}0 {p}1 {p}2 {p}3 {p}4'''.format(p=name_prefix))
# we don't use this interface for this test
self.if_router = None
- self.writeConfig('/run/systemd/network/test.netdev', '''\
+ self.write_network('test.netdev', '''\
[NetDev]
Name=dummy0
Kind=dummy
MACAddress=12:34:56:78:9a:bc''')
- self.writeConfig('/run/systemd/network/test.network', '''\
+ self.write_network('test.network', '''\
[Match]
Name=dummy0
[Network]
Address=192.168.42.100
DNS=192.168.42.1''')
- self.writeConfig('/run/systemd/network/test.network.d/dns.conf', '''\
+ self.write_network_dropin('test.network', 'dns', '''\
[Network]
DNS=127.0.0.1''')
@@ -695,6 +774,115 @@ DNS=127.0.0.1''')
raise
+class MatchClientTest(unittest.TestCase, NetworkdTestingUtilities):
+ """Test [Match] sections in .network files.
+
+ Be aware that matching the test host's interfaces will wipe their
+ configuration, so as a precaution, all network files should have a
+ restrictive [Match] section to only ever interfere with the
+ temporary veth interfaces created here.
+ """
+
+ def tearDown(self):
+ """Stop networkd."""
+ subprocess.call(['systemctl', 'stop', 'systemd-networkd'])
+
+ def test_basic_matching(self):
+ """Verify the Name= line works throughout this class."""
+ self.add_veth_pair('test_if1', 'fake_if2')
+ self.write_network('test.network', "[Match]\nName=test_*\n[Network]")
+ subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
+ self.assert_link_states(test_if1='managed', fake_if2='unmanaged')
+
+ def test_inverted_matching(self):
+ """Verify that a '!'-prefixed value inverts the match."""
+ # Use a MAC address as the interfaces' common matching attribute
+ # to avoid depending on udev, to support testing in containers.
+ mac = '00:01:02:03:98:99'
+ self.add_veth_pair('test_veth', 'test_peer',
+ ['addr', mac], ['addr', mac])
+ self.write_network('no-veth.network', """\
+[Match]
+MACAddress=%s
+Name=!nonexistent *peer*
+[Network]""" % mac)
+ subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
+ self.assert_link_states(test_veth='managed', test_peer='unmanaged')
+
+
+class UnmanagedClientTest(unittest.TestCase, NetworkdTestingUtilities):
+ """Test if networkd manages the correct interfaces."""
+
+ def setUp(self):
+ """Write .network files to match the named veth devices."""
+ # Define the veth+peer pairs to be created.
+ # Their pairing doesn't actually matter, only their names do.
+ self.veths = {
+ 'm1def': 'm0unm',
+ 'm1man': 'm1unm',
+ }
+
+ # Define the contents of .network files to be read in order.
+ self.configs = (
+ "[Match]\nName=m1def\n",
+ "[Match]\nName=m1unm\n[Link]\nUnmanaged=yes\n",
+ "[Match]\nName=m1*\n[Link]\nUnmanaged=no\n",
+ )
+
+ # Write out the .network files to be cleaned up automatically.
+ for i, config in enumerate(self.configs):
+ self.write_network("%02d-test.network" % i, config)
+
+ def tearDown(self):
+ """Stop networkd."""
+ subprocess.call(['systemctl', 'stop', 'systemd-networkd'])
+
+ def create_iface(self):
+ """Create temporary veth pairs for interface matching."""
+ for veth, peer in self.veths.items():
+ self.add_veth_pair(veth, peer)
+
+ def test_unmanaged_setting(self):
+ """Verify link states with Unmanaged= settings, hot-plug."""
+ subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
+ self.create_iface()
+ self.assert_link_states(m1def='managed',
+ m1man='managed',
+ m1unm='unmanaged',
+ m0unm='unmanaged')
+
+ def test_unmanaged_setting_coldplug(self):
+ """Verify link states with Unmanaged= settings, cold-plug."""
+ self.create_iface()
+ subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
+ self.assert_link_states(m1def='managed',
+ m1man='managed',
+ m1unm='unmanaged',
+ m0unm='unmanaged')
+
+ def test_catchall_config(self):
+ """Verify link states with a catch-all config, hot-plug."""
+ # Don't actually catch ALL interfaces. It messes up the host.
+ self.write_network('all.network', "[Match]\nName=m[01]???\n")
+ subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
+ self.create_iface()
+ self.assert_link_states(m1def='managed',
+ m1man='managed',
+ m1unm='unmanaged',
+ m0unm='managed')
+
+ def test_catchall_config_coldplug(self):
+ """Verify link states with a catch-all config, cold-plug."""
+ # Don't actually catch ALL interfaces. It messes up the host.
+ self.write_network('all.network', "[Match]\nName=m[01]???\n")
+ self.create_iface()
+ subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
+ self.assert_link_states(m1def='managed',
+ m1man='managed',
+ m1unm='unmanaged',
+ m0unm='managed')
+
+
if __name__ == '__main__':
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout,
verbosity=2))
diff --git a/test/rule-syntax-check.py b/test/rule-syntax-check.py
index e4185cb0fa..dab01f1d8a 100755
--- a/test/rule-syntax-check.py
+++ b/test/rule-syntax-check.py
@@ -34,10 +34,10 @@ else:
sys.exit(2)
rules_files = glob(os.path.join(rules_dir, '*.rules'))
-no_args_tests = re.compile('(ACTION|DEVPATH|KERNELS?|NAME|SYMLINK|SUBSYSTEMS?|DRIVERS?|TAG|RESULT|TEST)\s*(?:=|!)=\s*"([^"]*)"$')
-args_tests = re.compile('(ATTRS?|ENV|TEST){([a-zA-Z0-9/_.*%-]+)}\s*(?:=|!)=\s*"([^"]*)"$')
-no_args_assign = re.compile('(NAME|SYMLINK|OWNER|GROUP|MODE|TAG|PROGRAM|RUN|LABEL|GOTO|OPTIONS|IMPORT)\s*(?:\+=|:=|=)\s*"([^"]*)"$')
-args_assign = re.compile('(ATTR|ENV|IMPORT|RUN){([a-zA-Z0-9/_.*%-]+)}\s*(=|\+=)\s*"([^"]*)"$')
+no_args_tests = re.compile(r'(ACTION|DEVPATH|KERNELS?|NAME|SYMLINK|SUBSYSTEMS?|DRIVERS?|TAG|RESULT|TEST)\s*(?:=|!)=\s*"([^"]*)"$')
+args_tests = re.compile(r'(ATTRS?|ENV|TEST){([a-zA-Z0-9/_.*%-]+)}\s*(?:=|!)=\s*"([^"]*)"$')
+no_args_assign = re.compile(r'(NAME|SYMLINK|OWNER|GROUP|MODE|TAG|PROGRAM|RUN|LABEL|GOTO|OPTIONS|IMPORT)\s*(?:\+=|:=|=)\s*"([^"]*)"$')
+args_assign = re.compile(r'(ATTR|ENV|IMPORT|RUN){([a-zA-Z0-9/_.*%-]+)}\s*(=|\+=)\s*"([^"]*)"$')
result = 0
buffer = ''
diff --git a/test/sysv-generator-test.py b/test/sysv-generator-test.py
index 50175485f7..16ea65690a 100755
--- a/test/sysv-generator-test.py
+++ b/test/sysv-generator-test.py
@@ -308,7 +308,7 @@ class SysvGeneratorTest(unittest.TestCase):
err, results = self.run_generator()
self.assertEqual(list(results), ['foo.service'])
self.assertEqual(os.readlink(os.path.join(self.out_dir, 'foo\\x2b.service')),
- 'foo.service')
+ 'foo.service')
self.assertNotIn('Overwriting', err)
def test_same_provides_in_multiple_scripts(self):
diff --git a/tools/catalog-report.py b/tools/catalog-report.py
new file mode 100644
index 0000000000..b220060cc8
--- /dev/null
+++ b/tools/catalog-report.py
@@ -0,0 +1,87 @@
+#!/usr/bin/python3
+# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
+#
+# This file is part of systemd. It is distrubuted under the MIT license, see
+# below.
+#
+# Copyright 2016 Zbigniew Jędrzejewski-Szmek
+#
+# The MIT License (MIT)
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+"""
+Prints out journal entries with no or bad catalog explanations.
+"""
+
+import re
+from systemd import journal, id128
+
+j = journal.Reader()
+
+logged = set()
+pattern = re.compile('@[A-Z0-9_]+@')
+
+mids = {v:k for k,v in id128.__dict__.items()
+ if k.startswith('SD_MESSAGE')}
+
+freq = 1000
+
+def log_entry(x):
+ if 'CODE_FILE' in x:
+ # some of our code was using 'CODE_FUNCTION' instead of 'CODE_FUNC'
+ print('{}:{} {}'.format(x.get('CODE_FILE', '???'),
+ x.get('CODE_LINE', '???'),
+ x.get('CODE_FUNC', None) or x.get('CODE_FUNCTION', '???')))
+ print(' {}'.format(x.get('MESSAGE', 'no message!')))
+ for k, v in x.items():
+ if k.startswith('CODE_') or k in {'MESSAGE_ID', 'MESSAGE'}:
+ continue
+ print(' {}={}'.format(k, v))
+ print()
+
+for i, x in enumerate(j):
+ if i % freq == 0:
+ print(i, end='\r')
+
+ try:
+ mid = x['MESSAGE_ID']
+ except KeyError:
+ continue
+ name = mids.get(mid, 'unknown')
+
+ try:
+ desc = journal.get_catalog(mid)
+ except FileNotFoundError:
+ if mid in logged:
+ continue
+
+ print('{} {.hex}: no catalog entry'.format(name, mid))
+ log_entry(x)
+ logged.add(mid)
+ continue
+
+ fields = [field[1:-1] for field in pattern.findall(desc)]
+ for field in fields:
+ index = (mid, field)
+ if field in x or index in logged:
+ continue
+ print('{} {.hex}: no field {}'.format(name, mid, field))
+ log_entry(x)
+ logged.add(index)