diff options
180 files changed, 4959 insertions, 1177 deletions
@@ -124,3 +124,4 @@ Josef Andersson <josef.andersson@fripost.org> Josef Andersson <l10nl18nsweja@gmail.com> Hendrik Westerberg <hendrik@gestorf.com> Stefan Pietsch <mail.ipv4v6@gmail.com> +Jérémy Rosen <jeremy.rosen@enst-bretagne.fr> @@ -16,6 +16,11 @@ CHANGES WITH 235: respectively. They complement the existing "set-log-level" and "set-log-target" verbs, which can be used to change those values. + * systemd-networkd .network DHCP setting UseMTU default has changed + from false to true. Meaning, DHCP server advertised mtu setting is + now applied by default. This resolves networking issues on low-mtu + networks. + CHANGES WITH 234: * Meson is now supported as build system in addition to Automake. It is @@ -131,9 +131,8 @@ REQUIREMENTS: glibc >= 2.16 libcap - libmount >= 2.27.1 (from util-linux) - (util-linux < 2.29 *must* be built with --enable-libmount-force-mountinfo, - and later versions without --enable-libmount-support-mtab.) + libmount >= 2.30 (from util-linux) + (util-linux *must* be built without --enable-libmount-support-mtab) libseccomp >= 2.3.1 (optional) libblkid >= 2.24 (from util-linux) (optional) libkmod >= 15 (optional) @@ -27,11 +27,6 @@ Features: * dissect: when we discover squashfs, don't claim we had a "writable" partition in systemd-dissect -* systemd-run should have a way how to connect a pair of pipes to - stdout/stderr/stdin of the invoked service - -* set LockPersonality= on all our services - * Add AddUser= setting to unit files, similar to DynamicUser=1 which however creates a static, persistent user rather than a dynamic, transient user. We can leverage code from sysusers.d for this. @@ -45,8 +40,6 @@ Features: diffs remain minimal (in particular: the OUI databases we import are not sorted, and not stable) -* set SystemCallArchitectures=native on all our services - * maybe add call sd_journal_set_block_timeout() or so to set SO_SNDTIMEO for the sd-journal logging socket, and, if the timeout is set to 0, sets O_NONBLOCK on it. That way people can control if and when to block for @@ -55,13 +48,6 @@ Features: * tighten sd_notify() MAINPID= checks a bit: don't accept foreign PIDs (i.e. PIDs not managed by the service manager) -* journald: when we recv a log datagram via the native or syslog transports, - search for the PID in the active stream connections, and let's make sure to - always process the datagrams before the streams. Then, cache client metadata - per stream in the stream object. This way we can somewhat fix the race with - quickly exiting processes which log as long as they had their own stream - connection... - * hostnamed: populate form factor data from a new hwdb database, so that old yogas can be recognized as "convertible" too, even if they predate the DMI "convertible" form factor @@ -102,8 +88,6 @@ Features: * maybe introduce gpt auto discovery for /var/tmp? -* set ProtectSystem=strict for all our usual services. - * fix PrivateNetwork= so that we fall back gracefully on kernels lacking namespacing support (similar for the other namespacing options) @@ -148,8 +132,6 @@ Features: * enable LockMLOCK to take a percentage value relative to physical memory -* switch to ProtectSystem=strict for all our long-running services where that's possible - * Permit masking specific netlink APIs with RestrictAddressFamily= * nspawn: start UID allocation loop from hash of container name @@ -474,8 +456,6 @@ Features: * maybe add a generator that looks for "systemd.run=" on the kernel cmdline for container usercases... -* cgtop: make cgtop useful in a container - * test/: - add 'set -e' to scripts in test/ - make stuff in test/ work with separate output dir @@ -725,8 +705,6 @@ Features: * cryptsetup: - cryptsetup-generator: allow specification of passwords in crypttab itself - - move cryptsetup key caching into kernel keyctl? - https://bugs.freedesktop.org/show_bug.cgi?id=54982 - support rd.luks.allow-discards= kernel cmdline params in cryptsetup generator * hw watchdog: optionally try to use the preset watchdog timeout instead of always overriding it @@ -746,8 +724,6 @@ Features: - add trigger --subsystem-match=usb/usb_device device - reimport udev db after MOVE events for devices without dev_t -* when a service has the same env var set twice we actually store it twice and return that in systemctl show -p... We should only show the last setting - * There's currently no way to cancel fsck (used to be possible via C-c or c on the console) * add option to sockets to avoid activation. Instead just drop packets/connections, see http://cyberelk.net/tim/2012/02/15/portreserve-systemd-solution/ @@ -806,10 +782,7 @@ Features: * dot output for --test showing the 'initial transaction' -* fingerprint.target, wireless.target, gps.target, netdevice.target - * pid1: - - .timer units should optionally support CLOCK_BOOTTIME in addition to CLOCK_MONOTONIC - When logging about multiple units (stopping BoundTo units, conflicts, etc.), log both units as UNIT=, so that journalctl -u triggers on both. - generate better errors when people try to set transient properties @@ -845,12 +818,9 @@ Features: - load-fragment: when loading a unit file via a chain of symlinks verify that it is not masked via any of the names traversed. - introduce Type=pid-file - - ExecOnFailure=/usr/bin/foo - introduce mix of BindTo and Requisite - add a concept of RemainAfterExit= to scope units - - Set NoNewPrivileges= on all of our own services, where that makes sense - Allow multiple ExecStart= for all Type= settings, so that we can cover rescue.service nicely - - consider adding RuntimeDirectoryUser= + RuntimeDirectoryGroup= * udev-link-config: - Make sure ID_PATH is always exported and complete for @@ -892,7 +862,6 @@ Features: * dhcp: - figure out how much we can increase Maximum Message Size - - support RFC4702 (pass FQDN) * dhcp6: - add functions to set previously stored IPv6 addresses on startup and get @@ -924,8 +893,6 @@ External: * drop accountsservice's StandardOutput=syslog and Type=dbus fields -* dbus: in fedora, make /var/lib/dbus/machine-id a symlink to /etc/machine-id - * /usr/bin/service should actually show the new command line * fedora: suggest auto-restart on failure, but not on success and not on coredump. also, ask people to think about changing the start limit logic. Also point people to RestartPreventExitStatus=, SuccessExitStatus= diff --git a/hwdb/60-sensor.hwdb b/hwdb/60-sensor.hwdb index 10de01ca45..403ebba584 100644 --- a/hwdb/60-sensor.hwdb +++ b/hwdb/60-sensor.hwdb @@ -136,6 +136,12 @@ sensor:modalias:acpi:KIOX000A*:dmi:*svnLAMINA:pnT-1016BNORD* ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1 ######################################### +# Lenovo +######################################### +sensor:modalias:acpi:NCPE0388*:dmi:*:rnLenovoYOGA510-14IKB:* + ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, 1 + +######################################### # Peaq ######################################### sensor:modalias:acpi:SMO8500*:dmi:*:svnPEAQ:pnPEAQPMMC1010MD99187:* diff --git a/man/machinectl.xml b/man/machinectl.xml index 46dcb44ca6..cf46fe8024 100644 --- a/man/machinectl.xml +++ b/man/machinectl.xml @@ -79,17 +79,18 @@ OS kernel with the host OS, in order to run OS userspace instances on top the host OS.</para></listitem> - <listitem><para>The host system itself</para></listitem> + <listitem><para>The host system itself.</para></listitem> </itemizedlist> <para>Machines are identified by names that follow the same rules - as UNIX and DNS host names, for details, see below. Machines are - instantiated from disk or file system images that frequently — but not - necessarily — carry the same name as machines running from - them. Images in this sense are considered:</para> + as UNIX and DNS host names. For details, see below.</para> + + <para>Machines are instantiated from disk or file system images that + frequently — but not necessarily — carry the same name as machines running + from them. Images in this sense may be:</para> <itemizedlist> - <listitem><para>Directory trees containing an OS, including its + <listitem><para>Directory trees containing an OS, including the top-level directories <filename>/usr</filename>, <filename>/etc</filename>, and so on.</para></listitem> @@ -299,7 +300,16 @@ </varlistentry> <xi:include href="user-system-options.xml" xpointer="host" /> - <xi:include href="user-system-options.xml" xpointer="machine" /> + + <varlistentry> + <term><option>-M</option></term> + <term><option>--machine=</option></term> + + <listitem><para>Connect to + <citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> + running in a local container, to perform the specified operation within + the container.</para></listitem> + </varlistentry> <xi:include href="standard-options.xml" xpointer="no-pager" /> <xi:include href="standard-options.xml" xpointer="no-legend" /> @@ -809,7 +819,7 @@ qcow2 or raw disk image, possibly compressed with xz, gzip or bzip2. If the second argument (the resulting image name) is not specified, it is automatically derived from the file - name. If the file name is passed as <literal>-</literal>, the + name. If the filename is passed as <literal>-</literal>, the image is read from standard input, in which case the second argument is mandatory.</para> diff --git a/man/meson.build b/man/meson.build index 8ddbd5557c..5b6a21fb9f 100644 --- a/man/meson.build +++ b/man/meson.build @@ -11,6 +11,7 @@ want_html = want_html != 'false' and xsltproc.found() xsltproc_flags = [ '--nonet', '--xinclude', + '--maxdepth', '9000', '--stringparam', 'man.output.quietly', '1', '--stringparam', 'funcsynopsis.style', 'ansi', '--stringparam', 'man.authors.section.enabled', '0', diff --git a/man/sd-login.xml b/man/sd-login.xml index 6861fbe257..b2131a9af9 100644 --- a/man/sd-login.xml +++ b/man/sd-login.xml @@ -66,11 +66,6 @@ and monitor seat, login session and user status information on the local system. </para> - <para>See <ulink - url="https://www.freedesktop.org/wiki/Software/systemd/multiseat">Multi-Seat - on Linux</ulink> for an introduction into multi-seat support on - Linux, the background for this set of APIs.</para> - <para>Note that these APIs only allow purely passive access and monitoring of seats, sessions and users. To actively make changes to the seat configuration, terminate login sessions, or switch @@ -115,6 +110,146 @@ implemented.</para> </refsect1> + <refsect1> + <title>Definition of Terms</title> + + <variablelist> + <varlistentry> + <term>seat</term> + + <listitem><para>A seat consists of all hardware devices assigned to a specific + workplace. It consists of at least one graphics device, and usually also includes + keyboard, mouse. It can also include video cameras, sound cards and more. Seats + are identified by seat names, which are strings (<= 255 characters), that start + with the four characters <literal>seat</literal> followed by at least one + character from the range [a-zA-Z0-9], <literal>_</literal> and + <literal>-</literal>. They are suitable for use as file names. Seat names may or + may not be stable and may be reused if a seat becomes available again. + </para></listitem> + </varlistentry> + + <varlistentry> + <term>session</term> + + <listitem><para>A session is defined by the time a user is logged in until they + log out. A session is bound to one or no seats (the latter for 'virtual' ssh + logins). Multiple sessions can be attached to the same seat, but only one of them + can be active, the others are in the background. A session is identified by a + short string.</para> + + <para> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> + ensures that audit sessions are identical to systemd sessions, and uses the audit + session ID as session ID in systemd (if auditing is enabled). In general the + session identifier is a short string consisting only of [a-zA-Z0-9], + <literal>_</literal> and <literal>-</literal>, suitable for use as a file name. + Session IDs are unique on the local machine and are + never reused as long as the machine is online. A user (the way we know it on UNIX) + corresponds to the person using a computer. A single user can have multiple + sessions open at the same time. A user is identified by a numeric user id (UID) or + a user name (a string). A multi-session system allows multiple user sessions on + the same seat at the same time. A multi-seat system allows multiple independent + seats that can be individually and simultaneously used by different users.</para> + </listitem> + </varlistentry> + </variablelist> + + <para>All hardware devices that are eligible to being assigned to a seat, are assigned + to one. A device can be assigned to only one seat at a time. If a device is not + assigned to any particular other seat it is implicitly assigned to the special default + seat called <literal>seat0</literal>.</para> + + <para>Note that hardware like printers, hard disks or network cards is generally not + assigned to a specific seat. They are available to all seats equally. (Well, with one + exception: USB sticks can be assigned to a seat.)</para> + + <para><literal>seat0</literal> always exists.</para> + </refsect1> + + <refsect1> + <title>udev Rules</title> + + <para>Assignment of hardware devices to seats is managed inside the udev database, via + settings on the devices:</para> + + <variablelist> + <varlistentry> + <term>Tag <literal>seat</literal></term> + + <listitem><para>When set, a device is eligible to be assigned to a seat. This tag + is set for graphics devices, mice, keyboards, video cards, sound cards and + more. Note that some devices like sound cards consist of multiple subdevices + (i.e. a PCM for input and another one for output). This tag will be set only for + the originating device, not for the individual subdevices. A UI for configuring + assignment of devices to seats should enumerate and subscribe to all devices with + this tag set and show them in the UI. Note that USB hubs can be assigned to a seat + as well, in which case all (current and future) devices plugged into it will also + be assigned to the same seat (unless they are explicitly assigned to another + seat). + </para></listitem> + </varlistentry> + + <varlistentry> + <term>Tag <literal>master-of-seat</literal></term> + + <listitem><para>When set, this device is enough for a seat to be considered + existent. This tag is usually set for the framebuffer device of graphics cards. A + seat hence consists of an arbitrary number of devices marked with the + <literal>seat</literal> tag, but (at least) one of these devices needs to be + tagged with <literal>master-of-seat</literal> before the seat is actually + considered to be around.</para></listitem> + </varlistentry> + + <varlistentry> + <term>Property <varname>ID_SEAT</varname></term> + + <listitem><para>This property specifies the name of the seat a specific device is + assigned to. If not set the device is assigned to <literal>seat0</literal>. Also, + to speed up enumeration of hardware belonging to a specific seat, the seat is also + set as tag on the device. I.e. if the property + <varname>ID_SEAT=seat-waldo</varname> is set for a device, the tag + <literal>seat-waldo</literal> will be set as well. Note that if a device is + assigned to <literal>seat0</literal>, it will usually not carry such a tag and you + need to enumerate all devices and check the <varname>ID_SEAT</varname> property + manually. Again, if a device is assigned to seat0 this is visible on the device in + two ways: with a property <varname>ID_SEAT=seat0</varname> and with no property + <varname>ID_SEAT</varname> set for it at all.</para></listitem> + </varlistentry> + + <varlistentry> + <term>Property <varname>ID_AUTOSEAT</varname></term> + + <listitem><para>When set to <literal>1</literal>, this device automatically + generates a new and independent seat, which is named after the path of the + device. This is set for specialized USB hubs like the Plugable devices, which when + plugged in should create a hotplug seat without further configuration.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>Property <varname>ID_FOR_SEAT</varname></term> + + <listitem><para>When creating additional (manual) seats starting from a graphics + device this is a good choice to name the seat after. It is created from the path + of the device. This is useful in UIs for configuring seats: as soon as you create + a new seat from a graphics device, read this property and prefix it with + <literal>seat-</literal> and use it as name for the seat.</para></listitem> + </varlistentry> + </variablelist> + + <para>A seat exists only and exclusively because a properly tagged device with the + right <varname>ID_SEAT</varname> property exists. Besides udev rules there is no + persistent data about seats stored on disk.</para> + + <para>Note that + <citerefentry><refentrytitle>systemd-logind</refentrytitle><manvolnum>8</manvolnum></citerefentry> + manages ACLs on a number of device classes, to allow user code to access the device + nodes attached to a seat as long as the user has an active session on it. This is + mostly transparent to applications. As mentioned above, for certain user software it + might be a good idea to watch whether they can access device nodes instead of thinking + about seats.</para> + </refsect1> + <xi:include href="libsystemd-pkgconfig.xml" /> <refsect1> @@ -130,6 +265,11 @@ <citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> </para> + + <para> + <ulink url="https://www.freedesktop.org/wiki/Software/systemd/multiseat">Multi-Seat on Linux</ulink> + for an introduction to multi-seat support on Linux and the background for this set of APIs. + </para> </refsect1> </refentry> diff --git a/man/sd_bus_default.xml b/man/sd_bus_default.xml index 6d5a90de72..d9102a36ce 100644 --- a/man/sd_bus_default.xml +++ b/man/sd_bus_default.xml @@ -165,13 +165,17 @@ not set, a suitable default for the default system D-Bus instance will be used.</para> - <para><function>sd_bus_open_system_remote()</function> connects to - the system bus on the specified <parameter>host</parameter> using - <citerefentry - project='die-net'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>. <parameter>host</parameter> - consists of an optional user name followed by the - <literal>@</literal> symbol, and the hostname. - </para> + <para><function>sd_bus_open_system_remote()</function> connects to the system bus on + the specified host using + <citerefentry project='die-net'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>. + <parameter>host</parameter> consists of an optional user name followed by the + <literal>@</literal> symbol, and the hostname, optionally followed by a + <literal>:</literal> and a machine name. If the machine name is given, a connection + is created to the system bus in the specified container on the remote machine, and + otherwise a connection to the system bus on the specified host is created.</para> + + <para>Note that entering a container is a privileged operation, and will likely only + work for the root user on the remote machine.</para> <para><function>sd_bus_open_system_machine()</function> connects to the system bus in the specified <parameter>machine</parameter>, diff --git a/man/sd_is_fifo.xml b/man/sd_is_fifo.xml index 1192ca1681..3bd388d80e 100644 --- a/man/sd_is_fifo.xml +++ b/man/sd_is_fifo.xml @@ -183,7 +183,7 @@ whether the specified file descriptor refers to a special file. If the <parameter>path</parameter> parameter is not <constant>NULL</constant>, it is checked whether the file - descriptor is bound to the specified file name. Special files in + descriptor is bound to the specified filename. Special files in this context are character device nodes and files in <filename>/proc</filename> or <filename>/sys</filename>.</para> </refsect1> diff --git a/man/standard-conf.xml b/man/standard-conf.xml index 6edbb7ff83..40764f525b 100644 --- a/man/standard-conf.xml +++ b/man/standard-conf.xml @@ -32,7 +32,6 @@ <filename>/etc/</filename>, with the same filename as the vendor configuration file. If the vendor configuration file is included in the initrd image, the image has to be regenerated.</para> - </refsection> <refsection id='main-conf'> diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index a3a3ce0e54..e74739498c 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -297,13 +297,23 @@ </varlistentry> <varlistentry> - <term><option>--no-man</option></term> + <term><option>--man=no</option></term> <listitem><para>Do not invoke man to verify the existence of man pages listed in <varname>Documentation=</varname>. </para></listitem> </varlistentry> + <varlistentry> + <term><option>--generators</option></term> + + <listitem><para>Invoke unit generators, see + <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>. + Some generators require root privileges. When run under a + normal users, enabling generators will generally result in + some warnings.</para></listitem> + </varlistentry> + <xi:include href="user-system-options.xml" xpointer="host" /> <xi:include href="user-system-options.xml" xpointer="machine" /> diff --git a/man/systemd-gpt-auto-generator.xml b/man/systemd-gpt-auto-generator.xml index eb7a2c4c28..2927fcd291 100644 --- a/man/systemd-gpt-auto-generator.xml +++ b/man/systemd-gpt-auto-generator.xml @@ -70,7 +70,7 @@ <citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>), the units this generator creates are overridden, but additional - automatic dependencies might be created.</para> + implicit dependencies might be created.</para> <para>This generator will only look for root partitions on the same physical disk the EFI System Partition (ESP) is located on. diff --git a/man/systemd-journald.service.xml b/man/systemd-journald.service.xml index 2810638bc2..559ef1090a 100644 --- a/man/systemd-journald.service.xml +++ b/man/systemd-journald.service.xml @@ -70,19 +70,18 @@ <itemizedlist> <listitem><para>Kernel log messages, via kmsg</para></listitem> - <listitem><para>Simple system log messages, via the libc - <citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry> + <listitem><para>Simple system log messages, via the <filename>libc</filename> <citerefentry + project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry> call</para></listitem> <listitem><para>Structured system log messages via the native Journal API, see <citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>4</manvolnum></citerefentry></para></listitem> - <listitem><para>Standard output and standard error of system - services</para></listitem> + <listitem><para>Standard output and standard error of service units. For further details see + below.</para></listitem> - <listitem><para>Audit records, via the audit - subsystem</para></listitem> + <listitem><para>Audit records, originating from the kernel audit subsystem</para></listitem> </itemizedlist> <para>The daemon will implicitly collect numerous metadata fields @@ -112,6 +111,49 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting> </refsect1> <refsect1> + <title>Stream logging</title> + + <para>The systemd service manager invokes all service processes with standard output and standard error connected + to the journal by default. This behaviour may be altered via the + <varname>StandardOutput=</varname>/<varname>StandardError=</varname> unit file settings, see + <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details. The + journal converts the log byte stream received this way into individual log records, splitting the stream at newline + (<literal>\n</literal>, ASCII <constant>10</constant>) and <constant>NUL</constant> bytes.</para> + + <para>If <filename>systemd-journald.service</filename> is stopped, the stream connections associated with all + services are terminated. Further writes to those streams by the service will result in <constant>EPIPE</constant> + errors. In order to react gracefully in this case it is recommended that programs logging to standard output/error + ignore such errors. If the the <constant>SIGPIPE</constant> UNIX signal handler is not blocked or turned off, such + write attempts will also result in such process signals being generated, see + <citerefentry><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry>. To mitigate this issue, + systemd service manager explicitly turns off the <constant>SIGPIPE</constant> signal for all invoked processes by + default (this may be changed for each unit individually via the <varname>IgnoreSIGPIPE=</varname> option, see + <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for + details). After the standard output/standard error streams have been terminated they may not be recovered until the + services they are associated with are restarted. Note that during normal operation, + <filename>systemd-journald.service</filename> stores copies of the file descriptors for those streams in the + service manager. If <filename>systemd-journald.service</filename> is restarted using <command>systemctl + restart</command> or equivalent operation instead of a pair of separate <command>systemctl stop</command> and + <command>systemctl start</command> commands (or equivalent operations), these stream connections are not terminated + and survive the restart. It is thus safe to restart <filename>systemd-journald.service</filename>, but stopping it + is not recommended.</para> + + <para>Note that the log record metadata for records transferred via such standard output/error streams reflect the + metadata of the peer the stream was originally created for. If the stream connection is passed on to other + processes (such as further child processes forked off the main service process), the log records will not reflect + their metadata, but will continue to describe the original process. This is different from the other logging + transports listed above, which are inherently record based and where the metadata is always associated with the + individual record.</para> + + <para>In addition to the the implicit standard output/error logging of services, stream logging is also available + via the <citerefentry><refentrytitle>systemd-cat</refentrytitle><manvolnum>1</manvolnum></citerefentry> command + line tool.</para> + + <para> Currently, the number of parallel log streams <filename>systemd-journald</filename> will accept is limited + to 4096.</para> + </refsect1> + + <refsect1> <title>Signals</title> <variablelist> diff --git a/man/systemd-logind.service.xml b/man/systemd-logind.service.xml index 5433269638..47089fd8c7 100644 --- a/man/systemd-logind.service.xml +++ b/man/systemd-logind.service.xml @@ -63,13 +63,13 @@ <listitem><para>Keeping track of users and sessions, their processes and their idle state. This is implemented by allocating a systemd slice unit for each user below <filename>user.slice</filename>, and a scope unit below it for each concurrent session of a user. Also, a per-user service manager is started as system service instance of - <filename>user@.service</filename> for each user logged in.</para></listitem> + <filename>user@.service</filename> for each logged in user.</para></listitem> - <listitem><para>Generating and managing session IDs. If auditing is available and an audit session ID is set for - a session already, the session ID is initialized from it. Otherwise, an independent session counter is + <listitem><para>Generating and managing session IDs. If auditing is available and an audit session ID is already set for + a session, then this ID is reused as the session ID. Otherwise, an independent session counter is used.</para></listitem> - <listitem><para>Providing PolicyKit-based access for users to + <listitem><para>Providing PolicyKit-based access for users for operations such as system shutdown or sleep</para></listitem> <listitem><para>Implementing a shutdown/sleep inhibition logic diff --git a/man/systemd-networkd.service.xml b/man/systemd-networkd.service.xml index 0bfe5519bc..a6e079c887 100644 --- a/man/systemd-networkd.service.xml +++ b/man/systemd-networkd.service.xml @@ -64,13 +64,30 @@ networks, see <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> - <para>Network configurations applied before networkd is started - are not removed, and static configuration applied by networkd is - not removed when networkd exits. Dynamic configuration applied by - networkd may also optionally be left in place on shutdown. This - ensures restarting networkd does not cut the network connection, - and, in particular, that it is safe to transition between the - initrd and the real root, and back.</para> + <para><command>systemd-networkd</command> will create network devices based + on the configuration in + <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry> + files, respecting the [Match] sections in those files.</para> + + <para><command>systemd-networkd</command> will manage network addresses and + routes for any link for which it finds a <filename>.network</filename> file + with an appropriate [Match] section, see + <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>. + For those links, it will flush existing network addresses and routes when + bringing up the device. Any links not matched by one of the + <filename>.network</filename> files will be ignored. It is also possible to + explicitly tell <filename>systemd-networkd</filename> to ignore a link by + using <varname>Unmanaged=yes</varname> option, see + <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>. + </para> + + <para>When <filename>systemd-networkd</filename> exits, it generally leaves + existing network devices and configuration intact. This makes it possible to + transition from the initrams and to restart the service without breaking + connectivity. This also means that when configuration is updated and + <filename>systemd-networkd</filename> is restarted, netdev interfaces for + which configuration was removed will not be dropped, and may need to be + cleaned up manually.</para> </refsect1> <refsect1><title>Configuration Files</title> diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 5d3212dec7..3951e32e8f 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -714,6 +714,23 @@ </varlistentry> <varlistentry> + <term><option>--system-call-filter=</option></term> + + <listitem><para>Alter the system call filter applied to containers. Takes a space-separated list of system call + names or group names (the latter prefixed with <literal>@</literal>, as listed by the + <command>syscall-filter</command> command of <citerefentry + project='man-pages'><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>). Passed + system calls will be permitted. The list may optionally be prefixed by <literal>~</literal>, in which case all + listed system calls are prohibited. If this command line option is used multiple times the configured lists are + combined. If both a positive and a negative list (that is one system call list without and one with the + <literal>~</literal> prefix) are configured, the negative list takes precedence over the positive list. Note + that <command>systemd-nspawn</command> always implements a system call whitelist (as opposed to a blacklist), + and this command line option hence adds or removes entries from the default whitelist, depending on the + <literal>~</literal> prefix. Note that the applied system call filter is also altered implicitly if additional + capabilities are passed using the <command>--capabilities=</command>.</para></listitem> + </varlistentry> + + <varlistentry> <term><option>--kill-signal=</option></term> <listitem><para>Specify the process signal to send to the diff --git a/man/systemd-run.xml b/man/systemd-run.xml index 5e44b1523d..7477195dab 100644 --- a/man/systemd-run.xml +++ b/man/systemd-run.xml @@ -219,14 +219,32 @@ <term><option>--pty</option></term> <term><option>-t</option></term> - <listitem><para>When invoking the command, the transient service connects its standard input and output to the - terminal <command>systemd-run</command> is invoked on, via a pseudo TTY device. This allows running binaries - that expect interactive user input as services, such as interactive command shells.</para> + <listitem><para>When invoking the command, the transient service connects its standard input, output and error + to the terminal <command>systemd-run</command> is invoked on, via a pseudo TTY device. This allows running + programs that expect interactive user input/output as services, such as interactive command shells.</para> <para>Note that <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s <command>shell</command> command is usually a better alternative for requesting a new, interactive login - session on the local host or a local container.</para></listitem> + session on the local host or a local container.</para> + + <para>See below for details on how this switch combines with <option>--pipe</option>.</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>--pipe</option></term> + <term><option>-P</option></term> + + <listitem><para>If specified, standard input, output, and error of the transient service are inherited from the + <command>systemd-run</command> command itself. This allows <command>systemd-run</command> + to be used within shell pipelines. + Note that this mode is not suitable for interactive command shells and similar, as the + service process will not become a TTY controller when invoked on a terminal. Use <option>--pty</option> instead + in that case.</para> + + <para>When both <option>--pipe</option> and <option>--pty</option> are used in combination the more appropriate + option is automatically determined and used. Specifically, when invoked with standard input, output and error + connected to a TTY <option>--pty</option> is used, and otherwise <option>--pipe</option>.</para></listitem> </varlistentry> <varlistentry> diff --git a/man/systemd-sysusers.xml b/man/systemd-sysusers.xml index 4892caad12..990b935cf2 100644 --- a/man/systemd-sysusers.xml +++ b/man/systemd-sysusers.xml @@ -74,7 +74,7 @@ specified in <citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> are searched for a matching file. If the string - <filename>-</filename> is specified as filename, entries from the + <literal>-</literal> is specified instead of a filename, entries from the standard input of the process are read.</para> </refsect1> diff --git a/man/systemd.automount.xml b/man/systemd.automount.xml index a43dc981bd..49ea7e510c 100644 --- a/man/systemd.automount.xml +++ b/man/systemd.automount.xml @@ -87,19 +87,30 @@ </refsect1> <refsect1> - <title>Automatic Dependencies</title> + <title>Implicit Dependencies</title> - <para>If an automount unit is beneath another mount unit in the - file system hierarchy, both a requirement and an ordering - dependency between both units are created automatically.</para> + <para>The following dependencies are implicitly added:</para> - <para>An implicit <varname>Before=</varname> dependency is created - between an automount unit and the mount unit it activates.</para> + <itemizedlist> + <listitem><para>If an automount unit is beneath another mount unit in the + file system hierarchy, both a requirement and an ordering + dependency between both units are created automatically.</para></listitem> - <para>Automount units acquire automatic <varname>Before=</varname> and <varname>Conflicts=</varname> on - <filename>umount.target</filename> in order to be stopped during shutdown, unless - <varname>DefaultDependencies=no</varname> is set in the <literal>[Unit]</literal> section.</para> + <listitem><para>An implicit <varname>Before=</varname> dependency is created + between an automount unit and the mount unit it activates.</para></listitem> + </itemizedlist> + </refsect1> + + <refsect1> + <title>Default Dependencies</title> + + <para>The following dependencies are added unless <varname>DefaultDependencies=no</varname> is set:</para> + <itemizedlist> + <listitem><para>Automount units acquire automatic <varname>Before=</varname> and + <varname>Conflicts=</varname> on <filename>umount.target</filename> in order to be stopped during + shutdown.</para></listitem> + </itemizedlist> </refsect1> <refsect1> diff --git a/man/systemd.device.xml b/man/systemd.device.xml index effed098dd..c60b9c035e 100644 --- a/man/systemd.device.xml +++ b/man/systemd.device.xml @@ -86,7 +86,7 @@ </refsect1> <refsect1> - <title>Automatic Dependencies</title> + <title>Implicit Dependencies</title> <para>Many unit types automatically acquire dependencies on device units of devices they require. For example, @@ -98,6 +98,12 @@ </refsect1> <refsect1> + <title>Default Dependencies</title> + + <para>There are no default dependencies for device units.</para> + </refsect1> + + <refsect1> <title>The udev Database</title> <para>The settings of device units may either be configured via diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 707cab28aa..f0b48e4a41 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -81,27 +81,30 @@ </refsect1> <refsect1> - <title>Automatic Dependencies</title> - - <para>A few execution parameters result in additional, automatic - dependencies to be added.</para> - - <para>Units with <varname>WorkingDirectory=</varname>, <varname>RootDirectory=</varname>, <varname>RootImage=</varname>, - <varname>RuntimeDirectory=</varname>, <varname>StateDirectory=</varname>, <varname>CacheDirectory=</varname>, - <varname>LogsDirectory=</varname> or <varname>ConfigurationDirectory=</varname> set automatically gain dependencies - of type <varname>Requires=</varname> and <varname>After=</varname> on all mount units required to access the specified paths. - This is equivalent to having them listed explicitly in <varname>RequiresMountsFor=</varname>.</para> - - <para>Similar, units with <varname>PrivateTmp=</varname> enabled automatically get mount unit dependencies for all - mounts required to access <filename>/tmp</filename> and <filename>/var/tmp</filename>. They will also gain an - automatic <varname>After=</varname> dependency on - <citerefentry><refentrytitle>systemd-tmpfiles-setup.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para> - - <para>Units whose standard output or error output is connected to <option>journal</option>, <option>syslog</option> - or <option>kmsg</option> (or their combinations with console output, see below) automatically acquire dependencies - of type <varname>After=</varname> on <filename>systemd-journald.socket</filename>.</para> + <title>Implicit Dependencies</title> + + <para>A few execution parameters result in additional, automatic dependencies to be added:</para> + + <itemizedlist> + <listitem><para>Units with <varname>WorkingDirectory=</varname>, <varname>RootDirectory=</varname>, <varname>RootImage=</varname>, + <varname>RuntimeDirectory=</varname>, <varname>StateDirectory=</varname>, <varname>CacheDirectory=</varname>, + <varname>LogsDirectory=</varname> or <varname>ConfigurationDirectory=</varname> set automatically gain dependencies + of type <varname>Requires=</varname> and <varname>After=</varname> on all mount units required to access the specified paths. + This is equivalent to having them listed explicitly in <varname>RequiresMountsFor=</varname>.</para></listitem> + + <listitem><para>Similar, units with <varname>PrivateTmp=</varname> enabled automatically get mount unit dependencies for all + mounts required to access <filename>/tmp</filename> and <filename>/var/tmp</filename>. They will also gain an + automatic <varname>After=</varname> dependency on + <citerefentry><refentrytitle>systemd-tmpfiles-setup.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem> + + <listitem><para>Units whose standard output or error output is connected to <option>journal</option>, <option>syslog</option> + or <option>kmsg</option> (or their combinations with console output, see below) automatically acquire dependencies + of type <varname>After=</varname> on <filename>systemd-journald.socket</filename>.</para></listitem> + </itemizedlist> </refsect1> + <!-- We don't have any default dependency here. --> + <refsect1> <title>Options</title> @@ -423,17 +426,17 @@ <varlistentry> <term><varname>PassEnvironment=</varname></term> - <listitem><para>Pass environment variables from the systemd system - manager to executed processes. Takes a space-separated list of variable - names. This option may be specified more than once, in which case all - listed variables will be set. If the empty string is assigned to this - option, the list of environment variables is reset, all prior - assignments have no effect. Variables that are not set in the system - manager will not be passed and will be silently ignored.</para> + <listitem><para>Pass environment variables set for the system service manager to executed processes. Takes a + space-separated list of variable names. This option may be specified more than once, in which case all listed + variables will be passed. If the empty string is assigned to this option, the list of environment variables to + pass is reset, all prior assignments have no effect. Variables specified that are not set for the system + manager will not be passed and will be silently ignored. Note that this option is only relevant for the system + service manager, as system services by default do not automatically inherit any environment variables set for + the service manager itself. However, in case of the user service manager all environment variables are passed + to the executed processes anyway, hence this option is without effect for the user service manager.</para> - <para>Variables passed from this setting are overridden by those passed - from <varname>Environment=</varname> or - <varname>EnvironmentFile=</varname>.</para> + <para>Variables set for invoked processes due to this setting are subject to being overridden by those + configured with <varname>Environment=</varname> or <varname>EnvironmentFile=</varname>.</para> <para>Example: <programlisting>PassEnvironment=VAR1 VAR2 VAR3</programlisting> @@ -448,6 +451,30 @@ </varlistentry> <varlistentry> + <term><varname>UnsetEnvironment=</varname></term> + + <listitem><para>Explicitly unset environment variable assignments that would normally be passed from the + service manager to invoked processes of this unit. Takes a space-separated list of variable names or variable + assignments. This option may be specified more than once, in which case all listed variables/assignments will + be unset. If the empty string is assigned to this option, the list of environment variables/assignments to + unset is reset. If a variable assignment is specified (that is: a variable name, followed by + <literal>=</literal>, followed by its value), then any environment variable matching this precise assignment is + removed. If a variable name is specified (that is a variable name without any following <literal>=</literal> or + value), then any assignment matching the variable name, regardless of its value is removed. Note that the + effect of <varname>UnsetEnvironment=</varname> is applied as final step when the environment list passed to + executed processes is compiled. That means it may undo assignments from any configuration source, including + assignments made through <varname>Environment=</varname> or <varname>EnvironmentFile=</varname>, inherited from + the system manager's global set of environment variables, inherited via <varname>PassEnvironment=</varname>, + set by the service manager itself (such as <varname>$NOTIFY_SOCKET</varname> and such), or set by a PAM module + (in case <varname>PAMName=</varname> is used).</para> + + <para> + See + <citerefentry project='man-pages'><refentrytitle>environ</refentrytitle><manvolnum>7</manvolnum></citerefentry> + for details about environment variables.</para></listitem> + </varlistentry> + + <varlistentry> <term><varname>StandardInput=</varname></term> <listitem><para>Controls where file descriptor 0 (STDIN) of the executed processes is connected to. Takes one of @@ -591,7 +618,7 @@ <para>If the standard output (or error output, see below) of a unit is connected to the journal, syslog or the kernel log buffer, the unit will implicitly gain a dependency of type <varname>After=</varname> on - <filename>systemd-journald.socket</filename> (also see the automatic dependencies section above).</para> + <filename>systemd-journald.socket</filename> (also see the "Implicit Dependencies" section above).</para> <para>This setting defaults to the value set with <option>DefaultStandardOutput=</option> in @@ -1462,6 +1489,10 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting> <entry>System calls for CPU emulation functionality (<citerefentry project='man-pages'><refentrytitle>vm86</refentrytitle><manvolnum>2</manvolnum></citerefentry> and related calls)</entry> </row> <row> + <entry>@credentials</entry> + <entry>System calls for querying process credentials (<citerefentry project='man-pages'><refentrytitle>getuid</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>capget</refentrytitle><manvolnum>2</manvolnum></citerefentry>, and related calls)</entry> + </row> + <row> <entry>@debug</entry> <entry>Debugging, performance monitoring and tracing functionality (<citerefentry project='man-pages'><refentrytitle>ptrace</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>perf_event_open</refentrytitle><manvolnum>2</manvolnum></citerefentry> and related calls)</entry> </row> @@ -1482,6 +1513,10 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting> <entry>Kernel keyring access (<citerefentry project='man-pages'><refentrytitle>keyctl</refentrytitle><manvolnum>2</manvolnum></citerefentry> and related calls)</entry> </row> <row> + <entry>@memlock</entry> + <entry>Locking of memory into RAM (<citerefentry project='man-pages'><refentrytitle>mlock</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>mlockall</refentrytitle><manvolnum>2</manvolnum></citerefentry> and related calls)</entry> + </row> + <row> <entry>@module</entry> <entry>Loading and unloading of kernel modules (<citerefentry project='man-pages'><refentrytitle>init_module</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>delete_module</refentrytitle><manvolnum>2</manvolnum></citerefentry> and related calls)</entry> </row> @@ -1522,9 +1557,17 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting> <entry>System calls for changing user ID and group ID credentials, (<citerefentry project='man-pages'><refentrytitle>setuid</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>setgid</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>setresuid</refentrytitle><manvolnum>2</manvolnum></citerefentry>, …)</entry> </row> <row> + <entry>@signal</entry> + <entry>System calls for manipulating and handling process signals (<citerefentry project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>sigprocmask</refentrytitle><manvolnum>2</manvolnum></citerefentry>, …)</entry> + </row> + <row> <entry>@swap</entry> <entry>System calls for enabling/disabling swap devices (<citerefentry project='man-pages'><refentrytitle>swapon</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>swapoff</refentrytitle><manvolnum>2</manvolnum></citerefentry>)</entry> </row> + <row> + <entry>@timer</entry> + <entry>System calls for scheduling operations by time (<citerefentry project='man-pages'><refentrytitle>alarm</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>timer_create</refentrytitle><manvolnum>2</manvolnum></citerefentry>, …)</entry> + </row> </tbody> </tgroup> </table> @@ -1678,6 +1721,26 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting> </varlistentry> <varlistentry> + <term><varname>KeyringMode=</varname></term> + + <listitem><para>Controls how the kernel session keyring is set up for the service (see <citerefentry + project='man-pages'><refentrytitle>session-keyring</refentrytitle><manvolnum>7</manvolnum></citerefentry> for + details on the session keyring). Takes one of <option>inherit</option>, <option>private</option>, + <option>shared</option>. If set to <option>inherit</option> no special keyring setup is done, and the kernel's + default behaviour is applied. If <option>private</option> is used a new session keyring is allocated when a + service process is invoked, and it is not linked up with any user keyring. This is the recommended setting for + system services, as this ensures that multiple services running under the same system user ID (in particular + the root user) do not share their key material among each other. If <option>shared</option> is used a new + session keyring is allocated as for <option>private</option>, but the user keyring of the user configured with + <varname>User=</varname> is linked into it, so that keys assigned to the user may be requested by the unit's + processes. In this modes multiple units running processes under the same user ID may share key material. Unless + <option>inherit</option> is selected the unique invocation ID for the unit (see below) is added as a protected + key by the name <literal>invocation_id</literal> to the newly created session keyring. Defaults to + <option>private</option> for the system service manager and to <option>inherit</option> for the user service + manager.</para></listitem> + </varlistentry> + + <varlistentry> <term><varname>RuntimeDirectory=</varname></term> <listitem><para>Takes a whitespace-separated list of directory names. The specified directory names must be @@ -1799,12 +1862,38 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting> <refsect1> <title>Environment variables in spawned processes</title> - <para>Processes started by the system are executed in a clean - environment in which select variables listed below are set. System - processes started by systemd do not inherit variables from PID 1, - but processes started by user systemd instances inherit all - environment variables from the user systemd instance. - </para> + <para>Processes started by the service manager are executed with an environment variable block assembled from + multiple sources. Processes started by the system service manager generally do not inherit environment variables + set for the service manager itself (but this may be altered via <varname>PassEnvironment=</varname>), but processes + started by the user service manager instances generally do inherit all environment variables set for the service + manager itself.</para> + + <para>For each invoked process the list of environment variables set is compiled from the following sources:</para> + + <itemizedlist> + <listitem><para>Variables globally configured for the service manager, using the + <varname>DefaultEnvironment=</varname> setting in + <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>, the kernel command line option <varname>systemd.setenv=</varname> (see + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>) or via + <command>systemctl set-environment</command> (see <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>).</para></listitem> + + <listitem><para>Variables defined by the service manager itself (see the list below)</para></listitem> + + <listitem><para>Variables set in the service manager's own environment variable block (subject to <varname>PassEnvironment=</varname> for the system service manager)</para></listitem> + + <listitem><para>Variables set via <varname>Environment=</varname> in the unit file</para></listitem> + + <listitem><para>Variables read from files specified via <varname>EnvironmentFiles=</varname> in the unit file</para></listitem> + + <listitem><para>Variables set by any PAM modules in case <varname>PAMName=</varname> is in effect, cf. <citerefentry project='man-pages'><refentrytitle>pam_env</refentrytitle><manvolnum>8</manvolnum></citerefentry></para></listitem> + </itemizedlist> + + <para>If the same environment variables are set by multiple of these sources, the later source — according to the + order of the list above — wins. Note that as final step all variables listed in + <varname>UnsetEnvironment=</varname> are removed again from the compiled environment variable list, immediately + before it is passed to the executed process.</para> + + <para>The following select environment variables are set by the service manager itself for each invoked process:</para> <variablelist class='environment-variables'> <varlistentry> @@ -1943,6 +2032,12 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting> <varname>$JOURNAL_STREAM</varname> is set at all as services might invoke external processes replacing their standard output or standard error output, without unsetting the environment variable.</para> + <para>If both standard output and standard error of the executed processes are connected to the journal via a + stream socket, this environment variable will contain information about the standard error stream, as that's + usually the preferred destination for log data. (Note that typically the same stream is used for both standard + output and standard error, hence very likely the environment variable contains device and inode information + matching both stream file descriptors.)</para> + <para>This environment variable is primarily useful to allow services to optionally upgrade their used log protocol to the native journal protocol (using <citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>3</manvolnum></citerefentry> and other @@ -1955,15 +2050,60 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting> <listitem><para>Only defined for the service unit type, this environment variable is passed to all <varname>ExecStop=</varname> and <varname>ExecStopPost=</varname> processes, and encodes the service - "result". Currently, the following values are defined: <literal>protocol</literal> (in case of a protocol - violation; if a service did not take the steps required by its unit configuration), <literal>timeout</literal> - (in case of an operation timeout), <literal>exit-code</literal> (if a service process exited with a non-zero - exit code; see <varname>$EXIT_CODE</varname> below for the actual exit code returned), <literal>signal</literal> - (if a service process was terminated abnormally by a signal; see <varname>$EXIT_CODE</varname> below for the - actual signal used for the termination), <literal>core-dump</literal> (if a service process terminated - abnormally and dumped core), <literal>watchdog</literal> (if the watchdog keep-alive ping was enabled for the - service but it missed the deadline), or <literal>resources</literal> (a catch-all condition in case a system - operation failed).</para> + "result". Currently, the following values are defined:</para> + + <table> + <title>Defined <varname>$SERVICE_RESULT</varname> values</title> + <tgroup cols='2'> + <colspec colname='result'/> + <colspec colname='meaning'/> + <thead> + <row> + <entry>Value</entry> + <entry>Meaning</entry> + </row> + </thead> + + <tbody> + <row> + <entry><literal>success</literal></entry> + <entry>The service ran successfully and exited cleanly.</entry> + </row> + <row> + <entry><literal>protocol</literal></entry> + <entry>A protocol violation occurred: the service did not take the steps required by its unit configuration (specifically what is configured in its <varname>Type=</varname> setting).</entry> + </row> + <row> + <entry><literal>timeout</literal></entry> + <entry>One of the steps timed out.</entry> + </row> + <row> + <entry><literal>exit-code</literal></entry> + <entry>Service process exited with a non-zero exit code; see <varname>$EXIT_CODE</varname> below for the actual exit code returned.</entry> + </row> + <row> + <entry><literal>signal</literal></entry> + <entry>A service process was terminated abnormally by a signal, without dumping core. See <varname>$EXIT_CODE</varname> below for the actual signal causing the termination.</entry> + </row> + <row> + <entry><literal>core-dump</literal></entry> + <entry>A service process terminated abnormally with a signal and dumped core. See <varname>$EXIT_CODE</varname> below for the signal causing the termination.</entry> + </row> + <row> + <entry><literal>watchdog</literal></entry> + <entry>Watchdog keep-alive ping was enabled for the service, but the deadline was missed.</entry> + </row> + <row> + <entry><literal>start-limit-hit</literal></entry> + <entry>A start limit was defined for the unit and it was hit, causing the unit to fail to start. See <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>'s <varname>StartLimitIntervalSec=</varname> and <varname>StartLimitBurst=</varname> for details.</entry> + </row> + <row> + <entry><literal>resources</literal></entry> + <entry>A catch-all condition in case a system operation failed.</entry> + </row> + </tbody> + </tgroup> + </table> <para>This environment variable is useful to monitor failure or successful termination of a service. Even though this variable is available in both <varname>ExecStop=</varname> and <varname>ExecStopPost=</varname>, it @@ -2002,6 +2142,11 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting> <tbody> <row> + <entry valign="top"><literal>success</literal></entry> + <entry valign="top"><literal>exited</literal></entry> + <entry><literal>0</literal></entry> + </row> + <row> <entry morerows="1" valign="top"><literal>protocol</literal></entry> <entry valign="top">not set</entry> <entry>not set</entry> @@ -2010,7 +2155,6 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting> <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> @@ -2021,26 +2165,22 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting> <entry><literal>0</literal>, <literal>1</literal>, <literal>2</literal>, <literal >3</literal>, …, <literal>255</literal></entry> </row> - <row> <entry valign="top"><literal>exit-code</literal></entry> <entry valign="top"><literal>exited</literal></entry> - <entry><literal>0</literal>, <literal>1</literal>, <literal>2</literal>, <literal + <entry><literal>1</literal>, <literal>2</literal>, <literal >3</literal>, …, <literal>255</literal></entry> </row> - <row> <entry valign="top"><literal>signal</literal></entry> <entry valign="top"><literal>killed</literal></entry> <entry><literal>HUP</literal>, <literal>INT</literal>, <literal>KILL</literal>, …</entry> </row> - <row> <entry valign="top"><literal>core-dump</literal></entry> <entry valign="top"><literal>dumped</literal></entry> <entry><literal>ABRT</literal>, <literal>SEGV</literal>, <literal>QUIT</literal>, …</entry> </row> - <row> <entry morerows="2" valign="top"><literal>watchdog</literal></entry> <entry><literal>dumped</literal></entry> @@ -2055,15 +2195,18 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting> <entry><literal>0</literal>, <literal>1</literal>, <literal>2</literal>, <literal >3</literal>, …, <literal>255</literal></entry> </row> - + <row> + <entry><literal>start-limit-hit</literal></entry> + <entry>not set</entry> + <entry>not set</entry> + </row> <row> <entry><literal>resources</literal></entry> <entry>any of the above</entry> <entry>any of the above</entry> </row> - <row> - <entry namest="results" nameend="code">Note: the process may be also terminated by a signal not sent by systemd. In particular the process may send an arbitrary signal to itself in a handler for any of the non-maskable signals. Nevertheless, in the <literal>timeout</literal> and <literal>watchdog</literal> rows above only the signals that systemd sends have been included.</entry> + <entry namest="results" nameend="status">Note: the process may be also terminated by a signal not sent by systemd. In particular the process may send an arbitrary signal to itself in a handler for any of the non-maskable signals. Nevertheless, in the <literal>timeout</literal> and <literal>watchdog</literal> rows above only the signals that systemd sends have been included. Moreover, using <varname>SuccessExitStatus=</varname> additional exit statuses may be declared to indicate clean termination, which is not reflected by this table.</entry> </row> </tbody> </tgroup> @@ -2072,18 +2215,316 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting> </listitem> </varlistentry> </variablelist> + </refsect1> - <para>Additional variables may be configured by the following - means: for processes spawned in specific units, use the - <varname>Environment=</varname>, <varname>EnvironmentFile=</varname> - and <varname>PassEnvironment=</varname> options above; to specify - variables globally, use <varname>DefaultEnvironment=</varname> - (see - <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>) - or the kernel option <varname>systemd.setenv=</varname> (see - <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>). - Additional variables may also be set through PAM, - cf. <citerefentry project='man-pages'><refentrytitle>pam_env</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para> + <refsect1> + <title>Process exit codes</title> + + <para>When invoking a unit process the service manager possibly fails to apply the execution parameters configured + with the settings above. In that case the already created service process will exit with a non-zero exit code + before the configured command line is executed. (Or in other words, the child process possibly exits with these + error codes, after having been created by the <citerefentry + project='man-pages'><refentrytitle>fork</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call, but + before the matching <citerefentry + project='man-pages'><refentrytitle>execve</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call is + called.) Specifically, exit codes defined by the C library, by the LSB specification and by the systemd service + manager itself are used.</para> + + <para>The following basic service exit codes are defined by the C library.</para> + + <table> + <title>Basic C library exit codes</title> + <tgroup cols='3'> + <thead> + <row> + <entry>Exit Code</entry> + <entry>Symbolic Name</entry> + <entry>Description</entry> + </row> + </thead> + <tbody> + <row> + <entry>0</entry> + <entry><constant>EXIT_SUCCESS</constant></entry> + <entry>Generic success code.</entry> + </row> + <row> + <entry>1</entry> + <entry><constant>EXIT_FAILURE</constant></entry> + <entry>Generic failure or unspecified error.</entry> + </row> + </tbody> + </tgroup> + </table> + + <para>The following service exit codes are defined by the <ulink + url="https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html">LSB specification + </ulink>. + </para> + + <table> + <title>LSB service exit codes</title> + <tgroup cols='3'> + <thead> + <row> + <entry>Exit Code</entry> + <entry>Symbolic Name</entry> + <entry>Description</entry> + </row> + </thead> + <tbody> + <row> + <entry>2</entry> + <entry><constant>EXIT_INVALIDARGUMENT</constant></entry> + <entry>Invalid or excess arguments.</entry> + </row> + <row> + <entry>3</entry> + <entry><constant>EXIT_NOTIMPLEMENTED</constant></entry> + <entry>Unimplemented feature.</entry> + </row> + <row> + <entry>4</entry> + <entry><constant>EXIT_NOPERMISSION</constant></entry> + <entry>The user has insufficient privileges.</entry> + </row> + <row> + <entry>5</entry> + <entry><constant>EXIT_NOTINSTALLED</constant></entry> + <entry>The program is not installed.</entry> + </row> + <row> + <entry>6</entry> + <entry><constant>EXIT_NOTCONFIGURED</constant></entry> + <entry>The program is not configured.</entry> + </row> + <row> + <entry>7</entry> + <entry><constant>EXIT_NOTRUNNING</constant></entry> + <entry>The program is not running.</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + The LSB specification suggests that error codes 200 and above are reserved for implementations. Some of them are + used by the service manager to indicate problems during process invocation: + </para> + <table> + <title>systemd-specific exit codes</title> + <tgroup cols='3'> + <thead> + <row> + <entry>Exit Code</entry> + <entry>Symbolic Name</entry> + <entry>Description</entry> + </row> + </thead> + <tbody> + <row> + <entry>200</entry> + <entry><constant>EXIT_CHDIR</constant></entry> + <entry>Changing to the requested working directory failed. See <varname>WorkingDirectory=</varname> above.</entry> + </row> + <row> + <entry>201</entry> + <entry><constant>EXIT_NICE</constant></entry> + <entry>Failed to set up process scheduling priority (nice level). See <varname>Nice=</varname> above.</entry> + </row> + <row> + <entry>202</entry> + <entry><constant>EXIT_FDS</constant></entry> + <entry>Failed to close unwanted file descriptors, or to adjust passed file descriptors.</entry> + </row> + <row> + <entry>203</entry> + <entry><constant>EXIT_EXEC</constant></entry> + <entry>The actual process execution failed (specifically, the <citerefentry project='man-pages'><refentrytitle>execve</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call). Most likely this is caused by a missing or non-accessible executable file.</entry> + </row> + <row> + <entry>204</entry> + <entry><constant>EXIT_MEMORY</constant></entry> + <entry>Failed to perform an action due to memory shortage.</entry> + </row> + <row> + <entry>205</entry> + <entry><constant>EXIT_LIMITS</constant></entry> + <entry>Failed to adjust resoure limits. See <varname>LimitCPU=</varname> and related settings above.</entry> + </row> + <row> + <entry>206</entry> + <entry><constant>EXIT_OOM_ADJUST</constant></entry> + <entry>Failed to adjust the OOM setting. See <varname>OOMScoreAdjust=</varname> above.</entry> + </row> + <row> + <entry>207</entry> + <entry><constant>EXIT_SIGNAL_MASK</constant></entry> + <entry>Failed to set process signal mask.</entry> + </row> + <row> + <entry>208</entry> + <entry><constant>EXIT_STDIN</constant></entry> + <entry>Failed to set up standard input. See <varname>StandardInput=</varname> above.</entry> + </row> + <row> + <entry>209</entry> + <entry><constant>EXIT_STDOUT</constant></entry> + <entry>Failed to set up standard output. See <varname>StandardOutput=</varname> above.</entry> + </row> + <row> + <entry>210</entry> + <entry><constant>EXIT_CHROOT</constant></entry> + <entry>Failed to change root directory (<citerefentry project='man-pages'><refentrytitle>chroot</refentrytitle><manvolnum>2</manvolnum></citerefentry>). See <varname>RootDirectory=</varname>/<varname>RootImage=</varname> above.</entry> + </row> + <row> + <entry>211</entry> + <entry><constant>EXIT_IOPRIO</constant></entry> + <entry>Failed to set up IO scheduling priority. See <varname>IOSchedulingClass=</varname>/<varname>IOSchedulingPriority=</varname> above.</entry> + </row> + <row> + <entry>212</entry> + <entry><constant>EXIT_TIMERSLACK</constant></entry> + <entry>Failed to set up timer slack. See <varname>TimerSlackNSec=</varname> above.</entry> + </row> + <row> + <entry>213</entry> + <entry><constant>EXIT_SECUREBITS</constant></entry> + <entry>Failed to set process secure bits. See <varname>SecureBits=</varname> above.</entry> + </row> + <row> + <entry>214</entry> + <entry><constant>EXIT_SETSCHEDULER</constant></entry> + <entry>Failed to set up CPU scheduling. See <varname>CPUSchedulingPolicy=</varname>/<varname>CPUSchedulingPriority=</varname> above.</entry> + </row> + <row> + <entry>215</entry> + <entry><constant>EXIT_CPUAFFINITY</constant></entry> + <entry>Failed to set up CPU affinity. See <varname>CPUAffinity=</varname> above.</entry> + </row> + <row> + <entry>216</entry> + <entry><constant>EXIT_GROUP</constant></entry> + <entry>Failed to determine or change group credentials. See <varname>Group=</varname>/<varname>SupplementaryGroups=</varname> above.</entry> + </row> + <row> + <entry>217</entry> + <entry><constant>EXIT_USER</constant></entry> + <entry>Failed to determine or change user credentials, or to set up user namespacing. See <varname>User=</varname>/<varname>PrivateUsers=</varname> above.</entry> + </row> + <row> + <entry>218</entry> + <entry><constant>EXIT_CAPABILITIES</constant></entry> + <entry>Failed to drop capabilities, or apply ambient capabilities. See <varname>CapabilityBoundingSet=</varname>/<varname>AmbientCapabilities=</varname> above.</entry> + </row> + <row> + <entry>219</entry> + <entry><constant>EXIT_CGROUP</constant></entry> + <entry>Setting up the service control group failed.</entry> + </row> + <row> + <entry>220</entry> + <entry><constant>EXIT_SETSID</constant></entry> + <entry>Failed to create new process session.</entry> + </row> + <row> + <entry>221</entry> + <entry><constant>EXIT_CONFIRM</constant></entry> + <entry>Execution has been cancelled by the user. See the <varname>systemd.confirm_spawn=</varname> kernel command line setting on <citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry> for details.</entry> + </row> + <row> + <entry>222</entry> + <entry><constant>EXIT_STDERR</constant></entry> + <entry>Failed to set up standard error output. See <varname>StandardError=</varname> above.</entry> + </row> + <row> + <entry>224</entry> + <entry><constant>EXIT_PAM</constant></entry> + <entry>Failed to set up PAM session. See <varname>PAMName=</varname> above.</entry> + </row> + <row> + <entry>225</entry> + <entry><constant>EXIT_NETWORK</constant></entry> + <entry>Failed to set up network namespacing. See <varname>PrivateNetwork=</varname> above.</entry> + </row> + <row> + <entry>226</entry> + <entry><constant>EXIT_NAMESPACE</constant></entry> + <entry>Failed to set up mount namespacing. See <varname>ReadOnlyPaths=</varname> and related settings above.</entry> + </row> + <row> + <entry>227</entry> + <entry><constant>EXIT_NO_NEW_PRIVILEGES</constant></entry> + <entry>Failed to disable new priviliges. See <varname>NoNewPrivileges=yes</varname> above.</entry> + </row> + <row> + <entry>228</entry> + <entry><constant>EXIT_SECCOMP</constant></entry> + <entry>Failed to apply system call filters. See <varname>SystemCallFilter=</varname> and related settings above.</entry> + </row> + <row> + <entry>229</entry> + <entry><constant>EXIT_SELINUX_CONTEXT</constant></entry> + <entry>Determining or changing SELinux context failed. See <varname>SELinuxContext=</varname> above.</entry> + </row> + <row> + <entry>230</entry> + <entry><constant>EXIT_PERSONALITY</constant></entry> + <entry>Failed to set up a execution domain (personality). See <varname>Personality=</varname> above.</entry> + </row> + <row> + <entry>231</entry> + <entry><constant>EXIT_APPARMOR_PROFILE</constant></entry> + <entry>Failed to prepare changing AppArmor profile. See <varname>AppArmorProfile=</varname> above.</entry> + </row> + <row> + <entry>232</entry> + <entry><constant>EXIT_ADDRESS_FAMILIES</constant></entry> + <entry>Failed to restrict address families. See <varname>RestrictAddressFamilies=</varname> above.</entry> + </row> + <row> + <entry>233</entry> + <entry><constant>EXIT_RUNTIME_DIRECTORY</constant></entry> + <entry>Setting up runtime directory failed. See <varname>RuntimeDirectory=</varname> and related settings above.</entry> + </row> + <row> + <entry>235</entry> + <entry><constant>EXIT_CHOWN</constant></entry> + <entry>Failed to adjust socket ownership. Used for socket units only.</entry> + </row> + <row> + <entry>236</entry> + <entry><constant>EXIT_SMACK_PROCESS_LABEL</constant></entry> + <entry>Failed to set SMACK label. See <varname>SmackProcessLabel=</varname> above.</entry> + </row> + <row> + <entry>237</entry> + <entry><constant>EXIT_KEYRING</constant></entry> + <entry>Failed to set up kernel keyring.</entry> + </row> + <row> + <entry>238</entry> + <entry><constant>EXIT_STATE_DIRECTORY</constant></entry> + <entry>Failed to set up a the unit's state directory. See <varname>StateDirectory=</varname> above.</entry> + </row> + <row> + <entry>239</entry> + <entry><constant>EXIT_CACHE_DIRECTORY</constant></entry> + <entry>Failed to set up a the unit's cache directory. See <varname>CacheDirectory=</varname> above.</entry> + </row> + <row> + <entry>240</entry> + <entry><constant>EXIT_LOGS_DIRECTORY</constant></entry> + <entry>Failed to set up a the unit's logging directory. See <varname>LogsDirectory=</varname> above.</entry> + </row> + <row> + <entry>241</entry> + <entry><constant>EXIT_CONFIGURATION_DIRECTORY</constant></entry> + <entry>Failed to set up a the unit's configuration directory. See <varname>ConfigurationDirectory=</varname> above.</entry> + </row> + </tbody> + </tgroup> + </table> </refsect1> <refsect1> diff --git a/man/systemd.link.xml b/man/systemd.link.xml index 1a13a22877..99bb6a19fb 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -79,7 +79,7 @@ how the device should be configured. The first (in lexical order) of the link files that matches a given device is applied. Note that a default file <filename>99-default.link</filename> is - shipped by the system, any user-supplied + shipped by the system. Any user-supplied <filename>.link</filename> should hence have a lexically earlier name to be considered at all.</para> @@ -332,6 +332,16 @@ <varname>NamePolicy=</varname> fail, or in case <varname>NamePolicy=</varname> is missing or disabled.</para> + + <para>Note that specifying a name that the kernel might use for another + interface (for example <literal>eth0</literal>) is dangerous because the + name assignment done by udev will race with the assignment done by the + kernel, and only one interface may use the name. Depending on the order of + operations, either udev or the kernel will win, making the naming + unpredictable. It is best to use some different prefix, for example + <literal>internal0</literal>/<literal>external0</literal> or + <literal>lan0</literal>/<literal>lan1</literal>/<literal>lan3</literal>. + </para> </listitem> </varlistentry> <varlistentry> @@ -481,6 +491,14 @@ Defaults to "unset".</para> </listitem> </varlistentry> + <varlistentry> + <term><varname>TCP6SegmentationOffload=</varname></term> + <listitem> + <para>The TCP6 Segmentation Offload (tx-tcp6-segmentation) when true enables + TCP6 segmentation offload. Takes a boolean value. + Defaults to "unset".</para> + </listitem> + </varlistentry> <varlistentry> <term><varname>GenericSegmentationOffload=</varname></term> <listitem> diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index 3d3a0a7cc7..58cdb547ea 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -109,40 +109,57 @@ </refsect1> <refsect1> - <title>Automatic Dependencies</title> - - <para>If a mount unit is beneath another mount unit in the file - system hierarchy, both a requirement dependency and an ordering - dependency between both units are created automatically.</para> - - <para>Block device backed file systems automatically gain - <varname>BindsTo=</varname> and <varname>After=</varname> type - dependencies on the device unit encapsulating the block - device (see below).</para> - - <para>If traditional file system quota is enabled for a mount - unit, automatic <varname>Wants=</varname> and - <varname>Before=</varname> dependencies on - <filename>systemd-quotacheck.service</filename> and - <filename>quotaon.service</filename> are added.</para> - - <para>For mount units with <varname>DefaultDependencies=yes</varname> in the <literal>[Unit]</literal> section (the - default) a couple additional dependencies are added. Mount units referring to local file systems automatically gain - an <varname>After=</varname> dependency on <filename>local-fs-pre.target</filename>. Network mount units - automatically acquire <varname>After=</varname> dependencies on <filename>remote-fs-pre.target</filename>, - <filename>network.target</filename> and <filename>network-online.target</filename>. Towards the latter a - <varname>Wants=</varname> unit is added as well. Mount units referring to local and network file systems are + <title>Implicit Dependencies</title> + + <para>The following dependencies are implicitly added:</para> + + <itemizedlist> + <listitem><para>If a mount unit is beneath another mount unit in the file + system hierarchy, both a requirement dependency and an ordering + dependency between both units are created automatically.</para></listitem> + + <listitem><para>Block device backed file systems automatically gain + <varname>BindsTo=</varname> and <varname>After=</varname> type + dependencies on the device unit encapsulating the block + device (see below).</para></listitem> + + <listitem><para>If traditional file system quota is enabled for a mount + unit, automatic <varname>Wants=</varname> and + <varname>Before=</varname> dependencies on + <filename>systemd-quotacheck.service</filename> and + <filename>quotaon.service</filename> are added.</para></listitem> + + <listitem><para>Additional implicit dependencies may be added as result of + execution and resource control parameters as documented in + <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> + and + <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>. + </para></listitem> + </itemizedlist> + </refsect1> + + <refsect1> + <title>Default Dependencies</title> + + <para>The following dependencies are added unless <varname>DefaultDependencies=no</varname> is set:</para> + + <itemizedlist> + <listitem><para>All mount units acquire automatic <varname>Before=</varname> and <varname>Conflicts=</varname> on + <filename>umount.target</filename> in order to be stopped during shutdown.</para></listitem> + + <listitem><para>Mount units referring to local file systems automatically gain + an <varname>After=</varname> dependency on <filename>local-fs-pre.target</filename>.</para></listitem> + + <listitem><para>Network mount units + automatically acquire <varname>After=</varname> dependencies on <filename>remote-fs-pre.target</filename>, + <filename>network.target</filename> and <filename>network-online.target</filename>. Towards the latter a + <varname>Wants=</varname> unit is added as well.</para></listitem> + </itemizedlist> + + <para>Mount units referring to local and network file systems are distinguished by their file system type specification. In some cases this is not sufficient (for example network block device based mounts, such as iSCSI), in which case <option>_netdev</option> may be added to the mount option - string of the unit, which forces systemd to consider the mount unit a network mount. Mount units (regardless if - local or network) also acquire automatic <varname>Before=</varname> and <varname>Conflicts=</varname> on - <filename>umount.target</filename> in order to be stopped during shutdown.</para> - - <para>Additional implicit dependencies may be added as result of - execution and resource control parameters as documented in - <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> - and - <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> + string of the unit, which forces systemd to consider the mount unit a network mount.</para> </refsect1> <refsect1> diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 96d3811372..9750902661 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -878,6 +878,55 @@ </variablelist> </refsect1> + <refsect1> + <title>[RoutingPolicyRule] Section Options</title> + + <para>An <literal>[RoutingPolicyRule]</literal> section accepts the + following keys. Specify several <literal>[RoutingPolicyRule]</literal> + sections to configure several rules.</para> + + <variablelist class='network-directives'> + <varlistentry> + <term><varname>TypeOfService=</varname></term> + <listitem> + <para>Specifies the type of service to match a number between 0 to 255.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>From=</varname></term> + <listitem> + <para>Specifies the source address prefix to match. Possibly followed by a slash and the prefix length.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>To=</varname></term> + <listitem> + <para>Specifies the destination address prefix to match. Possibly followed by a slash and the prefix length.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>FirewallMark=</varname></term> + <listitem> + <para>Specifies the iptables firewall mark value to match (a number between 1 and 4294967295).</para> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>Table=</varname></term> + <listitem> + <para>Specifies the routing table identifier to lookup if the rule + selector matches. The table identifier for a route (a number between 1 and 4294967295).</para> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>Priority=</varname></term> + <listitem> + <para>Specifies the priority of this rule. <varname>Priority=</varname> is an unsigned + integer. Higher number means lower priority, and rules get processed in order of increasing number.</para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + <refsect1> <title>[Route] Section Options</title> <para>The <literal>[Route]</literal> section accepts the @@ -966,6 +1015,19 @@ </para> </listitem> </varlistentry> + <varlistentry> + <term><varname>Type=</varname></term> + <listitem> + <para>The Type identifier for special route types, which can be + <literal>unicast</literal> route to a destination network address which describes the path to the destination, + <literal>blackhole</literal> packets are discarded silently, + <literal>unreachable</literal> packets are discarded and the ICMP message host unreachable is generated, + <literal>prohibit</literal> packets are discarded and the ICMP message communication administratively + prohibited is generated. Defaults to <literal>unicast</literal>. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect1> @@ -1001,7 +1063,7 @@ <listitem> <para>When true, the interface maximum transmission unit from the DHCP server will be used on the current link. - Defaults to false.</para> + Defaults to true.</para> </listitem> </varlistentry> <varlistentry> @@ -1395,10 +1457,19 @@ </varlistentry> <varlistentry> + <term><varname>Domains=</varname></term> + + <listitem><para>A list of DNS search domains distributed via + Router Advertisement messages. Defaults to empty, i.e. no search + domains are sent.</para></listitem> + </varlistentry> + + <varlistentry> <term><varname>DNSLifetimeSec=</varname></term> <listitem><para>Lifetime in seconds for the DNS server addresses listed - in <varname>DNS=</varname>.</para></listitem> + in <varname>DNS=</varname> and search domains listed in + <varname>Domains=</varname>.</para></listitem> </varlistentry> </variablelist> diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml index 4f3f052911..58024a071d 100644 --- a/man/systemd.nspawn.xml +++ b/man/systemd.nspawn.xml @@ -274,11 +274,21 @@ <varlistentry> <term><varname>NotifyReady=</varname></term> - <listitem><para>Configures support for notifications from the container's init process. - This is equivalent to use <option>--notify-ready=</option> command line switch, - and takes the same options. See <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> - for details about the specific options supported.</para></listitem> + <listitem><para>Configures support for notifications from the container's init process. This is equivalent to + the <option>--notify-ready=</option> command line switch, and takes the same paramaters. See + <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> for details + about the specific options supported.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>SystemCallFilter=</varname></term> + + <listitem><para>Configures the system call filter applied to containers. This is equivalent to the + <option>--system-call-filter=</option> command line switch, and takes the same list parameter. See + <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> for + details.</para></listitem> </varlistentry> + </variablelist> </refsect1> diff --git a/man/systemd.path.xml b/man/systemd.path.xml index 7200c8fe27..9e8f2c66c1 100644 --- a/man/systemd.path.xml +++ b/man/systemd.path.xml @@ -82,23 +82,36 @@ </refsect1> <refsect1> - <title>Automatic Dependencies</title> - - <para>If a path unit is beneath another mount unit in the file - system hierarchy, both a requirement and an ordering dependency - between both units are created automatically.</para> - - <para>An implicit <varname>Before=</varname> dependency is added - between a path unit and the unit it is supposed to activate.</para> - - <para>Unless <varname>DefaultDependencies=false</varname> in the <literal>[Unit]</literal> section is used, path - units will implicitly have dependencies of type <varname>Before=</varname> on <filename>paths.target</filename>, - dependencies of type <varname>After=</varname> and <varname>Requires=</varname> on - <filename>sysinit.target</filename>, and have dependencies of type <varname>Conflicts=</varname> and - <varname>Before=</varname> on <filename>shutdown.target</filename>. These ensure that path units are terminated - cleanly prior to system shutdown. Only path units involved with early boot or late system shutdown should disable - this option. - </para> + <title>Implicit Dependencies</title> + + <para>The following dependencies are implicitly added:</para> + + <itemizedlist> + <listitem><para>If a path unit is beneath another mount unit in the file + system hierarchy, both a requirement and an ordering dependency + between both units are created automatically.</para></listitem> + + <listitem><para>An implicit <varname>Before=</varname> dependency is added + between a path unit and the unit it is supposed to activate.</para></listitem> + </itemizedlist> + </refsect1> + + <refsect1> + <title>Default Dependencies</title> + + <para>The following dependencies are added unless <varname>DefaultDependencies=no</varname> is set:</para> + + <itemizedlist> + <listitem><para>Path units will automatically have dependencies of type <varname>Before=</varname> on + <filename>paths.target</filename>, + dependencies of type <varname>After=</varname> and <varname>Requires=</varname> on + <filename>sysinit.target</filename>, and have dependencies of type <varname>Conflicts=</varname> and + <varname>Before=</varname> on <filename>shutdown.target</filename>. These ensure that path units are terminated + cleanly prior to system shutdown. Only path units involved with early boot or late system shutdown should + disable <varname>DefaultDependencies=</varname> option.</para></listitem> + </itemizedlist> + + <para></para> </refsect1> <refsect1> diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml index 9b1f5dbbab..bb69599f99 100644 --- a/man/systemd.resource-control.xml +++ b/man/systemd.resource-control.xml @@ -93,12 +93,19 @@ </refsect1> <refsect1> - <title>Automatic Dependencies</title> + <title>Implicit Dependencies</title> - <para>Units with the <varname>Slice=</varname> setting set automatically acquire <varname>Requires=</varname> and - <varname>After=</varname> dependencies on the specified slice unit.</para> + <para>The following dependencies are implicitly added:</para> + + <itemizedlist> + <listitem><para>Units with the <varname>Slice=</varname> setting set automatically acquire + <varname>Requires=</varname> and <varname>After=</varname> dependencies on the specified + slice unit.</para></listitem> + </itemizedlist> </refsect1> + <!-- We don't have any default dependency here. --> + <refsect1> <title>Unified and Legacy Control Group Hierarchies</title> @@ -499,7 +506,7 @@ <filename>/proc/devices</filename>. The latter is useful to whitelist all current and future devices belonging to a specific device group at once. The device group is matched - according to file name globbing rules, you may hence use the + according to filename globbing rules, you may hence use the <literal>*</literal> and <literal>?</literal> wildcards. Examples: <filename>/dev/sda5</filename> is a path to a device node, referring to an ATA or SCSI block @@ -578,7 +585,7 @@ <para>Special care should be taken when relying on the default slice assignment in templated service units that have <varname>DefaultDependencies=no</varname> set, see <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>, section - "Automatic Dependencies" for details.</para> + "Default Dependencies" for details.</para> </listitem> </varlistentry> diff --git a/man/systemd.scope.xml b/man/systemd.scope.xml index 36f24d46a1..e41493cdd3 100644 --- a/man/systemd.scope.xml +++ b/man/systemd.scope.xml @@ -75,22 +75,31 @@ </refsect1> <refsect1> - <title>Automatic Dependencies</title> - - <para>Unless <varname>DefaultDependencies=false</varname> - is used, scope units will implicitly have dependencies of - type <varname>Conflicts=</varname> and - <varname>Before=</varname> on - <filename>shutdown.target</filename>. These ensure - that scope units are removed prior to system - shutdown. Only scope units involved with early boot or - late system shutdown should disable this option. - </para> + <title>Implicit Dependencies</title> - <para>Additional implicit dependencies may be added as result of + <para>Implicit dependencies may be added as result of resource control parameters as documented in <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> + </refsect1> + <refsect1> + <title>Default Dependencies</title> + + <para>The following dependencies are added unless + <varname>DefaultDependencies=no</varname> is set:</para> + + <itemizedlist> + <listitem><para>Scope units will automatically have dependencies of + type <varname>Conflicts=</varname> and + <varname>Before=</varname> on + <filename>shutdown.target</filename>. These ensure + that scope units are removed prior to system + shutdown. Only scope units involved with early boot or + late system shutdown should disable + <varname>DefaultDependencies=</varname> option.</para></listitem> + </itemizedlist> + + <para></para> </refsect1> <refsect1> diff --git a/man/systemd.service.xml b/man/systemd.service.xml index c6fa0edea0..2b183a9cef 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -89,37 +89,23 @@ </refsect1> <refsect1> - <title>Automatic Dependencies</title> - - <para>Services with <varname>Type=dbus</varname> set automatically - acquire dependencies of type <varname>Requires=</varname> and - <varname>After=</varname> on - <filename>dbus.socket</filename>.</para> - - <para>Socket activated services are automatically ordered after - their activating <filename>.socket</filename> units via an - automatic <varname>After=</varname> dependency. - Services also pull in all <filename>.socket</filename> units - listed in <varname>Sockets=</varname> via automatic - <varname>Wants=</varname> and <varname>After=</varname> dependencies.</para> - - <para>Unless <varname>DefaultDependencies=</varname> in the <literal>[Unit]</literal> is set to - <option>false</option>, service units will implicitly have dependencies of type <varname>Requires=</varname> and - <varname>After=</varname> on <filename>sysinit.target</filename>, a dependency of type <varname>After=</varname> on - <filename>basic.target</filename> as well as dependencies of type <varname>Conflicts=</varname> and - <varname>Before=</varname> on <filename>shutdown.target</filename>. These ensure that normal service units pull in - basic system initialization, and are terminated cleanly prior to system shutdown. Only services involved with early - boot or late system shutdown should disable this option.</para> - - <para>Instanced service units (i.e. service units with an <literal>@</literal> in their name) are assigned by - default a per-template slice unit (see - <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>), named after the - template unit, containing all instances of the specific template. This slice is normally stopped at shutdown, - together with all template instances. If that is not desired, set <varname>DefaultDependencies=no</varname> in the - template unit, and either define your own per-template slice unit file that also sets - <varname>DefaultDependencies=no</varname>, or set <varname>Slice=system.slice</varname> (or another suitable slice) - in the template unit. Also see - <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> + <title>Implicit Dependencies</title> + + <para>The following dependencies are implicitly added:</para> + + <itemizedlist> + <listitem><para>Services with <varname>Type=dbus</varname> set automatically + acquire dependencies of type <varname>Requires=</varname> and + <varname>After=</varname> on + <filename>dbus.socket</filename>.</para></listitem> + + <listitem><para>Socket activated services are automatically ordered after + their activating <filename>.socket</filename> units via an + automatic <varname>After=</varname> dependency. + Services also pull in all <filename>.socket</filename> units + listed in <varname>Sockets=</varname> via automatic + <varname>Wants=</varname> and <varname>After=</varname> dependencies.</para></listitem> + </itemizedlist> <para>Additional implicit dependencies may be added as result of execution and resource control parameters as documented in @@ -129,6 +115,32 @@ </refsect1> <refsect1> + <title>Default Dependencies</title> + + <para>The following dependencies are added unless <varname>DefaultDependencies=no</varname> is set:</para> + + <itemizedlist> + <listitem><para>Service units will have dependencies of type <varname>Requires=</varname> and + <varname>After=</varname> on <filename>sysinit.target</filename>, a dependency of type <varname>After=</varname> on + <filename>basic.target</filename> as well as dependencies of type <varname>Conflicts=</varname> and + <varname>Before=</varname> on <filename>shutdown.target</filename>. These ensure that normal service units pull in + basic system initialization, and are terminated cleanly prior to system shutdown. Only services involved with early + boot or late system shutdown should disable this option.</para></listitem> + + <listitem><para>Instanced service units (i.e. service units with an <literal>@</literal> in their name) are assigned by + default a per-template slice unit (see + <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>), named after the + template unit, containing all instances of the specific template. This slice is normally stopped at shutdown, + together with all template instances. If that is not desired, set <varname>DefaultDependencies=no</varname> in the + template unit, and either define your own per-template slice unit file that also sets + <varname>DefaultDependencies=no</varname>, or set <varname>Slice=system.slice</varname> (or another suitable slice) + in the template unit. Also see + <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>. + </para></listitem> + </itemizedlist> + </refsect1> + + <refsect1> <title>Options</title> <para>Service files must include a <literal>[Service]</literal> @@ -250,7 +262,7 @@ <varlistentry> <term><varname>PIDFile=</varname></term> - <listitem><para>Takes an absolute file name pointing to the + <listitem><para>Takes an absolute filename pointing to the PID file of this daemon. Use of this option is recommended for services where <varname>Type=</varname> is set to <option>forking</option>. systemd will read the PID of the @@ -290,7 +302,7 @@ <varname>ExecStop=</varname> are not valid.)</para> <para>For each of the specified commands, the first argument must be an absolute path to an - executable. Optionally, this file name may be prefixed with a number of special characters:</para> + executable. Optionally, this filename may be prefixed with a number of special characters:</para> <table> <title>Special executable prefixes</title> @@ -741,16 +753,6 @@ considered clean service terminations. </para> - <para>Note that if a process has a signal handler installed - and exits by calling - <citerefentry><refentrytitle>_exit</refentrytitle><manvolnum>2</manvolnum></citerefentry> - in response to a signal, the information about the signal is - lost. Programs should instead perform cleanup and kill - themselves with the same signal instead. See - <ulink url="http://www.cons.org/cracauer/sigint.html">Proper - handling of SIGINT/SIGQUIT — How to be a proper - program</ulink>.</para> - <para>This option may appear more than once, in which case the list of successful exit statuses is merged. If the empty string is assigned to this option, the list is reset, all diff --git a/man/systemd.slice.xml b/man/systemd.slice.xml index 3ff3cc5188..67f7a93448 100644 --- a/man/systemd.slice.xml +++ b/man/systemd.slice.xml @@ -103,17 +103,29 @@ </refsect1> <refsect1> - <title>Automatic Dependencies</title> + <title>Implicit Dependencies</title> - <para>Slice units automatically gain dependencies of type - <varname>After=</varname> and <varname>Requires=</varname> on - their immediate parent slice unit.</para> + <para>The following dependencies are implicitly added:</para> - <para>Unless <varname>DefaultDependencies=false</varname> is used in the <literal>[Unit]</literal> section, slice - units will implicitly have dependencies of type <varname>Conflicts=</varname> and <varname>Before=</varname> on - <filename>shutdown.target</filename>. These ensure that slice units are removed prior to system shutdown. Only - slice units involved with early boot or late system shutdown should disable this option. - </para> + <itemizedlist> + <listitem><para>Slice units automatically gain dependencies of type + <varname>After=</varname> and <varname>Requires=</varname> on + their immediate parent slice unit.</para></listitem> + </itemizedlist> + </refsect1> + + <refsect1> + <title>Default Dependencies</title> + + <para>The following dependencies are added unless <varname>DefaultDependencies=no</varname> is set:</para> + + <itemizedlist> + <listitem><para>Slice units will automatically have dependencies of type <varname>Conflicts=</varname> and + <varname>Before=</varname> on + <filename>shutdown.target</filename>. These ensure that slice units are removed prior to system shutdown. + Only slice units involved with late system shutdown should disable + <varname>DefaultDependencies=</varname> option.</para></listitem> + </itemizedlist> </refsect1> <refsect1> diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 1d20a8f7f7..2dfca713e4 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -97,16 +97,7 @@ <filename>foo@.service</filename> must exist from which services are instantiated for each incoming connection.</para> - <para>Unless <varname>DefaultDependencies=</varname> in the <literal>[Unit]</literal> section is set to - <option>false</option>, socket units will implicitly have dependencies of type <varname>Requires=</varname> and - <varname>After=</varname> on <filename>sysinit.target</filename> as well as dependencies of type - <varname>Conflicts=</varname> and <varname>Before=</varname> on <filename>shutdown.target</filename>. These ensure - that socket units pull in basic system initialization, and are terminated cleanly prior to system shutdown. Only - sockets involved with early boot or late system shutdown should disable this option.</para> - - <para>Socket units will have a <varname>Before=</varname> - dependency on the service which they trigger added implicitly. No - implicit <varname>WantedBy=</varname> or + <para>No implicit <varname>WantedBy=</varname> or <varname>RequiredBy=</varname> dependency from the socket to the service is added. This means that the service may be started without the socket, in which case it must be able to open sockets @@ -130,31 +121,24 @@ </refsect1> <refsect1> - <title>Automatic Dependencies</title> - - <para>Socket units automatically gain a <varname>Before=</varname> - dependency on the service units they activate.</para> - - <para>Socket units referring to file system paths (such as AF_UNIX - sockets or FIFOs) implicitly gain <varname>Requires=</varname> and - <varname>After=</varname> dependencies on all mount units - necessary to access those paths.</para> - - <para>Socket units using the <varname>BindToDevice=</varname> - setting automatically gain a <varname>BindsTo=</varname> and - <varname>After=</varname> dependency on the device unit - encapsulating the specified network interface.</para> - - <para>If <varname>DefaultDependencies=yes</varname> is set (the - default), socket units automatically gain a - <varname>Before=</varname> dependency on - <filename>sockets.target</filename>. They also gain a pair of - <varname>After=</varname> and <varname>Requires=</varname> - dependency on <filename>sysinit.target</filename>, and a pair of - <varname>Before=</varname> and <varname>Conflicts=</varname> - dependencies on <filename>shutdown.target</filename>. These - dependencies ensure that the socket unit is started before normal - services at boot, and is stopped on shutdown.</para> + <title>Implicit Dependencies</title> + + <para>The following dependencies are implicitly added:</para> + + <itemizedlist> + <listitem><para>Socket units automatically gain a <varname>Before=</varname> + dependency on the service units they activate.</para></listitem> + + <listitem><para>Socket units referring to file system paths (such as AF_UNIX + sockets or FIFOs) implicitly gain <varname>Requires=</varname> and + <varname>After=</varname> dependencies on all mount units + necessary to access those paths.</para></listitem> + + <listitem><para>Socket units using the <varname>BindToDevice=</varname> + setting automatically gain a <varname>BindsTo=</varname> and + <varname>After=</varname> dependency on the device unit + encapsulating the specified network interface.</para></listitem> + </itemizedlist> <para>Additional implicit dependencies may be added as result of execution and resource control parameters as documented in @@ -164,6 +148,29 @@ </refsect1> <refsect1> + <title>Default Dependencies</title> + + <para>The following dependencies are added unless + <varname>DefaultDependencies=no</varname> is set:</para> + + <itemizedlist> + <listitem><para>Socket units automatically gain a + <varname>Before=</varname> dependency on + <filename>sockets.target</filename>.</para></listitem> + + <listitem><para>Socket units automatically gain a pair of + <varname>After=</varname> and <varname>Requires=</varname> + dependency on <filename>sysinit.target</filename>, and a pair of + <varname>Before=</varname> and <varname>Conflicts=</varname> + dependencies on <filename>shutdown.target</filename>. These + dependencies ensure that the socket unit is started before normal + services at boot, and is stopped on shutdown. Only sockets + involved with early boot or late system shutdown should disable + <varname>DefaultDependencies=</varname> option.</para></listitem> + </itemizedlist> + </refsect1> + + <refsect1> <title>Options</title> <para>Socket files must include a [Socket] section, which carries @@ -358,7 +365,7 @@ specified network interfaces. This controls the SO_BINDTODEVICE socket option (see <citerefentry project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry> - for details). If this option is used, an automatic dependency + for details). If this option is used, an implicit dependency from this socket unit on the network interface device unit (<citerefentry><refentrytitle>systemd.device</refentrytitle><manvolnum>5</manvolnum></citerefentry> is created. Note that setting this parameter might result in @@ -803,8 +810,8 @@ is used, only one AF_UNIX socket in the file system or one FIFO may be configured for the socket unit. Use this option to manage one or more symlinked alias names for a socket, binding - their lifecycle together. Defaults to the empty - list.</para></listitem> + their lifecycle together. If the empty string is assigned, the + list of paths is reset. Defaults to the empty list.</para></listitem> </varlistentry> <varlistentry> diff --git a/man/systemd.special.xml b/man/systemd.special.xml index 2993d67c76..73e1e720e9 100644 --- a/man/systemd.special.xml +++ b/man/systemd.special.xml @@ -603,9 +603,9 @@ <listitem> <para>A special target unit that sets up all slice units (see <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry> for - details) that shall be active after boot. By default the generic <filename>user.slice</filename>, - <filename>system.slice</filename>, <filename>machines.slice</filename> slice units, as well as the root - slice unit <filename>-.slice</filename> are pulled in and ordered before this unit (see below).</para> + details) that shall be active after boot. By default the generic <filename>system.slice</filename> + slice unit, as well as the root slice unit <filename>-.slice</filename>, is pulled in and ordered before + this unit (see below).</para> <para>It's a good idea to add <varname>WantedBy=slices.target</varname> lines to the <literal>[Install]</literal> section of all slices units that may be installed dynamically.</para> @@ -1036,7 +1036,8 @@ PartOf=graphical-session.target <listitem> <para>By default, all user processes and services started on behalf of the user, including the per-user systemd instance - are found in this slice.</para> + are found in this slice. This is pulled in by + <filename>systemd-logind.service</filename></para> </listitem> </varlistentry> @@ -1045,8 +1046,8 @@ PartOf=graphical-session.target <listitem> <para>By default, all virtual machines and containers registered with <command>systemd-machined</command> are - found in this slice. - </para> + found in this slice. This is pulled in by + <filename>systemd-machined.service</filename></para> </listitem> </varlistentry> </variablelist> diff --git a/man/systemd.swap.xml b/man/systemd.swap.xml index 184abff260..254389e774 100644 --- a/man/systemd.swap.xml +++ b/man/systemd.swap.xml @@ -87,17 +87,16 @@ </refsect1> <refsect1> - <title>Automatic Dependencies</title> + <title>Implicit Dependencies</title> - <para>All swap units automatically get the - <varname>BindsTo=</varname> and <varname>After=</varname> - dependencies on the device units or the mount units of the files - they are activated from.</para> + <para>The following dependencies are implicitly added:</para> - <para>Swap units with <varname>DefaultDependencies=</varname> set to its default <option>yes</option> value in the - <literal>[Unit]</literal> section enabled implicitly acquire a <varname>Conflicts=</varname> and a - <varname>Before=</varname> dependency on <filename>umount.target</filename> so that they are deactivated at - shutdown as well as a <varname>Before=swap.target</varname> dependency.</para> + <itemizedlist> + <listitem><para>All swap units automatically get the + <varname>BindsTo=</varname> and <varname>After=</varname> + dependencies on the device units or the mount units of the files + they are activated from.</para></listitem> + </itemizedlist> <para>Additional implicit dependencies may be added as result of execution and resource control parameters as documented in @@ -107,6 +106,18 @@ </refsect1> <refsect1> + <title>Default Dependencies</title> + + <para>The following dependencies are added unless <varname>DefaultDependencies=no</varname> is set:</para> + + <itemizedlist> + <listitem><para>Swap units automatically acquire a <varname>Conflicts=</varname> and a + <varname>Before=</varname> dependency on <filename>umount.target</filename> so that they are deactivated at + shutdown as well as a <varname>Before=swap.target</varname> dependency.</para></listitem> + </itemizedlist> + </refsect1> + + <refsect1> <title><filename>fstab</filename></title> <para>Swap units may either be configured via unit files, or via diff --git a/man/systemd.target.xml b/man/systemd.target.xml index dbe7ff014b..281f5d4d6c 100644 --- a/man/systemd.target.xml +++ b/man/systemd.target.xml @@ -80,22 +80,29 @@ </refsect1> <refsect1> - <title>Automatic Dependencies</title> - - <para>Unless <varname>DefaultDependencies=</varname> is set to - <option>no</option> in either of related units or an explicit ordering - dependency is already defined, target units will implicitly complement all - configured dependencies of type <varname>Wants=</varname> or - <varname>Requires=</varname> with dependencies of type - <varname>After=</varname>. Note that <varname>Wants=</varname> or - <varname>Requires=</varname> must be defined in the target unit itself — if - you for example define <varname>Wants=</varname>some.target in - some.service, the implicit ordering will not be added.</para> - - <para>All target units automatically gain <varname>Conflicts=</varname> - dependency against shutdown.target unless <varname>DefaultDependencies=</varname> - is set to <option>no</option>.</para> + <title>Implicit Dependencies</title> + <para>There are no implicit dependencies for target units.</para> + </refsect1> + + <refsect1> + <title>Default Dependencies</title> + + <para>The following dependencies are added unless + <varname>DefaultDependencies=no</varname> is set:</para> + + <itemizedlist> + <listitem><para>Target units will automatically complement all + configured dependencies of type <varname>Wants=</varname> or + <varname>Requires=</varname> with dependencies of type + <varname>After=</varname>. Note that <varname>Wants=</varname> or + <varname>Requires=</varname> must be defined in the target unit itself — if + you for example define <varname>Wants=</varname>some.target in + some.service, the automatic ordering will not be added.</para></listitem> + + <listitem><para>Target units automatically gain <varname>Conflicts=</varname> + dependency against <filename>shutdown.target</filename>.</para></listitem> + </itemizedlist> </refsect1> <refsect1> diff --git a/man/systemd.time.xml b/man/systemd.time.xml index 659f14328e..a4f6a785d5 100644 --- a/man/systemd.time.xml +++ b/man/systemd.time.xml @@ -122,8 +122,12 @@ <title>Parsing Timestamps</title> <para>When parsing, systemd will accept a similar syntax, but expects no timezone specification, unless it is given - as the literal string <literal>UTC</literal> (for the UTC timezone) or is specified to be the locally configured - timezone. Other timezones than the local and UTC are not supported. The weekday specification is optional, but when + as the literal string <literal>UTC</literal> (for the UTC timezone), or is specified to be the locally configured + timezone, or the timezone name in the IANA timezone database format. The complete list of timezones + supported on your system can be obtained using the <literal>timedatectl list-timezones</literal> + (see <citerefentry><refentrytitle>timedatectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>). + Using IANA format is recommended over local timezone names, as less prone to errors (eg: with local timezone it's possible to + specify daylight saving time in winter, while it's incorrect). The weekday specification is optional, but when the weekday is specified, it must either be in the abbreviated (<literal>Wed</literal>) or non-abbreviated (<literal>Wednesday</literal>) English language form (case does not matter), and is not subject to the locale choice of the user. Either the date, or the time part may be omitted, in which case the current date or 00:00:00, @@ -159,22 +163,23 @@ (assuming the current time was 2012-11-23 18:15:22 and the timezone was UTC+8, for example TZ=Asia/Shanghai):</para> - <programlisting>Fri 2012-11-23 11:12:13 → Fri 2012-11-23 11:12:13 - 2012-11-23 11:12:13 → Fri 2012-11-23 11:12:13 -2012-11-23 11:12:13 UTC → Fri 2012-11-23 19:12:13 - 2012-11-23 → Fri 2012-11-23 00:00:00 - 12-11-23 → Fri 2012-11-23 00:00:00 - 11:12:13 → Fri 2012-11-23 11:12:13 - 11:12 → Fri 2012-11-23 11:12:00 - now → Fri 2012-11-23 18:15:22 - today → Fri 2012-11-23 00:00:00 - today UTC → Fri 2012-11-23 16:00:00 - yesterday → Fri 2012-11-22 00:00:00 - tomorrow → Fri 2012-11-24 00:00:00 - +3h30min → Fri 2012-11-23 21:45:22 - -5s → Fri 2012-11-23 18:15:17 - 11min ago → Fri 2012-11-23 18:04:22 - @1395716396 → Tue 2014-03-25 03:59:56</programlisting> + <programlisting> Fri 2012-11-23 11:12:13 → Fri 2012-11-23 11:12:13 + 2012-11-23 11:12:13 → Fri 2012-11-23 11:12:13 + 2012-11-23 11:12:13 UTC → Fri 2012-11-23 19:12:13 + 2012-11-23 → Fri 2012-11-23 00:00:00 + 12-11-23 → Fri 2012-11-23 00:00:00 + 11:12:13 → Fri 2012-11-23 11:12:13 + 11:12 → Fri 2012-11-23 11:12:00 + now → Fri 2012-11-23 18:15:22 + today → Fri 2012-11-23 00:00:00 + today UTC → Fri 2012-11-23 16:00:00 + yesterday → Fri 2012-11-22 00:00:00 + tomorrow → Fri 2012-11-24 00:00:00 +tomorrow Pacific/Auckland → Thu 2012-11-23 19:00:00 + +3h30min → Fri 2012-11-23 21:45:22 + -5s → Fri 2012-11-23 18:15:17 + 11min ago → Fri 2012-11-23 18:04:22 + @1395716396 → Tue 2014-03-25 03:59:56</programlisting> <para>Note that timestamps displayed by remote systems with a non-matching timezone are usually not parsable locally, as the timezone component is not understood (unless it happens to be <literal>UTC</literal>).</para> @@ -238,9 +243,9 @@ second component is not specified, <literal>:00</literal> is assumed.</para> - <para>A timezone specification is not expected, unless it is given as the literal string <literal>UTC</literal>, or - the local timezone, similar to the supported syntax of timestamps (see above). Non-local timezones except for UTC - are not supported.</para> + <para>Timezone can be specified as the literal string <literal>UTC</literal>, or + the local timezone, similar to the supported syntax of timestamps (see above), or the timezone + in the IANA timezone database format (also see above).</para> <para>The following special expressions may be used as shorthands for longer normalized forms:</para> @@ -286,6 +291,7 @@ Wed..Sat,Tue 12-10-15 1:2:3 → Tue..Sat 2012-10-15 01:02:03 daily UTC → *-*-* 00:00:00 UTC monthly → *-*-01 00:00:00 weekly → Mon *-*-* 00:00:00 + weekly Pacific/Auckland → Mon *-*-* 00:00:00 Pacific/Auckland yearly → *-01-01 00:00:00 annually → *-01-01 00:00:00 *:2/3 → *-*-* *:02/3:00</programlisting> diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml index 26a47a1e5a..b8f921f3af 100644 --- a/man/systemd.timer.xml +++ b/man/systemd.timer.xml @@ -82,20 +82,34 @@ </refsect1> <refsect1> - <title>Automatic Dependencies</title> - - <para>Timer units automatically gain a <varname>Before=</varname> - dependency on the service they are supposed to activate.</para> - - <para>Unless <varname>DefaultDependencies=</varname> in the <literal>[Unit]</literal> section is set to - <option>false</option>, all timer units will implicitly have dependencies of type <varname>Requires=</varname> and - <varname>After=</varname> on <filename>sysinit.target</filename>, a dependency of type <varname>Before=</varname> - on <filename>timers.target</filename>, as well as <varname>Conflicts=</varname> and <varname>Before=</varname> on - <filename>shutdown.target</filename> to ensure that they are stopped cleanly prior to system shutdown. Timer units - with at least one <varname>OnCalendar=</varname> directive will have an additional <varname>After=</varname> - dependency on <filename>time-sync.target</filename> to avoid being started before the system clock has been - correctly set. Only timer units involved with early boot or late system shutdown should disable the - <varname>DefaultDependencies=</varname> option.</para> + <title>Implicit Dependencies</title> + + <para>The following dependencies are implicitly added:</para> + + <itemizedlist> + <listitem><para>Timer units automatically gain a <varname>Before=</varname> + dependency on the service they are supposed to activate.</para></listitem> + </itemizedlist> + </refsect1> + + <refsect1> + <title>Default Dependencies</title> + + <para>The following dependencies are added unless <varname>DefaultDependencies=no</varname> is set:</para> + + <itemizedlist> + <listitem><para>Timer units will automatically have dependencies of type <varname>Requires=</varname> and + <varname>After=</varname> on <filename>sysinit.target</filename>, a dependency of type <varname>Before=</varname> + on <filename>timers.target</filename>, as well as <varname>Conflicts=</varname> and <varname>Before=</varname> on + <filename>shutdown.target</filename> to ensure that they are stopped cleanly prior to system shutdown. Only timer + units involved with early boot or late system shutdown should disable the + <varname>DefaultDependencies=</varname> option.</para></listitem> + + <listitem><para>Timer units + with at least one <varname>OnCalendar=</varname> directive will have an additional <varname>After=</varname> + dependency on <filename>time-sync.target</filename> to avoid being started before the system clock has been + correctly set.</para></listitem> + </itemizedlist> </refsect1> <refsect1> diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index d1819aa0ed..64ad4c2b60 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -209,6 +209,12 @@ <!-- Note that we do not document .include here, as we consider it mostly obsolete, and want people to use .d/ drop-ins instead. --> + <para>Note that while systemd offers a flexible dependency system + between units it is recommended to use this functionality only + sparingly and instead rely on techniques such as bus-based or + socket-based activation which make dependencies implicit, + resulting in a both simpler and more flexible system.</para> + <para>Some unit names reflect paths existing in the file system namespace. Example: a device unit <filename>dev-sda.device</filename> refers to a device with the @@ -262,28 +268,39 @@ </refsect1> <refsect1> - <title>Automatic Dependencies</title> - - <para>Note that while systemd offers a flexible dependency system - between units it is recommended to use this functionality only - sparingly and instead rely on techniques such as bus-based or - socket-based activation which make dependencies implicit, - resulting in a both simpler and more flexible system.</para> + <title>Implicit Dependencies</title> + + <para>A number of unit dependencies are implicitly established, + depending on unit type and unit configuration. These implicit + dependencies can make unit configuration file cleaner. For the + implicit dependencies in each unit type, please refer to + section "Implicit Dependencies" in respective man pages.</para> + + <para>For example, service units with <varname>Type=dbus</varname> + automatically acquire dependencies of type <varname>Requires=</varname> + and <varname>After=</varname> on <filename>dbus.socket</filename>. See + <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> + for details.</para> + </refsect1> - <para>A number of unit dependencies are automatically established, - depending on unit configuration. On top of that, for units with - <varname>DefaultDependencies=yes</varname> (the default) a couple - of additional dependencies are added. The precise effect of - <varname>DefaultDependencies=yes</varname> depends on the unit - type (see below).</para> - - <para>If <varname>DefaultDependencies=yes</varname> is set, units - that are referenced by other units of type - <filename>.target</filename> via a <varname>Wants=</varname> or - <varname>Requires=</varname> dependency might automatically gain - an <varname>Before=</varname> dependency too. See + <refsect1> + <title>Default Dependencies</title> + + <para>Default dependencies are similar to implicit dependencies, + but can be turned on and off by setting + <varname>DefaultDependencies=</varname> to <varname>yes</varname> + (the default) and <varname>no</varname>, while implicit dependencies + are always in effect. See section "Default Dependencies" in respective + man pages for the effect of enabling + <varname>DefaultDependencies=</varname> in each unit types.</para> + + <para>For example, target units will complement all configured + dependencies of type type <varname>Wants=</varname> or + <varname>Requires=</varname> with dependencies of type + <varname>After=</varname>. See <citerefentry><refentrytitle>systemd.target</refentrytitle><manvolnum>5</manvolnum></citerefentry> - for details.</para> + for details. Note that this behavior can be turned off by setting + <varname>DefaultDependencies=no</varname>.</para> </refsect1> <refsect1> @@ -1120,7 +1137,7 @@ <term><varname>Alias=</varname></term> <listitem><para>A space-separated list of additional names this unit shall be installed under. The names listed - here must have the same suffix (i.e. type) as the unit file name. This option may be specified more than once, + here must have the same suffix (i.e. type) as the unit filename. This option may be specified more than once, in which case all listed names are used. At installation time, <command>systemctl enable</command> will create symlinks from these names to the unit filename. Note that not all unit types support such alias names, and this setting is not supported for them. Specifically, mount, slice, swap, and automount units do not support diff --git a/man/sysusers.d.xml b/man/sysusers.d.xml index 2b87385d9c..fbe97544d7 100644 --- a/man/sysusers.d.xml +++ b/man/sysusers.d.xml @@ -63,7 +63,7 @@ </refsect1> <refsect1> - <title>Configuration Format</title> + <title>Configuration Directories and Precedence</title> <para>Each configuration file shall be named in the style of <filename><replaceable>package</replaceable>.conf</filename> or @@ -71,15 +71,42 @@ The second variant should be used when it is desirable to make it easy to override just this part of configuration.</para> + <para>Files in <filename>/etc/sysusers.d</filename> override files + with the same name in <filename>/usr/lib/sysusers.d</filename> and + <filename>/run/sysusers.d</filename>. Files in + <filename>/run/sysusers.d</filename> override files with the same + name in <filename>/usr/lib/sysusers.d</filename>. Packages should + install their configuration files in + <filename>/usr/lib/sysusers.d</filename>. Files in + <filename>/etc/sysusers.d</filename> are reserved for the local + administrator, who may use this logic to override the + configuration files installed by vendor packages. All + configuration files are sorted by their filename in lexicographic + order, regardless of which of the directories they reside in. If + multiple files specify the same path, the entry in the file with + the lexicographically earliest name will be applied. All later + entries for the same user and group names will be logged as warnings. + </para> + + <para>If the administrator wants to disable a configuration file + supplied by the vendor, the recommended way is to place a symlink + to <filename>/dev/null</filename> in + <filename>/etc/sysusers.d/</filename> bearing the same filename. + </para> + </refsect1> + + <refsect1> + <title>Configuration File Format</title> + <para>The file format is one line per user or group containing name, ID, GECOS field description and home directory:</para> - <programlisting># Type Name ID GECOS -u httpd 440 "HTTP User" -u authd /usr/bin/authd "Authorization user" -g input - - -m authd input -u root 0 "Superuser" /root</programlisting> + <programlisting>#Type Name ID GECOS Home directory +u httpd 440 "HTTP User" +u authd /usr/bin/authd "Authorization user" +g input - - +m authd input +u root 0 "Superuser" /root</programlisting> <para>Empty lines and lines beginning with the <literal>#</literal> character are ignored, and may be used for commenting.</para> @@ -198,11 +225,8 @@ u root 0 "Superuser" /root</programlisting> should otherwise be left unset, or be set to <literal>-</literal>.</para> </refsect2> - </refsect1> - <xi:include href="standard-conf.xml" xpointer="confd" /> - <refsect1> <title>Idempotence</title> diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml index 29f8553196..68ae43eb90 100644 --- a/man/tmpfiles.d.xml +++ b/man/tmpfiles.d.xml @@ -76,7 +76,7 @@ </refsect1> <refsect1> - <title>Configuration Format</title> + <title>Configuration Directories and Precedence</title> <para>Each configuration file shall be named in the style of <filename><replaceable>package</replaceable>.conf</filename> or @@ -112,6 +112,10 @@ to <filename>/dev/null</filename> in <filename>/etc/tmpfiles.d/</filename> bearing the same filename. </para> + </refsect1> + + <refsect1> + <title>Configuration File Format</title> <para>The configuration format is one line per path containing type, path, mode, ownership, age, and argument fields:</para> @@ -678,11 +682,11 @@ d /var/tmp/abrt 0755 abrt abrt - r! /var/cache/dnf/*/*/download_lock.pid r! /var/cache/dnf/*/*/metadata_lock.pid r! /var/lib/dnf/rpmdb_lock.pid -e /var/chache/dnf/ - - - 30d +e /var/cache/dnf/ - - - 30d </programlisting> <para>The lock files will be removed during boot. Any files and directories in - <filename>/var/chache/dnf/</filename> will be removed after they have not been + <filename>/var/cache/dnf/</filename> will be removed after they have not been accessed in 30 days.</para> </example> diff --git a/man/udevadm.xml b/man/udevadm.xml index 8d4fe31ec1..7ace4f9826 100644 --- a/man/udevadm.xml +++ b/man/udevadm.xml @@ -308,9 +308,11 @@ <term><option>-y</option></term> <term><option>--sysname-match=<replaceable>PATH</replaceable></option></term> <listitem> - <para>Trigger events for devices with a matching sys - device path. This option can be specified multiple times - and supports shell style pattern matching.</para> + <para>Trigger events for devices for which the last component + (i.e. the filename) of the <filename>/sys</filename> path matches + the specified <replaceable>PATH</replaceable>. This option can be + specified multiple times and also supports shell style pattern + matching.</para> </listitem> </varlistentry> <varlistentry> diff --git a/meson.build b/meson.build index 5fb90ddea3..3e85442a6f 100644 --- a/meson.build +++ b/meson.build @@ -374,6 +374,7 @@ conf.set('SIZEOF_RLIM_T', cc.sizeof('rlim_t', prefix : '#include <sys/resource.h decl_headers = ''' #include <uchar.h> #include <linux/ethtool.h> +#include <linux/fib_rules.h> ''' # FIXME: key_serial_t is only defined in keyutils.h, this is bound to fail @@ -381,6 +382,7 @@ foreach decl : ['char16_t', 'char32_t', 'key_serial_t', 'struct ethtool_link_settings', + 'struct fib_rule_uid_range', ] # We get -1 if the size cannot be determined @@ -409,6 +411,7 @@ foreach decl : [['IFLA_INET6_ADDR_GEN_MODE', 'linux/if_link.h'], ['IFLA_BR_VLAN_DEFAULT_PVID', 'linux/if_link.h'], ['NDA_IFINDEX', 'linux/neighbour.h'], ['IFA_FLAGS', 'linux/if_addr.h'], + ['FRA_UID_RANGE', 'linux/fib_rules.h'], ['LO_FLAGS_PARTSCAN', 'linux/loop.h'], ] prefix = decl.length() > 2 ? decl[2] : '' @@ -656,7 +659,7 @@ if not libcap.found() endif libmount = dependency('mount', - version : '>= 2.27') + version : '>= 2.30') want_seccomp = get_option('seccomp') if want_seccomp != 'false' diff --git a/rules/60-persistent-storage.rules b/rules/60-persistent-storage.rules index d2745f65f4..72fec5c884 100644 --- a/rules/60-persistent-storage.rules +++ b/rules/60-persistent-storage.rules @@ -57,6 +57,9 @@ 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" +# UBI-MTD +SUBSYSTEM=="ubi", KERNEL=="ubi*_*", ATTRS{mtd_num}=="*", SYMLINK+="ubi_mtd%s{mtd_num}_%s{name}" + # Memstick KERNEL=="msblk[0-9]|mspblk[0-9]", SUBSYSTEMS=="memstick", ATTRS{name}=="?*", ATTRS{serial}=="?*", \ ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}" diff --git a/rules/64-btrfs.rules.in b/rules/64-btrfs.rules.in index 5b86b9b8e9..c3a0972f2e 100644 --- a/rules/64-btrfs.rules.in +++ b/rules/64-btrfs.rules.in @@ -11,6 +11,6 @@ IMPORT{builtin}="btrfs ready $devnode" ENV{ID_BTRFS_READY}=="0", ENV{SYSTEMD_READY}="0" # reconsider pending devices in case when multidevice volume awaits -ENV{ID_BTRFS_READY}=="1", RUN+="@rootlibexecdir@/udevadm trigger -s block -p ID_BTRFS_READY=0" +ENV{ID_BTRFS_READY}=="1", RUN+="@rootbindir@/udevadm trigger -s block -p ID_BTRFS_READY=0" LABEL="btrfs_end" diff --git a/rules/80-drivers.rules b/rules/80-drivers.rules index 8551f47a4b..16fa5d8e32 100644 --- a/rules/80-drivers.rules +++ b/rules/80-drivers.rules @@ -1,6 +1,6 @@ # do not edit this file, it will be overwritten on update -ACTION=="remove", GOTO="drivers_end" +ACTION!="add", GOTO="drivers_end" ENV{MODALIAS}=="?*", RUN{builtin}+="kmod load $env{MODALIAS}" SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="SD", RUN{builtin}+="kmod load tifm_sd" diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c index 0ce0276d92..f34c24c53a 100644 --- a/src/analyze/analyze-verify.c +++ b/src/analyze/analyze-verify.c @@ -242,7 +242,7 @@ static int verify_unit(Unit *u, bool check_man) { return r; } -int verify_units(char **filenames, UnitFileScope scope, bool check_man) { +int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators) { _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; _cleanup_free_ char *var = NULL; Manager *m = NULL; @@ -253,6 +253,8 @@ int verify_units(char **filenames, UnitFileScope scope, bool check_man) { Unit *units[strv_length(filenames)]; int i, count = 0; + const uint8_t flags = MANAGER_TEST_RUN_ENV_GENERATORS | + run_generators * MANAGER_TEST_RUN_GENERATORS; if (strv_isempty(filenames)) return 0; @@ -264,7 +266,7 @@ int verify_units(char **filenames, UnitFileScope scope, bool check_man) { assert_se(set_unit_path(var) >= 0); - r = manager_new(scope, true, &m); + r = manager_new(scope, flags, &m); if (r < 0) return log_error_errno(r, "Failed to initialize manager: %m"); diff --git a/src/analyze/analyze-verify.h b/src/analyze/analyze-verify.h index d8204dc69c..d5466ecdf9 100644 --- a/src/analyze/analyze-verify.h +++ b/src/analyze/analyze-verify.h @@ -23,4 +23,8 @@ #include "path-lookup.h" -int verify_units(char **filenames, UnitFileScope scope, bool check_man); +int verify_units( + char **filenames, + UnitFileScope scope, + bool check_man, + bool run_generators); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index a6a36a3d2e..0e0fb08922 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -79,6 +79,7 @@ static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; static char *arg_host = NULL; static bool arg_user = false; static bool arg_man = true; +static bool arg_generators = false; struct boot_times { usec_t firmware_time; @@ -1413,6 +1414,7 @@ static void help(void) { " --fuzz=SECONDS Also print also services which finished SECONDS\n" " earlier than the latest in the branch\n" " --man[=BOOL] Do [not] check for existence of man pages\n\n" + " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n\n" "Commands:\n" " time Print time spent in the kernel\n" " blame Print list of running units ordered by time to init\n" @@ -1445,6 +1447,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_FUZZ, ARG_NO_PAGER, ARG_MAN, + ARG_GENERATORS, }; static const struct option options[] = { @@ -1459,6 +1462,7 @@ static int parse_argv(int argc, char *argv[]) { { "fuzz", required_argument, NULL, ARG_FUZZ }, { "no-pager", no_argument, NULL, ARG_NO_PAGER }, { "man", optional_argument, NULL, ARG_MAN }, + { "generators", optional_argument, NULL, ARG_GENERATORS }, { "host", required_argument, NULL, 'H' }, { "machine", required_argument, NULL, 'M' }, {} @@ -1541,6 +1545,20 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_GENERATORS: + if (optarg) { + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --generators= argument."); + return -EINVAL; + } + + arg_generators = !!r; + } else + arg_generators = true; + + break; + case '?': return -EINVAL; @@ -1566,7 +1584,8 @@ int main(int argc, char *argv[]) { if (streq_ptr(argv[optind], "verify")) r = verify_units(argv+optind+1, arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM, - arg_man); + arg_man, + arg_generators); else { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index b0712a4bad..be7328af40 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -25,6 +25,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/mman.h> #include <time.h> #include "alloc-util.h" @@ -33,6 +34,7 @@ #include "macro.h" #include "parse-util.h" #include "string-util.h" +#include "time-util.h" #define BITS_WEEKDAYS 127 #define MIN_YEAR 1970 @@ -59,6 +61,7 @@ void calendar_spec_free(CalendarSpec *c) { free_chain(c->hour); free_chain(c->minute); free_chain(c->microsecond); + free(c->timezone); free(c); } @@ -351,7 +354,10 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) { if (c->utc) fputs_unlocked(" UTC", f); - else if (IN_SET(c->dst, 0, 1)) { + else if (c->timezone != NULL) { + fputc_unlocked(' ', f); + fputs_unlocked(c->timezone, f); + } else if (IN_SET(c->dst, 0, 1)) { /* If daylight saving is explicitly on or off, let's show the used timezone. */ @@ -888,6 +894,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { if (!c) return -ENOMEM; c->dst = -1; + c->timezone = NULL; utc = endswith_no_case(p, " UTC"); if (utc) { @@ -919,6 +926,19 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { if (IN_SET(j, 0, 1)) { p = strndupa(p, e - p - 1); c->dst = j; + } else { + const char *last_space; + + last_space = strrchr(p, ' '); + if (last_space != NULL && timezone_is_valid(last_space + 1)) { + c->timezone = strdup(last_space + 1); + if (!c->timezone) { + r = -ENOMEM; + goto fail; + } + + p = strndupa(p, last_space - p); + } } } @@ -1293,7 +1313,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { } } -int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) { +static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, usec_t *next) { struct tm tm; time_t t; int r; @@ -1321,3 +1341,58 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) *next = (usec_t) t * USEC_PER_SEC + tm_usec; return 0; } + +typedef struct SpecNextResult { + usec_t next; + int return_value; +} SpecNextResult; + +int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) { + pid_t pid; + SpecNextResult *shared; + SpecNextResult tmp; + int r; + + if (isempty(spec->timezone)) + return calendar_spec_next_usec_impl(spec, usec, next); + + shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); + if (shared == MAP_FAILED) + return negative_errno(); + + pid = fork(); + + if (pid == -1) { + int fork_errno = errno; + (void) munmap(shared, sizeof *shared); + return -fork_errno; + } + + if (pid == 0) { + if (setenv("TZ", spec->timezone, 1) != 0) { + shared->return_value = negative_errno(); + _exit(EXIT_FAILURE); + } + + tzset(); + + shared->return_value = calendar_spec_next_usec_impl(spec, usec, &shared->next); + + _exit(EXIT_SUCCESS); + } + + r = wait_for_terminate(pid, NULL); + if (r < 0) { + (void) munmap(shared, sizeof *shared); + return r; + } + + tmp = *shared; + if (munmap(shared, sizeof *shared) != 0) + return negative_errno(); + + if (tmp.return_value == 0) + *next = tmp.next; + + return tmp.return_value; +} diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h index 3d8798de0b..8888251705 100644 --- a/src/basic/calendarspec.h +++ b/src/basic/calendarspec.h @@ -40,6 +40,7 @@ typedef struct CalendarSpec { bool end_of_month; bool utc; int dst; + char *timezone; CalendarComponent *year; CalendarComponent *month; diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index b8f0f5d03d..907d1350fe 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -32,11 +32,12 @@ #include "macro.h" #include "missing.h" #include "path-util.h" +#include "stat-util.h" #include "string-util.h" #include "strv.h" #include "util.h" -static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) { +static int files_add(Hashmap *h, const char *suffix, const char *root, unsigned flags, const char *path) { _cleanup_closedir_ DIR *dir = NULL; const char *dirpath; struct dirent *de; @@ -56,8 +57,40 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char FOREACH_DIRENT(de, dir, return -errno) { char *p; - if (!dirent_is_file_with_suffix(de, suffix)) + if (!dirent_is_file_with_suffix(de, suffix)) { + log_debug("Ignoring %s/%s, because it's not a regular file with suffix %s.", dirpath, de->d_name, strna(suffix)); continue; + } + + if (flags & CONF_FILES_EXECUTABLE) { + struct stat st; + + /* As requested: check if the file is marked exectuable. Note that we don't check access(X_OK) + * here, as we care about whether the file is marked executable at all, and not whether it is + * executable for us, because if such errors are stuff we should log about. */ + + if (fstatat(dirfd(dir), de->d_name, &st, 0) < 0) { + log_debug_errno(errno, "Failed to stat %s/%s, ignoring: %m", dirpath, de->d_name); + continue; + } + + if (!null_or_empty(&st)) { + /* A mask is a symlink to /dev/null or an empty file. It does not even + * have to be executable. Other entries must be regular executable files + * or symlinks to them. */ + if (S_ISREG(st.st_mode)) { + if ((st.st_mode & 0111) == 0) { /* not executable */ + log_debug("Ignoring %s/%s, as it is not marked executable.", + dirpath, de->d_name); + continue; + } + } else { + log_debug("Ignoring %s/%s, as it is neither a regular file nor a mask.", + dirpath, de->d_name); + continue; + } + } + } p = strjoin(dirpath, "/", de->d_name); if (!p) @@ -87,7 +120,7 @@ static int base_cmp(const void *a, const void *b) { return strcmp(basename(s1), basename(s2)); } -static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, char **dirs) { +static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, unsigned flags, char **dirs) { _cleanup_hashmap_free_ Hashmap *fh = NULL; char **files, **p; int r; @@ -103,7 +136,7 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const return -ENOMEM; STRV_FOREACH(p, dirs) { - r = files_add(fh, root, *p, suffix); + r = files_add(fh, suffix, root, flags, *p); if (r == -ENOMEM) return r; if (r < 0) @@ -120,7 +153,7 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const return 0; } -int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs) { +int conf_files_list_strv(char ***strv, const char *suffix, const char *root, unsigned flags, const char* const* dirs) { _cleanup_strv_free_ char **copy = NULL; assert(strv); @@ -129,10 +162,10 @@ int conf_files_list_strv(char ***strv, const char *suffix, const char *root, con if (!copy) return -ENOMEM; - return conf_files_list_strv_internal(strv, suffix, root, copy); + return conf_files_list_strv_internal(strv, suffix, root, flags, copy); } -int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...) { +int conf_files_list(char ***strv, const char *suffix, const char *root, unsigned flags, const char *dir, ...) { _cleanup_strv_free_ char **dirs = NULL; va_list ap; @@ -145,10 +178,10 @@ int conf_files_list(char ***strv, const char *suffix, const char *root, const ch if (!dirs) return -ENOMEM; - return conf_files_list_strv_internal(strv, suffix, root, dirs); + return conf_files_list_strv_internal(strv, suffix, root, flags, dirs); } -int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *d) { +int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, unsigned flags, const char *d) { _cleanup_strv_free_ char **dirs = NULL; assert(strv); @@ -157,5 +190,5 @@ int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, c if (!dirs) return -ENOMEM; - return conf_files_list_strv_internal(strv, suffix, root, dirs); + return conf_files_list_strv_internal(strv, suffix, root, flags, dirs); } diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h index e00e0e81fb..20ecf6e5f1 100644 --- a/src/basic/conf-files.h +++ b/src/basic/conf-files.h @@ -20,6 +20,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -int conf_files_list(char ***ret, const char *suffix, const char *root, const char *dir, ...); -int conf_files_list_strv(char ***ret, const char *suffix, const char *root, const char* const* dirs); -int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, const char *dirs); +enum { + CONF_FILES_EXECUTABLE = 1, +}; + +int conf_files_list(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dir, ...); +int conf_files_list_strv(char ***ret, const char *suffix, const char *root, unsigned flags, const char* const* dirs); +int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dirs); diff --git a/src/basic/env-util.c b/src/basic/env-util.c index 56e7b6fd8c..d72940acb3 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -769,6 +769,16 @@ int getenv_bool(const char *p) { return parse_boolean(e); } +int getenv_bool_secure(const char *p) { + const char *e; + + e = secure_getenv(p); + if (!e) + return -ENXIO; + + return parse_boolean(e); +} + int serialize_environment(FILE *f, char **environment) { char **e; diff --git a/src/basic/env-util.h b/src/basic/env-util.h index e88fa6aac0..d5da8cd67b 100644 --- a/src/basic/env-util.h +++ b/src/basic/env-util.h @@ -61,6 +61,7 @@ char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure char *strv_env_get(char **x, const char *n) _pure_; int getenv_bool(const char *p); +int getenv_bool_secure(const char *p); int serialize_environment(FILE *f, char **environment); int deserialize_environment(char ***environment, const char *line); diff --git a/src/basic/exec-util.c b/src/basic/exec-util.c index aced9e8e3d..ade8511466 100644 --- a/src/basic/exec-util.c +++ b/src/basic/exec-util.c @@ -111,7 +111,7 @@ static int do_execute( assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - r = conf_files_list_strv(&paths, NULL, NULL, (const char* const*) directories); + r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE, (const char* const*) directories); if (r < 0) return r; diff --git a/src/basic/exit-status.c b/src/basic/exit-status.c index fba012601d..7cfebbae72 100644 --- a/src/basic/exit-status.c +++ b/src/basic/exit-status.c @@ -140,9 +140,6 @@ const char* exit_status_to_string(int status, ExitStatusLevel level) { case EXIT_RUNTIME_DIRECTORY: return "RUNTIME_DIRECTORY"; - case EXIT_MAKE_STARTER: - return "MAKE_STARTER"; - case EXIT_CHOWN: return "CHOWN"; diff --git a/src/basic/exit-status.h b/src/basic/exit-status.h index e2c76f84f8..195b3bc486 100644 --- a/src/basic/exit-status.h +++ b/src/basic/exit-status.h @@ -79,7 +79,7 @@ enum { EXIT_APPARMOR_PROFILE, EXIT_ADDRESS_FAMILIES, EXIT_RUNTIME_DIRECTORY, - EXIT_MAKE_STARTER, + _EXIT_RESERVED2, /* used to be used by kdbus, don't reuse */ EXIT_CHOWN, EXIT_SMACK_PROCESS_LABEL, EXIT_KEYRING, diff --git a/src/basic/missing.h b/src/basic/missing.h index 7830a4f415..eb6c42e132 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -606,14 +606,13 @@ struct input_mask { #else #define __O_TMPFILE 020000000 #endif +#endif /* a horrid kludge trying to make sure that this will fail on old kernels */ #ifndef O_TMPFILE #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) #endif -#endif - #if !HAVE_DECL_LO_FLAGS_PARTSCAN #define LO_FLAGS_PARTSCAN 8 #endif @@ -921,6 +920,33 @@ struct input_mask { #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) #endif +#if !HAVE_DECL_FRA_UID_RANGE +#define FRA_UNSPEC 0 +#define FRA_DST 1 +#define FRA_SRC 2 +#define FRA_IIFNAME 3 +#define FRA_GOTO 4 +#define FRA_UNUSED2 5 +#define FRA_PRIORITY 6 +#define FRA_UNUSED3 7 +#define FRA_UNUSED4 8 +#define FRA_UNUSED5 9 +#define FRA_FWMARK 10 +#define FRA_FLOW 11 +#define FRA_TUN_ID 12 +#define FRA_SUPPRESS_IFGROUP 13 +#define FRA_SUPPRESS_PREFIXLEN 14 +#define FRA_TABLE 15 +#define FRA_FWMASK 16 +#define FRA_OIFNAME 17 +#define FRA_PAD 18 +#define FRA_L3MDEV 19 +#define FRA_UID_RANGE 20 +#define __FRA_MAX 12 + +#define FRA_MAX (__FRA_MAX - 1) +#endif + #if !HAVE_DECL_IFLA_BRPORT_PROXYARP #define IFLA_BRPORT_PROXYARP 10 #endif @@ -1102,6 +1128,10 @@ typedef int32_t key_serial_t; #define KEYCTL_DESCRIBE 6 #endif +#ifndef KEYCTL_LINK +#define KEYCTL_LINK 8 +#endif + #ifndef KEYCTL_READ #define KEYCTL_READ 11 #endif @@ -1217,6 +1247,15 @@ struct ethtool_link_settings { #endif +#ifndef HAVE_STRUCT_FIB_RULE_UID_RANGE + +struct fib_rule_uid_range { + __u32 start; + __u32 end; +}; + +#endif + #endif #ifndef SOL_ALG diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 9d0154117b..b77d83c99f 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -1023,7 +1023,7 @@ pid_t getpid_cached(void) { * objects were used across fork()s. With this caching the old behaviour is somewhat restored. * * https://bugzilla.redhat.com/show_bug.cgi?id=1443976 - * https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=1d2bc2eae969543b89850e35e532f3144122d80a + * https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e */ current_value = __sync_val_compare_and_swap(&cached_pid, CACHED_PID_UNSET, CACHED_PID_BUSY); diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 68ba86f6a5..00979bfe59 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -21,6 +21,7 @@ #include <limits.h> #include <stdlib.h> #include <string.h> +#include <sys/mman.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/timerfd.h> @@ -596,7 +597,7 @@ int timestamp_deserialize(const char *value, usec_t *timestamp) { return r; } -int parse_timestamp(const char *t, usec_t *usec) { +static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { static const struct { const char *name; const int nr; @@ -617,7 +618,7 @@ int parse_timestamp(const char *t, usec_t *usec) { { "Sat", 6 }, }; - const char *k, *utc, *tzn = NULL; + const char *k, *utc = NULL, *tzn = NULL; struct tm tm, copy; time_t x; usec_t x_usec, plus = 0, minus = 0, ret; @@ -645,84 +646,86 @@ int parse_timestamp(const char *t, usec_t *usec) { assert(t); assert(usec); - if (t[0] == '@') + if (t[0] == '@' && !with_tz) return parse_sec(t + 1, usec); ret = now(CLOCK_REALTIME); - if (streq(t, "now")) - goto finish; + if (!with_tz) { + if (streq(t, "now")) + goto finish; - else if (t[0] == '+') { - r = parse_sec(t+1, &plus); - if (r < 0) - return r; + else if (t[0] == '+') { + r = parse_sec(t+1, &plus); + if (r < 0) + return r; - goto finish; + goto finish; - } else if (t[0] == '-') { - r = parse_sec(t+1, &minus); - if (r < 0) - return r; + } else if (t[0] == '-') { + r = parse_sec(t+1, &minus); + if (r < 0) + return r; - goto finish; + goto finish; - } else if ((k = endswith(t, " ago"))) { - t = strndupa(t, k - t); + } else if ((k = endswith(t, " ago"))) { + t = strndupa(t, k - t); - r = parse_sec(t, &minus); - if (r < 0) - return r; + r = parse_sec(t, &minus); + if (r < 0) + return r; - goto finish; + goto finish; - } else if ((k = endswith(t, " left"))) { - t = strndupa(t, k - t); + } else if ((k = endswith(t, " left"))) { + t = strndupa(t, k - t); - r = parse_sec(t, &plus); - if (r < 0) - return r; + r = parse_sec(t, &plus); + if (r < 0) + return r; - goto finish; - } + goto finish; + } - /* See if the timestamp is suffixed with UTC */ - utc = endswith_no_case(t, " UTC"); - if (utc) - t = strndupa(t, utc - t); - else { - const char *e = NULL; - int j; + /* See if the timestamp is suffixed with UTC */ + utc = endswith_no_case(t, " UTC"); + if (utc) + t = strndupa(t, utc - t); + else { + const char *e = NULL; + int j; - tzset(); + tzset(); - /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only - * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because - * there are no nice APIs available to cover this. By accepting the local time zone strings, we make - * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't - * support arbitrary timezone specifications. */ + /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only + * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because + * there are no nice APIs available to cover this. By accepting the local time zone strings, we make + * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't + * support arbitrary timezone specifications. */ - for (j = 0; j <= 1; j++) { + for (j = 0; j <= 1; j++) { - if (isempty(tzname[j])) - continue; + if (isempty(tzname[j])) + continue; - e = endswith_no_case(t, tzname[j]); - if (!e) - continue; - if (e == t) - continue; - if (e[-1] != ' ') - continue; + e = endswith_no_case(t, tzname[j]); + if (!e) + continue; + if (e == t) + continue; + if (e[-1] != ' ') + continue; - break; - } + break; + } - if (IN_SET(j, 0, 1)) { - /* Found one of the two timezones specified. */ - t = strndupa(t, e - t - 1); - dst = j; - tzn = tzname[j]; + if (IN_SET(j, 0, 1)) { + /* Found one of the two timezones specified. */ + t = strndupa(t, e - t - 1); + dst = j; + tzn = tzname[j]; + } } } @@ -733,7 +736,7 @@ int parse_timestamp(const char *t, usec_t *usec) { return -EINVAL; tm.tm_isdst = dst; - if (tzn) + if (!with_tz && tzn) tm.tm_zone = tzn; if (streq(t, "today")) { @@ -874,6 +877,67 @@ finish: return 0; } +typedef struct ParseTimestampResult { + usec_t usec; + int return_value; +} ParseTimestampResult; + +int parse_timestamp(const char *t, usec_t *usec) { + char *last_space, *timezone = NULL; + ParseTimestampResult *shared, tmp; + int r; + pid_t pid; + + last_space = strrchr(t, ' '); + if (last_space != NULL && timezone_is_valid(last_space + 1)) + timezone = last_space + 1; + + if (timezone == NULL || endswith_no_case(t, " UTC")) + return parse_timestamp_impl(t, usec, false); + + t = strndupa(t, last_space - t); + + shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); + if (shared == MAP_FAILED) + return negative_errno(); + + pid = fork(); + + if (pid == -1) { + int fork_errno = errno; + (void) munmap(shared, sizeof *shared); + return -fork_errno; + } + + if (pid == 0) { + if (setenv("TZ", timezone, 1) != 0) { + shared->return_value = negative_errno(); + _exit(EXIT_FAILURE); + } + + tzset(); + + shared->return_value = parse_timestamp_impl(t, &shared->usec, true); + + _exit(EXIT_SUCCESS); + } + + r = wait_for_terminate(pid, NULL); + if (r < 0) { + (void) munmap(shared, sizeof *shared); + return r; + } + + tmp = *shared; + if (munmap(shared, sizeof *shared) != 0) + return negative_errno(); + + if (tmp.return_value == 0) + *usec = tmp.usec; + + return tmp.return_value; +} + static char* extract_multiplier(char *p, usec_t *multiplier) { static const struct { const char *suffix; diff --git a/src/basic/util.c b/src/basic/util.c index 2f45128ed4..daaee99284 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -34,6 +34,7 @@ #include <unistd.h> #include "alloc-util.h" +#include "btrfs-util.h" #include "build.h" #include "cgroup-util.h" #include "def.h" @@ -719,3 +720,133 @@ int version(void) { SYSTEMD_FEATURES); return 0; } + +int get_block_device(const char *path, dev_t *dev) { + struct stat st; + struct statfs sfs; + + assert(path); + assert(dev); + + /* Get's the block device directly backing a file system. If + * the block device is encrypted, returns the device mapper + * block device. */ + + if (lstat(path, &st)) + return -errno; + + if (major(st.st_dev) != 0) { + *dev = st.st_dev; + return 1; + } + + if (statfs(path, &sfs) < 0) + return -errno; + + if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) + return btrfs_get_block_device(path, dev); + + return 0; +} + +int get_block_device_harder(const char *path, dev_t *dev) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_free_ char *p = NULL, *t = NULL; + struct dirent *de, *found = NULL; + const char *q; + unsigned maj, min; + dev_t dt; + int r; + + assert(path); + assert(dev); + + /* Gets the backing block device for a file system, and + * handles LUKS encrypted file systems, looking for its + * immediate parent, if there is one. */ + + r = get_block_device(path, &dt); + if (r <= 0) + return r; + + if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0) + return -ENOMEM; + + d = opendir(p); + if (!d) { + if (errno == ENOENT) + goto fallback; + + return -errno; + } + + FOREACH_DIRENT_ALL(de, d, return -errno) { + + if (dot_or_dot_dot(de->d_name)) + continue; + + if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) + continue; + + if (found) { + _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL; + + /* We found a device backed by multiple other devices. We don't really support automatic + * discovery on such setups, with the exception of dm-verity partitions. In this case there are + * two backing devices: the data partition and the hash partition. We are fine with such + * setups, however, only if both partitions are on the same physical device. Hence, let's + * verify this. */ + + u = strjoin(p, "/", de->d_name, "/../dev"); + if (!u) + return -ENOMEM; + + v = strjoin(p, "/", found->d_name, "/../dev"); + if (!v) + return -ENOMEM; + + r = read_one_line_file(u, &a); + if (r < 0) { + log_debug_errno(r, "Failed to read %s: %m", u); + goto fallback; + } + + r = read_one_line_file(v, &b); + if (r < 0) { + log_debug_errno(r, "Failed to read %s: %m", v); + goto fallback; + } + + /* Check if the parent device is the same. If not, then the two backing devices are on + * different physical devices, and we don't support that. */ + if (!streq(a, b)) + goto fallback; + } + + found = de; + } + + if (!found) + goto fallback; + + q = strjoina(p, "/", found->d_name, "/dev"); + + r = read_one_line_file(q, &t); + if (r == -ENOENT) + goto fallback; + if (r < 0) + return r; + + if (sscanf(t, "%u:%u", &maj, &min) != 2) + return -EINVAL; + + if (maj == 0) + goto fallback; + + *dev = makedev(maj, min); + return 1; + +fallback: + *dev = dt; + return 1; +} diff --git a/src/basic/util.h b/src/basic/util.h index c7da6c39bf..b31dfd1c92 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -192,3 +192,6 @@ uint64_t system_tasks_max_scale(uint64_t v, uint64_t max); int update_reboot_parameter_and_warn(const char *param); int version(void); + +int get_block_device(const char *path, dev_t *dev); +int get_block_device_harder(const char *path, dev_t *dev); diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c index eeef04fb1c..17af233ef8 100644 --- a/src/binfmt/binfmt.c +++ b/src/binfmt/binfmt.c @@ -182,7 +182,7 @@ int main(int argc, char *argv[]) { _cleanup_strv_free_ char **files = NULL; char **f; - r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); + r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs); if (r < 0) { log_error_errno(r, "Failed to enumerate binfmt.d files: %m"); goto finish; diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 94a4f9ae29..c806d6b7cb 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -1813,7 +1813,7 @@ int manager_setup_cgroup(Manager *m) { log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER_LEGACY ". File system hierarchy is at %s.", path); } - if (!m->test_run) { + if (!m->test_run_flags) { const char *scope_path; /* 3. Install agent */ diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index eb0af24450..a8cdae5bf2 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -53,12 +53,11 @@ #include "utf8.h" BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutput); - static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode); - static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_preserve_mode, exec_preserve_mode, ExecPreserveMode); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode); static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_home, protect_home, ProtectHome); static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_system, protect_system, ProtectSystem); @@ -758,6 +757,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("EnvironmentFiles", "a(sb)", property_get_environment_files, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PassEnvironment", "as", NULL, offsetof(ExecContext, pass_environment), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("UnsetEnvironment", "as", NULL, offsetof(ExecContext, unset_environment), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("UMask", "u", bus_property_get_mode, offsetof(ExecContext, umask), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("LimitCPU", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("LimitCPUSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST), @@ -872,6 +872,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("BindPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("BindReadOnlyPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MountAPIVFS", "b", bus_property_get_bool, offsetof(ExecContext, mount_apivfs), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KeyringMode", "s", property_get_exec_keyring_mode, offsetof(ExecContext, keyring_mode), SD_BUS_VTABLE_PROPERTY_CONST), /* Obsolete/redundant properties: */ SD_BUS_PROPERTY("Capabilities", "s", property_get_empty_string, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), @@ -1816,13 +1817,13 @@ int bus_exec_context_set_transient_property( if (r < 0) return r; - if (!strv_env_is_valid(l)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment block."); - r = unit_full_printf_strv(u, l, &q); if (r < 0) return r; + if (!strv_env_is_valid(q)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment block."); + if (mode != UNIT_CHECK) { if (strv_length(q) == 0) { c->environment = strv_free(c->environment); @@ -1839,7 +1840,7 @@ int bus_exec_context_set_transient_property( c->environment = e; /* We write just the new settings out to file, with unresolved specifiers */ - joined = strv_join_quoted(q); + joined = strv_join_quoted(l); if (!joined) return -ENOMEM; @@ -1849,6 +1850,47 @@ int bus_exec_context_set_transient_property( return 1; + } else if (streq(name, "UnsetEnvironment")) { + + _cleanup_strv_free_ char **l = NULL, **q = NULL; + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + r = unit_full_printf_strv(u, l, &q); + if (r < 0) + return r; + + if (!strv_env_name_or_assignment_is_valid(q)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UnsetEnvironment= list."); + + if (mode != UNIT_CHECK) { + if (strv_length(q) == 0) { + c->unset_environment = strv_free(c->unset_environment); + unit_write_drop_in_private_format(u, mode, name, "UnsetEnvironment="); + } else { + _cleanup_free_ char *joined = NULL; + char **e; + + e = strv_env_merge(2, c->unset_environment, q); + if (!e) + return -ENOMEM; + + strv_free(c->unset_environment); + c->unset_environment = e; + + /* We write just the new settings out to file, with unresolved specifiers */ + joined = strv_join_quoted(l); + if (!joined) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "UnsetEnvironment=%s", joined); + } + } + + return 1; + } else if (streq(name, "TimerSlackNSec")) { nsec_t n; @@ -1958,14 +2000,18 @@ int bus_exec_context_set_transient_property( } else if (streq(name, "PassEnvironment")) { - _cleanup_strv_free_ char **l = NULL; + _cleanup_strv_free_ char **l = NULL, **q = NULL; r = sd_bus_message_read_strv(message, &l); if (r < 0) return r; - if (!strv_env_name_is_valid(l)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment block."); + r = unit_full_printf_strv(u, l, &q); + if (r < 0) + return r; + + if (!strv_env_name_is_valid(q)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment= block."); if (mode != UNIT_CHECK) { if (strv_isempty(l)) { @@ -1974,11 +2020,12 @@ int bus_exec_context_set_transient_property( } else { _cleanup_free_ char *joined = NULL; - r = strv_extend_strv(&c->pass_environment, l, true); + r = strv_extend_strv(&c->pass_environment, q, true); if (r < 0) return r; - joined = strv_join_quoted(c->pass_environment); + /* We write just the new settings out to file, with unresolved specifiers. */ + joined = strv_join_quoted(l); if (!joined) return -ENOMEM; @@ -2092,6 +2139,27 @@ int bus_exec_context_set_transient_property( return 1; + } else if (streq(name, "KeyringMode")) { + + const char *s; + ExecKeyringMode m; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + m = exec_keyring_mode_from_string(s); + if (m < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid keyring mode"); + + if (mode != UNIT_CHECK) { + c->keyring_mode = m; + + unit_write_drop_in_private_format(u, mode, name, "KeyringMode=%s", exec_keyring_mode_to_string(m)); + } + + return 1; + } else if (streq(name, "RuntimeDirectoryPreserve")) { const char *s; ExecPreserveMode m; diff --git a/src/core/execute.c b/src/core/execute.c index 21c0149e22..28c6b2fc38 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -595,9 +595,13 @@ static int setup_output( /* If we connected this fd to the journal via a stream, patch the device/inode into the passed * parameters, but only then. This is useful so that we can set $JOURNAL_STREAM that permits - * services to detect whether they are connected to the journal or not. */ + * services to detect whether they are connected to the journal or not. + * + * If both stdout and stderr are connected to a stream then let's make sure to store the data + * about STDERR as that's usually the best way to do logging. */ - if (fstat(fileno, &st) >= 0) { + if (fstat(fileno, &st) >= 0 && + (*journal_stream_ino == 0 || fileno == STDERR_FILENO)) { *journal_stream_dev = st.st_dev; *journal_stream_ino = st.st_ino; } @@ -1673,8 +1677,10 @@ static int build_pass_environment(const ExecContext *c, char ***ret) { x = strjoin(*i, "=", v); if (!x) return -ENOMEM; + if (!GREEDY_REALLOC(pass_env, n_bufsize, n_env + 2)) return -ENOMEM; + pass_env[n_env++] = x; pass_env[n_env] = NULL; x = NULL; @@ -2157,10 +2163,17 @@ static int apply_working_directory( return 0; } -static int setup_keyring(Unit *u, const ExecParameters *p, uid_t uid, gid_t gid) { +static int setup_keyring( + Unit *u, + const ExecContext *context, + const ExecParameters *p, + uid_t uid, gid_t gid) { + key_serial_t keyring; + int r; assert(u); + assert(context); assert(p); /* Let's set up a new per-service "session" kernel keyring for each system service. This has the benefit that @@ -2173,6 +2186,9 @@ static int setup_keyring(Unit *u, const ExecParameters *p, uid_t uid, gid_t gid) if (!(p->flags & EXEC_NEW_KEYRING)) return 0; + if (context->keyring_mode == EXEC_KEYRING_INHERIT) + return 0; + keyring = keyctl(KEYCTL_JOIN_SESSION_KEYRING, 0, 0, 0, 0); if (keyring == -1) { if (errno == ENOSYS) @@ -2207,6 +2223,55 @@ static int setup_keyring(Unit *u, const ExecParameters *p, uid_t uid, gid_t gid) if (keyctl(KEYCTL_CHOWN, keyring, uid, gid, 0) < 0) return log_error_errno(errno, "Failed to change ownership of session keyring: %m"); + /* When requested link the user keyring into the session keyring. */ + if (context->keyring_mode == EXEC_KEYRING_SHARED) { + uid_t saved_uid; + gid_t saved_gid; + + /* Acquiring a reference to the user keyring is nasty. We briefly change identity in order to get things + * set up properly by the kernel. If we don't do that then we can't create it atomically, and that + * sucks for parallel execution. This mimics what pam_keyinit does, too.*/ + + saved_uid = getuid(); + saved_gid = getgid(); + + if (gid_is_valid(gid) && gid != saved_gid) { + if (setregid(gid, -1) < 0) + return log_error_errno(errno, "Failed to change GID for user keyring: %m"); + } + + if (uid_is_valid(uid) && uid != saved_uid) { + if (setreuid(uid, -1) < 0) { + (void) setregid(saved_gid, -1); + return log_error_errno(errno, "Failed to change UID for user keyring: %m"); + } + } + + if (keyctl(KEYCTL_LINK, + KEY_SPEC_USER_KEYRING, + KEY_SPEC_SESSION_KEYRING, 0, 0) < 0) { + + r = -errno; + + (void) setreuid(saved_uid, -1); + (void) setregid(saved_gid, -1); + + return log_error_errno(r, "Failed to link user keyring into session keyring: %m"); + } + + if (uid_is_valid(uid) && uid != saved_uid) { + if (setreuid(saved_uid, -1) < 0) { + (void) setregid(saved_gid, -1); + return log_error_errno(errno, "Failed to change UID back for user keyring: %m"); + } + } + + if (gid_is_valid(gid) && gid != saved_gid) { + if (setregid(saved_gid, -1) < 0) + return log_error_errno(errno, "Failed to change GID back for user keyring: %m"); + } + } + return 0; } @@ -2393,7 +2458,7 @@ static int exec_child( r = reset_signal_mask(); if (r < 0) { *exit_status = EXIT_SIGNAL_MASK; - *error_message = strdup("Failed to reset signal mask"); + *error_message = strdup("Failed to set process signal mask"); /* If strdup fails, here and below, we will just print the generic error message. */ return r; } @@ -2411,13 +2476,14 @@ static int exec_child( r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, fds, n_fds); if (r < 0) { *exit_status = EXIT_FDS; - *error_message = strdup("Failed to close remaining fds"); + *error_message = strdup("Failed to close unwanted file descriptors"); return r; } if (!context->same_pgrp) if (setsid() < 0) { *exit_status = EXIT_SETSID; + *error_message = strdup("Failed to create new process session"); return -errno; } @@ -2429,7 +2495,7 @@ static int exec_child( cmdline = exec_command_line(argv); if (!cmdline) { - *exit_status = EXIT_CONFIRM; + *exit_status = EXIT_MEMORY; return -ENOMEM; } @@ -2440,7 +2506,7 @@ static int exec_child( return 0; } *exit_status = EXIT_CONFIRM; - *error_message = strdup("Execution cancelled"); + *error_message = strdup("Execution cancelled by the user"); return -ECANCELED; } } @@ -2526,21 +2592,21 @@ static int exec_child( r = setup_input(context, params, socket_fd, named_iofds); if (r < 0) { *exit_status = EXIT_STDIN; - *error_message = strdup("Failed to set up stdin"); + *error_message = strdup("Failed to set up standard input"); return r; } r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, named_iofds, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino); if (r < 0) { *exit_status = EXIT_STDOUT; - *error_message = strdup("Failed to set up stdout"); + *error_message = strdup("Failed to set up standard output"); return r; } r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, named_iofds, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino); if (r < 0) { *exit_status = EXIT_STDERR; - *error_message = strdup("Failed to set up stderr"); + *error_message = strdup("Failed to set up standard error output"); return r; } @@ -2569,7 +2635,7 @@ static int exec_child( log_close(); } else if (r < 0) { *exit_status = EXIT_OOM_ADJUST; - *error_message = strdup("Failed to write /proc/self/oom_score_adj"); + *error_message = strdup("Failed to adjust OOM setting"); return -errno; } } @@ -2577,6 +2643,7 @@ static int exec_child( if (context->nice_set) if (setpriority(PRIO_PROCESS, 0, context->nice) < 0) { *exit_status = EXIT_NICE; + *error_message = strdup("Failed to set up process scheduling priority (nice level)"); return -errno; } @@ -2592,6 +2659,7 @@ static int exec_child( ¶m); if (r < 0) { *exit_status = EXIT_SETSCHEDULER; + *error_message = strdup("Failed to set up CPU scheduling"); return -errno; } } @@ -2599,18 +2667,21 @@ static int exec_child( if (context->cpuset) if (sched_setaffinity(0, CPU_ALLOC_SIZE(context->cpuset_ncpus), context->cpuset) < 0) { *exit_status = EXIT_CPUAFFINITY; + *error_message = strdup("Failed to set up CPU affinity"); return -errno; } if (context->ioprio_set) if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) { *exit_status = EXIT_IOPRIO; + *error_message = strdup("Failed to set up IO scheduling priority"); return -errno; } if (context->timer_slack_nsec != NSEC_INFINITY) if (prctl(PR_SET_TIMERSLACK, context->timer_slack_nsec) < 0) { *exit_status = EXIT_TIMERSLACK; + *error_message = strdup("Failed to set up timer slack"); return -errno; } @@ -2618,6 +2689,7 @@ static int exec_child( r = safe_personality(context->personality); if (r < 0) { *exit_status = EXIT_PERSONALITY; + *error_message = strdup("Failed to set up execution domain (personality)"); return r; } } @@ -2634,6 +2706,7 @@ static int exec_child( r = chown_terminal(STDIN_FILENO, uid); if (r < 0) { *exit_status = EXIT_STDIN; + *error_message = strdup("Failed to change ownership of terminal"); return r; } } @@ -2645,6 +2718,7 @@ static int exec_child( r = cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, 0644, uid, gid); if (r < 0) { *exit_status = EXIT_CGROUP; + *error_message = strdup("Failed to adjust control group access"); return r; } @@ -2652,14 +2726,17 @@ static int exec_child( r = cg_set_group_access(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, 0755, uid, gid); if (r < 0) { *exit_status = EXIT_CGROUP; + *error_message = strdup("Failed to adjust control group access"); return r; } } for (dt = 0; dt < _EXEC_DIRECTORY_MAX; dt++) { r = setup_exec_directory(context, params, uid, gid, dt, exit_status); - if (r < 0) + if (r < 0) { + *error_message = strdup("Failed to set up special execution directory"); return r; + } } r = build_environment( @@ -2699,9 +2776,10 @@ static int exec_child( (void) umask(context->umask); - r = setup_keyring(unit, params, uid, gid); + r = setup_keyring(unit, context, params, uid, gid); if (r < 0) { *exit_status = EXIT_KEYRING; + *error_message = strdup("Failed to set up kernel keyring"); return r; } @@ -2738,6 +2816,7 @@ static int exec_child( r = setup_pam(context->pam_name, username, uid, gid, context->tty_path, &accum_env, fds, n_fds); if (r < 0) { *exit_status = EXIT_PAM; + *error_message = strdup("Failed to set up PAM session"); return r; } } @@ -2747,6 +2826,7 @@ static int exec_child( r = setup_netns(runtime->netns_storage_socket); if (r < 0) { *exit_status = EXIT_NETWORK; + *error_message = strdup("Failed to set up network namespacing"); return r; } } @@ -2756,19 +2836,23 @@ static int exec_child( r = apply_mount_namespace(unit, command, context, params, runtime); if (r < 0) { *exit_status = EXIT_NAMESPACE; + *error_message = strdup("Failed to set up mount namespacing"); return r; } } /* Apply just after mount namespace setup */ r = apply_working_directory(context, params, home, needs_mount_namespace, exit_status); - if (r < 0) + if (r < 0) { + *error_message = strdup("Changing to the requested working directory failed"); return r; + } /* Drop groups as early as possbile */ if (needs_setuid) { r = enforce_groups(context, gid, supplementary_gids, ngids); if (r < 0) { + *error_message = strdup("Changing group credentials failed"); *exit_status = EXIT_GROUP; return r; } @@ -2779,6 +2863,7 @@ static int exec_child( if (use_selinux && params->selinux_context_net && socket_fd >= 0) { r = mac_selinux_get_child_mls_label(socket_fd, command->path, context->selinux_context, &mac_selinux_context_net); if (r < 0) { + *error_message = strdup("Failed to determine SELinux context"); *exit_status = EXIT_SELINUX_CONTEXT; return r; } @@ -2788,6 +2873,7 @@ static int exec_child( if (context->private_users) { r = setup_private_users(uid, gid); if (r < 0) { + *error_message = strdup("Failed to set up user namespacing"); *exit_status = EXIT_USER; return r; } @@ -2803,6 +2889,7 @@ static int exec_child( if (r >= 0) r = flags_fds(fds, n_storage_fds, n_socket_fds, context->non_blocking); if (r < 0) { + *error_message = strdup("Failed to adjust passed file descriptors"); *exit_status = EXIT_FDS; return r; } @@ -2819,6 +2906,7 @@ static int exec_child( r = setrlimit_closest(i, context->rlimit[i]); if (r < 0) { + *error_message = strdup("Failed to adjust resource limits"); *exit_status = EXIT_LIMITS; return r; } @@ -2827,6 +2915,7 @@ static int exec_child( /* Set the RTPRIO resource limit to 0, but only if nothing else was explicitly requested. */ if (context->restrict_realtime && !context->rlimit[RLIMIT_RTPRIO]) { if (setrlimit(RLIMIT_RTPRIO, &RLIMIT_MAKE_CONST(0)) < 0) { + *error_message = strdup("Failed to adjust RLIMIT_RTPRIO resource limit"); *exit_status = EXIT_LIMITS; return -errno; } @@ -2909,7 +2998,7 @@ static int exec_child( r = setexeccon(exec_context); if (r < 0) { *exit_status = EXIT_SELINUX_CONTEXT; - (void) asprintf(error_message, "Failed to set SELinux context to %s", exec_context); + (void) asprintf(error_message, "Failed to change SELinux context to %s", exec_context); return r; } } @@ -2945,7 +3034,7 @@ static int exec_child( if (prctl(PR_GET_SECUREBITS) != secure_bits) if (prctl(PR_SET_SECUREBITS, secure_bits) < 0) { *exit_status = EXIT_SECUREBITS; - *error_message = strdup("Failed to set secure bits"); + *error_message = strdup("Failed to set process secure bits"); return -errno; } @@ -3025,12 +3114,25 @@ static int exec_child( r = apply_syscall_filter(unit, context, needs_ambient_hack); if (r < 0) { *exit_status = EXIT_SECCOMP; - *error_message = strdup("Failed to apply syscall filters"); + *error_message = strdup("Failed to apply system call filters"); return r; } #endif } + if (!strv_isempty(context->unset_environment)) { + char **ee = NULL; + + ee = strv_env_delete(accum_env, 1, context->unset_environment); + if (!ee) { + *exit_status = EXIT_MEMORY; + return -ENOMEM; + } + + strv_free(accum_env); + accum_env = ee; + } + final_argv = replace_env_argv(argv, accum_env); if (!final_argv) { *exit_status = EXIT_MEMORY; @@ -3222,6 +3324,7 @@ void exec_context_done(ExecContext *c) { c->environment = strv_free(c->environment); c->environment_files = strv_free(c->environment_files); c->pass_environment = strv_free(c->pass_environment); + c->unset_environment = strv_free(c->unset_environment); for (l = 0; l < ELEMENTSOF(c->rlimit); l++) c->rlimit[l] = mfree(c->rlimit[l]); @@ -3551,7 +3654,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { "%sMountAPIVFS: %s\n" "%sIgnoreSIGPIPE: %s\n" "%sMemoryDenyWriteExecute: %s\n" - "%sRestrictRealtime: %s\n", + "%sRestrictRealtime: %s\n" + "%sKeyringMode: %s\n", prefix, c->umask, prefix, c->working_directory ? c->working_directory : "/", prefix, c->root_directory ? c->root_directory : "/", @@ -3568,7 +3672,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { prefix, yes_no(c->mount_apivfs), prefix, yes_no(c->ignore_sigpipe), prefix, yes_no(c->memory_deny_write_execute), - prefix, yes_no(c->restrict_realtime)); + prefix, yes_no(c->restrict_realtime), + prefix, exec_keyring_mode_to_string(c->keyring_mode)); if (c->root_image) fprintf(f, "%sRootImage: %s\n", prefix, c->root_image); @@ -3582,6 +3687,9 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { STRV_FOREACH(e, c->pass_environment) fprintf(f, "%sPassEnvironment: %s\n", prefix, *e); + STRV_FOREACH(e, c->unset_environment) + fprintf(f, "%sUnsetEnvironment: %s\n", prefix, *e); + fprintf(f, "%sRuntimeDirectoryPreserve: %s\n", prefix, exec_preserve_mode_to_string(c->runtime_directory_preserve_mode)); for (dt = 0; dt < _EXEC_DIRECTORY_MAX; dt++) { @@ -4345,3 +4453,11 @@ static const char* const exec_directory_type_table[_EXEC_DIRECTORY_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType); + +static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = { + [EXEC_KEYRING_INHERIT] = "inherit", + [EXEC_KEYRING_PRIVATE] = "private", + [EXEC_KEYRING_SHARED] = "shared", +}; + +DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode); diff --git a/src/core/execute.h b/src/core/execute.h index 8a7ce8449b..133eca5846 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -80,6 +80,14 @@ typedef enum ExecPreserveMode { _EXEC_PRESERVE_MODE_INVALID = -1 } ExecPreserveMode; +typedef enum ExecKeyringMode { + EXEC_KEYRING_INHERIT, + EXEC_KEYRING_PRIVATE, + EXEC_KEYRING_SHARED, + _EXEC_KEYRING_MODE_MAX, + _EXEC_KEYRING_MODE_INVALID = -1, +} ExecKeyringMode; + struct ExecStatus { dual_timestamp start_timestamp; dual_timestamp exit_timestamp; @@ -133,6 +141,7 @@ struct ExecContext { char **environment; char **environment_files; char **pass_environment; + char **unset_environment; struct rlimit *rlimit[_RLIMIT_MAX]; char *working_directory, *root_directory, *root_image; @@ -188,6 +197,8 @@ struct ExecContext { bool smack_process_label_ignore; char *smack_process_label; + ExecKeyringMode keyring_mode; + char **read_write_paths, **read_only_paths, **inaccessible_paths; unsigned long mount_flags; BindMount *bind_mounts; @@ -367,5 +378,8 @@ ExecUtmpMode exec_utmp_mode_from_string(const char *s) _pure_; const char* exec_preserve_mode_to_string(ExecPreserveMode i) _const_; ExecPreserveMode exec_preserve_mode_from_string(const char *s) _pure_; +const char* exec_keyring_mode_to_string(ExecKeyringMode i) _const_; +ExecKeyringMode exec_keyring_mode_from_string(const char *s) _pure_; + const char* exec_directory_type_to_string(ExecDirectoryType i) _const_; ExecDirectoryType exec_directory_type_from_string(const char *s) _pure_; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 94f3d657f6..f7d5f24861 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -35,6 +35,7 @@ $1.UMask, config_parse_mode, 0, $1.Environment, config_parse_environ, 0, offsetof($1, exec_context.environment) $1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files) $1.PassEnvironment, config_parse_pass_environ, 0, offsetof($1, exec_context.pass_environment) +$1.UnsetEnvironment, config_parse_unset_environ, 0, offsetof($1, exec_context.unset_environment) $1.DynamicUser, config_parse_bool, true, offsetof($1, exec_context.dynamic_user) $1.StandardInput, config_parse_exec_input, 0, offsetof($1, exec_context) $1.StandardOutput, config_parse_exec_output, 0, offsetof($1, exec_context) @@ -53,6 +54,7 @@ $1.CapabilityBoundingSet, config_parse_capability_set, 0, $1.AmbientCapabilities, config_parse_capability_set, 0, offsetof($1, exec_context.capability_ambient_set) $1.TimerSlackNSec, config_parse_nsec, 0, offsetof($1, exec_context.timer_slack_nsec) $1.NoNewPrivileges, config_parse_no_new_privileges, 0, offsetof($1, exec_context) +$1.KeyringMode, config_parse_exec_keyring_mode, 0, offsetof($1, exec_context.keyring_mode) m4_ifdef(`HAVE_SECCOMP', `$1.SystemCallFilter, config_parse_syscall_filter, 0, offsetof($1, exec_context) $1.SystemCallArchitectures, config_parse_syscall_archs, 0, offsetof($1, exec_context.syscall_archs) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 7fa1bafaef..d319934ee2 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -283,6 +283,23 @@ int config_parse_unit_path_strv_printf( assert(rvalue); assert(u); + if (isempty(rvalue)) { + char **empty; + + /* Empty assignment resets the list. As a special rule + * we actually fill in a real empty array here rather + * than NULL, since some code wants to know if + * something was set at all... */ + empty = new0(char*, 1); + if (!empty) + return log_oom(); + + strv_free(*x); + *x = empty; + + return 0; + } + for (p = rvalue;;) { _cleanup_free_ char *word = NULL, *k = NULL; @@ -2108,16 +2125,17 @@ int config_parse_unit_env_file(const char *unit, return 0; } -int config_parse_environ(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_environ( + 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) { Unit *u = userdata; char ***env = data; @@ -2153,7 +2171,7 @@ int config_parse_environ(const char *unit, r = unit_full_printf(u, word, &k); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to resolve specifiers, ignoring: %s", k); + "Failed to resolve specifiers, ignoring: %s", word); continue; } } else { @@ -2170,25 +2188,28 @@ int config_parse_environ(const char *unit, r = strv_env_replace(env, k); if (r < 0) return log_oom(); + k = NULL; } } -int config_parse_pass_environ(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_pass_environ( + 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) { const char *whole_rvalue = rvalue; - char*** passenv = data; _cleanup_strv_free_ char **n = NULL; size_t nlen = 0, nbufsize = 0; + char*** passenv = data; + Unit *u = userdata; int r; assert(filename); @@ -2203,7 +2224,7 @@ int config_parse_pass_environ(const char *unit, } for (;;) { - _cleanup_free_ char *word = NULL; + _cleanup_free_ char *word = NULL, *k = NULL; r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES); if (r == 0) @@ -2216,17 +2237,30 @@ int config_parse_pass_environ(const char *unit, break; } - if (!env_name_is_valid(word)) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Invalid environment name for %s, ignoring: %s", lvalue, word); + if (u) { + r = unit_full_printf(u, word, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to resolve specifiers, ignoring: %s", word); + continue; + } + } else { + k = word; + word = NULL; + } + + if (!env_name_is_valid(k)) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Invalid environment name for %s, ignoring: %s", lvalue, k); continue; } if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) return log_oom(); - n[nlen++] = word; + + n[nlen++] = k; n[nlen] = NULL; - word = NULL; + k = NULL; } if (n) { @@ -2238,6 +2272,85 @@ int config_parse_pass_environ(const char *unit, return 0; } +int config_parse_unset_environ( + 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) { + + _cleanup_strv_free_ char **n = NULL; + const char *whole_rvalue = rvalue; + size_t nlen = 0, nbufsize = 0; + char*** unsetenv = data; + Unit *u = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + *unsetenv = strv_free(*unsetenv); + return 0; + } + + for (;;) { + _cleanup_free_ char *word = NULL, *k = NULL; + + r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue); + break; + } + + if (u) { + r = unit_full_printf(u, word, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to resolve specifiers, ignoring: %s", word); + continue; + } + } else { + k = word; + word = NULL; + } + + if (!env_assignment_is_valid(k) && !env_name_is_valid(k)) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Invalid environment name or assignment %s, ignoring: %s", lvalue, k); + continue; + } + + if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) + return log_oom(); + + n[nlen++] = k; + n[nlen] = NULL; + k = NULL; + } + + if (n) { + r = strv_extend_strv(unsetenv, n, true); + if (r < 0) + return r; + } + + return 0; +} + int config_parse_ip_tos(const char *unit, const char *filename, unsigned line, @@ -4050,6 +4163,8 @@ int config_parse_protect_system( return 0; } +DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode"); + #define FOLLOW_MAX 8 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index ec338ccb9a..50910586ac 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -79,6 +79,7 @@ int config_parse_syscall_archs(const char *unit, const char *filename, unsigned int config_parse_syscall_errno(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_environ(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_pass_environ(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_unset_environ(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_unit_slice(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_cpu_weight(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_cpu_shares(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); @@ -116,6 +117,7 @@ int config_parse_user_group(const char *unit, const char *filename, unsigned lin int config_parse_user_group_strv(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_restrict_namespaces(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_paths(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_exec_keyring_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); /* gperf prototypes */ const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length); diff --git a/src/core/main.c b/src/core/main.c index 11ac9cf7a0..fbf8876a2d 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1679,20 +1679,22 @@ int main(int argc, char *argv[]) { log_close(); /* Remember open file descriptors for later deserialization */ - r = fdset_new_fill(&fds); - if (r < 0) { - log_emergency_errno(r, "Failed to allocate fd set: %m"); - error_message = "Failed to allocate fd set"; - goto finish; - } else - fdset_cloexec(fds, true); + if (arg_action == ACTION_RUN) { + r = fdset_new_fill(&fds); + if (r < 0) { + log_emergency_errno(r, "Failed to allocate fd set: %m"); + error_message = "Failed to allocate fd set"; + goto finish; + } else + fdset_cloexec(fds, true); - if (arg_serialization) - assert_se(fdset_remove(fds, fileno(arg_serialization)) >= 0); + if (arg_serialization) + assert_se(fdset_remove(fds, fileno(arg_serialization)) >= 0); - if (arg_system) - /* Become a session leader if we aren't one yet. */ - setsid(); + if (arg_system) + /* Become a session leader if we aren't one yet. */ + setsid(); + } /* Move out of the way, so that we won't block unmounts */ assert_se(chdir("/") == 0); @@ -1762,58 +1764,62 @@ int main(int argc, char *argv[]) { arg_action == ACTION_TEST ? " test" : "", getuid(), t); } - if (arg_system && !skip_setup) { - if (arg_show_status > 0) - status_welcome(); + if (arg_action == ACTION_RUN) { + if (arg_system && !skip_setup) { + if (arg_show_status > 0) + status_welcome(); - hostname_setup(); - machine_id_setup(NULL, arg_machine_id, NULL); - loopback_setup(); - bump_unix_max_dgram_qlen(); + hostname_setup(); + machine_id_setup(NULL, arg_machine_id, NULL); + loopback_setup(); + bump_unix_max_dgram_qlen(); - test_usr(); - } + test_usr(); + } - if (arg_system && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY) - watchdog_set_timeout(&arg_runtime_watchdog); + if (arg_system && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY) + watchdog_set_timeout(&arg_runtime_watchdog); - if (arg_timer_slack_nsec != NSEC_INFINITY) - if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0) - log_error_errno(errno, "Failed to adjust timer slack: %m"); + if (arg_timer_slack_nsec != NSEC_INFINITY) + if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0) + log_error_errno(errno, "Failed to adjust timer slack: %m"); - if (arg_system && !cap_test_all(arg_capability_bounding_set)) { - r = capability_bounding_set_drop_usermode(arg_capability_bounding_set); - if (r < 0) { - log_emergency_errno(r, "Failed to drop capability bounding set of usermode helpers: %m"); - error_message = "Failed to drop capability bounding set of usermode helpers"; - goto finish; - } - r = capability_bounding_set_drop(arg_capability_bounding_set, true); - if (r < 0) { - log_emergency_errno(r, "Failed to drop capability bounding set: %m"); - error_message = "Failed to drop capability bounding set"; - goto finish; + if (arg_system && !cap_test_all(arg_capability_bounding_set)) { + r = capability_bounding_set_drop_usermode(arg_capability_bounding_set); + if (r < 0) { + log_emergency_errno(r, "Failed to drop capability bounding set of usermode helpers: %m"); + error_message = "Failed to drop capability bounding set of usermode helpers"; + goto finish; + } + r = capability_bounding_set_drop(arg_capability_bounding_set, true); + if (r < 0) { + log_emergency_errno(r, "Failed to drop capability bounding set: %m"); + error_message = "Failed to drop capability bounding set"; + goto finish; + } } - } - if (arg_syscall_archs) { - r = enforce_syscall_archs(arg_syscall_archs); - if (r < 0) { - error_message = "Failed to set syscall architectures"; - goto finish; + if (arg_syscall_archs) { + r = enforce_syscall_archs(arg_syscall_archs); + if (r < 0) { + error_message = "Failed to set syscall architectures"; + goto finish; + } } - } - if (!arg_system) - /* Become reaper of our children */ - if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) - log_warning_errno(errno, "Failed to make us a subreaper: %m"); + if (!arg_system) + /* Become reaper of our children */ + if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) + log_warning_errno(errno, "Failed to make us a subreaper: %m"); - if (arg_system) - /* Bump up RLIMIT_NOFILE for systemd itself */ - (void) bump_rlimit_nofile(&saved_rlimit_nofile); + if (arg_system) + /* Bump up RLIMIT_NOFILE for systemd itself */ + (void) bump_rlimit_nofile(&saved_rlimit_nofile); + } - r = manager_new(arg_system ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, arg_action == ACTION_TEST, &m); + r = manager_new(arg_system ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, + arg_action == ACTION_TEST ? MANAGER_TEST_FULL : 0, + &m); if (r < 0) { log_emergency_errno(r, "Failed to allocate manager object: %m"); error_message = "Failed to allocate manager object"; diff --git a/src/core/manager.c b/src/core/manager.c index 5237b47660..46036aa50c 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -358,7 +358,7 @@ static int manager_setup_time_change(Manager *m) { assert(m); assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX)); - if (m->test_run) + if (m->test_run_flags) return 0; /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever @@ -390,7 +390,7 @@ static int enable_special_signals(Manager *m) { assert(m); - if (m->test_run) + if (m->test_run_flags) return 0; /* Enable that we get SIGINT on control-alt-del. In containers @@ -600,7 +600,7 @@ static int manager_setup_prefix(Manager *m) { return 0; } -int manager_new(UnitFileScope scope, bool test_run, Manager **_m) { +int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) { Manager *m; int r; @@ -650,7 +650,7 @@ int manager_new(UnitFileScope scope, bool test_run, Manager **_m) { m->have_ask_password = -EINVAL; /* we don't know */ m->first_boot = -1; - m->test_run = test_run; + m->test_run_flags = test_run_flags; /* Reboot immediately if the user hits C-A-D more often than 7x per 2s */ RATELIMIT_INIT(m->ctrl_alt_del_ratelimit, 2 * USEC_PER_SEC, 7); @@ -731,7 +731,7 @@ fail: static int manager_setup_notify(Manager *m) { int r; - if (m->test_run) + if (m->test_run_flags) return 0; if (m->notify_fd < 0) { @@ -811,7 +811,7 @@ static int manager_setup_cgroups_agent(Manager *m) { * to it. The system instance hence listens on this special socket, but the user instances listen on the system * bus for these messages. */ - if (m->test_run) + if (m->test_run_flags) return 0; if (!MANAGER_IS_SYSTEM(m)) @@ -925,7 +925,7 @@ static int manager_connect_bus(Manager *m, bool reexecuting) { assert(m); - if (m->test_run) + if (m->test_run_flags) return 0; u = manager_get_unit(m, SPECIAL_DBUS_SERVICE); @@ -1127,8 +1127,7 @@ Manager* manager_free(Manager *m) { if (unit_vtable[c]->shutdown) unit_vtable[c]->shutdown(m); - /* If we reexecute ourselves, we keep the root cgroup - * around */ + /* If we reexecute ourselves, we keep the root cgroup around */ manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE); lookup_paths_flush_generator(&m->lookup_paths); @@ -1311,7 +1310,11 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { assert(m); - r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL); + /* If we are running in test mode, we still want to run the generators, + * but we should not touch the real generator directories. */ + r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, + m->test_run_flags ? LOOKUP_PATHS_TEMPORARY_GENERATED : 0, + NULL); if (r < 0) return r; @@ -1319,12 +1322,11 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { if (r < 0) return r; - /* Make sure the transient directory always exists, so that it remains in the search path */ - if (!m->test_run) { - r = mkdir_p_label(m->lookup_paths.transient, 0755); - if (r < 0) - return r; - } + /* Make sure the transient directory always exists, so that it remains + * in the search path */ + r = mkdir_p_label(m->lookup_paths.transient, 0755); + if (r < 0) + return r; dual_timestamp_get(&m->generators_start_timestamp); r = manager_run_generators(m); @@ -1332,7 +1334,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { if (r < 0) return r; - if (m->first_boot && m->unit_file_scope == UNIT_FILE_SYSTEM) { + if (m->first_boot > 0 && + m->unit_file_scope == UNIT_FILE_SYSTEM && + !m->test_run_flags) { + q = unit_file_preset_all(UNIT_FILE_SYSTEM, 0, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, NULL, 0); if (q < 0) log_full_errno(q == -EEXIST ? LOG_NOTICE : LOG_WARNING, q, "Failed to populate /etc with preset unit settings, ignoring: %m"); @@ -2998,7 +3003,7 @@ static void manager_notify_finished(Manager *m) { char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX]; usec_t firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec; - if (m->test_run) + if (m->test_run_flags) return; if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) { @@ -3143,7 +3148,7 @@ static int manager_run_environment_generators(Manager *m) { const char **paths; void* args[] = {&tmp, &tmp, &m->environment}; - if (m->test_run) + if (m->test_run_flags && !(m->test_run_flags & MANAGER_TEST_RUN_ENV_GENERATORS)) return 0; paths = MANAGER_IS_SYSTEM(m) ? system_env_generator_binary_paths : user_env_generator_binary_paths; @@ -3161,7 +3166,7 @@ static int manager_run_generators(Manager *m) { assert(m); - if (m->test_run) + if (m->test_run_flags && !(m->test_run_flags & MANAGER_TEST_RUN_GENERATORS)) return 0; paths = generator_binary_paths(m->unit_file_scope); diff --git a/src/core/manager.h b/src/core/manager.h index 8d621da4e1..713d2db70c 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -74,6 +74,15 @@ typedef enum StatusType { #include "show-status.h" #include "unit-name.h" +enum { + /* 0 = run normally */ + MANAGER_TEST_RUN_MINIMAL = 1, /* run test w/o generators */ + MANAGER_TEST_RUN_ENV_GENERATORS = 2, /* also run env generators */ + MANAGER_TEST_RUN_GENERATORS = 4, /* also run unit generators */ + MANAGER_TEST_FULL = MANAGER_TEST_RUN_ENV_GENERATORS | MANAGER_TEST_RUN_GENERATORS, +}; +assert_cc((MANAGER_TEST_FULL & UINT8_MAX) == MANAGER_TEST_FULL); + struct Manager { /* Note that the set of units we know of is allowed to be * inconsistent. However the subset of it that is loaded may @@ -238,7 +247,8 @@ struct Manager { bool dispatching_dbus_queue:1; bool taint_usr:1; - bool test_run:1; + + unsigned test_run_flags:8; /* If non-zero, exit with the following value when the systemd * process terminate. Useful for containers: systemd-nspawn could get @@ -326,7 +336,7 @@ struct Manager { #define MANAGER_IS_RELOADING(m) ((m)->n_reloading > 0) -int manager_new(UnitFileScope scope, bool test_run, Manager **m); +int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **m); Manager* manager_free(Manager *m); void manager_enumerate(Manager *m); diff --git a/src/core/timer.c b/src/core/timer.c index 701949fd60..3032a237b1 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -614,9 +614,23 @@ static int timer_start(Unit *u) { if (t->stamp_path) { struct stat st; - if (stat(t->stamp_path, &st) >= 0) - t->last_trigger.realtime = timespec_load(&st.st_atim); - else if (errno == ENOENT) + if (stat(t->stamp_path, &st) >= 0) { + usec_t ft; + + /* Load the file timestamp, but only if it is actually in the past. If it is in the future, + * something is wrong with the system clock. */ + + ft = timespec_load(&st.st_mtim); + if (ft < now(CLOCK_REALTIME)) + t->last_trigger.realtime = ft; + else { + char z[FORMAT_TIMESTAMP_MAX]; + + log_unit_warning(u, "Not using persistent file timestamp %s as it is in the future.", + format_timestamp(z, sizeof(z), ft)); + } + + } else if (errno == ENOENT) /* The timer has never run before, * make sure a stamp file exists. */ diff --git a/src/core/umount.c b/src/core/umount.c index b83f631141..7e9ea20388 100644 --- a/src/core/umount.c +++ b/src/core/umount.c @@ -97,7 +97,7 @@ static int mount_points_list_get(MountPoint **head) { "%ms" /* (11) mount options */ "%*[^\n]", /* some rubbish at the end */ &path, &type, &options); - if (k != 2) { + if (k != 3) { if (k == EOF) break; @@ -517,22 +517,22 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed) { static int dm_points_list_detach(MountPoint **head, bool *changed) { MountPoint *m, *n; - int n_failed = 0, k; - struct stat root_st; + int n_failed = 0, r; + dev_t rootdev; assert(head); - k = lstat("/", &root_st); + r = get_block_device("/", &rootdev); + if (r <= 0) + rootdev = 0; LIST_FOREACH_SAFE(mount_point, m, n, *head) { - int r; - if (k >= 0 && - major(root_st.st_dev) != 0 && - root_st.st_dev == m->devnum) { - n_failed++; - continue; - } + if (major(rootdev) != 0) + if (rootdev == m->devnum) { + n_failed ++; + continue; + } log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum)); r = delete_dm(m->devnum); diff --git a/src/core/unit.c b/src/core/unit.c index 2037de6d33..df89f3d01f 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -162,9 +162,13 @@ static void unit_init(Unit *u) { } ec = unit_get_exec_context(u); - if (ec) + if (ec) { exec_context_init(ec); + ec->keyring_mode = MANAGER_IS_SYSTEM(u->manager) ? + EXEC_KEYRING_PRIVATE : EXEC_KEYRING_INHERIT; + } + kc = unit_get_kill_context(u); if (kc) kill_context_init(kc); @@ -1759,19 +1763,25 @@ int unit_reload(Unit *u) { unit_add_to_dbus_queue(u); + if (!UNIT_VTABLE(u)->reload) { + /* Unit doesn't have a reload function, but we need to propagate the reload anyway */ + unit_notify(u, unit_active_state(u), unit_active_state(u), true); + return 0; + } + return UNIT_VTABLE(u)->reload(u); } bool unit_can_reload(Unit *u) { assert(u); - if (!UNIT_VTABLE(u)->reload) - return false; + if (UNIT_VTABLE(u)->can_reload) + return UNIT_VTABLE(u)->can_reload(u); - if (!UNIT_VTABLE(u)->can_reload) + if (!set_isempty(u->dependencies[UNIT_PROPAGATES_RELOAD_TO])) return true; - return UNIT_VTABLE(u)->can_reload(u); + return UNIT_VTABLE(u)->reload; } static void unit_check_unneeded(Unit *u) { diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c index 11d9892658..f882a4f80e 100644 --- a/src/cryptsetup/cryptsetup-generator.c +++ b/src/cryptsetup/cryptsetup-generator.c @@ -170,6 +170,7 @@ static int create_disk( "Type=oneshot\n" "RemainAfterExit=yes\n" "TimeoutSec=0\n" /* the binary handles timeouts anyway */ + "KeyringMode=shared\n" /* make sure we can share cached keys among instances */ "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n" "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n", name, u, strempty(password), strempty(filtered), diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 08ed7e53ba..e1e1a9675e 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -52,7 +52,9 @@ static bool arg_verify = false; static bool arg_discards = false; static bool arg_tcrypt_hidden = false; static bool arg_tcrypt_system = false; +#ifdef CRYPT_TCRYPT_VERA_MODES static bool arg_tcrypt_veracrypt = false; +#endif static char **arg_tcrypt_keyfiles = NULL; static uint64_t arg_offset = 0; static uint64_t arg_skip = 0; diff --git a/src/environment-d-generator/environment-d-generator.c b/src/environment-d-generator/environment-d-generator.c index 9c72502373..55de68550d 100644 --- a/src/environment-d-generator/environment-d-generator.c +++ b/src/environment-d-generator/environment-d-generator.c @@ -58,7 +58,7 @@ static int load_and_print(void) { if (r < 0) return r; - r = conf_files_list_strv(&files, ".conf", NULL, (const char **) dirs); + r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char **) dirs); if (r < 0) return r; diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index a6c0b33fbb..bf28af0a6f 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -99,6 +99,7 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, bool requir "Type=oneshot\n" "RemainAfterExit=yes\n" "TimeoutSec=0\n" /* the binary handles timeouts anyway */ + "KeyringMode=shared\n" /* make sure we can share cached keys among instances */ "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '' '%s'\n" "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n", d, d, @@ -571,136 +572,6 @@ static int enumerate_partitions(dev_t devnum) { return r; } -static int get_block_device(const char *path, dev_t *dev) { - struct stat st; - struct statfs sfs; - - assert(path); - assert(dev); - - /* Get's the block device directly backing a file system. If - * the block device is encrypted, returns the device mapper - * block device. */ - - if (lstat(path, &st)) - return -errno; - - if (major(st.st_dev) != 0) { - *dev = st.st_dev; - return 1; - } - - if (statfs(path, &sfs) < 0) - return -errno; - - if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) - return btrfs_get_block_device(path, dev); - - return 0; -} - -static int get_block_device_harder(const char *path, dev_t *dev) { - _cleanup_closedir_ DIR *d = NULL; - _cleanup_free_ char *p = NULL, *t = NULL; - struct dirent *de, *found = NULL; - const char *q; - unsigned maj, min; - dev_t dt; - int r; - - assert(path); - assert(dev); - - /* Gets the backing block device for a file system, and - * handles LUKS encrypted file systems, looking for its - * immediate parent, if there is one. */ - - r = get_block_device(path, &dt); - if (r <= 0) - return r; - - if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0) - return -ENOMEM; - - d = opendir(p); - if (!d) { - if (errno == ENOENT) - goto fallback; - - return -errno; - } - - FOREACH_DIRENT_ALL(de, d, return -errno) { - - if (dot_or_dot_dot(de->d_name)) - continue; - - if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) - continue; - - if (found) { - _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL; - - /* We found a device backed by multiple other devices. We don't really support automatic - * discovery on such setups, with the exception of dm-verity partitions. In this case there are - * two backing devices: the data partition and the hash partition. We are fine with such - * setups, however, only if both partitions are on the same physical device. Hence, let's - * verify this. */ - - u = strjoin(p, "/", de->d_name, "/../dev"); - if (!u) - return -ENOMEM; - - v = strjoin(p, "/", found->d_name, "/../dev"); - if (!v) - return -ENOMEM; - - r = read_one_line_file(u, &a); - if (r < 0) { - log_debug_errno(r, "Failed to read %s: %m", u); - goto fallback; - } - - r = read_one_line_file(v, &b); - if (r < 0) { - log_debug_errno(r, "Failed to read %s: %m", v); - goto fallback; - } - - /* Check if the parent device is the same. If not, then the two backing devices are on - * different physical devices, and we don't support that. */ - if (!streq(a, b)) - goto fallback; - } - - found = de; - } - - if (!found) - goto fallback; - - q = strjoina(p, "/", found->d_name, "/dev"); - - r = read_one_line_file(q, &t); - if (r == -ENOENT) - goto fallback; - if (r < 0) - return r; - - if (sscanf(t, "%u:%u", &maj, &min) != 2) - return -EINVAL; - - if (maj == 0) - goto fallback; - - *dev = makedev(maj, min); - return 1; - -fallback: - *dev = dt; - return 1; -} - static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { int r; diff --git a/src/hwdb/hwdb.c b/src/hwdb/hwdb.c index ead4e1770b..c964a28bce 100644 --- a/src/hwdb/hwdb.c +++ b/src/hwdb/hwdb.c @@ -653,7 +653,7 @@ static int hwdb_update(int argc, char *argv[], void *userdata) { trie->nodes_count++; - r = conf_files_list_strv(&files, ".hwdb", arg_root, conf_file_dirs); + r = conf_files_list_strv(&files, ".hwdb", arg_root, 0, conf_file_dirs); if (r < 0) return log_error_errno(r, "Failed to enumerate hwdb files: %m"); diff --git a/src/journal/catalog.c b/src/journal/catalog.c index 886f6efd8b..f42be0adf9 100644 --- a/src/journal/catalog.c +++ b/src/journal/catalog.c @@ -479,7 +479,7 @@ int catalog_update(const char* database, const char* root, const char* const* di goto finish; } - r = conf_files_list_strv(&files, ".catalog", root, dirs); + r = conf_files_list_strv(&files, ".catalog", root, 0, dirs); if (r < 0) { log_error_errno(r, "Failed to get catalog files: %m"); goto finish; diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h index c3f847ec05..5601be844d 100644 --- a/src/libsystemd-network/radv-internal.h +++ b/src/libsystemd-network/radv-internal.h @@ -36,6 +36,7 @@ assert_cc(SD_RADV_DEFAULT_MIN_TIMEOUT_USEC <= SD_RADV_DEFAULT_MAX_TIMEOUT_USEC) #define SD_RADV_MAX_RA_DELAY_TIME_USEC (500*USEC_PER_MSEC) #define SD_RADV_OPT_RDNSS 25 +#define SD_RADV_OPT_DNSSL 31 enum RAdvState { SD_RADV_STATE_IDLE = 0, @@ -75,6 +76,7 @@ struct sd_radv { size_t n_rdnss; struct sd_radv_opt_dns *rdnss; + struct sd_radv_opt_dns *dnssl; }; struct sd_radv_prefix { diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index 70772b4f15..a3aa5c9c62 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -26,12 +26,14 @@ #include "macro.h" #include "alloc-util.h" +#include "dns-domain.h" #include "fd-util.h" #include "icmp6-util.h" #include "in-addr-util.h" #include "radv-internal.h" #include "socket-util.h" #include "string-util.h" +#include "strv.h" #include "util.h" #include "random-util.h" @@ -127,6 +129,7 @@ _public_ sd_radv *sd_radv_unref(sd_radv *ra) { } free(ra->rdnss); + free(ra->dnssl); radv_reset(ra); @@ -204,6 +207,12 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, msg.msg_iovlen++; } + if (ra->dnssl) { + iov[msg.msg_iovlen].iov_base = ra->dnssl; + iov[msg.msg_iovlen].iov_len = ra->dnssl->length * 8; + msg.msg_iovlen++; + } + if (sendmsg(ra->fd, &msg, 0) < 0) return -errno; @@ -590,6 +599,58 @@ _public_ int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime, return 0; } +_public_ int sd_radv_set_dnssl(sd_radv *ra, uint32_t lifetime, + char **search_list) { + _cleanup_free_ struct sd_radv_opt_dns *opt_dnssl = NULL; + size_t len = 0; + char **s; + uint8_t *p; + + assert_return(ra, -EINVAL); + + if (!search_list || *search_list == NULL) { + ra->dnssl = mfree(ra->dnssl); + + return 0; + } + + STRV_FOREACH(s, search_list) + len += strlen(*s) + 2; + + len = (sizeof(struct sd_radv_opt_dns) + len + 7) & ~0x7; + + opt_dnssl = malloc0(len); + if (!opt_dnssl) + return -ENOMEM; + + opt_dnssl->type = SD_RADV_OPT_DNSSL; + opt_dnssl->length = len / 8; + opt_dnssl->lifetime = htobe32(lifetime); + + p = (uint8_t *)(opt_dnssl + 1); + len -= sizeof(struct sd_radv_opt_dns); + + STRV_FOREACH(s, search_list) { + int r; + + r = dns_name_to_wire_format(*s, p, len, false); + if (r < 0) + return r; + + if (len < (size_t)r) + return -ENOBUFS; + + p += r; + len -= r; + } + + free(ra->dnssl); + ra->dnssl = opt_dnssl; + opt_dnssl = NULL; + + return 0; +} + _public_ int sd_radv_prefix_new(sd_radv_prefix **ret) { _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL; diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c index eaa49c6a72..18205ef2c9 100644 --- a/src/libsystemd-network/test-ndisc-ra.c +++ b/src/libsystemd-network/test-ndisc-ra.c @@ -57,7 +57,7 @@ static uint8_t advertisement[] = { 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - /* DNS Search List Option - not yet supported */ + /* DNS Search List Option */ 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -110,6 +110,8 @@ static const struct in6_addr test_rdnss = { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }; +static const char *test_dnssl[] = { "lab.intra", + NULL }; static int test_rs_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) { @@ -220,6 +222,11 @@ static void test_radv(void) { assert_se(sd_radv_set_rdnss(ra, 0, &test_rdnss, 1) >= 0); assert_se(sd_radv_set_rdnss(ra, 0, NULL, 0) >= 0); + assert_se(sd_radv_set_dnssl(ra, 0, NULL) >= 0); + assert_se(sd_radv_set_dnssl(ra, 600, NULL) >= 0); + assert_se(sd_radv_set_dnssl(ra, 0, (char **)test_dnssl) >= 0); + assert_se(sd_radv_set_dnssl(ra, 600, (char **)test_dnssl) >= 0); + ra = sd_radv_unref(ra); assert_se(!ra); } @@ -251,7 +258,7 @@ int icmp6_receive(int fd, void *iov_base, size_t iov_len, static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { sd_radv *ra = userdata; - unsigned char buf[144]; + unsigned char buf[168]; size_t i; read(test_fd[0], &buf, sizeof(buf)); @@ -319,6 +326,7 @@ static void test_ra(void) { assert_se(sd_radv_set_managed_information(ra, true) >= 0); assert_se(sd_radv_set_other_information(ra, true) >= 0); assert_se(sd_radv_set_rdnss(ra, 60, &test_rdnss, 1) >= 0); + assert_se(sd_radv_set_dnssl(ra, 60, (char **)test_dnssl) >= 0); for (i = 0; i < ELEMENTSOF(prefix); i++) { sd_radv_prefix *p; diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h index 3a181e7a28..9a3bc9dd98 100644 --- a/src/libsystemd/sd-bus/bus-internal.h +++ b/src/libsystemd/sd-bus/bus-internal.h @@ -27,6 +27,7 @@ #include "bus-error.h" #include "bus-kernel.h" #include "bus-match.h" +#include "def.h" #include "hashmap.h" #include "list.h" #include "prioq.h" @@ -316,8 +317,13 @@ struct sd_bus { LIST_HEAD(sd_bus_track, tracks); }; +/* For method calls we time-out at 25s, like in the D-Bus reference implementation */ #define BUS_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC)) +/* For the authentication phase we grant 90s, to provide extra room during boot, when RNGs and such are not filled up + * with enough entropy yet and might delay the boot */ +#define BUS_AUTH_TIMEOUT ((usec_t) DEFAULT_TIMEOUT_USEC) + #define BUS_WQUEUE_MAX (192*1024) #define BUS_RQUEUE_MAX (192*1024) diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index 982af68c01..6e4bd541bf 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -660,7 +660,7 @@ int bus_socket_start_auth(sd_bus *b) { bus_get_peercred(b); b->state = BUS_AUTHENTICATING; - b->auth_timeout = now(CLOCK_MONOTONIC) + BUS_DEFAULT_TIMEOUT; + b->auth_timeout = now(CLOCK_MONOTONIC) + BUS_AUTH_TIMEOUT; if (sd_is_socket(b->input_fd, AF_UNIX, 0, 0) <= 0) b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD; diff --git a/src/libsystemd/sd-bus/test-bus-cleanup.c b/src/libsystemd/sd-bus/test-bus-cleanup.c index 250a5b2908..9a088a5bf5 100644 --- a/src/libsystemd/sd-bus/test-bus-cleanup.c +++ b/src/libsystemd/sd-bus/test-bus-cleanup.c @@ -37,7 +37,7 @@ static int test_bus_open(void) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; - r = sd_bus_open_system(&bus); + r = sd_bus_open_user(&bus); if (r == -ECONNREFUSED || r == -ENOENT) return r; @@ -51,7 +51,7 @@ static void test_bus_new_method_call(void) { sd_bus *bus = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - assert_se(sd_bus_open_system(&bus) >= 0); + assert_se(sd_bus_open_user(&bus) >= 0); assert_se(sd_bus_message_new_method_call(bus, &m, "a.service.name", "/an/object/path", "an.interface.name", "AMethodName") >= 0); @@ -65,7 +65,7 @@ static void test_bus_new_signal(void) { sd_bus *bus = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - assert_se(sd_bus_open_system(&bus) >= 0); + assert_se(sd_bus_open_user(&bus) >= 0); assert_se(sd_bus_message_new_signal(bus, &m, "/an/object/path", "an.interface.name", "Name") >= 0); diff --git a/src/libsystemd/sd-bus/test-bus-gvariant.c b/src/libsystemd/sd-bus/test-bus-gvariant.c index 83f114a0fe..e8301f9556 100644 --- a/src/libsystemd/sd-bus/test-bus-gvariant.c +++ b/src/libsystemd/sd-bus/test-bus-gvariant.c @@ -137,7 +137,7 @@ static void test_marshal(void) { size_t sz; int r; - r = sd_bus_open_system(&bus); + r = sd_bus_open_user(&bus); if (r < 0) exit(EXIT_TEST_SKIP); diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c index a28cc5b79e..e60c30b1b4 100644 --- a/src/libsystemd/sd-bus/test-bus-marshal.c +++ b/src/libsystemd/sd-bus/test-bus-marshal.c @@ -137,7 +137,7 @@ int main(int argc, char *argv[]) { double dbl; uint64_t u64; - r = sd_bus_default_system(&bus); + r = sd_bus_default_user(&bus); if (r < 0) return EXIT_TEST_SKIP; diff --git a/src/libsystemd/sd-bus/test-bus-match.c b/src/libsystemd/sd-bus/test-bus-match.c index 29c4529f95..8fd9d03ddf 100644 --- a/src/libsystemd/sd-bus/test-bus-match.c +++ b/src/libsystemd/sd-bus/test-bus-match.c @@ -94,7 +94,7 @@ int main(int argc, char *argv[]) { sd_bus_slot slots[19]; int r; - r = sd_bus_open_system(&bus); + r = sd_bus_open_user(&bus); if (r < 0) return EXIT_TEST_SKIP; diff --git a/src/libsystemd/sd-bus/test-bus-track.c b/src/libsystemd/sd-bus/test-bus-track.c index 06c6167511..5f5661cb33 100644 --- a/src/libsystemd/sd-bus/test-bus-track.c +++ b/src/libsystemd/sd-bus/test-bus-track.c @@ -64,7 +64,7 @@ int main(int argc, char *argv[]) { r = sd_event_default(&event); assert_se(r >= 0); - r = sd_bus_open_system(&a); + r = sd_bus_open_user(&a); if (IN_SET(r, -ECONNREFUSED, -ENOENT)) { log_info("Failed to connect to bus, skipping tests."); return EXIT_TEST_SKIP; @@ -74,7 +74,7 @@ int main(int argc, char *argv[]) { r = sd_bus_attach_event(a, event, SD_EVENT_PRIORITY_NORMAL); assert_se(r >= 0); - r = sd_bus_open_system(&b); + r = sd_bus_open_user(&b); assert_se(r >= 0); r = sd_bus_attach_event(b, event, SD_EVENT_PRIORITY_NORMAL); diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index e8c8abac2a..ac0427f949 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -101,12 +101,8 @@ int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type) { int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) { assert_return(m, -EINVAL); assert_return(m->hdr, -EINVAL); - assert_return(m->hdr->nlmsg_type == RTM_GETLINK || - m->hdr->nlmsg_type == RTM_GETADDR || - m->hdr->nlmsg_type == RTM_GETROUTE || - m->hdr->nlmsg_type == RTM_GETNEIGH || - m->hdr->nlmsg_type == RTM_GETADDRLABEL , - -EINVAL); + + assert_return(IN_SET(m->hdr->nlmsg_type, RTM_GETLINK, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH, RTM_GETRULE, RTM_GETADDRLABEL), -EINVAL); SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump); diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index 923f7dd10c..979dc6824f 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -22,6 +22,7 @@ #include <linux/netlink.h> #include <linux/rtnetlink.h> #include <linux/can/netlink.h> +#include <linux/fib_rules.h> #include <linux/in6.h> #include <linux/veth.h> #include <linux/if_bridge.h> @@ -29,8 +30,11 @@ #include <linux/if_addrlabel.h> #include <linux/if.h> #include <linux/ip.h> +#include <linux/if_addr.h> +#include <linux/if_bridge.h> #include <linux/if_link.h> #include <linux/if_tunnel.h> +#include <linux/fib_rules.h> #include "macro.h" #include "missing.h" @@ -597,6 +601,31 @@ static const NLTypeSystem rtnl_addrlabel_type_system = { .types = rtnl_addrlabel_types, }; +static const NLType rtnl_routing_policy_rule_types[] = { + [FRA_DST] = { .type = NETLINK_TYPE_IN_ADDR }, + [FRA_SRC] = { .type = NETLINK_TYPE_IN_ADDR }, + [FRA_IIFNAME] = { .type = NETLINK_TYPE_STRING }, + [RTA_OIF] = { .type = NETLINK_TYPE_U32 }, + [RTA_GATEWAY] = { .type = NETLINK_TYPE_IN_ADDR }, + [FRA_PRIORITY] = { .type = NETLINK_TYPE_U32 }, + [FRA_FWMARK] = { .type = NETLINK_TYPE_U32 }, + [FRA_FLOW] = { .type = NETLINK_TYPE_U32 }, + [FRA_TUN_ID] = { .type = NETLINK_TYPE_U32 }, + [FRA_SUPPRESS_IFGROUP] = { .type = NETLINK_TYPE_U32 }, + [FRA_SUPPRESS_PREFIXLEN] = { .type = NETLINK_TYPE_U32 }, + [FRA_TABLE] = { .type = NETLINK_TYPE_U32 }, + [FRA_FWMASK] = { .type = NETLINK_TYPE_U32 }, + [FRA_OIFNAME] = { .type = NETLINK_TYPE_STRING }, + [FRA_PAD] = { .type = NETLINK_TYPE_U32 }, + [FRA_L3MDEV] = { .type = NETLINK_TYPE_U64 }, + [FRA_UID_RANGE] = { .size = sizeof(struct fib_rule_uid_range) }, +}; + +static const NLTypeSystem rtnl_routing_policy_rule_type_system = { + .count = ELEMENTSOF(rtnl_routing_policy_rule_types), + .types = rtnl_routing_policy_rule_types, +}; + static const NLType rtnl_types[] = { [NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 }, [NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) }, @@ -616,6 +645,9 @@ static const NLType rtnl_types[] = { [RTM_NEWADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) }, [RTM_DELADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) }, [RTM_GETADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) }, + [RTM_NEWRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) }, + [RTM_DELRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) }, + [RTM_GETRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) }, }; const NLTypeSystem type_system_root = { diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h index 215af12406..d2fb651204 100644 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ b/src/libsystemd/sd-netlink/netlink-util.h @@ -47,6 +47,10 @@ static inline bool rtnl_message_type_is_addrlabel(uint16_t type) { return IN_SET(type, RTM_NEWADDRLABEL, RTM_DELADDRLABEL, RTM_GETADDRLABEL); } +static inline bool rtnl_message_type_is_routing_policy_rule(uint16_t type) { + return IN_SET(type, RTM_NEWRULE, RTM_DELRULE, RTM_GETRULE); +} + int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name); int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, const struct ether_addr *mac, unsigned mtu); diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c index 12c51ffe2e..d6c52b5b43 100644 --- a/src/libsystemd/sd-netlink/rtnl-message.c +++ b/src/libsystemd/sd-netlink/rtnl-message.c @@ -155,6 +155,35 @@ int sd_rtnl_message_route_set_family(sd_netlink_message *m, int family) { return 0; } +int sd_rtnl_message_route_get_type(sd_netlink_message *m, unsigned char *type) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + assert_return(type, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *type = rtm->rtm_type; + + return 0; +} + +int sd_rtnl_message_route_set_type(sd_netlink_message *m, unsigned char type) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + rtm->rtm_type = type; + + return 0; +} + int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol) { struct rtmsg *rtm; @@ -697,6 +726,14 @@ int sd_rtnl_message_get_family(sd_netlink_message *m, int *family) { *family = ifa->ifa_family; return 0; + } else if (rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type)) { + struct rtmsg *rtm; + + rtm = NLMSG_DATA(m->hdr); + + *family = rtm->rtm_family; + + return 0; } return -EOPNOTSUPP; @@ -754,3 +791,166 @@ int sd_rtnl_message_addrlabel_get_prefixlen(sd_netlink_message *m, unsigned char return 0; } + +int sd_rtnl_message_new_routing_policy_rule(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int ifal_family) { + struct rtmsg *rtm; + int r; + + assert_return(rtnl_message_type_is_routing_policy_rule(nlmsg_type), -EINVAL); + assert_return(ret, -EINVAL); + + r = message_new(rtnl, ret, nlmsg_type); + if (r < 0) + return r; + + if (nlmsg_type == RTM_NEWRULE) + (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; + + rtm = NLMSG_DATA((*ret)->hdr); + rtm->rtm_family = ifal_family; + rtm->rtm_protocol = RTPROT_BOOT; + rtm->rtm_scope = RT_SCOPE_UNIVERSE; + rtm->rtm_type = RTN_UNICAST; + + return 0; +} + +int sd_rtnl_message_routing_policy_rule_set_tos(sd_netlink_message *m, unsigned char tos) { + struct rtmsg *routing_policy_rule; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL); + + routing_policy_rule = NLMSG_DATA(m->hdr); + + routing_policy_rule->rtm_tos = tos; + + return 0; +} + +int sd_rtnl_message_routing_policy_rule_get_tos(sd_netlink_message *m, unsigned char *tos) { + struct rtmsg *routing_policy_rule; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL); + + routing_policy_rule = NLMSG_DATA(m->hdr); + + *tos = routing_policy_rule->rtm_tos; + + return 0; +} + +int sd_rtnl_message_routing_policy_rule_set_table(sd_netlink_message *m, unsigned char table) { + struct rtmsg *routing_policy_rule; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL); + + routing_policy_rule = NLMSG_DATA(m->hdr); + + routing_policy_rule->rtm_table = table; + + return 0; +} + +int sd_rtnl_message_routing_policy_rule_get_table(sd_netlink_message *m, unsigned char *table) { + struct rtmsg *routing_policy_rule; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL); + + routing_policy_rule = NLMSG_DATA(m->hdr); + + *table = routing_policy_rule->rtm_table; + + return 0; +} + +int sd_rtnl_message_routing_policy_rule_set_rtm_type(sd_netlink_message *m, unsigned char type) { + struct rtmsg *routing_policy_rule; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL); + + routing_policy_rule = NLMSG_DATA(m->hdr); + + routing_policy_rule->rtm_type = type; + + return 0; +} + +int sd_rtnl_message_routing_policy_rule_get_rtm_type(sd_netlink_message *m, unsigned char *type) { + struct rtmsg *routing_policy_rule; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL); + + routing_policy_rule = NLMSG_DATA(m->hdr); + + *type = routing_policy_rule->rtm_type; + + return 0; +} + +int sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(sd_netlink_message *m, unsigned char len) { + struct rtmsg *routing_policy_rule; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL); + + routing_policy_rule = NLMSG_DATA(m->hdr); + + routing_policy_rule->rtm_dst_len = len; + + return 0; +} + +int sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(sd_netlink_message *m, unsigned char *len) { + struct rtmsg *routing_policy_rule; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL); + + routing_policy_rule = NLMSG_DATA(m->hdr); + + *len = routing_policy_rule->rtm_dst_len; + + return 0; +} + +int sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(sd_netlink_message *m, unsigned char len) { + struct rtmsg *routing_policy_rule; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL); + + routing_policy_rule = NLMSG_DATA(m->hdr); + + routing_policy_rule->rtm_src_len = len; + + return 0; +} + +int sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(sd_netlink_message *m, unsigned char *len) { + struct rtmsg *routing_policy_rule; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL); + + routing_policy_rule = NLMSG_DATA(m->hdr); + + *len = routing_policy_rule->rtm_src_len; + + return 0; +} diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index d67244676c..77f4d5b635 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -894,6 +894,16 @@ int sd_netlink_add_match(sd_netlink *rtnl, if (r < 0) return r; break; + case RTM_NEWRULE: + case RTM_DELRULE: + r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_RULE); + if (r < 0) + return r; + + r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_RULE); + if (r < 0) + return r; + break; default: return -EOPNOTSUPP; } diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c index 615998a6f6..6efebcd94f 100644 --- a/src/modules-load/modules-load.c +++ b/src/modules-load/modules-load.c @@ -264,7 +264,7 @@ int main(int argc, char *argv[]) { r = k; } - k = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); + k = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs); if (k < 0) { log_error_errno(k, "Failed to enumerate modules-load.d files: %m"); if (r == 0) diff --git a/src/network/meson.build b/src/network/meson.build index 35ecd86379..83a837d6c9 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -61,6 +61,8 @@ sources = files(''' networkd-network.h networkd-route.c networkd-route.h + networkd-routing-policy-rule.c + networkd-routing-policy-rule.h networkd-util.c networkd-util.h '''.split()) diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 1cf72bf6d2..0e1a7d1335 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -766,7 +766,7 @@ int netdev_load(Manager *manager) { while ((netdev = hashmap_first(manager->netdevs))) netdev_unref(netdev); - r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs); + r = conf_files_list_strv(&files, ".netdev", NULL, 0, network_dirs); if (r < 0) return log_error_errno(r, "Failed to enumerate netdev files: %m"); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 2fb1dd67ab..6b591271a0 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -33,6 +33,7 @@ #include "networkd-manager.h" #include "networkd-ndisc.h" #include "networkd-radv.h" +#include "networkd-routing-policy-rule.h" #include "set.h" #include "socket-util.h" #include "stdio-util.h" @@ -497,8 +498,8 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { static void link_free(Link *link) { Address *address; - Iterator i; Link *carrier; + Iterator i; if (!link) return; @@ -1014,6 +1015,7 @@ static int link_set_bridge_fdb(Link *link) { } static int link_enter_set_addresses(Link *link) { + RoutingPolicyRule *rule, *rrule = NULL; AddressLabel *label; Address *ad; int r; @@ -1050,6 +1052,26 @@ static int link_enter_set_addresses(Link *link) { link->link_messages++; } + LIST_FOREACH(rules, rule, link->network->rules) { + r = routing_policy_rule_get(link->manager, rule->family, &rule->from, rule->from_prefixlen, &rule->to, + rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, &rrule); + if (r == 1) { + (void) routing_policy_rule_make_local(link->manager, rrule); + continue; + } + + r = routing_policy_rule_configure(rule, link, link_routing_policy_rule_handler, false); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set routing policy rules: %m"); + link_enter_failed(link); + return r; + } + + link->link_messages++; + } + + routing_policy_rule_purge(link->manager, link); + /* now that we can figure out a default address for the dhcp server, start it */ if (link_dhcp4_server_enabled(link)) { diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 45f9b3d359..b9cf42d72f 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -19,6 +19,7 @@ #include <sys/socket.h> #include <linux/if.h> +#include <linux/fib_rules.h> #include "sd-daemon.h" #include "sd-netlink.h" @@ -295,7 +296,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo Link *link = NULL; uint16_t type; uint32_t ifindex, priority = 0; - unsigned char protocol, scope, tos, table; + unsigned char protocol, scope, tos, table, rt_type; int family; unsigned char dst_prefixlen, src_prefixlen; union in_addr_union dst = {}, gw = {}, src = {}, prefsrc = {}; @@ -440,6 +441,12 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo return 0; } + r = sd_rtnl_message_route_get_type(message, &rt_type); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received route with invalid type, ignoring: %m"); + return 0; + } + r = sd_rtnl_message_route_get_table(message, &table); if (r < 0) { log_link_warning_errno(link, r, "rtnl: received route with invalid table, ignoring: %m"); @@ -463,7 +470,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo return 0; } - route_update(route, &src, src_prefixlen, &gw, &prefsrc, scope, protocol); + route_update(route, &src, src_prefixlen, &gw, &prefsrc, scope, rt_type, protocol); break; @@ -718,6 +725,113 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa return 1; } +int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) { + uint8_t tos = 0, to_prefixlen = 0, from_prefixlen = 0; + RoutingPolicyRule *rule = NULL; + union in_addr_union to, from; + uint32_t fwmark = 0, table = 0; + Manager *m = userdata; + uint16_t type; + int family; + int r; + + assert(rtnl); + assert(message); + assert(m); + + if (sd_netlink_message_is_error(message)) { + r = sd_netlink_message_get_errno(message); + if (r < 0) + log_warning_errno(r, "rtnl: failed to receive rule: %m"); + + return 0; + } + + r = sd_netlink_message_get_type(message, &type); + if (r < 0) { + log_warning_errno(r, "rtnl: could not get message type: %m"); + return 0; + } else if (!IN_SET(type, RTM_NEWRULE, RTM_DELRULE)) { + log_warning("rtnl: received unexpected message type '%u' when processing rule.", type); + return 0; + } + + r = sd_rtnl_message_get_family(message, &family); + if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) { + log_warning_errno(r, "rtnl: received address with invalid family type %u, ignoring.", type); + return 0; + } + + switch (family) { + case AF_INET: + r = sd_netlink_message_read_in_addr(message, FRA_SRC, &from.in); + if (r >= 0) { + r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &from_prefixlen); + if (r < 0) + log_warning_errno(r, "rtnl: failed to retrive rule from prefix length: %m"); + } + + r = sd_netlink_message_read_in_addr(message, FRA_DST, &to.in); + if (r >= 0) { + r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &to_prefixlen); + if (r < 0) + log_warning_errno(r, "rtnl: failed to retrive rule to prefix length: %m"); + } + + break; + + case AF_INET6: + r = sd_netlink_message_read_in6_addr(message, FRA_SRC, &from.in6); + if (r >= 0) { + r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &from_prefixlen); + if (r < 0) + log_warning_errno(r, "rtnl: failed to retrive rule from prefix length: %m"); + } + + r = sd_netlink_message_read_in6_addr(message, FRA_DST, &to.in6); + if (r >= 0) { + r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &to_prefixlen); + if (r < 0) + log_warning_errno(r, "rtnl: failed to retrive rule to prefix length: %m"); + } + + break; + + default: + log_debug("rtnl: ignoring unsupported rule family: %d", family); + } + + if (from_prefixlen == 0 && to_prefixlen == 0) + return 0; + + (void) sd_netlink_message_read_u32(message, FRA_FWMARK, &fwmark); + (void) sd_netlink_message_read_u32(message, FRA_TABLE, &table); + (void) sd_rtnl_message_routing_policy_rule_get_tos(message, &tos); + + (void) routing_policy_rule_get(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, &rule); + + switch (type) { + case RTM_NEWRULE: + if(!rule) { + r = routing_policy_rule_add_foreign(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, &rule); + if (r < 0) { + log_warning_errno(r, "Could not add rule: %m"); + return 0; + } + } + break; + case RTM_DELRULE: + routing_policy_rule_free(rule); + + break; + + default: + assert_not_reached("Received invalid RTNL message type"); + } + + return 1; +} + static int systemd_netlink_fd(void) { int n, fd, rtnl_fd = -EINVAL; @@ -782,6 +896,14 @@ static int manager_connect_rtnl(Manager *m) { if (r < 0) return r; + r = sd_netlink_add_match(m->rtnl, RTM_NEWRULE, &manager_rtnl_process_rule, m); + if (r < 0) + return r; + + r = sd_netlink_add_match(m->rtnl, RTM_DELRULE, &manager_rtnl_process_rule, m); + if (r < 0) + return r; + return 0; } @@ -875,6 +997,8 @@ static void print_string_set(FILE *f, const char *field, OrderedSet *s) { static int manager_save(Manager *m) { _cleanup_ordered_set_free_free_ OrderedSet *dns = NULL, *ntp = NULL, *search_domains = NULL, *route_domains = NULL; + RoutingPolicyRule *rule = NULL; + bool space = false; Link *link; Iterator i; _cleanup_free_ char *temp_path = NULL; @@ -999,6 +1123,28 @@ static int manager_save(Manager *m) { print_string_set(f, "DOMAINS=", search_domains); print_string_set(f, "ROUTE_DOMAINS=", route_domains); + SET_FOREACH(rule, m->rules, i) { + _cleanup_free_ char *from_str = NULL, *to_str = NULL; + fputs("RULE=", f); + + if (!in_addr_is_null(rule->family, &rule->from)) { + r = in_addr_to_string(rule->family, &rule->from, &from_str); + if (r < 0) + goto fail; + } + + if (!in_addr_is_null(rule->family, &rule->to)) { + r = in_addr_to_string(rule->family, &rule->to, &to_str); + if (r < 0) + goto fail; + } + + fprintf(f, "from=%s%s/%hhu to=%s%s/%hhu tos=%hhu fwmark=%"PRIu32"/%"PRIu32" table=%hhu", space ? " " : "", from_str, + rule->from_prefixlen, space ? " " : "", to_str, rule->to_prefixlen, rule->tos, rule->fwmark, rule->fwmask, rule->table); + + fputc('\n', f); + } + r = fflush_and_check(f); if (r < 0) goto fail; @@ -1084,6 +1230,8 @@ int manager_new(Manager **ret, sd_event *event) { m->duid.type = DUID_TYPE_EN; + (void) routing_policy_rule_load(m); + *ret = m; m = NULL; @@ -1091,6 +1239,7 @@ int manager_new(Manager **ret, sd_event *event) { } void manager_free(Manager *m) { + RoutingPolicyRule *rule; Network *network; NetDev *netdev; Link *link; @@ -1101,13 +1250,13 @@ void manager_free(Manager *m) { free(m->state_file); + while ((network = m->networks)) + network_free(network); + while ((link = hashmap_first(m->links))) link_unref(link); hashmap_free(m->links); - while ((network = m->networks)) - network_free(network); - hashmap_free(m->networks_by_name); while ((netdev = hashmap_first(m->netdevs))) @@ -1117,6 +1266,14 @@ void manager_free(Manager *m) { while ((pool = m->address_pools)) address_pool_free(pool); + set_free(m->rules); + set_free(m->rules_foreign); + + while ((rule = set_steal_first(m->rules_saved))) + free(rule); + + set_free(m->rules_saved); + sd_netlink_unref(m->rtnl); sd_event_unref(m->event); @@ -1277,6 +1434,41 @@ int manager_rtnl_enumerate_routes(Manager *m) { return r; } +int manager_rtnl_enumerate_rules(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + sd_netlink_message *rule; + int r; + + assert(m); + assert(m->rtnl); + + r = sd_rtnl_message_new_routing_policy_rule(m->rtnl, &req, RTM_GETRULE, 0); + if (r < 0) + return r; + + r = sd_netlink_message_request_dump(req, true); + if (r < 0) + return r; + + r = sd_netlink_call(m->rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (rule = reply; rule; rule = sd_netlink_message_next(rule)) { + int k; + + m->enumerating = true; + + k = manager_rtnl_process_rule(m->rtnl, rule, m); + if (k < 0) + r = k; + + m->enumerating = false; + } + + return r; +} + int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) { AddressPool *p; int r; diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index e2447c2230..254aab8452 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -65,6 +65,10 @@ struct Manager { DUID duid; char* dynamic_hostname; char* dynamic_timezone; + + Set *rules; + Set *rules_foreign; + Set *rules_saved; }; static inline const DUID* link_duid(const Link *link) { @@ -88,9 +92,11 @@ bool manager_should_reload(Manager *m); int manager_rtnl_enumerate_links(Manager *m); int manager_rtnl_enumerate_addresses(Manager *m); int manager_rtnl_enumerate_routes(Manager *m); +int manager_rtnl_enumerate_rules(Manager *m); int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, void *userdata); int manager_rtnl_process_route(sd_netlink *nl, sd_netlink_message *message, void *userdata); +int manager_rtnl_process_rule(sd_netlink *nl, sd_netlink_message *message, void *userdata); int manager_send_changed(Manager *m, const char *property, ...) _sentinel_; void manager_dirty(Manager *m); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 112efd2dfb..5947f6dc15 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -84,6 +84,12 @@ Address.AutoJoin, config_parse_address_flags, Address.Scope, config_parse_address_scope, 0, 0 IPv6AddressLabel.Prefix, config_parse_address_label_prefix, 0, 0 IPv6AddressLabel.Label, config_parse_address_label, 0, 0 +RoutingPolicyRule.TypeOfService, config_parse_routing_policy_rule_tos, 0, 0 +RoutingPolicyRule.Priority, config_parse_routing_policy_rule_priority, 0, 0 +RoutingPolicyRule.Table, config_parse_routing_policy_rule_table, 0, 0 +RoutingPolicyRule.FirewallMark, config_parse_routing_policy_rule_fwmark_mask, 0, 0 +RoutingPolicyRule.From, config_parse_routing_policy_rule_prefix, 0, 0 +RoutingPolicyRule.To, config_parse_routing_policy_rule_prefix, 0, 0 Route.Gateway, config_parse_gateway, 0, 0 Route.Destination, config_parse_destination, 0, 0 Route.Source, config_parse_destination, 0, 0 @@ -94,6 +100,7 @@ Route.Table, config_parse_route_table, Route.GatewayOnlink, config_parse_gateway_onlink, 0, 0 Route.IPv6Preference, config_parse_ipv6_route_preference, 0, 0 Route.Protocol, config_parse_route_protocol, 0, 0 +Route.Type, config_parse_route_type, 0, 0 DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp) @@ -146,6 +153,7 @@ IPv6PrefixDelegation.Managed, config_parse_bool, IPv6PrefixDelegation.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information) IPv6PrefixDelegation.RouterPreference, config_parse_router_preference, 0, 0 IPv6PrefixDelegation.DNS, config_parse_radv_dns, 0, 0 +IPv6PrefixDelegation.Domains, config_parse_radv_search_domains, 0, 0 IPv6PrefixDelegation.DNSLifetimeSec, config_parse_sec, 0, offsetof(Network, router_dns_lifetime_usec) IPv6Prefix.Prefix, config_parse_prefix, 0, 0 IPv6Prefix.OnLink, config_parse_prefix_flags, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index a873e55d4c..24f8ee6e9c 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -35,6 +35,7 @@ #include "stat-util.h" #include "string-table.h" #include "string-util.h" +#include "strv.h" #include "util.h" static void network_config_hash_func(const void *p, struct siphash *state) { @@ -77,7 +78,7 @@ int network_config_section_new(const char *filename, unsigned line, NetworkConfi } void network_config_section_free(NetworkConfigSection *cs) { - free(cs); + free(cs); } /* Set defaults following RFC7844 */ @@ -157,6 +158,7 @@ static int network_load_one(Manager *manager, const char *filename) { LIST_HEAD_INIT(network->ipv6_proxy_ndp_addresses); LIST_HEAD_INIT(network->address_labels); LIST_HEAD_INIT(network->static_prefixes); + LIST_HEAD_INIT(network->rules); network->stacked_netdevs = hashmap_new(&string_hash_ops); if (!network->stacked_netdevs) @@ -182,6 +184,10 @@ static int network_load_one(Manager *manager, const char *filename) { if (!network->prefixes_by_section) return log_oom(); + network->rules_by_section = hashmap_new(&network_config_hash_ops); + if (!network->rules_by_section) + return log_oom(); + network->filename = strdup(filename); if (!network->filename) return log_oom(); @@ -216,8 +222,7 @@ static int network_load_one(Manager *manager, const char *filename) { * even if they are commented in the man? * These vars might be overwriten by network_apply_anonymize_if_set */ network->dhcp_vendor_class_identifier = false; - /* NOTE: from man: UseMTU=... Defaults to false*/ - network->dhcp_use_mtu = false; + network->dhcp_use_mtu = true; /* NOTE: from man: UseTimezone=... Defaults to "no".*/ network->dhcp_use_timezone = false; @@ -258,6 +263,7 @@ static int network_load_one(Manager *manager, const char *filename) { "Network\0" "Address\0" "IPv6AddressLabel\0" + "RoutingPolicyRule\0" "Route\0" "DHCP\0" "DHCPv4\0" /* compat */ @@ -322,7 +328,7 @@ int network_load(Manager *manager) { while ((network = manager->networks)) network_free(network); - r = conf_files_list_strv(&files, ".network", NULL, network_dirs); + r = conf_files_list_strv(&files, ".network", NULL, 0, network_dirs); if (r < 0) return log_error_errno(r, "Failed to enumerate network files: %m"); @@ -336,13 +342,14 @@ int network_load(Manager *manager) { } void network_free(Network *network) { - NetDev *netdev; - Route *route; - Address *address; - FdbEntry *fdb_entry; IPv6ProxyNDPAddress *ipv6_proxy_ndp_address; + RoutingPolicyRule *rule; + FdbEntry *fdb_entry; AddressLabel *label; Prefix *prefix; + Address *address; + NetDev *netdev; + Route *route; Iterator i; if (!network) @@ -396,11 +403,15 @@ void network_free(Network *network) { while ((prefix = network->static_prefixes)) prefix_free(prefix); + while ((rule = network->rules)) + routing_policy_rule_free(rule); + hashmap_free(network->addresses_by_section); hashmap_free(network->routes_by_section); hashmap_free(network->fdb_entries_by_section); hashmap_free(network->address_labels_by_section); hashmap_free(network->prefixes_by_section); + hashmap_free(network->rules_by_section); if (network->manager) { if (network->manager->networks) @@ -746,7 +757,7 @@ int config_parse_tunnel(const char *unit, netdev->kind != NETDEV_KIND_VTI && netdev->kind != NETDEV_KIND_VTI6 && netdev->kind != NETDEV_KIND_IP6TNL - ) { + ) { log_syntax(unit, LOG_ERR, filename, line, 0, "NetDev is not a tunnel, ignoring assignment: %s", rvalue); return 0; @@ -1116,6 +1127,55 @@ int config_parse_radv_dns( return 0; } +int config_parse_radv_search_domains( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Network *n = data; + const char *p = rvalue; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + for (;;) { + _cleanup_free_ char *w = NULL; + _cleanup_free_ char *idna = NULL; + + r = extract_first_word(&p, &w, NULL, 0); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); + return 0; + } + if (r == 0) + break; + + r = dns_name_apply_idna(w, &idna); + if (r > 0) { + r = strv_push(&n->router_search_domains, idna); + if (r >= 0) + idna = NULL; + } else if (r == 0) { + r = strv_push(&n->router_search_domains, w); + if (r >= 0) + w = NULL; + } + } + + return 0; +} + int config_parse_dhcp_server_ntp( const char *unit, const char *filename, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 035b57d5f2..11f5bb1174 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -34,6 +34,7 @@ #include "networkd-lldp-tx.h" #include "networkd-ipv6-proxy-ndp.h" #include "networkd-route.h" +#include "networkd-routing-policy-rule.h" #include "networkd-util.h" #include "netdev/netdev.h" @@ -168,6 +169,7 @@ struct Network { usec_t router_dns_lifetime_usec; struct in6_addr *router_dns; unsigned n_router_dns; + char **router_search_domains; /* Bridge Support */ bool use_bpdu; @@ -218,6 +220,7 @@ struct Network { LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses); LIST_HEAD(AddressLabel, address_labels); LIST_HEAD(Prefix, static_prefixes); + LIST_HEAD(RoutingPolicyRule, rules); unsigned n_static_addresses; unsigned n_static_routes; @@ -225,12 +228,14 @@ struct Network { unsigned n_ipv6_proxy_ndp_addresses; unsigned n_address_labels; unsigned n_static_prefixes; + unsigned n_rules; Hashmap *addresses_by_section; Hashmap *routes_by_section; Hashmap *fdb_entries_by_section; Hashmap *address_labels_by_section; Hashmap *prefixes_by_section; + Hashmap *rules_by_section; struct in_addr_data *dns; unsigned n_dns; @@ -271,6 +276,7 @@ int config_parse_hostname(const char *unit, const char *filename, unsigned line, int config_parse_timezone(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_dhcp_server_dns(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_radv_dns(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_radv_search_domains(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_dhcp_server_ntp(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_dnssec_negative_trust_anchors(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_dhcp_use_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index d47a2fd3fd..6768208cfa 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -85,5 +85,14 @@ int radv_configure(Link *link) { return r; } + if (link->network->router_search_domains) { + r = sd_radv_set_dnssl(link->radv, + DIV_ROUND_UP(link->network->router_dns_lifetime_usec, + USEC_PER_SEC), + link->network->router_search_domains); + if (r < 0) + return r; + } + return 0; } diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index e5d61ce8cc..5b4874795a 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -70,6 +70,7 @@ int route_new(Route **ret) { route->family = AF_UNSPEC; route->scope = RT_SCOPE_UNIVERSE; route->protocol = RTPROT_UNSPEC; + route->type = RTN_UNICAST; route->table = RT_TABLE_MAIN; route->lifetime = USEC_INFINITY; @@ -372,7 +373,8 @@ int route_update(Route *route, const union in_addr_union *gw, const union in_addr_union *prefsrc, unsigned char scope, - unsigned char protocol) { + unsigned char protocol, + unsigned char type) { assert(route); assert(src); @@ -385,6 +387,7 @@ int route_update(Route *route, route->prefsrc = *prefsrc; route->scope = scope; route->protocol = protocol; + route->type = type; return 0; } @@ -458,9 +461,15 @@ int route_remove(Route *route, Link *link, if (r < 0) return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m"); - r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex); + r = sd_rtnl_message_route_set_type(req, route->type); if (r < 0) - return log_error_errno(r, "Could not append RTA_OIF attribute: %m"); + return log_error_errno(r, "Could not set route type: %m"); + + if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE)) { + r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex); + if (r < 0) + return log_error_errno(r, "Could not append RTA_OIF attribute: %m"); + } r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL); if (r < 0) @@ -612,9 +621,15 @@ int route_configure( if (r < 0) return log_error_errno(r, "Could not append RTA_PREF attribute: %m"); - r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex); + r = sd_rtnl_message_route_set_type(req, route->type); if (r < 0) - return log_error_errno(r, "Could not append RTA_OIF attribute: %m"); + return log_error_errno(r, "Could not set route type: %m"); + + if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE)) { + r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex); + if (r < 0) + return log_error_errno(r, "Could not append RTA_OIF attribute: %m"); + } r = sd_netlink_message_open_container(req, RTA_METRICS); if (r < 0) @@ -1023,3 +1038,39 @@ int config_parse_route_protocol(const char *unit, return 0; } + +int config_parse_route_type(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Network *network = userdata; + _cleanup_route_free_ Route *n = NULL; + int r; + + r = route_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + if (streq(rvalue, "unicast")) + n->type = RTN_UNICAST; + else if (streq(rvalue, "blackhole")) + n->type = RTN_BLACKHOLE; + else if (streq(rvalue, "unreachable")) + n->type = RTN_UNREACHABLE; + else if (streq(rvalue, "prohibit")) + n->type = RTN_PROHIBIT; + else { + log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse route type \"%s\", ignoring assignment: %m", rvalue); + return 0; + } + + n = NULL; + + return 0; +} diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 3f389489da..89d32e9214 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -35,6 +35,7 @@ struct Route { unsigned char src_prefixlen; unsigned char scope; unsigned char protocol; /* RTPROT_* */ + unsigned char type; /* RTN_* */ unsigned char tos; uint32_t priority; /* note that ip(8) calls this 'metric' */ uint32_t table; @@ -62,7 +63,7 @@ int route_remove(Route *route, Link *link, sd_netlink_message_handler_t callback int route_get(Link *link, int family, const union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, uint32_t table, Route **ret); int route_add(Link *link, int family, const union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, uint32_t table, Route **ret); int route_add_foreign(Link *link, int family, const union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, uint32_t table, Route **ret); -int route_update(Route *route, const union in_addr_union *src, unsigned char src_prefixlen, const union in_addr_union *gw, const union in_addr_union *prefsrc, unsigned char scope, unsigned char protocol); +int route_update(Route *route, const union in_addr_union *src, unsigned char src_prefixlen, const union in_addr_union *gw, const union in_addr_union *prefsrc, unsigned char scope, unsigned char protocol, unsigned char type); int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata); @@ -78,3 +79,4 @@ int config_parse_route_table(const char *unit, const char *filename, unsigned li int config_parse_gateway_onlink(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_ipv6_route_preference(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_route_protocol(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_route_type(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c new file mode 100644 index 0000000000..6850135fc1 --- /dev/null +++ b/src/network/networkd-routing-policy-rule.c @@ -0,0 +1,900 @@ +/*** + This file is part of systemd. + + Copyright 2017 Susant Sahani + + 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 <net/if.h> +#include <linux/fib_rules.h> + +#include "alloc-util.h" +#include "conf-parser.h" +#include "fileio.h" +#include "networkd-routing-policy-rule.h" +#include "netlink-util.h" +#include "networkd-manager.h" +#include "parse-util.h" +#include "socket-util.h" +#include "string-util.h" + +int routing_policy_rule_new(RoutingPolicyRule **ret) { + RoutingPolicyRule *rule; + + rule = new0(RoutingPolicyRule, 1); + if (!rule) + return -ENOMEM; + + rule->family = AF_INET; + rule->table = RT_TABLE_MAIN; + + *ret = rule; + return 0; +} + +void routing_policy_rule_free(RoutingPolicyRule *rule) { + + if (!rule) + return; + + if (rule->network) { + LIST_REMOVE(rules, rule->network->rules, rule); + assert(rule->network->n_rules > 0); + rule->network->n_rules--; + + if (rule->section) { + hashmap_remove(rule->network->rules_by_section, rule->section); + network_config_section_free(rule->section); + } + + if (rule->network->manager) { + set_remove(rule->network->manager->rules, rule); + set_remove(rule->network->manager->rules_foreign, rule); + } + } + + free(rule); +} + +static void routing_policy_rule_hash_func(const void *b, struct siphash *state) { + const RoutingPolicyRule *rule = b; + + assert(rule); + + siphash24_compress(&rule->family, sizeof(rule->family), state); + + switch (rule->family) { + case AF_INET: + case AF_INET6: + + siphash24_compress(&rule->from, FAMILY_ADDRESS_SIZE(rule->family), state); + siphash24_compress(&rule->from_prefixlen, sizeof(rule->from_prefixlen), state); + + siphash24_compress(&rule->to, FAMILY_ADDRESS_SIZE(rule->family), state); + siphash24_compress(&rule->to_prefixlen, sizeof(rule->to_prefixlen), state); + + siphash24_compress(&rule->tos, sizeof(rule->tos), state); + siphash24_compress(&rule->fwmark, sizeof(rule->fwmark), state); + siphash24_compress(&rule->table, sizeof(rule->table), state); + + break; + default: + /* treat any other address family as AF_UNSPEC */ + break; + } +} + +static int routing_policy_rule_compare_func(const void *_a, const void *_b) { + const RoutingPolicyRule *a = _a, *b = _b; + int r; + + if (a->family < b->family) + return -1; + if (a->family > b->family) + return 1; + + switch (a->family) { + case AF_INET: + case AF_INET6: + if (a->from_prefixlen < b->from_prefixlen) + return -1; + if (a->from_prefixlen > b->from_prefixlen) + return 1; + + if (a->to_prefixlen < b->to_prefixlen) + return -1; + if (a->to_prefixlen > b->to_prefixlen) + return 1; + + if (a->tos < b->tos) + return -1; + if (a->tos > b->tos) + return 1; + + if (a->fwmask < b->fwmark) + return -1; + if (a->fwmask > b->fwmark) + return 1; + + if (a->table < b->table) + return -1; + if (a->table > b->table) + return 1; + + r = memcmp(&a->from, &b->from, FAMILY_ADDRESS_SIZE(a->family)); + if (r != 0) + return r; + + return memcmp(&a->to, &b->to, FAMILY_ADDRESS_SIZE(a->family)); + + default: + /* treat any other address family as AF_UNSPEC */ + return 0; + } +} + +const struct hash_ops routing_policy_rule_hash_ops = { + .hash = routing_policy_rule_hash_func, + .compare = routing_policy_rule_compare_func +}; + +int routing_policy_rule_get(Manager *m, + int family, + const union in_addr_union *from, + uint8_t from_prefixlen, + const union in_addr_union *to, + uint8_t to_prefixlen, + uint8_t tos, + uint32_t fwmark, + uint32_t table, + RoutingPolicyRule **ret) { + + RoutingPolicyRule rule, *existing; + + assert_return(m, -1); + + rule = (RoutingPolicyRule) { + .family = family, + .from = *from, + .from_prefixlen = from_prefixlen, + .to = *to, + .to_prefixlen = to_prefixlen, + .tos = tos, + .fwmark = fwmark, + .table = table, + }; + + if (m->rules) { + existing = set_get(m->rules, &rule); + if (existing) { + if (ret) + *ret = existing; + return 1; + } + } + + if (m->rules_foreign) { + existing = set_get(m->rules_foreign, &rule); + if (existing) { + if (ret) + *ret = existing; + return 1; + } + } + + return -ENOENT; +} + +int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule) { + int r; + + assert(m); + + if (set_contains(m->rules_foreign, rule)) { + set_remove(m->rules_foreign, rule); + + r = set_ensure_allocated(&m->rules, &routing_policy_rule_hash_ops); + if (r < 0) + return r; + + return set_put(m->rules, rule); + } + + return -ENOENT; +} + +static int routing_policy_rule_add_internal(Set **rules, + int family, + const union in_addr_union *from, + uint8_t from_prefixlen, + const union in_addr_union *to, + uint8_t to_prefixlen, + uint8_t tos, + uint32_t fwmark, + uint32_t table, + RoutingPolicyRule **ret) { + + _cleanup_routing_policy_rule_free_ RoutingPolicyRule *rule = NULL; + int r; + + assert_return(rules, -EINVAL); + + r = routing_policy_rule_new(&rule); + if (r < 0) + return r; + + rule->family = family; + rule->from = *from; + rule->from_prefixlen = from_prefixlen; + rule->to = *to; + rule->to_prefixlen = to_prefixlen; + rule->tos = tos; + rule->fwmark = fwmark; + rule->table = table; + + r = set_ensure_allocated(rules, &routing_policy_rule_hash_ops); + if (r < 0) + return r; + + r = set_put(*rules, rule); + if (r < 0) + return r; + + if (ret) + *ret = rule; + + rule = NULL; + + return 0; +} + +int routing_policy_rule_add(Manager *m, + int family, + const union in_addr_union *from, + uint8_t from_prefixlen, + const union in_addr_union *to, + uint8_t to_prefixlen, + uint8_t tos, + uint32_t fwmark, + uint32_t table, + RoutingPolicyRule **ret) { + + return routing_policy_rule_add_internal(&m->rules, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, ret); +} + +int routing_policy_rule_add_foreign(Manager *m, + int family, + const union in_addr_union *from, + uint8_t from_prefixlen, + const union in_addr_union *to, + uint8_t to_prefixlen, + uint8_t tos, + uint32_t fwmark, + uint32_t table, + RoutingPolicyRule **ret) { + return routing_policy_rule_add_internal(&m->rules_foreign, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, ret); +} + +static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(m); + assert(link); + assert(link->ifname); + + link->link_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0) + log_link_warning_errno(link, r, "Could not drop routing policy rule: %m"); + + return 1; +} + +int routing_policy_rule_remove(RoutingPolicyRule *routing_policy_rule, Link *link, sd_netlink_message_handler_t callback) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + assert(routing_policy_rule); + assert(link); + assert(link->manager); + assert(link->manager->rtnl); + assert(link->ifindex > 0); + assert(IN_SET(routing_policy_rule->family, AF_INET, AF_INET6)); + + r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_DELRULE, routing_policy_rule->family); + if (r < 0) + return log_error_errno(r, "Could not allocate RTM_DELRULE message: %m"); + + if (!in_addr_is_null(routing_policy_rule->family, &routing_policy_rule->from)) { + if (routing_policy_rule->family == AF_INET) + r = sd_netlink_message_append_in_addr(m, FRA_SRC, &routing_policy_rule->from.in); + else + r = sd_netlink_message_append_in6_addr(m, FRA_SRC, &routing_policy_rule->from.in6); + + if (r < 0) + return log_error_errno(r, "Could not append FRA_SRC attribute: %m"); + + r = sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(m, routing_policy_rule->from_prefixlen); + if (r < 0) + return log_error_errno(r, "Could not set source prefix length: %m"); + } + + if (!in_addr_is_null(routing_policy_rule->family, &routing_policy_rule->to)) { + if (routing_policy_rule->family == AF_INET) + r = sd_netlink_message_append_in_addr(m, FRA_DST, &routing_policy_rule->to.in); + else + r = sd_netlink_message_append_in6_addr(m, FRA_DST, &routing_policy_rule->to.in6); + + if (r < 0) + return log_error_errno(r, "Could not append FRA_DST attribute: %m"); + + r = sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(m, routing_policy_rule->to_prefixlen); + if (r < 0) + return log_error_errno(r, "Could not set destination prefix length: %m"); + } + + r = sd_netlink_call_async(link->manager->rtnl, m, callback, link, 0, NULL); + if (r < 0) + return log_error_errno(r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + return 0; +} + +static int routing_policy_rule_new_static(Network *network, const char *filename, unsigned section_line, RoutingPolicyRule **ret) { + _cleanup_routing_policy_rule_free_ RoutingPolicyRule *rule = NULL; + _cleanup_network_config_section_free_ NetworkConfigSection *n = NULL; + int r; + + assert(network); + assert(ret); + assert(!!filename == (section_line > 0)); + + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; + + rule = hashmap_get(network->rules_by_section, n); + if (rule) { + *ret = rule; + rule = NULL; + + return 0; + } + + r = routing_policy_rule_new(&rule); + if (r < 0) + return r; + + rule->section = n; + rule->network = network; + n = NULL; + + r = hashmap_put(network->rules_by_section, rule->section, rule); + if (r < 0) + return r; + + LIST_APPEND(rules, network->rules, rule); + network->n_rules++; + + *ret = rule; + rule = NULL; + + return 0; +} + +int link_routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(rtnl); + assert(m); + assert(link); + assert(link->ifname); + assert(link->link_messages > 0); + + link->link_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) + log_link_warning_errno(link, r, "Could not add routing policy rule: %m"); + + if (link->link_messages == 0) + log_link_debug(link, "Routing policy rule configured"); + + return 1; +} + +int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, sd_netlink_message_handler_t callback, bool update) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + assert(rule); + assert(link); + assert(link->ifindex > 0); + assert(link->manager); + assert(link->manager->rtnl); + + r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_NEWRULE, rule->family); + if (r < 0) + return log_error_errno(r, "Could not allocate RTM_NEWRULE message: %m"); + + if (!in_addr_is_null(rule->family, &rule->from)) { + if (rule->family == AF_INET) + r = sd_netlink_message_append_in_addr(m, FRA_SRC, &rule->from.in); + else + r = sd_netlink_message_append_in6_addr(m, FRA_SRC, &rule->from.in6); + + if (r < 0) + return log_error_errno(r, "Could not append FRA_SRC attribute: %m"); + + r = sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(m, rule->from_prefixlen); + if (r < 0) + return log_error_errno(r, "Could not set source prefix length: %m"); + } + + if (!in_addr_is_null(rule->family, &rule->to)) { + if (rule->family == AF_INET) + r = sd_netlink_message_append_in_addr(m, FRA_DST, &rule->to.in); + else + r = sd_netlink_message_append_in6_addr(m, FRA_DST, &rule->to.in6); + + if (r < 0) + return log_error_errno(r, "Could not append FRA_DST attribute: %m"); + + r = sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(m, rule->to_prefixlen); + if (r < 0) + return log_error_errno(r, "Could not set destination prefix length: %m"); + } + + r = sd_netlink_message_append_u32(m, FRA_PRIORITY, rule->priority); + if (r < 0) + return log_error_errno(r, "Could not append FRA_PRIORITY attribute: %m"); + + if (rule->tos > 0) { + r = sd_rtnl_message_routing_policy_rule_set_tos(m, rule->tos); + if (r < 0) + return log_error_errno(r, "Could not set ip rule tos: %m"); + } + + if (rule->table < 256) { + r = sd_rtnl_message_routing_policy_rule_set_table(m, rule->table); + if (r < 0) + return log_error_errno(r, "Could not set ip rule table: %m"); + } else { + r = sd_rtnl_message_routing_policy_rule_set_table(m, RT_TABLE_UNSPEC); + if (r < 0) + return log_error_errno(r, "Could not set ip rule table: %m"); + + r = sd_netlink_message_append_u32(m, FRA_TABLE, rule->table); + if (r < 0) + return log_error_errno(r, "Could not append FRA_TABLE attribute: %m"); + } + + if (rule->fwmark > 0) { + r = sd_netlink_message_append_u32(m, FRA_FWMARK, rule->fwmark); + if (r < 0) + return log_error_errno(r, "Could not append FRA_FWMARK attribute: %m"); + } + + if (rule->fwmask > 0) { + r = sd_netlink_message_append_u32(m, FRA_FWMASK, rule->fwmask); + if (r < 0) + return log_error_errno(r, "Could not append FRA_FWMASK attribute: %m"); + } + + rule->link = link; + + r = sd_netlink_call_async(link->manager->rtnl, m, callback, link, 0, NULL); + if (r < 0) + return log_error_errno(r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + r = routing_policy_rule_add(link->manager, rule->family, &rule->from, rule->from_prefixlen, &rule->to, + rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, NULL); + if (r < 0) + return log_error_errno(r, "Could not add rule : %m"); + + return 0; +} + +static int parse_fwmark_fwmask(const char *s, uint32_t *fwmark, uint32_t *fwmask) { + _cleanup_free_ char *f = NULL; + char *p; + int r; + + assert(s); + + f = strdup(s); + if (!f) + return -ENOMEM; + + p = strchr(f, '/'); + if (p) + *p++ = '\0'; + + r = safe_atou32(f, fwmark); + if (r < 0) + return log_error_errno(r, "Failed to parse RPDB rule firewall mark, ignoring: %s", f); + + if (p) { + r = safe_atou32(p, fwmask); + if (r < 0) + return log_error_errno(r, "Failed to parse RPDB rule mask, ignoring: %s", f); + } + + return 0; +} + +int config_parse_routing_policy_rule_tos( + 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) { + + _cleanup_routing_policy_rule_free_ RoutingPolicyRule *n = NULL; + Network *network = userdata; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = routing_policy_rule_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + r = safe_atou8(rvalue, &n->tos); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule tos, ignoring: %s", rvalue); + return 0; + } + + n = NULL; + + return 0; +} + +int config_parse_routing_policy_rule_priority( + 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) { + + _cleanup_routing_policy_rule_free_ RoutingPolicyRule *n = NULL; + Network *network = userdata; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = routing_policy_rule_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + r = safe_atou32(rvalue, &n->priority); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule priority, ignoring: %s", rvalue); + return 0; + } + + n = NULL; + + return 0; +} + +int config_parse_routing_policy_rule_table( + 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) { + + _cleanup_routing_policy_rule_free_ RoutingPolicyRule *n = NULL; + Network *network = userdata; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = routing_policy_rule_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + r = safe_atou32(rvalue, &n->table); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule table, ignoring: %s", rvalue); + return 0; + } + + n = NULL; + + return 0; +} + +int config_parse_routing_policy_rule_fwmark_mask( + 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) { + + _cleanup_routing_policy_rule_free_ RoutingPolicyRule *n = NULL; + _cleanup_free_ char *fwmark = NULL; + Network *network = userdata; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = routing_policy_rule_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + r = parse_fwmark_fwmask(rvalue, &n->fwmark, &n->fwmask); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", rvalue); + return 0; + } + + n = NULL; + + return 0; +} + +int config_parse_routing_policy_rule_prefix( + 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) { + + _cleanup_routing_policy_rule_free_ RoutingPolicyRule *n = NULL; + Network *network = userdata; + union in_addr_union buffer; + uint8_t prefixlen; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = routing_policy_rule_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + r = in_addr_prefix_from_string(rvalue, AF_INET, &buffer, &prefixlen); + if (r < 0) { + r = in_addr_prefix_from_string(rvalue, AF_INET6, &buffer, &prefixlen); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "RPDB rule prefix is invalid, ignoring assignment: %s", rvalue); + return 0; + } + + n->family = AF_INET6; + } else + n->family = AF_INET; + + if (streq(lvalue, "To")) { + n->to = buffer; + n->to_prefixlen = prefixlen; + } else { + n->from = buffer; + n->from_prefixlen = prefixlen; + } + + n = NULL; + + return 0; +} + +static int routing_policy_rule_read_full_file(char *state_file, char **ret) { + _cleanup_free_ char *s = NULL, *p = NULL; + size_t size; + int r; + + assert(state_file); + + r = read_full_file(state_file, &s, &size); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + if (size <= 0) + return -ENODATA; + + *ret = s; + s = NULL; + + return size; +} + +int routing_policy_rule_load(Manager *m) { + _cleanup_strv_free_ char **l = NULL; + _cleanup_free_ char *data = NULL; + const char *p; + char **i; + int r; + + assert(m); + + r = routing_policy_rule_read_full_file(m->state_file, &data); + if (r <= 0) + return r; + + l = strv_split_newlines(data); + if (!l) + return -ENOMEM; + + r = set_ensure_allocated(&m->rules_saved, &routing_policy_rule_hash_ops); + if (r < 0) + return r; + + STRV_FOREACH(i, l) { + _cleanup_routing_policy_rule_free_ RoutingPolicyRule *rule = NULL; + + p = startswith(*i, "RULE="); + if (!p) + continue; + + p = strchr(*i, '='); + p++; + + r = routing_policy_rule_new(&rule); + if (r < 0) + return r; + + for (;;) { + _cleanup_free_ char *word = NULL, *a = NULL, *b = NULL; + union in_addr_union buffer; + uint8_t prefixlen; + + r = extract_first_word(&p, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + + r = split_pair(word, "=", &a, &b); + if (r < 0) + continue; + + if (STR_IN_SET(a, "from", "to")) { + + r = in_addr_prefix_from_string(b, AF_INET, &buffer, &prefixlen); + if (r < 0) { + r = in_addr_prefix_from_string(b, AF_INET6, &buffer, &prefixlen); + if (r < 0) { + log_error_errno(r, "RPDB rule prefix is invalid, ignoring assignment: %s", b); + continue; + } + + rule->family = AF_INET6; + } else + rule->family = AF_INET; + + if (streq(a, "to")) { + rule->to = buffer; + rule->to_prefixlen = prefixlen; + } else { + rule->from = buffer; + rule->from_prefixlen = prefixlen; + } + } else if (streq(a, "tos")) { + r = safe_atou8(b, &rule->tos); + if (r < 0) { + log_error_errno(r, "Failed to parse RPDB rule tos, ignoring: %s", b); + continue; + } + } else if (streq(a, "table")) { + r = safe_atou32(b, &rule->table); + if (r < 0) { + log_error_errno(r, "Failed to parse RPDB rule table, ignoring: %s", b); + continue; + } + } else if (streq(a, "fwmark")) { + + r = parse_fwmark_fwmask(a, &rule->fwmark, &rule->fwmask); + if (r < 0) { + log_error_errno(r, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", a); + continue; + } + } + } + + r = set_put(m->rules_saved, rule); + if (r < 0) { + log_warning_errno(r, "Failed to add RPDB rule to saved DB, ignoring: %s", p); + continue; + } + + rule = NULL; + } + + return 0; +} + +void routing_policy_rule_purge(Manager *m, Link *link) { + RoutingPolicyRule *rule, *existing; + Iterator i; + int r; + + assert(m); + assert(link); + + SET_FOREACH(rule, m->rules_saved, i) { + existing = set_get(m->rules_foreign, rule); + if (existing) { + + r = routing_policy_rule_remove(rule, link, routing_policy_rule_remove_handler); + if (r < 0) { + log_warning_errno(r, "Could not remove routing policy rules: %m"); + continue; + } + + link->link_messages++; + } + } +} diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h new file mode 100644 index 0000000000..d9fd93b9bf --- /dev/null +++ b/src/network/networkd-routing-policy-rule.h @@ -0,0 +1,83 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2017 Susant Sahani + + 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 <stdbool.h> + +#include "in-addr-util.h" + +typedef struct RoutingPolicyRule RoutingPolicyRule; + +#include "networkd-link.h" +#include "networkd-network.h" + +typedef struct Network Network; +typedef struct Link Link; +typedef struct NetworkConfigSection NetworkConfigSection; + +struct RoutingPolicyRule { + Network *network; + Link *link; + NetworkConfigSection *section; + + uint8_t tos; + + uint32_t table; + uint32_t fwmark; + uint32_t fwmask; + uint32_t priority; + + int family; + unsigned char to_prefixlen; + unsigned char from_prefixlen; + + union in_addr_union to; + union in_addr_union from; + + LIST_FIELDS(RoutingPolicyRule, rules); +}; + +int routing_policy_rule_new(RoutingPolicyRule **ret); +void routing_policy_rule_free(RoutingPolicyRule *rule); + +DEFINE_TRIVIAL_CLEANUP_FUNC(RoutingPolicyRule*, routing_policy_rule_free); +#define _cleanup_routing_policy_rule_free_ _cleanup_(routing_policy_rule_freep) + +int routing_policy_rule_configure(RoutingPolicyRule *address, Link *link, sd_netlink_message_handler_t callback, bool update); +int routing_policy_rule_remove(RoutingPolicyRule *routing_policy_rule, Link *link, sd_netlink_message_handler_t callback); +int link_routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata); +int link_routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata); + +int routing_policy_rule_add(Manager *m, int family, const union in_addr_union *from, uint8_t from_prefixlen, const union in_addr_union *to, uint8_t to_prefixlen, + uint8_t tos, uint32_t fwmark, uint32_t table, RoutingPolicyRule **ret); +int routing_policy_rule_add_foreign(Manager *m, int family, const union in_addr_union *from, uint8_t from_prefixlen, const union in_addr_union *to, uint8_t to_prefixlen, + uint8_t tos, uint32_t fwmark, uint32_t table, RoutingPolicyRule **ret); +int routing_policy_rule_get(Manager *m, int family, const union in_addr_union *from, uint8_t from_prefixlen, const union in_addr_union *to, uint8_t to_prefixlen, uint8_t tos, + uint32_t fwmark, uint32_t table, RoutingPolicyRule **ret); +int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule); +int routing_policy_rule_load(Manager *m); +void routing_policy_rule_purge(Manager *m, Link *link); + +int config_parse_routing_policy_rule_tos(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_routing_policy_rule_table(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_routing_policy_rule_fwmark_mask(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_routing_policy_rule_prefix(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_routing_policy_rule_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data,void *userdata); diff --git a/src/network/networkd.c b/src/network/networkd.c index 8efd160aaa..d5ba6893e3 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -132,6 +132,12 @@ int main(int argc, char *argv[]) { goto out; } + r = manager_rtnl_enumerate_rules(m); + if (r < 0) { + log_error_errno(r, "Could not enumerate rules: %m"); + goto out; + } + r = manager_start(m); if (r < 0) { log_error_errno(r, "Could not start manager: %m"); diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf index e5fdf63162..b61b347ee7 100644 --- a/src/nspawn/nspawn-gperf.gperf +++ b/src/nspawn/nspawn-gperf.gperf @@ -29,6 +29,7 @@ Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, Exec.PivotRoot, config_parse_pivot_root, 0, 0 Exec.PrivateUsers, config_parse_private_users, 0, 0 Exec.NotifyReady, config_parse_bool, 0, offsetof(Settings, notify_ready) +Exec.SystemCallFilter, config_parse_syscall_filter,0, 0, Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only) Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode) Files.Bind, config_parse_bind, 0, 0 diff --git a/src/nspawn/nspawn-seccomp.c b/src/nspawn/nspawn-seccomp.c index 72ecc51b16..db3d098e7f 100644 --- a/src/nspawn/nspawn-seccomp.c +++ b/src/nspawn/nspawn-seccomp.c @@ -33,128 +33,212 @@ #include "seccomp-util.h" #endif #include "string-util.h" +#include "strv.h" #ifdef HAVE_SECCOMP static int seccomp_add_default_syscall_filter( scmp_filter_ctx ctx, uint32_t arch, - uint64_t cap_list_retain) { + uint64_t cap_list_retain, + char **syscall_whitelist, + char **syscall_blacklist) { static const struct { uint64_t capability; - int syscall_num; - } blacklist[] = { - { 0, SCMP_SYS(_sysctl) }, /* obsolete syscall */ - { 0, SCMP_SYS(add_key) }, /* keyring is not namespaced */ - { 0, SCMP_SYS(afs_syscall) }, /* obsolete syscall */ - { 0, SCMP_SYS(bdflush) }, -#ifdef __NR_bpf - { 0, SCMP_SYS(bpf) }, -#endif - { 0, SCMP_SYS(break) }, /* obsolete syscall */ - { 0, SCMP_SYS(create_module) }, /* obsolete syscall */ - { 0, SCMP_SYS(ftime) }, /* obsolete syscall */ - { 0, SCMP_SYS(get_kernel_syms) }, /* obsolete syscall */ - { 0, SCMP_SYS(getpmsg) }, /* obsolete syscall */ - { 0, SCMP_SYS(gtty) }, /* obsolete syscall */ -#ifdef __NR_kexec_file_load - { 0, SCMP_SYS(kexec_file_load) }, -#endif - { 0, SCMP_SYS(kexec_load) }, - { 0, SCMP_SYS(keyctl) }, /* keyring is not namespaced */ - { 0, SCMP_SYS(lock) }, /* obsolete syscall */ - { 0, SCMP_SYS(lookup_dcookie) }, - { 0, SCMP_SYS(mpx) }, /* obsolete syscall */ - { 0, SCMP_SYS(nfsservctl) }, /* obsolete syscall */ - { 0, SCMP_SYS(open_by_handle_at) }, - { 0, SCMP_SYS(perf_event_open) }, - { 0, SCMP_SYS(prof) }, /* obsolete syscall */ - { 0, SCMP_SYS(profil) }, /* obsolete syscall */ - { 0, SCMP_SYS(putpmsg) }, /* obsolete syscall */ - { 0, SCMP_SYS(query_module) }, /* obsolete syscall */ - { 0, SCMP_SYS(quotactl) }, - { 0, SCMP_SYS(request_key) }, /* keyring is not namespaced */ - { 0, SCMP_SYS(security) }, /* obsolete syscall */ - { 0, SCMP_SYS(sgetmask) }, /* obsolete syscall */ - { 0, SCMP_SYS(ssetmask) }, /* obsolete syscall */ - { 0, SCMP_SYS(stty) }, /* obsolete syscall */ - { 0, SCMP_SYS(swapoff) }, - { 0, SCMP_SYS(swapon) }, - { 0, SCMP_SYS(sysfs) }, /* obsolete syscall */ - { 0, SCMP_SYS(tuxcall) }, /* obsolete syscall */ - { 0, SCMP_SYS(ulimit) }, /* obsolete syscall */ - { 0, SCMP_SYS(uselib) }, /* obsolete syscall */ - { 0, SCMP_SYS(ustat) }, /* obsolete syscall */ - { 0, SCMP_SYS(vserver) }, /* obsolete syscall */ - { CAP_SYSLOG, SCMP_SYS(syslog) }, - { CAP_SYS_MODULE, SCMP_SYS(delete_module) }, - { CAP_SYS_MODULE, SCMP_SYS(finit_module) }, - { CAP_SYS_MODULE, SCMP_SYS(init_module) }, - { CAP_SYS_PACCT, SCMP_SYS(acct) }, - { CAP_SYS_PTRACE, SCMP_SYS(process_vm_readv) }, - { CAP_SYS_PTRACE, SCMP_SYS(process_vm_writev) }, - { CAP_SYS_PTRACE, SCMP_SYS(ptrace) }, - { CAP_SYS_RAWIO, SCMP_SYS(ioperm) }, - { CAP_SYS_RAWIO, SCMP_SYS(iopl) }, - { CAP_SYS_RAWIO, SCMP_SYS(pciconfig_iobase) }, - { CAP_SYS_RAWIO, SCMP_SYS(pciconfig_read) }, - { CAP_SYS_RAWIO, SCMP_SYS(pciconfig_write) }, -#ifdef __NR_s390_pci_mmio_read - { CAP_SYS_RAWIO, SCMP_SYS(s390_pci_mmio_read) }, -#endif -#ifdef __NR_s390_pci_mmio_write - { CAP_SYS_RAWIO, SCMP_SYS(s390_pci_mmio_write) }, -#endif - { CAP_SYS_TIME, SCMP_SYS(adjtimex) }, - { CAP_SYS_TIME, SCMP_SYS(clock_adjtime) }, - { CAP_SYS_TIME, SCMP_SYS(clock_settime) }, - { CAP_SYS_TIME, SCMP_SYS(settimeofday) }, - { CAP_SYS_TIME, SCMP_SYS(stime) }, + const char* name; + } whitelist[] = { + /* Let's use set names where we can */ + { 0, "@basic-io" }, + { 0, "@credentials" }, + { 0, "@default" }, + { 0, "@file-system" }, + { 0, "@io-event" }, + { 0, "@ipc" }, + { 0, "@mount" }, + { 0, "@network-io" }, + { 0, "@process" }, + { 0, "@resources" }, + { 0, "@setuid" }, + { 0, "@signal" }, + { 0, "@timer" }, + + /* The following four are sets we optionally enable, in case the caps have been configured for it */ + { CAP_SYS_TIME, "@clock" }, + { CAP_SYS_MODULE, "@module" }, + { CAP_SYS_RAWIO, "@raw-io" }, + { CAP_IPC_LOCK, "@memlock" }, + + /* Plus a good set of additional syscalls which are not part of any of the groups above */ + { 0, "brk" }, + { 0, "capset" }, + { 0, "chown" }, + { 0, "chown32" }, + { 0, "copy_file_range" }, + { 0, "fadvise64" }, + { 0, "fadvise64_64" }, + { 0, "fchown" }, + { 0, "fchown32" }, + { 0, "fchownat" }, + { 0, "fdatasync" }, + { 0, "flock" }, + { 0, "fsync" }, + { 0, "get_mempolicy" }, + { 0, "getcpu" }, + { 0, "getpriority" }, + { 0, "getrandom" }, + { 0, "io_cancel" }, + { 0, "io_destroy" }, + { 0, "io_getevents" }, + { 0, "io_setup" }, + { 0, "io_submit" }, + { 0, "ioctl" }, + { 0, "ioprio_get" }, + { 0, "kcmp" }, + { 0, "lchown" }, + { 0, "lchown32" }, + { 0, "madvise" }, + { 0, "mincore" }, + { 0, "mprotect" }, + { 0, "mremap" }, + { 0, "msync" }, + { 0, "name_to_handle_at" }, + { 0, "oldolduname" }, + { 0, "olduname" }, + { 0, "personality" }, + { 0, "preadv2" }, + { 0, "pwritev2" }, + { 0, "readahead" }, + { 0, "readdir" }, + { 0, "remap_file_pages" }, + { 0, "sched_get_priority_max" }, + { 0, "sched_get_priority_min" }, + { 0, "sched_getaffinity" }, + { 0, "sched_getattr" }, + { 0, "sched_getparam" }, + { 0, "sched_getscheduler" }, + { 0, "sched_rr_get_interval" }, + { 0, "sched_yield" }, + { 0, "seccomp" }, + { 0, "sendfile" }, + { 0, "sendfile64" }, + { 0, "setdomainname" }, + { 0, "setfsgid" }, + { 0, "setfsgid32" }, + { 0, "setfsuid" }, + { 0, "setfsuid32" }, + { 0, "sethostname" }, + { 0, "setpgid" }, + { 0, "setsid" }, + { 0, "splice" }, + { 0, "sync" }, + { 0, "sync_file_range" }, + { 0, "syncfs" }, + { 0, "sysinfo" }, + { 0, "tee" }, + { 0, "ugetrlimit" }, + { 0, "umask" }, + { 0, "uname" }, + { 0, "userfaultfd" }, + { 0, "vmsplice" }, + + /* The following individual syscalls are added depending on specified caps */ + { CAP_SYS_PACCT, "acct" }, + { CAP_SYS_PTRACE, "process_vm_readv" }, + { CAP_SYS_PTRACE, "process_vm_writev" }, + { CAP_SYS_PTRACE, "ptrace" }, + { CAP_SYS_BOOT, "reboot" }, + { CAP_SYSLOG, "syslog" }, + { CAP_SYS_TTY_CONFIG, "vhangup" }, + + /* + * The following syscalls and groups are knowingly excluded: + * + * @cpu-emulation + * @keyring (NB: keyring is not namespaced!) + * @obsolete + * @swap + * + * bpf (NB: bpffs is not namespaced!) + * fanotify_init + * fanotify_mark + * kexec_file_load + * kexec_load + * lookup_dcookie + * nfsservctl + * open_by_handle_at + * perf_event_open + * pkey_alloc + * pkey_free + * pkey_mprotect + * quotactl + */ }; - unsigned i; + int r, c = 0; + size_t i; + char **p; - for (i = 0; i < ELEMENTSOF(blacklist); i++) { - if (blacklist[i].capability != 0 && (cap_list_retain & (1ULL << blacklist[i].capability))) + for (i = 0; i < ELEMENTSOF(whitelist); i++) { + if (whitelist[i].capability != 0 && (cap_list_retain & (1ULL << whitelist[i].capability)) == 0) continue; - r = seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EPERM), blacklist[i].syscall_num, 0); - if (r < 0) { + r = seccomp_add_syscall_filter_item(ctx, whitelist[i].name, SCMP_ACT_ALLOW, syscall_blacklist); + if (r < 0) /* If the system call is not known on this architecture, then that's fine, let's ignore it */ - _cleanup_free_ char *n = NULL; + log_debug_errno(r, "Failed to add rule for system call %s on %s, ignoring: %m", whitelist[i].name, seccomp_arch_to_string(arch)); + else + c++; + } - n = seccomp_syscall_resolve_num_arch(arch, blacklist[i].syscall_num); - log_debug_errno(r, "Failed to add rule for system call %s, ignoring: %m", strna(n)); - } else + STRV_FOREACH(p, syscall_whitelist) { + r = seccomp_add_syscall_filter_item(ctx, *p, SCMP_ACT_ALLOW, syscall_blacklist); + if (r < 0) + log_debug_errno(r, "Failed to add rule for system call %s on %s, ignoring: %m", *p, seccomp_arch_to_string(arch)); + else c++; } return c; } -int setup_seccomp(uint64_t cap_list_retain) { +int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **syscall_blacklist) { uint32_t arch; int r; if (!is_seccomp_available()) { - log_debug("SECCOMP features not detected in the kernel, disabling SECCOMP audit filter"); + log_debug("SECCOMP features not detected in the kernel, disabling SECCOMP filterering"); return 0; } SECCOMP_FOREACH_LOCAL_ARCH(arch) { _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; - int n; - log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); + log_debug("Applying whitelist on architecture: %s", seccomp_arch_to_string(arch)); - r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW); + r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ERRNO(EPERM)); if (r < 0) return log_error_errno(r, "Failed to allocate seccomp object: %m"); - n = seccomp_add_default_syscall_filter(seccomp, arch, cap_list_retain); - if (n < 0) - return n; + r = seccomp_add_default_syscall_filter(seccomp, arch, cap_list_retain, syscall_whitelist, syscall_blacklist); + if (r < 0) + return r; + + r = seccomp_load(seccomp); + if (IN_SET(r, -EPERM, -EACCES)) + return log_error_errno(r, "Failed to install seccomp filter: %m"); + if (r < 0) + log_debug_errno(r, "Failed to install filter set for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); + } + + SECCOMP_FOREACH_LOCAL_ARCH(arch) { + _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; + + log_debug("Applying NETLINK_AUDIT mask on architecture: %s", seccomp_arch_to_string(arch)); + + r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW); + if (r < 0) + return log_error_errno(r, "Failed to allocate seccomp object: %m"); /* Audit is broken in containers, much of the userspace audit hookup will fail if running inside a @@ -171,13 +255,10 @@ int setup_seccomp(uint64_t cap_list_retain) { 2, SCMP_A0(SCMP_CMP_EQ, AF_NETLINK), SCMP_A2(SCMP_CMP_EQ, NETLINK_AUDIT)); - if (r < 0) + if (r < 0) { log_debug_errno(r, "Failed to add audit seccomp rule, ignoring: %m"); - else - n++; - - if (n <= 0) /* no rule added? then skip this architecture */ continue; + } r = seccomp_load(seccomp); if (IN_SET(r, -EPERM, -EACCES)) @@ -191,7 +272,7 @@ int setup_seccomp(uint64_t cap_list_retain) { #else -int setup_seccomp(uint64_t cap_list_retain) { +int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **syscall_blacklist) { return 0; } diff --git a/src/nspawn/nspawn-seccomp.h b/src/nspawn/nspawn-seccomp.h index 5bde16faf9..5cf5ad1e14 100644 --- a/src/nspawn/nspawn-seccomp.h +++ b/src/nspawn/nspawn-seccomp.h @@ -21,4 +21,4 @@ #include <sys/types.h> -int setup_seccomp(uint64_t cap_list_retain); +int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **syscall_blacklist); diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 5217d10665..c02c1ea697 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -93,6 +93,8 @@ Settings* settings_free(Settings *s) { free(s->pivot_root_new); free(s->pivot_root_old); free(s->working_directory); + strv_free(s->syscall_whitelist); + strv_free(s->syscall_blacklist); strv_free(s->network_interfaces); strv_free(s->network_macvlan); @@ -568,3 +570,51 @@ int config_parse_private_users( return 0; } + +int config_parse_syscall_filter( + 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; + bool negative; + const char *items; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + negative = rvalue[0] == '~'; + items = negative ? rvalue + 1 : rvalue; + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&items, &word, NULL, 0); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SystemCallFilter= parameter %s, ignoring: %m", rvalue); + return 0; + } + + if (negative) + r = strv_extend(&settings->syscall_blacklist, word); + else + r = strv_extend(&settings->syscall_whitelist, word); + if (r < 0) + return log_oom(); + } + + return 0; +} diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index 021403258f..75d68ce4cf 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -58,7 +58,8 @@ typedef enum SettingsMask { SETTING_USERNS = 1 << 13, SETTING_NOTIFY_READY = 1 << 14, SETTING_PIVOT_ROOT = 1 << 15, - _SETTINGS_MASK_ALL = (1 << 16) -1 + SETTING_SYSCALL_FILTER = 1 << 16, + _SETTINGS_MASK_ALL = (1 << 17) -1 } SettingsMask; typedef struct Settings { @@ -78,6 +79,8 @@ typedef struct Settings { UserNamespaceMode userns_mode; uid_t uid_shift, uid_range; bool notify_ready; + char **syscall_whitelist; + char **syscall_blacklist; /* [Image] */ int read_only; @@ -121,3 +124,4 @@ int config_parse_network_zone(const char *unit, const char *filename, unsigned l 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); int config_parse_pid2(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_private_users(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_syscall_filter(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 24a3da68ca..cf804ed1b3 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -208,6 +208,8 @@ 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 char **arg_syscall_whitelist = NULL; +static char **arg_syscall_blacklist = NULL; static void help(void) { printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n" @@ -267,6 +269,8 @@ static void help(void) { " --capability=CAP In addition to the default, retain specified\n" " capability\n" " --drop-capability=CAP Drop the specified capability from the default set\n" + " --system-call-filter=LIST|~LIST\n" + " Permit/prohibit specific system calls\n" " --kill-signal=SIGNAL Select signal to use for shutting down PID 1\n" " --link-journal=MODE Link up guest journal, one of no, auto, guest, \n" " host, try-guest, try-host\n" @@ -431,6 +435,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_PRIVATE_USERS_CHOWN, ARG_NOTIFY_READY, ARG_ROOT_HASH, + ARG_SYSTEM_CALL_FILTER, }; static const struct option options[] = { @@ -482,6 +487,7 @@ static int parse_argv(int argc, char *argv[]) { { "pivot-root", required_argument, NULL, ARG_PIVOT_ROOT }, { "notify-ready", required_argument, NULL, ARG_NOTIFY_READY }, { "root-hash", required_argument, NULL, ARG_ROOT_HASH }, + { "system-call-filter", required_argument, NULL, ARG_SYSTEM_CALL_FILTER }, {} }; @@ -1051,6 +1057,36 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_SYSTEM_CALL_FILTER: { + bool negative; + const char *items; + + negative = optarg[0] == '~'; + items = negative ? optarg + 1 : optarg; + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&items, &word, NULL, 0); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_error_errno(r, "Failed to parse system call filter: %m"); + + if (negative) + r = strv_extend(&arg_syscall_blacklist, word); + else + r = strv_extend(&arg_syscall_whitelist, word); + if (r < 0) + return log_oom(); + } + + arg_settings_mask |= SETTING_SYSCALL_FILTER; + break; + } + case '?': return -EINVAL; @@ -2606,7 +2642,7 @@ static int outer_child( if (r < 0) return r; - r = setup_seccomp(arg_caps_retain); + r = setup_seccomp(arg_caps_retain, arg_syscall_whitelist, arg_syscall_blacklist); if (r < 0) return r; @@ -3111,6 +3147,21 @@ static int load_settings(void) { if ((arg_settings_mask & SETTING_NOTIFY_READY) == 0) arg_notify_ready = settings->notify_ready; + if ((arg_settings_mask & SETTING_SYSCALL_FILTER) == 0) { + + if (!arg_settings_trusted && !strv_isempty(arg_syscall_whitelist)) + log_warning("Ignoring SystemCallFilter= settings, file %s is not trusted.", p); + else { + strv_free(arg_syscall_whitelist); + strv_free(arg_syscall_blacklist); + + arg_syscall_whitelist = settings->syscall_whitelist; + arg_syscall_blacklist = settings->syscall_blacklist; + + settings->syscall_whitelist = settings->syscall_blacklist = NULL; + } + } + return 0; } diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c index ea90953abb..6e468853a2 100644 --- a/src/nss-mymachines/nss-mymachines.c +++ b/src/nss-mymachines/nss-mymachines.c @@ -435,7 +435,7 @@ enum nss_status _nss_mymachines_getpwnam_r( if (!machine_name_is_valid(machine)) goto not_found; - if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) /* Make sure we can't deadlock if we are invoked by dbus-daemon. This way, it won't be able to resolve * these UIDs, but that should be unproblematic as containers should never be able to connect to a bus * running on the host. */ @@ -519,7 +519,7 @@ enum nss_status _nss_mymachines_getpwuid_r( if (uid < HOST_UID_LIMIT) goto not_found; - if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) goto not_found; r = sd_bus_open_system(&bus); @@ -613,7 +613,7 @@ enum nss_status _nss_mymachines_getgrnam_r( if (!machine_name_is_valid(machine)) goto not_found; - if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) goto not_found; r = sd_bus_open_system(&bus); @@ -691,7 +691,7 @@ enum nss_status _nss_mymachines_getgrgid_r( if (gid < HOST_GID_LIMIT) goto not_found; - if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) goto not_found; r = sd_bus_open_system(&bus); diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index f404755dac..dcb32e1e2b 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -129,22 +129,24 @@ enum nss_status _nss_systemd_getpwnam_r( goto not_found; /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */ - if (streq(name, root_passwd.pw_name)) { - *pwd = root_passwd; - *errnop = 0; - return NSS_STATUS_SUCCESS; - } - if (streq(name, nobody_passwd.pw_name)) { - *pwd = nobody_passwd; - *errnop = 0; - return NSS_STATUS_SUCCESS; + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { + if (streq(name, root_passwd.pw_name)) { + *pwd = root_passwd; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (streq(name, nobody_passwd.pw_name)) { + *pwd = nobody_passwd; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } } /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */ - if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) + if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) goto not_found; - if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) { + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) { /* Access the dynamic UID allocation directly if we are called from dbus-daemon, see above. */ r = direct_lookup_name(name, (uid_t*) &translated); @@ -231,24 +233,26 @@ enum nss_status _nss_systemd_getpwuid_r( goto not_found; /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */ - if (uid == root_passwd.pw_uid) { - *pwd = root_passwd; - *errnop = 0; - return NSS_STATUS_SUCCESS; - } - if (uid == nobody_passwd.pw_uid) { - *pwd = nobody_passwd; - *errnop = 0; - return NSS_STATUS_SUCCESS; + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { + if (uid == root_passwd.pw_uid) { + *pwd = root_passwd; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (uid == nobody_passwd.pw_uid) { + *pwd = nobody_passwd; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } } if (uid <= SYSTEM_UID_MAX) goto not_found; - if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) + if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) goto not_found; - if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) { + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) { r = direct_lookup_uid(uid, &direct); if (r == -ENOENT) @@ -331,21 +335,23 @@ enum nss_status _nss_systemd_getgrnam_r( goto not_found; /* Synthesize records for root and nobody, in case they are missing form /etc/group */ - if (streq(name, root_group.gr_name)) { - *gr = root_group; - *errnop = 0; - return NSS_STATUS_SUCCESS; - } - if (streq(name, nobody_group.gr_name)) { - *gr = nobody_group; - *errnop = 0; - return NSS_STATUS_SUCCESS; + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { + if (streq(name, root_group.gr_name)) { + *gr = root_group; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (streq(name, nobody_group.gr_name)) { + *gr = nobody_group; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } } - if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) + if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) goto not_found; - if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) { + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) { /* Access the dynamic GID allocation directly if we are called from dbus-daemon, see above. */ r = direct_lookup_name(name, (uid_t*) &translated); @@ -430,24 +436,26 @@ enum nss_status _nss_systemd_getgrgid_r( goto not_found; /* Synthesize records for root and nobody, in case they are missing from /etc/group */ - if (gid == root_group.gr_gid) { - *gr = root_group; - *errnop = 0; - return NSS_STATUS_SUCCESS; - } - if (gid == nobody_group.gr_gid) { - *gr = nobody_group; - *errnop = 0; - return NSS_STATUS_SUCCESS; + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { + if (gid == root_group.gr_gid) { + *gr = root_group; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (gid == nobody_group.gr_gid) { + *gr = nobody_group; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } } if (gid <= SYSTEM_GID_MAX) goto not_found; - if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) + if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) goto not_found; - if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) { + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) { r = direct_lookup_uid(gid, &direct); if (r == -ENOENT) diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index dda9875063..e169c8f02f 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -435,7 +435,7 @@ static int dns_trust_anchor_load_files( assert(suffix); assert(loader); - r = conf_files_list_nulstr(&files, suffix, NULL, trust_anchor_dirs); + r = conf_files_list_nulstr(&files, suffix, NULL, 0, trust_anchor_dirs); if (r < 0) return log_error_errno(r, "Failed to enumerate %s trust anchor files: %m", suffix); diff --git a/src/run/run.c b/src/run/run.c index 86e3040918..2e24f46b17 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -61,7 +61,12 @@ static int arg_nice = 0; static bool arg_nice_set = false; static char **arg_environment = NULL; static char **arg_property = NULL; -static bool arg_pty = false; +static enum { + ARG_STDIO_NONE, /* The default, as it is for normal services, stdin connected to /dev/null, and stdout+stderr to the journal */ + ARG_STDIO_PTY, /* Interactive behaviour, requested by --pty: we allocate a pty and connect it to the TTY we are invoked from */ + ARG_STDIO_DIRECT, /* Directly pass our stdin/stdout/stderr to the activated service, useful for usage in shell pipelines, requested by --pipe */ + ARG_STDIO_AUTO, /* If --pipe and --pty are used together we use --pty when invoked on a TTY, and --pipe otherwise */ +} arg_stdio = ARG_STDIO_NONE; static usec_t arg_on_active = 0; static usec_t arg_on_boot = 0; static usec_t arg_on_startup = 0; @@ -106,7 +111,9 @@ static void help(void) { " --gid=GROUP Run as system group\n" " --nice=NICE Nice level\n" " -E --setenv=NAME=VALUE Set environment\n" - " -t --pty Run service on pseudo tty\n" + " -t --pty Run service on pseudo TTY as STDIN/STDOUT/\n" + " STDERR\n" + " -P --pipe Pass STDIN/STDOUT/STDERR directly to service\n" " -q --quiet Suppress information messages during runtime\n\n" "Timer options:\n" " --on-active=SECONDS Run after SECONDS delay\n" @@ -170,8 +177,9 @@ static int parse_argv(int argc, char *argv[]) { { "nice", required_argument, NULL, ARG_NICE }, { "setenv", required_argument, NULL, 'E' }, { "property", required_argument, NULL, 'p' }, - { "tty", no_argument, NULL, 't' }, /* deprecated */ + { "tty", no_argument, NULL, 't' }, /* deprecated alias */ { "pty", no_argument, NULL, 't' }, + { "pipe", no_argument, NULL, 'P' }, { "quiet", no_argument, NULL, 'q' }, { "on-active", required_argument, NULL, ARG_ON_ACTIVE }, { "on-boot", required_argument, NULL, ARG_ON_BOOT }, @@ -190,7 +198,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tq", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tPq", options, NULL)) >= 0) switch (c) { @@ -279,8 +287,18 @@ static int parse_argv(int argc, char *argv[]) { break; - case 't': - arg_pty = true; + case 't': /* --pty */ + if (IN_SET(arg_stdio, ARG_STDIO_DIRECT, ARG_STDIO_AUTO)) /* if --pipe is already used, upgrade to auto mode */ + arg_stdio = ARG_STDIO_AUTO; + else + arg_stdio = ARG_STDIO_PTY; + break; + + case 'P': /* --pipe */ + if (IN_SET(arg_stdio, ARG_STDIO_PTY, ARG_STDIO_AUTO)) /* If --pty is already used, upgrade to auto mode */ + arg_stdio = ARG_STDIO_AUTO; + else + arg_stdio = ARG_STDIO_DIRECT; break; case 'q': @@ -373,6 +391,16 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } + + if (arg_stdio == ARG_STDIO_AUTO) { + /* If we both --pty and --pipe are specified we'll automatically pick --pty if we are connected fully + * to a TTY and pick direct fd passing otherwise. This way, we automatically adapt to usage in a shell + * pipeline, but we are neatly interactive with tty-level isolation otherwise. */ + arg_stdio = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ? + ARG_STDIO_PTY : + ARG_STDIO_DIRECT; + } + if ((optind >= argc) && (!arg_unit || !with_timer())) { log_error("Command line to execute required."); return -EINVAL; @@ -393,18 +421,18 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (arg_pty && (with_timer() || arg_scope)) { - log_error("--pty is not compatible in timer or --scope mode."); + if (arg_stdio != ARG_STDIO_NONE && (with_timer() || arg_scope)) { + log_error("--pty/--pipe is not compatible in timer or --scope mode."); return -EINVAL; } - if (arg_pty && arg_transport == BUS_TRANSPORT_REMOTE) { - log_error("--pty is only supported when connecting to the local system or containers."); + if (arg_stdio != ARG_STDIO_NONE && arg_transport == BUS_TRANSPORT_REMOTE) { + log_error("--pty/--pipe is only supported when connecting to the local system or containers."); return -EINVAL; } - if (arg_pty && arg_no_block) { - log_error("--pty is not compatible with --no-block."); + if (arg_stdio != ARG_STDIO_NONE && arg_no_block) { + log_error("--pty/--pipe is not compatible with --no-block."); return -EINVAL; } @@ -481,6 +509,7 @@ static int transient_kill_set_properties(sd_bus_message *m) { } static int transient_service_set_properties(sd_bus_message *m, char **argv, const char *pty_path) { + bool send_term = false; int r; assert(m); @@ -497,7 +526,7 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons if (r < 0) return r; - if (arg_wait || arg_pty) { + if (arg_wait || arg_stdio != ARG_STDIO_NONE) { r = sd_bus_message_append(m, "(sv)", "AddRef", "b", 1); if (r < 0) return r; @@ -534,8 +563,6 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons } if (pty_path) { - const char *e; - r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)", "StandardInput", "s", "tty", @@ -545,6 +572,23 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons if (r < 0) return r; + send_term = true; + + } else if (arg_stdio == ARG_STDIO_DIRECT) { + r = sd_bus_message_append(m, + "(sv)(sv)(sv)", + "StandardInputFileDescriptor", "h", STDIN_FILENO, + "StandardOutputFileDescriptor", "h", STDOUT_FILENO, + "StandardErrorFileDescriptor", "h", STDERR_FILENO); + if (r < 0) + return r; + + send_term = isatty(STDIN_FILENO) || isatty(STDOUT_FILENO) || isatty(STDERR_FILENO); + } + + if (send_term) { + const char *e; + e = getenv("TERM"); if (e) { char *n; @@ -875,7 +919,7 @@ static int start_transient_service( assert(argv); assert(retval); - if (arg_pty) { + if (arg_stdio == ARG_STDIO_PTY) { if (arg_transport == BUS_TRANSPORT_LOCAL) { master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY); @@ -1000,7 +1044,7 @@ static int start_transient_service( if (!arg_quiet) log_info("Running as unit: %s", service); - if (arg_wait || master >= 0) { + if (arg_wait || arg_stdio != ARG_STDIO_NONE) { _cleanup_(run_context_free) RunContext c = {}; _cleanup_free_ char *path = NULL; const char *mt; @@ -1440,7 +1484,7 @@ int main(int argc, char* argv[]) { /* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the limited direct * connection */ - if (arg_wait || arg_pty) + if (arg_wait || arg_stdio != ARG_STDIO_NONE) r = bus_connect_transport(arg_transport, arg_host, arg_user, &bus); else r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus); diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index c024f64ca9..d6b119987c 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -273,7 +273,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen "Description", "Slice", "Type", "WorkingDirectory", "RootDirectory", "SyslogIdentifier", "ProtectSystem", "ProtectHome", "SELinuxContext", "Restart", "RootImage", - "NotifyAccess", "RuntimeDirectoryPreserve", "Personality")) + "NotifyAccess", "RuntimeDirectoryPreserve", "Personality", + "KeyringMode")) r = sd_bus_message_append(m, "v", "s", eq); else if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) { @@ -641,7 +642,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "v", "i", (int32_t) q); - } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) { + } else if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment")) { const char *p; r = sd_bus_message_open_container(m, 'v', "as"); @@ -668,6 +669,11 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen log_error("Invalid environment assignment: %s", word); return -EINVAL; } + } else if (streq(field, "UnsetEnvironment")) { + if (!env_assignment_is_valid(word) && !env_name_is_valid(word)) { + log_error("Invalid environment name or assignment: %s", word); + return -EINVAL; + } } else { /* PassEnvironment */ if (!env_name_is_valid(word)) { log_error("Invalid environment variable name: %s", word); diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index e08402e3d2..a84b61243e 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -436,7 +436,7 @@ int config_parse_many_nulstr( _cleanup_strv_free_ char **files = NULL; int r; - r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); + r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs); if (r < 0) return r; @@ -465,7 +465,7 @@ int config_parse_many( if (r < 0) return r; - r = conf_files_list_strv(&files, ".conf", NULL, (const char* const*) dropin_dirs); + r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs); if (r < 0) return r; @@ -725,6 +725,11 @@ int config_parse_path( assert(rvalue); assert(data); + if (isempty(rvalue)) { + n = NULL; + goto finalize; + } + if (!utf8_is_valid(rvalue)) { log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); return fatal ? -ENOEXEC : 0; @@ -743,22 +748,24 @@ int config_parse_path( path_kill_slashes(n); +finalize: free(*s); *s = n; return 0; } -int config_parse_strv(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_strv( + 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) { char ***sv = data; int r; @@ -769,19 +776,7 @@ int config_parse_strv(const char *unit, assert(data); if (isempty(rvalue)) { - char **empty; - - /* Empty assignment resets the list. As a special rule - * we actually fill in a real empty array here rather - * than NULL, since some code wants to know if - * something was set at all... */ - empty = new0(char*, 1); - if (!empty) - return log_oom(); - - strv_free(*sv); - *sv = empty; - + *sv = strv_free(*sv); return 0; } @@ -803,6 +798,7 @@ int config_parse_strv(const char *unit, free(word); continue; } + r = strv_consume(sv, word); if (r < 0) return log_oom(); @@ -920,10 +916,14 @@ int config_parse_personality( assert(rvalue); assert(personality); - p = personality_from_string(rvalue); - if (p == PERSONALITY_INVALID) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue); - return 0; + if (isempty(rvalue)) + p = PERSONALITY_INVALID; + else { + p = personality_from_string(rvalue); + if (p == PERSONALITY_INVALID) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue); + return 0; + } } *personality = p; diff --git a/src/shared/dropin.c b/src/shared/dropin.c index 7d8fbeb435..059b50dbd0 100644 --- a/src/shared/dropin.c +++ b/src/shared/dropin.c @@ -203,7 +203,7 @@ int unit_file_find_dropin_paths( return 0; } - r = conf_files_list_strv(ret, file_suffix, NULL, (const char**) dirs); + r = conf_files_list_strv(ret, file_suffix, NULL, 0, (const char**) dirs); if (r < 0) return log_warning_errno(r, "Failed to create the list of configuration files: %m"); diff --git a/src/shared/install.c b/src/shared/install.c index d0a291b819..9e4e0eaf6f 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -2662,7 +2662,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres assert(presets); if (scope == UNIT_FILE_SYSTEM) - r = conf_files_list(&files, ".preset", root_dir, + r = conf_files_list(&files, ".preset", root_dir, 0, "/etc/systemd/system-preset", "/usr/local/lib/systemd/system-preset", "/usr/lib/systemd/system-preset", @@ -2671,7 +2671,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres #endif NULL); else if (scope == UNIT_FILE_GLOBAL) - r = conf_files_list(&files, ".preset", root_dir, + r = conf_files_list(&files, ".preset", root_dir, 0, "/etc/systemd/user-preset", "/usr/local/lib/systemd/user-preset", "/usr/lib/systemd/user-preset", diff --git a/src/shared/pager.c b/src/shared/pager.c index ad48652527..0661ff0bb9 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -84,7 +84,7 @@ int pager_open(bool no_pager, bool jump_to_end) { * pager so that we get the value from the actual tty */ (void) columns(); - if (pipe(fd) < 0) + if (pipe2(fd, O_CLOEXEC) < 0) return log_error_errno(errno, "Failed to create pager pipe: %m"); parent_pid = getpid_cached(); diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index e2b3f8b742..a289511be5 100644 --- a/src/shared/path-lookup.c +++ b/src/shared/path-lookup.c @@ -23,6 +23,8 @@ #include <string.h> #include "alloc-util.h" +#include "fileio.h" +#include "fs-util.h" #include "install.h" #include "log.h" #include "macro.h" @@ -226,50 +228,48 @@ static char** user_dirs( static int acquire_generator_dirs( UnitFileScope scope, + const char *tempdir, char **generator, char **generator_early, char **generator_late) { + _cleanup_(rmdir_and_freep) char *t = NULL; _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL; const char *prefix; assert(generator); assert(generator_early); assert(generator_late); + assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL)); - switch (scope) { + if (scope == UNIT_FILE_GLOBAL) + return -EOPNOTSUPP; - case UNIT_FILE_SYSTEM: - prefix = "/run/systemd/"; - break; + if (tempdir) + prefix = tempdir; + + else if (scope == UNIT_FILE_SYSTEM) + prefix = "/run/systemd"; - case UNIT_FILE_USER: { + else if (scope == UNIT_FILE_USER) { const char *e; e = getenv("XDG_RUNTIME_DIR"); if (!e) return -ENXIO; - prefix = strjoina(e, "/systemd/"); - break; - } - - case UNIT_FILE_GLOBAL: - return -EOPNOTSUPP; - - default: - assert_not_reached("Hmm, unexpected scope value."); + prefix = strjoina(e, "/systemd"); } - x = strappend(prefix, "generator"); + x = strappend(prefix, "/generator"); if (!x) return -ENOMEM; - y = strappend(prefix, "generator.early"); + y = strappend(prefix, "/generator.early"); if (!y) return -ENOMEM; - z = strappend(prefix, "generator.late"); + z = strappend(prefix, "/generator.late"); if (!z) return -ENOMEM; @@ -281,31 +281,30 @@ static int acquire_generator_dirs( return 0; } -static int acquire_transient_dir(UnitFileScope scope, char **ret) { - assert(ret); - - switch (scope) { +static int acquire_transient_dir( + UnitFileScope scope, + const char *tempdir, + char **ret) { - case UNIT_FILE_SYSTEM: { - char *transient; + char *transient; - transient = strdup("/run/systemd/transient"); - if (!transient) - return -ENOMEM; + assert(ret); + assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL)); - *ret = transient; - return 0; - } + if (scope == UNIT_FILE_GLOBAL) + return -EOPNOTSUPP; - case UNIT_FILE_USER: + if (tempdir) + transient = strjoin(tempdir, "/transient"); + else if (scope == UNIT_FILE_SYSTEM) + transient = strdup("/run/systemd/transient"); + else return user_runtime_dir(ret, "/systemd/transient"); - case UNIT_FILE_GLOBAL: - return -EOPNOTSUPP; - - default: - assert_not_reached("Hmm, unexpected scope value."); - } + if (!transient) + return -ENOMEM; + *ret = transient; + return 0; } static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) { @@ -457,6 +456,7 @@ int lookup_paths_init( LookupPathsFlags flags, const char *root_dir) { + _cleanup_(rmdir_and_freep) char *tempdir = NULL; _cleanup_free_ char *root = NULL, *persistent_config = NULL, *runtime_config = NULL, @@ -487,6 +487,12 @@ int lookup_paths_init( return -ENOMEM; } + if (flags & LOOKUP_PATHS_TEMPORARY_GENERATED) { + r = mkdtemp_malloc("/tmp/systemd-temporary-XXXXXX", &tempdir); + if (r < 0) + return log_error_errno(r, "Failed to create temporary directory: %m"); + } + /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_config to NULL */ r = acquire_config_dirs(scope, &persistent_config, &runtime_config); if (r < 0) @@ -494,13 +500,14 @@ int lookup_paths_init( if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) { /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */ - r = acquire_generator_dirs(scope, &generator, &generator_early, &generator_late); + r = acquire_generator_dirs(scope, tempdir, + &generator, &generator_early, &generator_late); if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) return r; } /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */ - r = acquire_transient_dir(scope, &transient); + r = acquire_transient_dir(scope, tempdir, &transient); if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) return r; @@ -669,6 +676,9 @@ int lookup_paths_init( p->root_dir = root; root = NULL; + p->temporary_dir = tempdir; + tempdir = NULL; + return 0; } @@ -691,6 +701,7 @@ void lookup_paths_free(LookupPaths *p) { p->runtime_control = mfree(p->runtime_control); p->root_dir = mfree(p->root_dir); + p->temporary_dir = mfree(p->temporary_dir); } int lookup_paths_reduce(LookupPaths *p) { @@ -811,6 +822,9 @@ void lookup_paths_flush_generator(LookupPaths *p) { (void) rm_rf(p->generator_early, REMOVE_ROOT); if (p->generator_late) (void) rm_rf(p->generator_late, REMOVE_ROOT); + + if (p->temporary_dir) + (void) rm_rf(p->temporary_dir, REMOVE_ROOT); } char **generator_binary_paths(UnitFileScope scope) { diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h index f9bb2fe237..45fba64d2a 100644 --- a/src/shared/path-lookup.h +++ b/src/shared/path-lookup.h @@ -28,6 +28,7 @@ typedef struct LookupPaths LookupPaths; typedef enum LookupPathsFlags { LOOKUP_PATHS_EXCLUDE_GENERATED = 1, + LOOKUP_PATHS_TEMPORARY_GENERATED, } LookupPathsFlags; struct LookupPaths { @@ -60,6 +61,9 @@ struct LookupPaths { /* The root directory prepended to all items above, or NULL */ char *root_dir; + + /* A temporary directory when running in test mode, to be nuked */ + char *temporary_dir; }; int lookup_paths_init(LookupPaths *p, UnitFileScope scope, LookupPathsFlags flags, const char *root_dir); diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 0857f5907e..5e78a088e9 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -278,11 +278,19 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "execve\0" "exit\0" "exit_group\0" + "futex\0" + "get_robust_list\0" + "get_thread_area\0" "getrlimit\0" /* make sure processes can query stack size and such */ "gettimeofday\0" + "membarrier\0" "nanosleep\0" "pause\0" + "restart_syscall\0" "rt_sigreturn\0" + "set_robust_list\0" + "set_thread_area\0" + "set_tid_address\0" "sigreturn\0" "time\0" }, @@ -290,10 +298,11 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { .name = "@basic-io", .help = "Basic IO", .value = + "_llseek\0" "close\0" + "dup\0" "dup2\0" "dup3\0" - "dup\0" "lseek\0" "pread64\0" "preadv\0" @@ -324,6 +333,32 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "vm86\0" "vm86old\0" }, + [SYSCALL_FILTER_SET_CREDENTIALS] = { + .name = "@credentials", + .help = "Query own process credentials", + .value = + "capget\0" + "getegid\0" + "getegid32\0" + "geteuid\0" + "geteuid32\0" + "getgid\0" + "getgid32\0" + "getgroups\0" + "getgroups32\0" + "getpgid\0" + "getpgrp\0" + "getpid\0" + "getppid\0" + "getresgid\0" + "getresgid32\0" + "getresuid\0" + "getresuid32\0" + "getsid\0" + "gettid\0" + "getuid\0" + "getuid32\0" + }, [SYSCALL_FILTER_SET_DEBUG] = { .name = "@debug", .help = "Debugging, performance monitoring and tracing functionality", @@ -353,24 +388,26 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "fchdir\0" "fchmod\0" "fchmodat\0" - "fcntl64\0" "fcntl\0" + "fcntl64\0" "fgetxattr\0" "flistxattr\0" + "fremovexattr\0" "fsetxattr\0" - "fstat64\0" "fstat\0" + "fstat64\0" "fstatat64\0" - "fstatfs64\0" "fstatfs\0" - "ftruncate64\0" + "fstatfs64\0" "ftruncate\0" + "ftruncate64\0" "futimesat\0" "getcwd\0" - "getdents64\0" "getdents\0" + "getdents64\0" "getxattr\0" "inotify_add_watch\0" + "inotify_init\0" "inotify_init1\0" "inotify_rm_watch\0" "lgetxattr\0" @@ -380,36 +417,43 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "llistxattr\0" "lremovexattr\0" "lsetxattr\0" - "lstat64\0" "lstat\0" + "lstat64\0" "mkdir\0" "mkdirat\0" "mknod\0" "mknodat\0" - "mmap2\0" "mmap\0" + "mmap2\0" "munmap\0" "newfstatat\0" + "oldfstat\0" + "oldlstat\0" + "oldstat\0" "open\0" "openat\0" "readlink\0" "readlinkat\0" "removexattr\0" "rename\0" - "renameat2\0" "renameat\0" + "renameat2\0" "rmdir\0" "setxattr\0" - "stat64\0" "stat\0" + "stat64\0" "statfs\0" + "statfs64\0" +#ifdef __PNR_statx "statx\0" +#endif "symlink\0" "symlinkat\0" - "truncate64\0" "truncate\0" + "truncate64\0" "unlink\0" "unlinkat\0" + "utime\0" "utimensat\0" "utimes\0" }, @@ -418,15 +462,15 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { .help = "Event loop system calls", .value = "_newselect\0" - "epoll_create1\0" "epoll_create\0" + "epoll_create1\0" "epoll_ctl\0" "epoll_ctl_old\0" "epoll_pwait\0" "epoll_wait\0" "epoll_wait_old\0" - "eventfd2\0" "eventfd\0" + "eventfd2\0" "poll\0" "ppoll\0" "pselect6\0" @@ -448,8 +492,8 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "msgget\0" "msgrcv\0" "msgsnd\0" - "pipe2\0" "pipe\0" + "pipe2\0" "process_vm_readv\0" "process_vm_writev\0" "semctl\0" @@ -469,6 +513,16 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "keyctl\0" "request_key\0" }, + [SYSCALL_FILTER_SET_MEMLOCK] = { + .name = "@memlock", + .help = "Memory locking control", + .value = + "mlock\0" + "mlock2\0" + "mlockall\0" + "munlock\0" + "munlockall\0" + }, [SYSCALL_FILTER_SET_MODULE] = { .name = "@module", .help = "Loading and unloading of kernel modules", @@ -484,15 +538,15 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "chroot\0" "mount\0" "pivot_root\0" - "umount2\0" "umount\0" + "umount2\0" }, [SYSCALL_FILTER_SET_NETWORK_IO] = { .name = "@network-io", .help = "Network or Unix socket IO, should not be needed if not network facing", .value = - "accept4\0" "accept\0" + "accept4\0" "bind\0" "connect\0" "getpeername\0" @@ -527,6 +581,7 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "get_kernel_syms\0" "getpmsg\0" "gtty\0" + "idle\0" "lock\0" "mpx\0" "prof\0" @@ -551,38 +606,38 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "@clock\0" "@module\0" "@raw-io\0" + "_sysctl\0" "acct\0" "bpf\0" "capset\0" - "chown32\0" "chown\0" + "chown32\0" "chroot\0" - "fchown32\0" "fchown\0" + "fchown32\0" "fchownat\0" "kexec_file_load\0" "kexec_load\0" - "lchown32\0" "lchown\0" + "lchown32\0" "nfsservctl\0" "pivot_root\0" "quotactl\0" "reboot\0" "setdomainname\0" - "setfsuid32\0" "setfsuid\0" - "setgroups32\0" + "setfsuid32\0" "setgroups\0" + "setgroups32\0" "sethostname\0" - "setresuid32\0" "setresuid\0" - "setreuid32\0" + "setresuid32\0" "setreuid\0" - "setuid32\0" + "setreuid32\0" "setuid\0" + "setuid32\0" "swapoff\0" "swapon\0" - "_sysctl\0" "vhangup\0" }, [SYSCALL_FILTER_SET_PROCESS] = { @@ -593,13 +648,23 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "clone\0" "execveat\0" "fork\0" + "getpid\0" + "getppid\0" + "getrusage\0" + "gettid\0" "kill\0" "prctl\0" + "rt_sigqueueinfo\0" + "rt_tgsigqueueinfo\0" "setns\0" "tgkill\0" + "times\0" "tkill\0" "unshare\0" "vfork\0" + "wait4\0" + "waitid\0" + "waitpid\0" }, [SYSCALL_FILTER_SET_RAW_IO] = { .name = "@raw-io", @@ -629,36 +694,56 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { .name = "@resources", .help = "Alter resource settings", .value = + "ioprio_set\0" + "mbind\0" + "migrate_pages\0" + "move_pages\0" + "nice\0" + "prlimit64\0" + "sched_setaffinity\0" + "sched_setattr\0" "sched_setparam\0" "sched_setscheduler\0" - "sched_setaffinity\0" + "set_mempolicy\0" "setpriority\0" "setrlimit\0" - "set_mempolicy\0" - "migrate_pages\0" - "move_pages\0" - "mbind\0" - "sched_setattr\0" - "prlimit64\0" }, [SYSCALL_FILTER_SET_SETUID] = { .name = "@setuid", .help = "Operations for changing user/group credentials", .value = - "setgid32\0" "setgid\0" - "setgroups32\0" + "setgid32\0" "setgroups\0" - "setregid32\0" + "setgroups32\0" "setregid\0" - "setresgid32\0" + "setregid32\0" "setresgid\0" - "setresuid32\0" + "setresgid32\0" "setresuid\0" - "setreuid32\0" + "setresuid32\0" "setreuid\0" - "setuid32\0" + "setreuid32\0" "setuid\0" + "setuid32\0" + }, + [SYSCALL_FILTER_SET_SIGNAL] = { + .name = "@signal", + .help = "Process signal handling", + .value = + "rt_sigaction\0" + "rt_sigpending\0" + "rt_sigprocmask\0" + "rt_sigsuspend\0" + "rt_sigtimedwait\0" + "sigaction\0" + "sigaltstack\0" + "signal\0" + "signalfd\0" + "signalfd4\0" + "sigpending\0" + "sigprocmask\0" + "sigsuspend\0" }, [SYSCALL_FILTER_SET_SWAP] = { .name = "@swap", @@ -667,6 +752,23 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "swapoff\0" "swapon\0" }, + [SYSCALL_FILTER_SET_TIMER] = { + .name = "@timer", + .help = "Schedule operations by time", + .value = + "alarm\0" + "getitimer\0" + "setitimer\0" + "timer_create\0" + "timer_delete\0" + "timer_getoverrun\0" + "timer_gettime\0" + "timer_settime\0" + "timerfd_create\0" + "timerfd_gettime\0" + "timerfd_settime\0" + "times\0" + }, }; const SyscallFilterSet *syscall_filter_set_find(const char *name) { @@ -682,11 +784,52 @@ const SyscallFilterSet *syscall_filter_set_find(const char *name) { return NULL; } +static int seccomp_add_syscall_filter_set(scmp_filter_ctx seccomp, const SyscallFilterSet *set, uint32_t action, char **exclude); + +int seccomp_add_syscall_filter_item(scmp_filter_ctx *seccomp, const char *name, uint32_t action, char **exclude) { + int r; + + assert(seccomp); + assert(name); + + if (strv_contains(exclude, name)) + return 0; + + if (name[0] == '@') { + const SyscallFilterSet *other; + + other = syscall_filter_set_find(name); + if (!other) { + log_debug("Filter set %s is not known!", name); + return -EINVAL; + } + + r = seccomp_add_syscall_filter_set(seccomp, other, action, exclude); + if (r < 0) + return r; + } else { + int id; + + id = seccomp_syscall_resolve_name(name); + if (id == __NR_SCMP_ERROR) { + log_debug("System call %s is not known!", name); + return -EINVAL; /* Not known at all? Then that's a real error */ + } + + r = seccomp_rule_add_exact(seccomp, action, id, 0); + if (r < 0) + /* If the system call is not known on this architecture, then that's fine, let's ignore it */ + log_debug_errno(r, "Failed to add rule for system call %s() / %d, ignoring: %m", name, id); + } + + return 0; +} + static int seccomp_add_syscall_filter_set( scmp_filter_ctx seccomp, - uint32_t default_action, const SyscallFilterSet *set, - uint32_t action) { + uint32_t action, + char **exclude) { const char *sys; int r; @@ -695,28 +838,9 @@ static int seccomp_add_syscall_filter_set( assert(set); NULSTR_FOREACH(sys, set->value) { - int id; - - if (sys[0] == '@') { - const SyscallFilterSet *other; - - other = syscall_filter_set_find(sys); - if (!other) - return -EINVAL; - - r = seccomp_add_syscall_filter_set(seccomp, default_action, other, action); - if (r < 0) - return r; - } else { - id = seccomp_syscall_resolve_name(sys); - if (id == __NR_SCMP_ERROR) - return -EINVAL; /* Not known at all? Then that's a real error */ - - r = seccomp_rule_add_exact(seccomp, action, id, 0); - if (r < 0) - /* If the system call is not known on this architecture, then that's fine, let's ignore it */ - log_debug_errno(r, "Failed to add rule for system call %s() / %d, ignoring: %m", sys, id); - } + r = seccomp_add_syscall_filter_item(seccomp, sys, action, exclude); + if (r < 0) + return r; } return 0; @@ -740,7 +864,7 @@ int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilter if (r < 0) return r; - r = seccomp_add_syscall_filter_set(seccomp, default_action, set, action); + r = seccomp_add_syscall_filter_set(seccomp, set, action, NULL); if (r < 0) { log_debug_errno(r, "Failed to add filter set, ignoring: %m"); continue; diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h index ca43ba8659..d4ff027df8 100644 --- a/src/shared/seccomp-util.h +++ b/src/shared/seccomp-util.h @@ -44,11 +44,13 @@ enum { SYSCALL_FILTER_SET_BASIC_IO, SYSCALL_FILTER_SET_CLOCK, SYSCALL_FILTER_SET_CPU_EMULATION, + SYSCALL_FILTER_SET_CREDENTIALS, SYSCALL_FILTER_SET_DEBUG, SYSCALL_FILTER_SET_FILE_SYSTEM, SYSCALL_FILTER_SET_IO_EVENT, SYSCALL_FILTER_SET_IPC, SYSCALL_FILTER_SET_KEYRING, + SYSCALL_FILTER_SET_MEMLOCK, SYSCALL_FILTER_SET_MODULE, SYSCALL_FILTER_SET_MOUNT, SYSCALL_FILTER_SET_NETWORK_IO, @@ -59,7 +61,9 @@ enum { SYSCALL_FILTER_SET_REBOOT, SYSCALL_FILTER_SET_RESOURCES, SYSCALL_FILTER_SET_SETUID, + SYSCALL_FILTER_SET_SIGNAL, SYSCALL_FILTER_SET_SWAP, + SYSCALL_FILTER_SET_TIMER, _SYSCALL_FILTER_SET_MAX }; @@ -69,6 +73,8 @@ const SyscallFilterSet *syscall_filter_set_find(const char *name); int seccomp_filter_set_add(Set *s, bool b, const SyscallFilterSet *set); +int seccomp_add_syscall_filter_item(scmp_filter_ctx *ctx, const char *name, uint32_t action, char **exclude); + int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action); int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Set* set, uint32_t action); diff --git a/src/shared/specifier.c b/src/shared/specifier.c index 1c17eb5251..81379041cc 100644 --- a/src/shared/specifier.c +++ b/src/shared/specifier.c @@ -107,6 +107,10 @@ int specifier_printf(const char *text, const Specifier table[], void *userdata, *(t++) = *f; } + /* if string ended with a stray %, also end with % */ + if (percent) + *(t++) = '%'; + *t = 0; *_ret = ret; return 0; diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index 3bac78b3e4..4c171a2fe5 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -91,7 +91,10 @@ static int execute(char **modes, char **states) { arg_verb, NULL }; - static const char* const dirs[] = {SYSTEM_SLEEP_PATH, NULL}; + static const char* const dirs[] = { + SYSTEM_SLEEP_PATH, + NULL + }; int r; _cleanup_fclose_ FILE *f = NULL; diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c index 41a6dcbd1b..ce4ff8c8a3 100644 --- a/src/sysctl/sysctl.c +++ b/src/sysctl/sysctl.c @@ -280,7 +280,7 @@ int main(int argc, char *argv[]) { _cleanup_strv_free_ char **files = NULL; char **f; - r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); + r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs); if (r < 0) { log_error_errno(r, "Failed to enumerate sysctl.d files: %m"); goto finish; diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index 3f5a6673cf..b28fc0da00 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -138,6 +138,7 @@ int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope); int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags); int sd_rtnl_message_route_set_table(sd_netlink_message *m, unsigned char table); +int sd_rtnl_message_route_set_type(sd_netlink_message *m, unsigned char type); int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags); int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family); int sd_rtnl_message_route_set_family(sd_netlink_message *m, int family); @@ -147,6 +148,7 @@ int sd_rtnl_message_route_get_tos(sd_netlink_message *m, unsigned char *tos); int sd_rtnl_message_route_get_table(sd_netlink_message *m, unsigned char *table); int sd_rtnl_message_route_get_dst_prefixlen(sd_netlink_message *m, unsigned char *dst_len); int sd_rtnl_message_route_get_src_prefixlen(sd_netlink_message *m, unsigned char *src_len); +int sd_rtnl_message_route_get_type(sd_netlink_message *m, unsigned char *type); int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags); int sd_rtnl_message_neigh_set_state(sd_netlink_message *m, uint16_t state); @@ -159,6 +161,18 @@ int sd_rtnl_message_new_addrlabel(sd_netlink *rtnl, sd_netlink_message **ret, ui int sd_rtnl_message_addrlabel_set_prefixlen(sd_netlink_message *m, unsigned char prefixlen); int sd_rtnl_message_addrlabel_get_prefixlen(sd_netlink_message *m, unsigned char *prefixlen); +int sd_rtnl_message_new_routing_policy_rule(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int ifal_family); +int sd_rtnl_message_routing_policy_rule_set_tos(sd_netlink_message *m, unsigned char tos); +int sd_rtnl_message_routing_policy_rule_get_tos(sd_netlink_message *m, unsigned char *tos); +int sd_rtnl_message_routing_policy_rule_set_table(sd_netlink_message *m, unsigned char table); +int sd_rtnl_message_routing_policy_rule_get_table(sd_netlink_message *m, unsigned char *table); +int sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(sd_netlink_message *m, unsigned char len); +int sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(sd_netlink_message *m, unsigned char *len); +int sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(sd_netlink_message *m, unsigned char len); +int sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(sd_netlink_message *m, unsigned char *len); +int sd_rtnl_message_routing_policy_rule_set_rtm_type(sd_netlink_message *m, unsigned char type); +int sd_rtnl_message_routing_policy_rule_get_rtm_type(sd_netlink_message *m, unsigned char *type); + _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink, sd_netlink_unref); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink_message, sd_netlink_message_unref); diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h index ce10ae0606..0cc1d670b9 100644 --- a/src/systemd/sd-radv.h +++ b/src/systemd/sd-radv.h @@ -59,6 +59,7 @@ int sd_radv_set_preference(sd_radv *ra, unsigned preference); int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p); int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime, const struct in6_addr *dns, size_t n_dns); +int sd_radv_set_dnssl(sd_radv *ra, uint32_t lifetime, char **search_list); /* Advertised prefixes */ int sd_radv_prefix_new(sd_radv_prefix **ret); diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 448f584733..e9e6dae10c 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -1798,7 +1798,7 @@ int main(int argc, char *argv[]) { _cleanup_strv_free_ char **files = NULL; char **f; - r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs); + r = conf_files_list_nulstr(&files, ".conf", arg_root, 0, conf_file_dirs); if (r < 0) { log_error_errno(r, "Failed to enumerate sysusers.d files: %m"); goto finish; @@ -1811,6 +1811,16 @@ int main(int argc, char *argv[]) { } } + /* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our detection + * whether the names or UID/GID area already used otherwise doesn't get confused. After all, even though + * nss-systemd synthesizes these users/groups, they should still appear in /etc/passwd and /etc/group, as the + * synthesizing logic is merely supposed to be fallback for cases where we run with a completely unpopulated + * /etc. */ + if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0) { + r = log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m"); + goto finish; + } + if (!uid_range) { /* Default to default range of 1..SYSTEMD_UID_MAX */ r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX); diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c index a026ce4ef1..bd5bebe66d 100644 --- a/src/test/test-calendarspec.c +++ b/src/test/test-calendarspec.c @@ -170,6 +170,8 @@ int main(int argc, char* argv[]) { test_one("annually", "*-01-01 00:00:00"); test_one("*:2/3", "*-*-* *:02/3:00"); test_one("2015-10-25 01:00:00 uTc", "2015-10-25 01:00:00 UTC"); + test_one("2015-10-25 01:00:00 Asia/Vladivostok", "2015-10-25 01:00:00 Asia/Vladivostok"); + test_one("weekly Pacific/Auckland", "Mon *-*-* 00:00:00 Pacific/Auckland"); test_one("2016-03-27 03:17:00.4200005", "2016-03-27 03:17:00.420001"); test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000"); test_one("9..11,13:00,30", "*-*-* 09..11,13:00,30:00"); @@ -219,6 +221,16 @@ int main(int argc, char* argv[]) { test_next("2017-08-06 9,11,13,15,17:00 UTC", "", 1502029800000000, 1502031600000000); test_next("2017-08-06 9..17/2:00 UTC", "", 1502029800000000, 1502031600000000); test_next("2016-12-* 3..21/6:00 UTC", "", 1482613200000001, 1482634800000000); + test_next("2017-09-24 03:30:00 Pacific/Auckland", "", 12345, 1506177000000000); + // Due to daylight saving time - 2017-09-24 02:30:00 does not exist + test_next("2017-09-24 02:30:00 Pacific/Auckland", "", 12345, -1); + test_next("2017-04-02 02:30:00 Pacific/Auckland", "", 12345, 1491053400000000); + // Confirm that even though it's a time change here (backward) 02:30 happens only once + test_next("2017-04-02 02:30:00 Pacific/Auckland", "", 1491053400000000, -1); + test_next("2017-04-02 03:30:00 Pacific/Auckland", "", 12345, 1491060600000000); + // Confirm that timezones in the Spec work regardless of current timezone + test_next("2017-09-09 20:42:00 Pacific/Auckland", "", 12345, 1504946520000000); + test_next("2017-09-09 20:42:00 Pacific/Auckland", "EET", 12345, 1504946520000000); assert_se(calendar_spec_from_string("test", &c) < 0); assert_se(calendar_spec_from_string(" utc", &c) < 0); diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c index 4a13d710dc..15d76bddda 100644 --- a/src/test/test-cgroup-mask.c +++ b/src/test/test-cgroup-mask.c @@ -39,7 +39,7 @@ static int test_cgroup_mask(void) { /* Prepare the manager. */ assert_se(set_unit_path(get_testdata_dir("")) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - r = manager_new(UNIT_FILE_USER, true, &m); + r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); if (r == -EPERM || r == -EACCES) { puts("manager_new: Permission denied. Skipping test."); return EXIT_TEST_SKIP; diff --git a/src/test/test-conf-files.c b/src/test/test-conf-files.c index 22b7c61204..777b5ca32b 100644 --- a/src/test/test-conf-files.c +++ b/src/test/test-conf-files.c @@ -75,7 +75,7 @@ static void test_conf_files_list(bool use_root) { log_debug("/* Check when filtered by suffix */"); - assert_se(conf_files_list(&found_files, ".conf", root_dir, search_1, search_2, NULL) == 0); + assert_se(conf_files_list(&found_files, ".conf", root_dir, 0, search_1, search_2, NULL) == 0); strv_print(found_files); assert_se(found_files); @@ -84,7 +84,7 @@ static void test_conf_files_list(bool use_root) { assert_se(found_files[2] == NULL); log_debug("/* Check when unfiltered */"); - assert_se(conf_files_list(&found_files2, NULL, root_dir, search_1, search_2, NULL) == 0); + assert_se(conf_files_list(&found_files2, NULL, root_dir, 0, search_1, search_2, NULL) == 0); strv_print(found_files2); assert_se(found_files2); diff --git a/src/test/test-date.c b/src/test/test-date.c index 0e7d44fade..c09d615396 100644 --- a/src/test/test-date.c +++ b/src/test/test-date.c @@ -33,6 +33,12 @@ static void test_should_pass(const char *p) { log_info("\"%s\" → \"%s\"", p, buf); assert_se(parse_timestamp(buf, &q) >= 0); + if (q != t) { + char tmp[FORMAT_TIMESTAMP_MAX]; + + log_error("round-trip failed: \"%s\" → \"%s\"", + buf, format_timestamp_us(tmp, sizeof(tmp), q)); + } assert_se(q == t); assert_se(format_timestamp_relative(buf_relative, sizeof(buf_relative), t)); @@ -77,6 +83,10 @@ static void test_one_noutc(const char *p) { } int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + test_one("17:41"); test_one("18:42:44"); test_one("18:42:44.0"); @@ -90,6 +100,10 @@ int main(int argc, char *argv[]) { test_one("yesterday"); test_one("today"); test_one("tomorrow"); + test_one_noutc("16:20 UTC"); + test_one_noutc("16:20 Asia/Seoul"); + test_one_noutc("tomorrow Asia/Seoul"); + test_one_noutc("2012-12-30 18:42 Asia/Seoul"); test_one_noutc("now"); test_one_noutc("+2d"); test_one_noutc("+2y 4d"); @@ -100,6 +114,9 @@ int main(int argc, char *argv[]) { test_should_fail("1969-12-31 UTC"); test_should_fail("-100y"); test_should_fail("today UTC UTC"); + test_should_fail("now Asia/Seoul"); + test_should_fail("+2d Asia/Seoul"); + test_should_fail("@1395716396 Asia/Seoul"); #if SIZEOF_TIME_T == 8 test_should_pass("9999-12-30 23:59:59 UTC"); test_should_fail("9999-12-31 00:00:00 UTC"); diff --git a/src/test/test-engine.c b/src/test/test-engine.c index b5d3da0d38..6916f838d4 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -42,7 +42,7 @@ int main(int argc, char *argv[]) { /* prepare the test */ assert_se(set_unit_path(get_testdata_dir("")) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - r = manager_new(UNIT_FILE_USER, true, &m); + r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); if (MANAGER_SKIP_TEST(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); return EXIT_TEST_SKIP; diff --git a/src/test/test-exec-util.c b/src/test/test-exec-util.c index 30c92019d9..c192d3522c 100644 --- a/src/test/test-exec-util.c +++ b/src/test/test-exec-util.c @@ -71,10 +71,14 @@ static const gather_stdout_callback_t ignore_stdout[] = { }; static void test_execute_directory(bool gather_stdout) { - char template_lo[] = "/tmp/test-exec-util.XXXXXXX"; - char template_hi[] = "/tmp/test-exec-util.XXXXXXX"; + char template_lo[] = "/tmp/test-exec-util.lo.XXXXXXX"; + char template_hi[] = "/tmp/test-exec-util.hi.XXXXXXX"; const char * dirs[] = {template_hi, template_lo, NULL}; - const char *name, *name2, *name3, *overridden, *override, *masked, *mask; + const char *name, *name2, *name3, + *overridden, *override, + *masked, *mask, + *masked2, *mask2, /* the mask is non-executable */ + *masked2e, *mask2e; /* the mask is executable */ log_info("/* %s (%s) */", __func__, gather_stdout ? "gathering stdout" : "asynchronous"); @@ -88,6 +92,10 @@ static void test_execute_directory(bool gather_stdout) { override = strjoina(template_hi, "/overridden"); masked = strjoina(template_lo, "/masked"); mask = strjoina(template_hi, "/masked"); + masked2 = strjoina(template_lo, "/masked2"); + mask2 = strjoina(template_hi, "/masked2"); + masked2e = strjoina(template_lo, "/masked2e"); + mask2e = strjoina(template_hi, "/masked2e"); assert_se(write_string_file(name, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works", @@ -104,7 +112,15 @@ static void test_execute_directory(bool gather_stdout) { assert_se(write_string_file(masked, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed", WRITE_STRING_FILE_CREATE) == 0); + assert_se(write_string_file(masked2, + "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed", + WRITE_STRING_FILE_CREATE) == 0); + assert_se(write_string_file(masked2e, + "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed", + WRITE_STRING_FILE_CREATE) == 0); assert_se(symlink("/dev/null", mask) == 0); + assert_se(touch(mask2) == 0); + assert_se(touch(mask2e) == 0); assert_se(touch(name3) >= 0); assert_se(chmod(name, 0755) == 0); @@ -112,6 +128,9 @@ static void test_execute_directory(bool gather_stdout) { assert_se(chmod(overridden, 0755) == 0); assert_se(chmod(override, 0755) == 0); assert_se(chmod(masked, 0755) == 0); + assert_se(chmod(masked2, 0755) == 0); + assert_se(chmod(masked2e, 0755) == 0); + assert_se(chmod(mask2e, 0755) == 0); if (gather_stdout) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL); diff --git a/src/test/test-execute.c b/src/test/test-execute.c index ac2cdd50ed..a38efc7cba 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -453,6 +453,10 @@ static void test_exec_read_only_path_suceed(Manager *m) { test(m, "exec-read-only-path-succeed.service", 0, CLD_EXITED); } +static void test_exec_unset_environment(Manager *m) { + test(m, "exec-unset-environment.service", 0, CLD_EXITED); +} + static int run_tests(UnitFileScope scope, const test_function_t *tests) { const test_function_t *test = NULL; Manager *m = NULL; @@ -460,7 +464,7 @@ static int run_tests(UnitFileScope scope, const test_function_t *tests) { assert_se(tests); - r = manager_new(scope, true, &m); + r = manager_new(scope, MANAGER_TEST_RUN_MINIMAL, &m); if (MANAGER_SKIP_TEST(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); return EXIT_TEST_SKIP; @@ -508,6 +512,7 @@ int main(int argc, char *argv[]) { test_exec_ioschedulingclass, test_exec_spec_interpolation, test_exec_read_only_path_suceed, + test_exec_unset_environment, NULL, }; static const test_function_t system_tests[] = { diff --git a/src/test/test-path.c b/src/test/test-path.c index 6fc3f79d80..c1915017df 100644 --- a/src/test/test-path.c +++ b/src/test/test-path.c @@ -47,7 +47,7 @@ static int setup_test(Manager **m) { enter_cgroup_subroot(); - r = manager_new(UNIT_FILE_USER, true, &tmp); + r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &tmp); if (MANAGER_SKIP_TEST(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); return -EXIT_TEST_SKIP; diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c index 062a02baf5..9bed4b3832 100644 --- a/src/test/test-sched-prio.c +++ b/src/test/test-sched-prio.c @@ -39,7 +39,7 @@ int main(int argc, char *argv[]) { /* prepare the test */ assert_se(set_unit_path(get_testdata_dir("")) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - r = manager_new(UNIT_FILE_USER, true, &m); + r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); if (MANAGER_SKIP_TEST(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); return EXIT_TEST_SKIP; diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c index 5056a08117..4d63b68809 100644 --- a/src/test/test-seccomp.c +++ b/src/test/test-seccomp.c @@ -612,6 +612,36 @@ static void test_lock_personality(void) { assert_se(wait_for_terminate_and_warn("lockpersonalityseccomp", pid, true) == EXIT_SUCCESS); } +static void test_filter_sets_ordered(void) { + size_t i; + + /* Ensure "@default" always remains at the beginning of the list */ + assert_se(SYSCALL_FILTER_SET_DEFAULT == 0); + assert_se(streq(syscall_filter_sets[0].name, "@default")); + + for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) { + const char *k, *p = NULL; + + /* Make sure each group has a description */ + assert_se(!isempty(syscall_filter_sets[0].help)); + + /* Make sure the groups are ordered alphabetically, except for the first entry */ + assert_se(i < 2 || strcmp(syscall_filter_sets[i-1].name, syscall_filter_sets[i].name) < 0); + + NULSTR_FOREACH(k, syscall_filter_sets[i].value) { + + /* Ensure each syscall list is in itself ordered, but groups before names */ + assert_se(!p || + (*p == '@' && *k != '@') || + (((*p == '@' && *k == '@') || + (*p != '@' && *k != '@')) && + strcmp(p, k) < 0)); + + p = k; + } + } +} + int main(int argc, char *argv[]) { log_set_max_level(LOG_DEBUG); @@ -629,6 +659,7 @@ int main(int argc, char *argv[]) { test_restrict_archs(); test_load_syscall_filter_set_raw(); test_lock_personality(); + test_filter_sets_ordered(); return 0; } diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index df2d9593e8..578e09447f 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -115,7 +115,7 @@ static void test_config_parse_exec(void) { Manager *m = NULL; Unit *u = NULL; - r = manager_new(UNIT_FILE_USER, true, &m); + r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); if (MANAGER_SKIP_TEST(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); return; diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index b8979c0e95..1992357e1b 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -211,7 +211,7 @@ static int test_unit_printf(void) { assert_se(get_home_dir(&home) >= 0); assert_se(get_shell(&shell) >= 0); - r = manager_new(UNIT_FILE_USER, true, &m); + r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); if (MANAGER_SKIP_TEST(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); return EXIT_TEST_SKIP; @@ -237,7 +237,8 @@ static int test_unit_printf(void) { /* general tests */ expect(u, "%%", "%"); expect(u, "%%s", "%s"); - expect(u, "%", ""); // REALLY? + expect(u, "%,", "%,"); + expect(u, "%", "%"); /* normal unit */ expect(u, "%n", "blah.service"); diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 875a886bf3..3d3967f271 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -2300,7 +2300,7 @@ int main(int argc, char *argv[]) { _cleanup_strv_free_ char **files = NULL; char **f; - r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs); + r = conf_files_list_nulstr(&files, ".conf", arg_root, 0, conf_file_dirs); if (r < 0) { log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m"); goto finish; diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c index 3e1481e4f7..a5b87cf51a 100644 --- a/src/udev/net/ethtool-util.c +++ b/src/udev/net/ethtool-util.c @@ -66,11 +66,12 @@ DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort); DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting"); static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = { - [NET_DEV_FEAT_GSO] = "tx-generic-segmentation", - [NET_DEV_FEAT_GRO] = "rx-gro", - [NET_DEV_FEAT_LRO] = "rx-lro", - [NET_DEV_FEAT_TSO] = "tx-tcp-segmentation", - [NET_DEV_FEAT_UFO] = "tx-udp-fragmentation", + [NET_DEV_FEAT_GSO] = "tx-generic-segmentation", + [NET_DEV_FEAT_GRO] = "rx-gro", + [NET_DEV_FEAT_LRO] = "rx-lro", + [NET_DEV_FEAT_TSO] = "tx-tcp-segmentation", + [NET_DEV_FEAT_TSO6] = "tx-tcp6-segmentation", + [NET_DEV_FEAT_UFO] = "tx-udp-fragmentation", }; int ethtool_connect(int *ret) { diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h index 89c531ae08..909b56b1e6 100644 --- a/src/udev/net/ethtool-util.h +++ b/src/udev/net/ethtool-util.h @@ -53,6 +53,7 @@ typedef enum NetDevFeature { NET_DEV_FEAT_GRO, NET_DEV_FEAT_LRO, NET_DEV_FEAT_TSO, + NET_DEV_FEAT_TSO6, NET_DEV_FEAT_UFO, _NET_DEV_FEAT_MAX, _NET_DEV_FEAT_INVALID = -1 diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index 5488867ba7..52bb4775dd 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -39,6 +39,7 @@ Link.WakeOnLan, config_parse_wol, 0, Link.Port, config_parse_port, 0, offsetof(link_config, port) Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GSO]) Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO]) +Link.TCP6SegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO6]) Link.UDPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_UFO]) Link.GenericReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GRO]) Link.LargeReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_LRO]) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 05a186357c..6ece1250f2 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -213,7 +213,7 @@ int link_config_load(link_config_ctx *ctx) { /* update timestamp */ paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, true); - r = conf_files_list_strv(&files, ".link", NULL, link_dirs); + r = conf_files_list_strv(&files, ".link", NULL, 0, link_dirs); if (r < 0) return log_error_errno(r, "failed to enumerate link files: %m"); diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index 601f0ee13d..09f7baf082 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -362,7 +362,7 @@ size_t udev_event_apply_format(struct udev_event *event, } copy: /* copy char */ - if (l == 0) + if (l < 2) /* need space for this char and the terminating NUL */ goto out; s[0] = from[0]; from++; @@ -377,12 +377,12 @@ subst: unsigned int i; from++; - for (i = 0; from[i] != '}'; i++) { + for (i = 0; from[i] != '}'; i++) if (from[i] == '\0') { log_error("missing closing brace for format '%s'", src); goto out; } - } + if (i >= sizeof(attrbuf)) goto out; memcpy(attrbuf, from, i); @@ -407,6 +407,7 @@ subst: } out: + assert(l >= 1); s[0] = '\0'; return l; } diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index 294a322547..76a1d35a19 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -1533,7 +1533,7 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) { udev_rules_check_timestamp(rules); - r = conf_files_list_strv(&files, ".rules", NULL, rules_dirs); + r = conf_files_list_strv(&files, ".rules", NULL, 0, rules_dirs); if (r < 0) { log_error_errno(r, "failed to enumerate rules files: %m"); return udev_rules_unref(rules); diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c index 7688f8192b..4a23270dfb 100644 --- a/src/udev/udevadm-hwdb.c +++ b/src/udev/udevadm-hwdb.c @@ -627,7 +627,7 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) { } trie->nodes_count++; - err = conf_files_list_strv(&files, ".hwdb", root, conf_file_dirs); + err = conf_files_list_strv(&files, ".hwdb", root, 0, conf_file_dirs); if (err < 0) { log_error_errno(err, "failed to enumerate hwdb files: %m"); rc = EXIT_FAILURE; diff --git a/test/meson.build b/test/meson.build index c16ca92702..bddeeb62b4 100644 --- a/test/meson.build +++ b/test/meson.build @@ -94,6 +94,7 @@ test_data_files = ''' test-execute/exec-systemcallfilter-not-failing.service test-execute/exec-systemcallfilter-system-user.service test-execute/exec-systemcallfilter-system-user-nfsnobody.service + test-execute/exec-unset-environment.service test-execute/exec-user.service test-execute/exec-user-nfsnobody.service test-execute/exec-workingdirectory.service diff --git a/test/test-execute/exec-unset-environment.service b/test/test-execute/exec-unset-environment.service new file mode 100644 index 0000000000..5b0123b81e --- /dev/null +++ b/test/test-execute/exec-unset-environment.service @@ -0,0 +1,8 @@ +[Unit] +Description=Test for UnsetEnvironment + +[Service] +ExecStart=/bin/sh -x -c 'test "$$FOO" = "bar" && test "$${QUUX-X}" = "X" && test "$$VAR3" = "value3" && test "$${VAR4-X}" = "X" && test "$$VAR5" = "value5" && test "$${X%b-X}" = "X"' +Type=oneshot +Environment=FOO=bar QUUX=waldo VAR3=value3 VAR4=value4 VAR5=value5 X%b=%U +UnsetEnvironment=QUUX=waldo VAR3=somethingelse VAR4 X%b=%U diff --git a/tmpfiles.d/journal-nocow.conf b/tmpfiles.d/journal-nocow.conf index e7938c8911..e32a38855a 100644 --- a/tmpfiles.d/journal-nocow.conf +++ b/tmpfiles.d/journal-nocow.conf @@ -8,7 +8,7 @@ # See tmpfiles.d(5) for details # Set the NOCOW attribute for directories of journal files. This flag -# is inheredited by their new files and sub-directories. Matters only +# is inherited by their new files and sub-directories. Matters only # for btrfs filesystems. # # WARNING: Enabling the NOCOW attribute improves journal performance diff --git a/units/container-getty@.service.m4.in b/units/container-getty@.service.m4.in index 4b822f0e3b..3d287b9491 100644 --- a/units/container-getty@.service.m4.in +++ b/units/container-getty@.service.m4.in @@ -19,8 +19,8 @@ ConditionPathExists=/dev/pts/%I # IgnoreOnIsolate is an issue: when someone isolates rescue.target, # tradition expects that we shut down all but the main console. -Conflicts=rescue.service -Before=rescue.service +Conflicts=rescue.service emergency.service +Before=rescue.service emergency.service [Service] # The '-o' option value tells agetty to replace 'login' arguments with an diff --git a/units/debug-shell.service.in b/units/debug-shell.service.in index 59f6e494b3..f72e5ef6b6 100644 --- a/units/debug-shell.service.in +++ b/units/debug-shell.service.in @@ -28,7 +28,7 @@ KillSignal=SIGHUP # Unset locale for the console getty since the console has problems # displaying some internationalized messages. -Environment=LANG= LANGUAGE= LC_CTYPE= LC_NUMERIC= LC_TIME= LC_COLLATE= LC_MONETARY= LC_MESSAGES= LC_PAPER= LC_NAME= LC_ADDRESS= LC_TELEPHONE= LC_MEASUREMENT= LC_IDENTIFICATION= +UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION [Install] WantedBy=sysinit.target diff --git a/units/getty@.service.m4 b/units/getty@.service.m4 index b62f1b9225..c2665e3f6b 100644 --- a/units/getty@.service.m4 +++ b/units/getty@.service.m4 @@ -23,8 +23,8 @@ IgnoreOnIsolate=yes # IgnoreOnIsolate causes issues with sulogin, if someone isolates # rescue.target or starts rescue.service from multi-user.target or # graphical.target. -Conflicts=rescue.service -Before=rescue.service +Conflicts=rescue.service emergency.service +Before=rescue.service emergency.service # On systems without virtual consoles, don't start any getty. Note # that serial gettys are covered by serial-getty@.service, not this @@ -51,7 +51,7 @@ SendSIGHUP=yes # Unset locale for the console getty since the console has problems # displaying some internationalized messages. -Environment=LANG= LANGUAGE= LC_CTYPE= LC_NUMERIC= LC_TIME= LC_COLLATE= LC_MONETARY= LC_MESSAGES= LC_PAPER= LC_NAME= LC_ADDRESS= LC_TELEPHONE= LC_MEASUREMENT= LC_IDENTIFICATION= +UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION [Install] WantedBy=getty.target diff --git a/units/serial-getty@.service.m4 b/units/serial-getty@.service.m4 index e56f47652c..d6da914454 100644 --- a/units/serial-getty@.service.m4 +++ b/units/serial-getty@.service.m4 @@ -24,8 +24,8 @@ IgnoreOnIsolate=yes # IgnoreOnIsolate causes issues with sulogin, if someone isolates # rescue.target or starts rescue.service from multi-user.target or # graphical.target. -Conflicts=rescue.service -Before=rescue.service +Conflicts=rescue.service emergency.service +Before=rescue.service emergency.service [Service] # The '-o' option value tells agetty to replace 'login' arguments with an diff --git a/units/sysinit.target b/units/sysinit.target index ec33503330..8d01cbdc1b 100644 --- a/units/sysinit.target +++ b/units/sysinit.target @@ -8,6 +8,13 @@ [Unit] Description=System Initialization Documentation=man:systemd.special(7) -Conflicts=emergency.service emergency.target -Wants=local-fs.target swap.target -After=local-fs.target swap.target emergency.service emergency.target +Wants=swap.target local-fs.target +After=swap.target local-fs.target + +# local-fs.target uses OnFailure="emergency.target" to start an emergency +# shell. In that case we also need to cancel everything that relies on +# local-fs.target. Flooding the screen with yellow [DEPEND] failures would +# be unhelpful. Therefore we break the chain here. We use a Wants +# dependency instead of Requires, and use a Conflict with emergency.target, +# so that we are cancelled without actually failing any more units. +Conflicts=emergency.target diff --git a/units/systemd-coredump@.service.in b/units/systemd-coredump@.service.in index c699a80f34..d7eaf3398e 100644 --- a/units/systemd-coredump@.service.in +++ b/units/systemd-coredump@.service.in @@ -33,4 +33,5 @@ RestrictNamespaces=yes RestrictAddressFamilies=AF_UNIX SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap SystemCallArchitectures=native +LockPersonality=yes StateDirectory=systemd/coredump diff --git a/units/systemd-hostnamed.service.in b/units/systemd-hostnamed.service.in index d29e9ff81b..9bb5ad8cac 100644 --- a/units/systemd-hostnamed.service.in +++ b/units/systemd-hostnamed.service.in @@ -29,4 +29,5 @@ RestrictNamespaces=yes RestrictAddressFamilies=AF_UNIX SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap SystemCallArchitectures=native +LockPersonality=yes ReadWritePaths=/etc diff --git a/units/systemd-importd.service.in b/units/systemd-importd.service.in index 58762055eb..695a5f21cb 100644 --- a/units/systemd-importd.service.in +++ b/units/systemd-importd.service.in @@ -23,3 +23,4 @@ RestrictNamespaces=net RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @obsolete @raw-io @reboot @swap SystemCallArchitectures=native +LockPersonality=yes diff --git a/units/systemd-journal-gatewayd.service.in b/units/systemd-journal-gatewayd.service.in index fd7a9718f7..b24d698c8a 100644 --- a/units/systemd-journal-gatewayd.service.in +++ b/units/systemd-journal-gatewayd.service.in @@ -25,6 +25,7 @@ RestrictRealtime=yes RestrictNamespaces=yes RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 SystemCallArchitectures=native +LockPersonality=yes # If there are many split upjournal files we need a lot of fds to # access them all and combine diff --git a/units/systemd-journal-remote.service.in b/units/systemd-journal-remote.service.in index c24e673d82..92cec21c2f 100644 --- a/units/systemd-journal-remote.service.in +++ b/units/systemd-journal-remote.service.in @@ -27,6 +27,7 @@ RestrictRealtime=yes RestrictNamespaces=yes RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 SystemCallArchitectures=native +LockPersonality=yes LogsDirectory=journal/remote [Install] diff --git a/units/systemd-journal-upload.service.in b/units/systemd-journal-upload.service.in index b0bee3925e..98a4b2bb7a 100644 --- a/units/systemd-journal-upload.service.in +++ b/units/systemd-journal-upload.service.in @@ -28,6 +28,7 @@ RestrictRealtime=yes RestrictNamespaces=yes RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 SystemCallArchitectures=native +LockPersonality=yes StateDirectory=systemd/journal-upload # If there are many split up journal files we need a lot of fds to diff --git a/units/systemd-journald.service.in b/units/systemd-journald.service.in index 1e86d63648..07e03e736e 100644 --- a/units/systemd-journald.service.in +++ b/units/systemd-journald.service.in @@ -29,6 +29,7 @@ RestrictNamespaces=yes RestrictAddressFamilies=AF_UNIX AF_NETLINK SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap SystemCallArchitectures=native +LockPersonality=yes # Increase the default a bit in order to allow many simultaneous # services being run since we keep one fd open per service. Also, when diff --git a/units/systemd-localed.service.in b/units/systemd-localed.service.in index 90a913881a..1366fa7910 100644 --- a/units/systemd-localed.service.in +++ b/units/systemd-localed.service.in @@ -29,4 +29,5 @@ RestrictNamespaces=yes RestrictAddressFamilies=AF_UNIX SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap SystemCallArchitectures=native +LockPersonality=yes ReadWritePaths=/etc diff --git a/units/systemd-logind.service.in b/units/systemd-logind.service.in index f851373658..f6daf7755c 100644 --- a/units/systemd-logind.service.in +++ b/units/systemd-logind.service.in @@ -30,6 +30,7 @@ RestrictNamespaces=yes RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @obsolete @raw-io @reboot @swap SystemCallArchitectures=native +LockPersonality=yes FileDescriptorStoreMax=512 # Increase the default a bit in order to allow many simultaneous diff --git a/units/systemd-machined.service.in b/units/systemd-machined.service.in index a4f86aa7c8..fb4df38293 100644 --- a/units/systemd-machined.service.in +++ b/units/systemd-machined.service.in @@ -23,6 +23,7 @@ RestrictRealtime=yes RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @obsolete @raw-io @reboot @swap SystemCallArchitectures=native +LockPersonality=yes # Note that machined cannot be placed in a mount namespace, since it # needs access to the host's mount namespace in order to implement the diff --git a/units/systemd-networkd.service.in b/units/systemd-networkd.service.in index 3f0ad77b7d..932dd63964 100644 --- a/units/systemd-networkd.service.in +++ b/units/systemd-networkd.service.in @@ -34,6 +34,7 @@ RestrictRealtime=yes RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 AF_PACKET SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap SystemCallArchitectures=native +LockPersonality=yes RuntimeDirectory=systemd/netif RuntimeDirectoryPreserve=yes diff --git a/units/systemd-resolved.service.in b/units/systemd-resolved.service.in index ba8d3f6bb1..cda83ee966 100644 --- a/units/systemd-resolved.service.in +++ b/units/systemd-resolved.service.in @@ -36,6 +36,7 @@ RestrictRealtime=yes RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap SystemCallArchitectures=native +LockPersonality=yes RuntimeDirectory=systemd/resolve RuntimeDirectoryPreserve=yes diff --git a/units/systemd-timedated.service.in b/units/systemd-timedated.service.in index 2b5f0744c9..9fca1d1905 100644 --- a/units/systemd-timedated.service.in +++ b/units/systemd-timedated.service.in @@ -27,4 +27,5 @@ RestrictNamespaces=yes RestrictAddressFamilies=AF_UNIX SystemCallFilter=~@cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap SystemCallArchitectures=native +LockPersonality=yes ReadWritePaths=/etc diff --git a/units/systemd-timesyncd.service.in b/units/systemd-timesyncd.service.in index a6e14d24d1..8d3f46cf5e 100644 --- a/units/systemd-timesyncd.service.in +++ b/units/systemd-timesyncd.service.in @@ -38,6 +38,7 @@ RestrictNamespaces=yes RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 SystemCallFilter=~@cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap SystemCallArchitectures=native +LockPersonality=yes StateDirectory=systemd/timesync [Install] diff --git a/units/systemd-udevd.service.in b/units/systemd-udevd.service.in index 3b92c6a866..d3d13ed7cf 100644 --- a/units/systemd-udevd.service.in +++ b/units/systemd-udevd.service.in @@ -28,3 +28,4 @@ MemoryDenyWriteExecute=yes RestrictRealtime=yes RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 SystemCallArchitectures=native +LockPersonality=yes |