diff options
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 @@ -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 <systemd/sd-id128.h> +#include <stdio.h> + +#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, &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&option2=value...]</uri></term> + <term><uri>/entries[?option1&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 < /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> @@ -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, ð_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, ©) >= 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) |