diff options
50 files changed, 967 insertions, 301 deletions
diff --git a/.gitignore b/.gitignore index baa975d813..016ba625e3 100644 --- a/.gitignore +++ b/.gitignore @@ -200,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/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 1895e33e05..a4e7e56067 100644 --- a/Makefile.am +++ b/Makefile.am @@ -943,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 \ @@ -3739,6 +3741,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 \ @@ -3908,6 +3911,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 @@ -4082,6 +4086,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: @@ -118,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... 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/parse_hwdb.py b/hwdb/parse_hwdb.py index 16f74b0777..18f13edd0a 100755 --- a/hwdb/parse_hwdb.py +++ b/hwdb/parse_hwdb.py @@ -60,12 +60,14 @@ 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() @@ -76,7 +78,7 @@ 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 @@ -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), @@ -105,6 +110,7 @@ def property_grammar(): ('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] @@ -117,7 +123,7 @@ def property_grammar(): Word(nums + ':')('VALUE') ] - grammar = Or(fixed_props + kbd_props + abs_props) + grammar = Or(fixed_props + kbd_props + abs_props) + EOL return grammar 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/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/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.network.xml b/man/systemd.network.xml index 53c49f817f..0fa68b7623 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> 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/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/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/fs-util.c b/src/basic/fs-util.c index b30cec4f92..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> @@ -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)) 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/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/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/util.c b/src/basic/util.c index c1b5ca1ef7..8a630049d7 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/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..6b36b2fc3a 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,7 +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; + const char *f; int i; semicolon = false; @@ -631,47 +632,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 +683,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 +705,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 +1336,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 +1384,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 +1432,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 +1699,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 +2565,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 +2583,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 +3726,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 +3828,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 +3844,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 +3855,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/manager.c b/src/core/manager.c index 1f663d3c1d..21cd6062c6 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, 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/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/delta/delta.c b/src/delta/delta.c index 107b105fde..9a44b15da7 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -297,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); @@ -313,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")) @@ -354,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) { 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/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 1081979bf9..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" @@ -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/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/nspawn/nspawn.c b/src/nspawn/nspawn.c index de05b6c5ef..d701f2158d 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -490,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) { @@ -1326,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, 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/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/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-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/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 d88687e9c2..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" @@ -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/networkd-test.py b/test/networkd-test.py index a932d32b92..39bd4f5b1b 100755 --- a/test/networkd-test.py +++ b/test/networkd-test.py @@ -74,6 +74,14 @@ class NetworkdTestingUtilities: 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) @@ -439,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']) @@ -768,6 +774,42 @@ 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.""" @@ -798,11 +840,7 @@ class UnmanagedClientTest(unittest.TestCase, NetworkdTestingUtilities): def create_iface(self): """Create temporary veth pairs for interface matching.""" for veth, peer in self.veths.items(): - subprocess.check_call(['ip', 'link', 'add', - 'name', veth, 'type', 'veth', - 'peer', 'name', peer]) - self.addCleanup(subprocess.call, - ['ip', 'link', 'del', 'dev', peer]) + self.add_veth_pair(veth, peer) def test_unmanaged_setting(self): """Verify link states with Unmanaged= settings, hot-plug.""" |