summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--DISTRO_PORTING7
-rw-r--r--Makefile.am2
-rw-r--r--TODO2
-rw-r--r--man/resolved.conf.xml12
-rw-r--r--man/systemd-journal-remote.xml17
-rw-r--r--man/systemd-nspawn.xml87
-rw-r--r--man/systemd.netdev.xml8
-rw-r--r--man/systemd.network.xml51
-rw-r--r--man/systemd.xml2
-rw-r--r--shell-completion/bash/systemctl.in41
-rw-r--r--src/basic/exit-status.c33
-rw-r--r--src/basic/exit-status.h14
-rw-r--r--src/basic/missing.h4
-rw-r--r--src/basic/path-util.c68
-rw-r--r--src/basic/path-util.h2
-rw-r--r--src/boot/efi/measure.c23
-rw-r--r--src/core/busname.c2
-rw-r--r--src/core/main.c24
-rw-r--r--src/core/mount.c48
-rw-r--r--src/core/service.c3
-rw-r--r--src/core/socket.c2
-rw-r--r--src/core/swap.c2
-rw-r--r--src/journal-remote/journal-remote.c4
-rw-r--r--src/login/systemd-user.m44
-rw-r--r--src/machine/machinectl.c1
-rw-r--r--src/network/networkd-address.c59
-rw-r--r--src/network/networkd-address.h6
-rw-r--r--src/network/networkd-netdev-gperf.gperf7
-rw-r--r--src/network/networkd-network-gperf.gperf5
-rw-r--r--src/nspawn/nspawn.c1069
-rw-r--r--src/remount-fs/remount-fs.c2
-rw-r--r--src/resolve/resolve-tool.c40
-rw-r--r--src/resolve/resolved-conf.c11
-rw-r--r--src/resolve/resolved-conf.h17
-rw-r--r--src/resolve/resolved-dns-scope.h3
-rw-r--r--src/resolve/resolved-dns-stream.h1
-rw-r--r--src/resolve/resolved-dns-stub.c162
-rw-r--r--src/resolve/resolved-dns-stub.h3
-rw-r--r--src/resolve/resolved-dns-transaction.h2
-rw-r--r--src/resolve/resolved-gperf.gperf13
-rw-r--r--src/resolve/resolved-link.h1
-rw-r--r--src/resolve/resolved-manager.c1
-rw-r--r--src/resolve/resolved-manager.h2
-rw-r--r--src/resolve/resolved.conf.in1
-rw-r--r--src/shared/gcrypt-util.h8
-rw-r--r--src/shared/install.c4
-rw-r--r--src/systemctl/systemctl.c2
-rw-r--r--src/sysv-generator/sysv-generator.c8
-rw-r--r--src/test/test-path-util.c19
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c2
-rw-r--r--src/udev/udevadm-control.c29
-rw-r--r--src/udev/udevd.c9
-rw-r--r--system-preset/90-systemd.preset1
-rw-r--r--units/.gitignore2
-rw-r--r--units/console-shell.service.m4.in31
-rw-r--r--units/initrd-switch-root.target2
56 files changed, 1148 insertions, 837 deletions
diff --git a/DISTRO_PORTING b/DISTRO_PORTING
index a397d700c6..9f61bd6de6 100644
--- a/DISTRO_PORTING
+++ b/DISTRO_PORTING
@@ -41,6 +41,13 @@ NTP POOL:
NTP servers, then you will get served wrong time, and will
rely on services that might not be supported for long.
+PAM:
+ The default PAM config shipped by systemd is really bare bones.
+ It does not include many modules your distro might want to enable
+ to provide a more seamless experience. For example, limits set in
+ /etc/security/limits.conf will not be read unless you load pam_limits.
+ Make sure you add modules your distro expects from user services.
+
CONTRIBUTING UPSTREAM:
We generally do no longer accept distribution-specific
diff --git a/Makefile.am b/Makefile.am
index f7ae9ff7aa..b09b0cf167 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -524,7 +524,6 @@ dist_systemunit_DATA_busnames += \
nodist_systemunit_DATA = \
units/getty@.service \
units/serial-getty@.service \
- units/console-shell.service \
units/console-getty.service \
units/container-getty@.service \
units/systemd-initctl.service \
@@ -587,7 +586,6 @@ dist_systempreset_DATA = \
EXTRA_DIST += \
units/getty@.service.m4 \
units/serial-getty@.service.m4 \
- units/console-shell.service.m4.in \
units/console-getty.service.m4.in \
units/container-getty@.service.m4.in \
units/rescue.service.in \
diff --git a/TODO b/TODO
index 9b949e93f7..b4443ce585 100644
--- a/TODO
+++ b/TODO
@@ -32,6 +32,8 @@ Janitorial Clean-ups:
Features:
+* on cgroupsv2 add DelegateControllers=, to pick the precise cgroup controllers to delegate
+
* in networkd, when matching device types, fix up DEVTYPE rubbish the kernel passes to us
* enable LockMLOCK to take a percentage value relative to physical memory
diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml
index a7710dcb44..4fc1ef1b33 100644
--- a/man/resolved.conf.xml
+++ b/man/resolved.conf.xml
@@ -213,6 +213,18 @@
(such as 127.0.0.1 or ::1), in order to avoid duplicate local caching.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>DNSStubListener=</varname></term>
+ <listitem><para>Takes a boolean argument or one of <literal>udp</literal> and <literal>tcp</literal>. If
+ <literal>udp</literal> (the default), a DNS stub resolver will listen for UDP requests on address 127.0.0.53
+ port 53. If <literal>tcp</literal>, the stub will listen for TCP requests on the same address and port. If
+ <literal>yes</literal>, the stub listens for both UDP and TCP requests. If <literal>no</literal>, the stub
+ listener is disabled.</para>
+
+ <para>Note that the DNS stub listener is turned off implicitly when its listening address and port are already
+ in use.</para></listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
diff --git a/man/systemd-journal-remote.xml b/man/systemd-journal-remote.xml
index f208f8deb4..ee2d5c2486 100644
--- a/man/systemd-journal-remote.xml
+++ b/man/systemd-journal-remote.xml
@@ -250,20 +250,19 @@
</varlistentry>
<varlistentry>
- <term><option>--compress</option></term>
- <term><option>--no-compress</option></term>
+ <term><option>--compress</option> [<replaceable>BOOL</replaceable>]</term>
- <listitem><para>Compress or not, respectively, the data in the
- journal using XZ.</para></listitem>
+ <listitem><para>If this is set to <literal>yes</literal> then compress
+ the data in the journal using XZ. The default is <literal>yes</literal>.
+ </para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--seal</option></term>
- <term><option>--no-seal</option></term>
+ <term><option>--seal</option> [<replaceable>BOOL</replaceable>]</term>
- <listitem><para>Periodically sign or not, respectively, the
- data in the journal using Forward Secure Sealing.
- </para></listitem>
+ <listitem><para>If this is set to <literal>yes</literal> then
+ periodically sign the data in the journal using Forward Secure Sealing.
+ The default is <literal>no</literal>.</para></listitem>
</varlistentry>
<varlistentry>
diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index bf3860604c..5ac54df81a 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -405,35 +405,36 @@
purposes (usually in the range beyond the host's UID/GID 65536). The parameter may be specified as follows:</para>
<orderedlist>
- <listitem><para>The value <literal>no</literal> turns off user namespacing. This is the default.</para></listitem>
-
- <listitem><para>The value <literal>yes</literal> (or the omission of a parameter) turns on user
- namespacing. The UID/GID range to use is determined automatically from the file ownership of the root
- directory of the container's directory tree. To use this option, make sure to prepare the directory tree in
- advance, and ensure that all files and directories in it are owned by UIDs/GIDs in the range you'd like to
- use. Also, make sure that used file ACLs exclusively reference UIDs/GIDs in the appropriate range. If this
- mode is used the number of UIDs/GIDs assigned to the container for use is 65536, and the UID/GID of the
- root directory must be a multiple of 65536.</para></listitem>
-
- <listitem><para>The value "pick" turns on user namespacing. In this case the UID/GID range is automatically
- chosen. As first step, the file owner of the root directory of the container's directory tree is read, and it
- is checked that it is currently not used by the system otherwise (in particular, that no other container is
- using it). If this check is successful, the UID/GID range determined this way is used, similar to the
- behaviour if "yes" is specified. If the check is not successful (and thus the UID/GID range indicated in the
- root directory's file owner is already used elsewhere) a new – currently unused – UID/GID range of 65536
- UIDs/GIDs is randomly chosen between the host UID/GIDs of 524288 and 1878982656, always starting at a
- multiple of 65536. This setting implies <option>--private-users-chown</option> (see below), which has the
- effect that the files and directories in the container's directory tree will be owned by the appropriate
- users of the range picked. Using this option makes user namespace behaviour fully automatic. Note that the
- first invocation of a previously unused container image might result in picking a new UID/GID range for it,
- and thus in the (possibly expensive) file ownership adjustment operation. However, subsequent invocations of
- the container will be cheap (unless of course the picked UID/GID range is assigned to a different use by
- then).</para></listitem>
-
- <listitem><para>Finally if one or two colon-separated numeric parameters are specified, user namespacing is
- turned on, too. The first parameter specifies the first host UID/GID to assign to the container, the second
- parameter specifies the number of host UIDs/GIDs to assign to the container. If the second parameter is
- omitted, 65536 UIDs/GIDs are assigned.</para></listitem>
+ <listitem><para>If one or two colon-separated numers are specified, user namespacing is turned on. The first
+ parameter specifies the first host UID/GID to assign to the container, the second parameter specifies the
+ number of host UIDs/GIDs to assign to the container. If the second parameter is omitted, 65536 UIDs/GIDs are
+ assigned.</para></listitem>
+
+ <listitem><para>If the parameter is omitted, or true, user namespacing is turned on. The UID/GID range to
+ use is determined automatically from the file ownership of the root directory of the container's directory
+ tree. To use this option, make sure to prepare the directory tree in advance, and ensure that all files and
+ directories in it are owned by UIDs/GIDs in the range you'd like to use. Also, make sure that used file ACLs
+ exclusively reference UIDs/GIDs in the appropriate range. If this mode is used the number of UIDs/GIDs
+ assigned to the container for use is 65536, and the UID/GID of the root directory must be a multiple of
+ 65536.</para></listitem>
+
+ <listitem><para>If the parameter is false, user namespacing is turned off. This is the default.</para>
+ </listitem>
+
+ <listitem><para>The special value <literal>pick</literal> turns on user namespacing. In this case the UID/GID
+ range is automatically chosen. As first step, the file owner of the root directory of the container's
+ directory tree is read, and it is checked that it is currently not used by the system otherwise (in
+ particular, that no other container is using it). If this check is successful, the UID/GID range determined
+ this way is used, similar to the behaviour if "yes" is specified. If the check is not successful (and thus
+ the UID/GID range indicated in the root directory's file owner is already used elsewhere) a new – currently
+ unused – UID/GID range of 65536 UIDs/GIDs is randomly chosen between the host UID/GIDs of 524288 and
+ 1878982656, always starting at a multiple of 65536. This setting implies
+ <option>--private-users-chown</option> (see below), which has the effect that the files and directories in
+ the container's directory tree will be owned by the appropriate users of the range picked. Using this option
+ makes user namespace behaviour fully automatic. Note that the first invocation of a previously unused
+ container image might result in picking a new UID/GID range for it, and thus in the (possibly expensive) file
+ ownership adjustment operation. However, subsequent invocations of the container will be cheap (unless of
+ course the picked UID/GID range is assigned to a different use by then).</para></listitem>
</orderedlist>
<para>It is recommended to assign at least 65536 UIDs/GIDs to each container, so that the usable UID/GID range in the
@@ -454,17 +455,6 @@
</varlistentry>
<varlistentry>
- <term><option>-U</option></term>
-
- <listitem><para>If the kernel supports the user namespaces feature, equivalent to
- <option>--private-users=pick</option>, otherwise equivalent to
- <option>--private-users=no</option>.</para>
-
- <para>Note that <option>-U</option> is the default if the <filename>systemd-nspawn@.service</filename> template unit
- file is used.</para></listitem>
- </varlistentry>
-
- <varlistentry>
<term><option>--private-users-chown</option></term>
<listitem><para>If specified, all files and directories in the container's directory tree will adjusted so that
@@ -477,6 +467,23 @@
</varlistentry>
<varlistentry>
+ <term><option>-U</option></term>
+
+ <listitem><para>If the kernel supports the user namespaces feature, equivalent to
+ <option>--private-users=pick --private-users-chown</option>, otherwise equivalent to
+ <option>--private-users=no</option>.</para>
+
+ <para>Note that <option>-U</option> is the default if the
+ <filename>systemd-nspawn@.service</filename> template unit file is used.</para>
+
+ <para>Note: it is possible to undo the effect of <option>--private-users-chown</option> (or
+ <option>-U</option>) on the file system by redoing the operation with the first UID of 0:</para>
+
+ <programlisting>systemd-nspawn … --private-users=0 --private-users-chown</programlisting>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>--private-network</option></term>
<listitem><para>Disconnect networking of the container from
diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml
index e378e61dd1..ffb66e735b 100644
--- a/man/systemd.netdev.xml
+++ b/man/systemd.netdev.xml
@@ -537,7 +537,7 @@
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>UDPCheckSum=</varname></term>
+ <term><varname>UDPChecksum=</varname></term>
<listitem>
<para>A boolean. When true, transmitting UDP checksums when doing VXLAN/IPv4 is turned on.</para>
</listitem>
@@ -549,19 +549,19 @@
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>UDP6ZeroCheckSumRx=</varname></term>
+ <term><varname>UDP6ZeroChecksumRx=</varname></term>
<listitem>
<para>A boolean. When true, receiving zero checksums in VXLAN/IPv6 is turned on.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>RemoteCheckSumTx=</varname></term>
+ <term><varname>RemoteChecksumTx=</varname></term>
<listitem>
<para>A boolean. When true, remote transmit checksum offload of VXLAN is turned on.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>RemoteCheckSumRx=</varname></term>
+ <term><varname>RemoteChecksumRx=</varname></term>
<listitem>
<para>A boolean. When true, remote receive checksum offload in VXLAN is turned on.</para>
</listitem>
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 0af927db19..2fb4907634 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -695,6 +695,57 @@
which is then configured to use them explicitly.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>HomeAddress=</varname></term>
+ <listitem>
+ <para>Takes a boolean argument. Designates this address the "home address" as defined in
+ <ulink url="https://tools.ietf.org/html/rfc6275">RFC 6275</ulink>.
+ Supported only on IPv6. Defaults to false.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>DuplicateAddressDetection=</varname></term>
+ <listitem>
+ <para>Takes a boolean argument. Do not perform Duplicate Address Detection
+ <ulink url="https://tools.ietf.org/html/rfc4862">RFC 4862</ulink> when adding this address.
+ Supported only on IPv6. Defaults to false.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>ManageTemporaryAddress=</varname></term>
+ <listitem>
+ <para>Takes a boolean argument. If true the kernel manage temporary addresses created
+ from this one as template on behalf of Privacy Extensions
+ <ulink url="https://tools.ietf.org/html/rfc3041">RFC 3041</ulink>. For this to become
+ active, the use_tempaddr sysctl setting has to be set to a value greater than zero.
+ The given address needs to have a prefix length of 64. This flag allows to use privacy
+ extensions in a manually configured network, just like if stateless auto-configuration
+ was active. Defaults to false. </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>PrefixRoute=</varname></term>
+ <listitem>
+ <para>Takes a boolean argument. When adding or modifying an IPv6 address, the userspace
+ application needs a way to suppress adding a prefix route. This is for example relevant
+ together with IFA_F_MANAGERTEMPADDR, where userspace creates autoconf generated addresses,
+ but depending on on-link, no route for the prefix should be added. Defaults to false.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>AutoJoin=</varname></term>
+ <listitem>
+ <para>Takes a boolean argument. Joining multicast group on ethernet level via
+ <command>ip maddr</command> command would not work if we have an Ethernet switch that does
+ IGMP snooping since the switch would not replicate multicast packets on ports that did not
+ have IGMP reports for the multicast addresses. Linux vxlan interfaces created via
+ <command>ip link add vxlan</command> or networkd's netdev kind vxlan have the group option
+ that enables then to do the required join. By extending ip address command with option
+ <literal>autojoin</literal> we can get similar functionality for openvswitch (OVS) vxlan
+ interfaces as well as other tunneling mechanisms that need to receive multicast traffic.
+ Defaults to <literal>no</literal>.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/man/systemd.xml b/man/systemd.xml
index e30333e209..7f24a874ed 100644
--- a/man/systemd.xml
+++ b/man/systemd.xml
@@ -272,7 +272,7 @@
<title>Concepts</title>
<para>systemd provides a dependency system between various
- entities called "units" of 12 different types. Units encapsulate
+ entities called "units" of 11 different types. Units encapsulate
various objects that are relevant for system boot-up and
maintenance. The majority of units are configured in unit
configuration files, whose syntax and basic set of options is
diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in
index 2a45dcbba0..dcf71a1f51 100644
--- a/shell-completion/bash/systemctl.in
+++ b/shell-completion/bash/systemctl.in
@@ -41,7 +41,7 @@ __contains_word () {
__filter_units_by_property () {
local mode=$1 property=$2 value=$3 ; shift 3
local units=("$@")
- local props
+ local props i
IFS=$'\n' read -rd '' -a props < \
<(__systemctl $mode show --property "$property" -- "${units[@]}")
for ((i=0; $i < ${#units[*]}; i++)); do
@@ -51,6 +51,33 @@ __filter_units_by_property () {
done
}
+__filter_units_by_properties () {
+ local mode=$1 properties=$2 values=$3 ; shift 3
+ local units=("$@")
+ local props i j conditions=()
+ IFS=$'\n' read -rd '' -a props < \
+ <(__systemctl $mode show --property "$properties" -- "${units[@]}")
+ IFS=$',' read -r -a properties < <(echo $properties)
+ IFS=$',' read -r -a values < <(echo $values)
+ for ((i=0; i < ${#properties[*]}; i++)); do
+ for ((j=0; j < ${#properties[*]}; j++)); do
+ if [[ ${props[i]%%=*} == ${properties[j]} ]]; then
+ conditions+=( "${properties[j]}=${values[j]}" )
+ fi
+ done
+ done
+ for ((i=0; i < ${#units[*]}; i++)); do
+ for ((j=0; j < ${#conditions[*]}; j++)); do
+ if [[ "${props[ i * ${#conditions[*]} + j]}" != "${conditions[j]}" ]]; then
+ break
+ fi
+ done
+ if (( j == ${#conditions[*]} )); then
+ echo " ${units[i]}"
+ fi
+ done
+}
+
__get_all_units () { { __systemctl $1 list-unit-files; __systemctl $1 list-units --all; } \
| { while read -r a b; do [[ $a =~ @\. ]] || echo " $a"; done; }; }
__get_template_names () { __systemctl $1 list-unit-files \
@@ -60,12 +87,12 @@ __get_active_units () { __systemctl $1 list-units \
| { while read -r a b; do echo " $a"; done; }; }
__get_startable_units () {
# find startable inactive units
- __filter_units_by_property $mode ActiveState inactive $(
- __filter_units_by_property $mode CanStart yes $(
- __systemctl $mode list-unit-files --state enabled,disabled,static | \
- { while read -r a b; do [[ $a =~ @\. ]] || echo " $a"; done; }
- __systemctl $mode list-units --state inactive,failed | \
- { while read -r a b; do echo " $a"; done; } ))
+ __filter_units_by_properties $mode ActiveState,CanStart inactive,yes $(
+ { __systemctl $mode list-unit-files --state enabled,enabled-runtime,linked,linked-runtime,static,indirect,disabled,generated,transient | \
+ { while read -r a b; do [[ $a =~ @\. ]] || echo " $a"; done; }
+ __systemctl $mode list-units --state inactive,failed | \
+ { while read -r a b c; do [[ $b == "loaded" ]] && echo " $a"; done; }
+ } | sort -u )
}
__get_restartable_units () {
# filter out masked and not-found
diff --git a/src/basic/exit-status.c b/src/basic/exit-status.c
index d488cfc59f..59557f8afe 100644
--- a/src/basic/exit-status.c
+++ b/src/basic/exit-status.c
@@ -24,12 +24,12 @@
#include "macro.h"
#include "set.h"
-const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
+const char* exit_status_to_string(int status, ExitStatusLevel level) {
/* We cast to int here, so that -Wenum doesn't complain that
* EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */
- switch ((int) status) {
+ switch (status) {
case EXIT_SUCCESS:
return "SUCCESS";
@@ -39,7 +39,7 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
}
if (IN_SET(level, EXIT_STATUS_SYSTEMD, EXIT_STATUS_LSB)) {
- switch ((int) status) {
+ switch (status) {
case EXIT_CHDIR:
return "CHDIR";
@@ -140,19 +140,19 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
case EXIT_RUNTIME_DIRECTORY:
return "RUNTIME_DIRECTORY";
- case EXIT_CHOWN:
- return "CHOWN";
-
case EXIT_MAKE_STARTER:
return "MAKE_STARTER";
+ case EXIT_CHOWN:
+ return "CHOWN";
+
case EXIT_SMACK_PROCESS_LABEL:
return "SMACK_PROCESS_LABEL";
}
}
if (level == EXIT_STATUS_LSB) {
- switch ((int) status) {
+ switch (status) {
case EXIT_INVALIDARGUMENT:
return "INVALIDARGUMENT";
@@ -177,34 +177,23 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
return NULL;
}
-
-bool is_clean_exit(int code, int status, ExitStatusSet *success_status) {
+bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status) {
if (code == CLD_EXITED)
return status == 0 ||
(success_status &&
set_contains(success_status->status, INT_TO_PTR(status)));
- /* If a daemon does not implement handlers for some of the
- * signals that's not considered an unclean shutdown */
+ /* If a daemon does not implement handlers for some of the signals that's not considered an unclean shutdown */
if (code == CLD_KILLED)
- return IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE) ||
+ return
+ (clean == EXIT_CLEAN_DAEMON && IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE)) ||
(success_status &&
set_contains(success_status->signal, INT_TO_PTR(status)));
return false;
}
-bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status) {
-
- if (is_clean_exit(code, status, success_status))
- return true;
-
- return
- code == CLD_EXITED &&
- IN_SET(status, EXIT_NOTINSTALLED, EXIT_NOTCONFIGURED);
-}
-
void exit_status_set_free(ExitStatusSet *x) {
assert(x);
diff --git a/src/basic/exit-status.h b/src/basic/exit-status.h
index 2309f68815..0cfdfd7891 100644
--- a/src/basic/exit-status.h
+++ b/src/basic/exit-status.h
@@ -31,7 +31,7 @@
* https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
*/
-typedef enum ExitStatus {
+enum {
/* EXIT_SUCCESS defined by libc */
/* EXIT_FAILURE defined by libc */
EXIT_INVALIDARGUMENT = 2,
@@ -82,7 +82,7 @@ typedef enum ExitStatus {
EXIT_MAKE_STARTER,
EXIT_CHOWN,
EXIT_SMACK_PROCESS_LABEL,
-} ExitStatus;
+};
typedef enum ExitStatusLevel {
EXIT_STATUS_MINIMAL, /* only cover libc EXIT_STATUS/EXIT_FAILURE */
@@ -96,10 +96,14 @@ typedef struct ExitStatusSet {
Set *signal;
} ExitStatusSet;
-const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) _const_;
+const char* exit_status_to_string(int status, ExitStatusLevel level) _const_;
-bool is_clean_exit(int code, int status, ExitStatusSet *success_status);
-bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status);
+typedef enum ExitClean {
+ EXIT_CLEAN_DAEMON,
+ EXIT_CLEAN_COMMAND,
+} ExitClean;
+
+bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status);
void exit_status_set_free(ExitStatusSet *x);
bool exit_status_set_is_empty(ExitStatusSet *x);
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 13ff51cd35..4a78269e33 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -1052,6 +1052,10 @@ typedef int32_t key_serial_t;
#define ETHERTYPE_LLDP 0x88cc
#endif
+#ifndef IFA_F_MCAUTOJOIN
+#define IFA_F_MCAUTOJOIN 0x400
+#endif
+
#endif
#include "missing_syscall.h"
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index b2fa81a294..c32e961af4 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -34,9 +34,11 @@
#include "alloc-util.h"
#include "extract-word.h"
#include "fs-util.h"
+#include "glob-util.h"
#include "log.h"
#include "macro.h"
#include "missing.h"
+#include "parse-util.h"
#include "path-util.h"
#include "stat-util.h"
#include "string-util.h"
@@ -814,3 +816,69 @@ bool is_device_path(const char *path) {
path_startswith(path, "/dev/") ||
path_startswith(path, "/sys/");
}
+
+int systemd_installation_has_version(const char *root, unsigned minimal_version) {
+ const char *pattern;
+ int r;
+
+ /* Try to guess if systemd installation is later than the specified version. This
+ * is hacky and likely to yield false negatives, particularly if the installation
+ * is non-standard. False positives should be relatively rare.
+ */
+
+ NULSTR_FOREACH(pattern,
+ /* /lib works for systems without usr-merge, and for systems with a sane
+ * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary
+ * for Gentoo which does a merge without making /lib a symlink.
+ */
+ "lib/systemd/libsystemd-shared-*.so\0"
+ "usr/lib/systemd/libsystemd-shared-*.so\0") {
+
+ _cleanup_strv_free_ char **names = NULL;
+ _cleanup_free_ char *path = NULL;
+ char *c, **name;
+
+ path = prefix_root(root, pattern);
+ if (!path)
+ return -ENOMEM;
+
+ r = glob_extend(&names, path);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return r;
+
+ assert_se((c = endswith(path, "*.so")));
+ *c = '\0'; /* truncate the glob part */
+
+ STRV_FOREACH(name, names) {
+ /* This is most likely to run only once, hence let's not optimize anything. */
+ char *t, *t2;
+ unsigned version;
+
+ t = startswith(*name, path);
+ if (!t)
+ continue;
+
+ t2 = endswith(t, ".so");
+ if (!t2)
+ continue;
+
+ t2[0] = '\0'; /* truncate the suffix */
+
+ r = safe_atou(t, &version);
+ if (r < 0) {
+ log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name);
+ continue;
+ }
+
+ log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).",
+ *name, version,
+ version >= minimal_version ? "OK" : "too old");
+ if (version >= minimal_version)
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index a27c13fcc3..78472f0961 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -125,3 +125,5 @@ char *file_in_same_dir(const char *path, const char *filename);
bool hidden_or_backup_file(const char *filename) _pure_;
bool is_device_path(const char *path);
+
+int systemd_installation_has_version(const char *root, unsigned minimal_version);
diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c
index 7c016387c1..4ac11a9bb0 100644
--- a/src/boot/efi/measure.c
+++ b/src/boot/efi/measure.c
@@ -209,12 +209,35 @@ static EFI_STATUS tpm1_measure_to_pcr_and_event_log(const EFI_TCG *tcg, UINT32 p
return EFI_SUCCESS;
}
+/*
+ * According to TCG EFI Protocol Specification for TPM 2.0 family,
+ * all events generated after the invocation of EFI_TCG2_GET_EVENT_LOG
+ * shall be stored in an instance of an EFI_CONFIGURATION_TABLE aka
+ * EFI TCG 2.0 final events table. Hence, it is necessary to trigger the
+ * internal switch through calling get_event_log() in order to allow
+ * to retrieve the logs from OS runtime.
+ */
+static EFI_STATUS trigger_tcg2_final_events_table(const EFI_TCG2 *tcg)
+{
+ return uefi_call_wrapper(tcg->GetEventLog, 5, tcg,
+ EFI_TCG2_EVENT_LOG_FORMAT_TCG_2, NULL,
+ NULL, NULL);
+}
static EFI_STATUS tpm2_measure_to_pcr_and_event_log(const EFI_TCG2 *tcg, UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer,
UINT64 buffer_size, const CHAR16 *description) {
EFI_STATUS status;
EFI_TCG2_EVENT *tcg_event;
UINTN desc_len;
+ static BOOLEAN triggered = FALSE;
+
+ if (triggered == FALSE) {
+ status = trigger_tcg2_final_events_table(tcg);
+ if (EFI_ERROR(status))
+ return status;
+
+ triggered = TRUE;
+ }
desc_len = StrLen(description) * sizeof(CHAR16);
diff --git a/src/core/busname.c b/src/core/busname.c
index 63c7dde0bd..b96ec09e67 100644
--- a/src/core/busname.c
+++ b/src/core/busname.c
@@ -872,7 +872,7 @@ static void busname_sigchld_event(Unit *u, pid_t pid, int code, int status) {
n->control_pid = 0;
- if (is_clean_exit(code, status, NULL))
+ if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
f = BUSNAME_SUCCESS;
else if (code == CLD_EXITED)
f = BUSNAME_FAILURE_EXIT_CODE;
diff --git a/src/core/main.c b/src/core/main.c
index 6fe440277e..4b82a57b3c 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -203,7 +203,7 @@ noreturn static void crash(int sig) {
pid, sigchld_code_to_string(status.si_code),
status.si_status,
strna(status.si_code == CLD_EXITED
- ? exit_status_to_string(status.si_status, EXIT_STATUS_FULL)
+ ? exit_status_to_string(status.si_status, EXIT_STATUS_MINIMAL)
: signal_to_string(status.si_status)));
else
log_emergency("Caught <%s>, dumped core as pid "PID_FMT".", signal_to_string(sig), pid);
@@ -1532,15 +1532,9 @@ int main(int argc, char *argv[]) {
* need to do that for user instances since they never log
* into the console. */
log_show_color(colors_enabled());
- make_null_stdio();
- }
-
- /* Initialize default unit */
- r = free_and_strdup(&arg_default_unit, SPECIAL_DEFAULT_TARGET);
- if (r < 0) {
- log_emergency_errno(r, "Failed to set default unit %s: %m", SPECIAL_DEFAULT_TARGET);
- error_message = "Failed to set default unit";
- goto finish;
+ r = make_null_stdio();
+ if (r < 0)
+ log_warning_errno(r, "Failed to redirect standard streams to /dev/null: %m");
}
r = initialize_join_controllers();
@@ -1590,6 +1584,16 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ /* Initialize default unit */
+ if (!arg_default_unit) {
+ arg_default_unit = strdup(SPECIAL_DEFAULT_TARGET);
+ if (!arg_default_unit) {
+ r = log_oom();
+ error_message = "Failed to set default unit";
+ goto finish;
+ }
+ }
+
if (arg_action == ACTION_TEST &&
geteuid() == 0) {
log_error("Don't run test mode as root.");
diff --git a/src/core/mount.c b/src/core/mount.c
index 436c0e1029..15619dffe3 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -830,7 +830,7 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
fail:
log_unit_warning_errno(UNIT(m), r, "Failed to kill processes: %m");
- if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL)
+ if (IN_SET(state, MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL))
mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
else
mount_enter_dead(m, MOUNT_FAILURE_RESOURCES);
@@ -986,18 +986,19 @@ static int mount_start(Unit *u) {
/* We cannot fulfill this request right now, try again later
* please! */
- if (m->state == MOUNT_UNMOUNTING ||
- m->state == MOUNT_UNMOUNTING_SIGTERM ||
- m->state == MOUNT_UNMOUNTING_SIGKILL ||
- m->state == MOUNT_MOUNTING_SIGTERM ||
- m->state == MOUNT_MOUNTING_SIGKILL)
+ if (IN_SET(m->state,
+ MOUNT_UNMOUNTING,
+ MOUNT_UNMOUNTING_SIGTERM,
+ MOUNT_UNMOUNTING_SIGKILL,
+ MOUNT_MOUNTING_SIGTERM,
+ MOUNT_MOUNTING_SIGKILL))
return -EAGAIN;
/* Already on it! */
if (m->state == MOUNT_MOUNTING)
return 0;
- assert(m->state == MOUNT_DEAD || m->state == MOUNT_FAILED);
+ assert(IN_SET(m->state, MOUNT_DEAD, MOUNT_FAILED));
r = unit_start_limit_test(u);
if (r < 0) {
@@ -1023,19 +1024,21 @@ static int mount_stop(Unit *u) {
assert(m);
/* Already on it */
- if (m->state == MOUNT_UNMOUNTING ||
- m->state == MOUNT_UNMOUNTING_SIGKILL ||
- m->state == MOUNT_UNMOUNTING_SIGTERM ||
- m->state == MOUNT_MOUNTING_SIGTERM ||
- m->state == MOUNT_MOUNTING_SIGKILL)
+ if (IN_SET(m->state,
+ MOUNT_UNMOUNTING,
+ MOUNT_UNMOUNTING_SIGKILL,
+ MOUNT_UNMOUNTING_SIGTERM,
+ MOUNT_MOUNTING_SIGTERM,
+ MOUNT_MOUNTING_SIGKILL))
return 0;
- assert(m->state == MOUNT_MOUNTING ||
- m->state == MOUNT_MOUNTING_DONE ||
- m->state == MOUNT_MOUNTED ||
- m->state == MOUNT_REMOUNTING ||
- m->state == MOUNT_REMOUNTING_SIGTERM ||
- m->state == MOUNT_REMOUNTING_SIGKILL);
+ assert(IN_SET(m->state,
+ MOUNT_MOUNTING,
+ MOUNT_MOUNTING_DONE,
+ MOUNT_MOUNTED,
+ MOUNT_REMOUNTING,
+ MOUNT_REMOUNTING_SIGTERM,
+ MOUNT_REMOUNTING_SIGKILL));
mount_enter_unmounting(m);
return 1;
@@ -1163,7 +1166,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
m->control_pid = 0;
- if (is_clean_exit(code, status, NULL))
+ if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
f = MOUNT_SUCCESS;
else if (code == CLD_EXITED)
f = MOUNT_FAILURE_EXIT_CODE;
@@ -1201,9 +1204,10 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
case MOUNT_MOUNTING_SIGKILL:
case MOUNT_MOUNTING_SIGTERM:
- if (f == MOUNT_SUCCESS)
- mount_enter_mounted(m, f);
- else if (m->from_proc_self_mountinfo)
+ if (f == MOUNT_SUCCESS || m->from_proc_self_mountinfo)
+ /* If /bin/mount returned success, or if we see the mount point in /proc/self/mountinfo we are
+ * happy. If we see the first condition first, we should see the the second condition
+ * immediately after – or /bin/mount lies to us and is broken. */
mount_enter_mounted(m, f);
else
mount_enter_dead(m, f);
diff --git a/src/core/service.c b/src/core/service.c
index 8ce25c494c..63045ede55 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -2604,8 +2604,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
assert(s);
assert(pid >= 0);
- if (UNIT(s)->fragment_path ? is_clean_exit(code, status, &s->success_status) :
- is_clean_exit_lsb(code, status, &s->success_status))
+ if (is_clean_exit(code, status, s->type == SERVICE_ONESHOT ? EXIT_CLEAN_COMMAND : EXIT_CLEAN_DAEMON, &s->success_status))
f = SERVICE_SUCCESS;
else if (code == CLD_EXITED)
f = SERVICE_FAILURE_EXIT_CODE;
diff --git a/src/core/socket.c b/src/core/socket.c
index ae8a1f751f..0b1c4acfec 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -2746,7 +2746,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_pid = 0;
- if (is_clean_exit(code, status, NULL))
+ if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
f = SOCKET_SUCCESS;
else if (code == CLD_EXITED)
f = SOCKET_FAILURE_EXIT_CODE;
diff --git a/src/core/swap.c b/src/core/swap.c
index 0333eaefb8..b592abb9fb 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -992,7 +992,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_pid = 0;
- if (is_clean_exit(code, status, NULL))
+ if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
f = SWAP_SUCCESS;
else if (code == CLD_EXITED)
f = SWAP_FAILURE_EXIT_CODE;
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index a9009cfefe..d86c3681b1 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -127,6 +127,10 @@ static int spawn_child(const char* child, char** argv) {
if (r < 0)
log_warning_errno(errno, "Failed to close write end of pipe: %m");
+ r = fd_nonblock(fd[0], true);
+ if (r < 0)
+ log_warning_errno(errno, "Failed to set child pipe to non-blocking: %m");
+
return fd[0];
}
diff --git a/src/login/systemd-user.m4 b/src/login/systemd-user.m4
index f188a8e548..fe38b24fef 100644
--- a/src/login/systemd-user.m4
+++ b/src/login/systemd-user.m4
@@ -2,11 +2,9 @@
#
# Used by systemd --user instances.
-account include system-auth
-
m4_ifdef(`HAVE_SELINUX',
session required pam_selinux.so close
session required pam_selinux.so nottys open
)m4_dnl
session required pam_loginuid.so
-session include system-auth
+session optional pam_systemd.so
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index e9de31e184..7b9be3b425 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -2629,6 +2629,7 @@ static int clean_images(int argc, char *argv[], void *userdata) {
}
static int help(int argc, char *argv[], void *userdata) {
+ pager_open(arg_no_pager, false);
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Send control commands to or query the virtual machine and container\n"
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 5498e352d8..ed52d5e42d 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -571,6 +571,21 @@ int address_configure(
address->flags |= IFA_F_PERMANENT;
+ if (address->home_address)
+ address->flags |= IFA_F_HOMEADDRESS;
+
+ if (address->duplicate_address_detection)
+ address->flags |= IFA_F_NODAD;
+
+ if (address->manage_temporary_address)
+ address->flags |= IFA_F_MANAGETEMPADDR;
+
+ if (address->prefix_route)
+ address->flags |= IFA_F_NOPREFIXROUTE;
+
+ if (address->autojoin)
+ address->flags |= IFA_F_MCAUTOJOIN;
+
r = sd_rtnl_message_addr_set_flags(req, (address->flags & 0xff));
if (r < 0)
return log_error_errno(r, "Could not set flags: %m");
@@ -856,6 +871,50 @@ int config_parse_lifetime(const char *unit,
return 0;
}
+int config_parse_address_flags(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_address_free_ Address *n = NULL;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = address_new_static(network, section_line, &n);
+ if (r < 0)
+ return r;
+
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (streq(lvalue, "HomeAddress"))
+ n->home_address = r;
+ else if (streq(lvalue, "DuplicateAddressDetection"))
+ n->duplicate_address_detection = r;
+ else if (streq(lvalue, "ManageTemporaryAddress"))
+ n->manage_temporary_address = r;
+ else if (streq(lvalue, "PrefixRoute"))
+ n->prefix_route = r;
+ else if (streq(lvalue, "AutoJoin"))
+ n->autojoin = r;
+
+ return 0;
+}
+
bool address_is_ready(const Address *a) {
assert(a);
diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h
index 03c4bea7c6..bc3b4fc7f3 100644
--- a/src/network/networkd-address.h
+++ b/src/network/networkd-address.h
@@ -53,6 +53,11 @@ struct Address {
union in_addr_union in_addr_peer;
bool ip_masquerade_done:1;
+ bool duplicate_address_detection;
+ bool manage_temporary_address;
+ bool home_address;
+ bool prefix_route;
+ bool autojoin;
LIST_FIELDS(Address, addresses);
};
@@ -77,3 +82,4 @@ int config_parse_address(const char *unit, const char *filename, unsigned line,
int config_parse_broadcast(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_label(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_lifetime(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_address_flags(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-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf
index 6b7ab7ab87..323eaa8032 100644
--- a/src/network/networkd-netdev-gperf.gperf
+++ b/src/network/networkd-netdev-gperf.gperf
@@ -63,10 +63,13 @@ VXLAN.L2MissNotification, config_parse_bool, 0,
VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss)
VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit)
VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
+VXLAN.UDPChecksum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
+VXLAN.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
-VXLAN.RemoteCheckSumTx, config_parse_bool, 0, offsetof(VxLan, remote_csum_tx)
-VXLAN.RemoteCheckSumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx)
+VXLAN.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
+VXLAN.RemoteChecksumTx, config_parse_bool, 0, offsetof(VxLan, remote_csum_tx)
+VXLAN.RemoteChecksumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx)
VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing)
VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy)
VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb)
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 62779c7c48..5587961b9f 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -70,6 +70,11 @@ Address.Peer, config_parse_address,
Address.Broadcast, config_parse_broadcast, 0, 0
Address.Label, config_parse_label, 0, 0
Address.PreferredLifetime, config_parse_lifetime, 0, 0
+Address.HomeAddress, config_parse_address_flags, 0, 0
+Address.DuplicateAddressDetection, config_parse_address_flags, 0, 0
+Address.ManageTemporaryAddress, config_parse_address_flags, 0, 0
+Address.PrefixRoute, config_parse_address_flags, 0, 0
+Address.AutoJoin, config_parse_address_flags, 0, 0
Route.Gateway, config_parse_gateway, 0, 0
Route.Destination, config_parse_destination, 0, 0
Route.Source, config_parse_destination, 0, 0
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index a08377b3a3..a173d171e1 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -316,17 +316,10 @@ static int custom_mounts_prepare(void) {
return 0;
}
-static int detect_unified_cgroup_hierarchy(void) {
+static int detect_unified_cgroup_hierarchy(const char *directory) {
const char *e;
int r, all_unified, systemd_unified;
- all_unified = cg_all_unified();
- systemd_unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
-
- if (all_unified < 0 || systemd_unified < 0)
- return log_error_errno(all_unified < 0 ? all_unified : systemd_unified,
- "Failed to determine whether the unified cgroups hierarchy is used: %m");
-
/* Allow the user to control whether the unified hierarchy is used */
e = getenv("UNIFIED_CGROUP_HIERARCHY");
if (e) {
@@ -341,12 +334,34 @@ static int detect_unified_cgroup_hierarchy(void) {
return 0;
}
+ all_unified = cg_all_unified();
+ systemd_unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
+
+ if (all_unified < 0 || systemd_unified < 0)
+ return log_error_errno(all_unified < 0 ? all_unified : systemd_unified,
+ "Failed to determine whether the unified cgroups hierarchy is used: %m");
+
/* Otherwise inherit the default from the host system */
- if (all_unified > 0)
- arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL;
- else if (systemd_unified > 0)
- arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_SYSTEMD;
- else
+ if (all_unified > 0) {
+ /* Unified cgroup hierarchy support was added in 230. Unfortunately the detection
+ * routine only detects 231, so we'll have a false negative here for 230. */
+ r = systemd_installation_has_version(directory, 230);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine systemd version in container: %m");
+ if (r > 0)
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL;
+ else
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE;
+ } else if (systemd_unified > 0) {
+ /* Mixed cgroup hierarchy support was added in 232 */
+ r = systemd_installation_has_version(directory, 232);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine systemd version in container: %m");
+ if (r > 0)
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_SYSTEMD;
+ else
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE;
+ } else
arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE;
return 0;
@@ -400,52 +415,52 @@ static int parse_argv(int argc, char *argv[]) {
};
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "directory", required_argument, NULL, 'D' },
- { "template", required_argument, NULL, ARG_TEMPLATE },
- { "ephemeral", no_argument, NULL, 'x' },
- { "user", required_argument, NULL, 'u' },
- { "private-network", no_argument, NULL, ARG_PRIVATE_NETWORK },
- { "as-pid2", no_argument, NULL, 'a' },
- { "boot", no_argument, NULL, 'b' },
- { "uuid", required_argument, NULL, ARG_UUID },
- { "read-only", no_argument, NULL, ARG_READ_ONLY },
- { "capability", required_argument, NULL, ARG_CAPABILITY },
- { "drop-capability", required_argument, NULL, ARG_DROP_CAPABILITY },
- { "link-journal", required_argument, NULL, ARG_LINK_JOURNAL },
- { "bind", required_argument, NULL, ARG_BIND },
- { "bind-ro", required_argument, NULL, ARG_BIND_RO },
- { "tmpfs", required_argument, NULL, ARG_TMPFS },
- { "overlay", required_argument, NULL, ARG_OVERLAY },
- { "overlay-ro", required_argument, NULL, ARG_OVERLAY_RO },
- { "machine", required_argument, NULL, 'M' },
- { "slice", required_argument, NULL, 'S' },
- { "setenv", required_argument, NULL, 'E' },
- { "selinux-context", required_argument, NULL, 'Z' },
- { "selinux-apifs-context", required_argument, NULL, 'L' },
- { "quiet", no_argument, NULL, 'q' },
- { "share-system", no_argument, NULL, ARG_SHARE_SYSTEM }, /* not documented */
- { "register", required_argument, NULL, ARG_REGISTER },
- { "keep-unit", no_argument, NULL, ARG_KEEP_UNIT },
- { "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE },
- { "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN },
- { "network-ipvlan", required_argument, NULL, ARG_NETWORK_IPVLAN },
- { "network-veth", no_argument, NULL, 'n' },
- { "network-veth-extra", required_argument, NULL, ARG_NETWORK_VETH_EXTRA},
- { "network-bridge", required_argument, NULL, ARG_NETWORK_BRIDGE },
- { "network-zone", required_argument, NULL, ARG_NETWORK_ZONE },
- { "personality", required_argument, NULL, ARG_PERSONALITY },
- { "image", required_argument, NULL, 'i' },
- { "volatile", optional_argument, NULL, ARG_VOLATILE },
- { "port", required_argument, NULL, 'p' },
- { "property", required_argument, NULL, ARG_PROPERTY },
- { "private-users", optional_argument, NULL, ARG_PRIVATE_USERS },
- { "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN},
- { "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL },
- { "settings", required_argument, NULL, ARG_SETTINGS },
- { "chdir", required_argument, NULL, ARG_CHDIR },
- { "notify-ready", required_argument, NULL, ARG_NOTIFY_READY },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "directory", required_argument, NULL, 'D' },
+ { "template", required_argument, NULL, ARG_TEMPLATE },
+ { "ephemeral", no_argument, NULL, 'x' },
+ { "user", required_argument, NULL, 'u' },
+ { "private-network", no_argument, NULL, ARG_PRIVATE_NETWORK },
+ { "as-pid2", no_argument, NULL, 'a' },
+ { "boot", no_argument, NULL, 'b' },
+ { "uuid", required_argument, NULL, ARG_UUID },
+ { "read-only", no_argument, NULL, ARG_READ_ONLY },
+ { "capability", required_argument, NULL, ARG_CAPABILITY },
+ { "drop-capability", required_argument, NULL, ARG_DROP_CAPABILITY },
+ { "link-journal", required_argument, NULL, ARG_LINK_JOURNAL },
+ { "bind", required_argument, NULL, ARG_BIND },
+ { "bind-ro", required_argument, NULL, ARG_BIND_RO },
+ { "tmpfs", required_argument, NULL, ARG_TMPFS },
+ { "overlay", required_argument, NULL, ARG_OVERLAY },
+ { "overlay-ro", required_argument, NULL, ARG_OVERLAY_RO },
+ { "machine", required_argument, NULL, 'M' },
+ { "slice", required_argument, NULL, 'S' },
+ { "setenv", required_argument, NULL, 'E' },
+ { "selinux-context", required_argument, NULL, 'Z' },
+ { "selinux-apifs-context", required_argument, NULL, 'L' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "share-system", no_argument, NULL, ARG_SHARE_SYSTEM }, /* not documented */
+ { "register", required_argument, NULL, ARG_REGISTER },
+ { "keep-unit", no_argument, NULL, ARG_KEEP_UNIT },
+ { "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE },
+ { "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN },
+ { "network-ipvlan", required_argument, NULL, ARG_NETWORK_IPVLAN },
+ { "network-veth", no_argument, NULL, 'n' },
+ { "network-veth-extra", required_argument, NULL, ARG_NETWORK_VETH_EXTRA },
+ { "network-bridge", required_argument, NULL, ARG_NETWORK_BRIDGE },
+ { "network-zone", required_argument, NULL, ARG_NETWORK_ZONE },
+ { "personality", required_argument, NULL, ARG_PERSONALITY },
+ { "image", required_argument, NULL, 'i' },
+ { "volatile", optional_argument, NULL, ARG_VOLATILE },
+ { "port", required_argument, NULL, 'p' },
+ { "property", required_argument, NULL, ARG_PROPERTY },
+ { "private-users", optional_argument, NULL, ARG_PRIVATE_USERS },
+ { "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN },
+ { "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL },
+ { "settings", required_argument, NULL, ARG_SETTINGS },
+ { "chdir", required_argument, NULL, ARG_CHDIR },
+ { "notify-ready", required_argument, NULL, ARG_NOTIFY_READY },
{}
};
@@ -898,15 +913,21 @@ static int parse_argv(int argc, char *argv[]) {
break;
- case ARG_PRIVATE_USERS:
+ case ARG_PRIVATE_USERS: {
+ int boolean = -1;
- r = optarg ? parse_boolean(optarg) : 1;
- if (r == 0) {
+ if (!optarg)
+ boolean = true;
+ else if (!in_charset(optarg, DIGITS))
+ /* do *not* parse numbers as booleans */
+ boolean = parse_boolean(optarg);
+
+ if (boolean == false) {
/* no: User namespacing off */
arg_userns_mode = USER_NAMESPACE_NO;
arg_uid_shift = UID_INVALID;
arg_uid_range = UINT32_C(0x10000);
- } else if (r > 0) {
+ } else if (boolean == true) {
/* yes: User namespacing on, UID range is read from root dir */
arg_userns_mode = USER_NAMESPACE_FIXED;
arg_uid_shift = UID_INVALID;
@@ -930,23 +951,27 @@ static int parse_argv(int argc, char *argv[]) {
shift = buffer;
range++;
- if (safe_atou32(range, &arg_uid_range) < 0 || arg_uid_range <= 0) {
- log_error("Failed to parse UID range: %s", range);
- return -EINVAL;
- }
+ r = safe_atou32(range, &arg_uid_range);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse UID range \"%s\": %m", range);
} else
shift = optarg;
- if (parse_uid(shift, &arg_uid_shift) < 0) {
- log_error("Failed to parse UID: %s", optarg);
- return -EINVAL;
- }
+ r = parse_uid(shift, &arg_uid_shift);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse UID \"%s\": %m", optarg);
arg_userns_mode = USER_NAMESPACE_FIXED;
}
+ if (arg_uid_range <= 0) {
+ log_error("UID range cannot be 0.");
+ return -EINVAL;
+ }
+
arg_settings_mask |= SETTING_USERNS;
break;
+ }
case 'U':
if (userns_supported()) {
@@ -1125,10 +1150,6 @@ static int parse_argv(int argc, char *argv[]) {
arg_caps_retain = (arg_caps_retain | plus | (arg_private_network ? 1ULL << CAP_NET_ADMIN : 0)) & ~minus;
- r = detect_unified_cgroup_hierarchy();
- if (r < 0)
- return r;
-
e = getenv("SYSTEMD_NSPAWN_CONTAINER_SERVICE");
if (e)
arg_container_service_name = e;
@@ -2970,6 +2991,10 @@ static int outer_child(
if (r < 0)
return r;
+ r = detect_unified_cgroup_hierarchy(directory);
+ if (r < 0)
+ return r;
+
if (arg_userns_mode != USER_NAMESPACE_NO) {
/* Let the parent know which UID shift we read from the image */
l = send(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL);
@@ -3575,18 +3600,437 @@ static int load_settings(void) {
return 0;
}
+static int run(int master,
+ const char* console,
+ const char *root_device, bool root_device_rw,
+ const char *home_device, bool home_device_rw,
+ const char *srv_device, bool srv_device_rw,
+ const char *esp_device,
+ bool interactive,
+ bool secondary,
+ FDSet *fds,
+ char veth_name[IFNAMSIZ], bool *veth_created,
+ union in_addr_union *exposed,
+ pid_t *pid, int *ret) {
+
+ static const struct sigaction sa = {
+ .sa_handler = nop_signal_handler,
+ .sa_flags = SA_NOCLDSTOP,
+ };
+
+ _cleanup_release_lock_file_ LockFile uid_shift_lock = LOCK_FILE_INIT;
+ _cleanup_close_ int etc_passwd_lock = -1;
+ _cleanup_close_pair_ int
+ kmsg_socket_pair[2] = { -1, -1 },
+ rtnl_socket_pair[2] = { -1, -1 },
+ pid_socket_pair[2] = { -1, -1 },
+ uuid_socket_pair[2] = { -1, -1 },
+ notify_socket_pair[2] = { -1, -1 },
+ uid_shift_socket_pair[2] = { -1, -1 };
+ _cleanup_close_ int notify_socket= -1;
+ _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ ContainerStatus container_status = 0;
+ char last_char = 0;
+ int ifi = 0, r;
+ ssize_t l;
+ sigset_t mask_chld;
+
+ assert_se(sigemptyset(&mask_chld) == 0);
+ assert_se(sigaddset(&mask_chld, SIGCHLD) == 0);
+
+ if (arg_userns_mode == USER_NAMESPACE_PICK) {
+ /* When we shall pick the UID/GID range, let's first lock /etc/passwd, so that we can safely
+ * check with getpwuid() if the specific user already exists. Note that /etc might be
+ * read-only, in which case this will fail with EROFS. But that's really OK, as in that case we
+ * can be reasonably sure that no users are going to be added. Note that getpwuid() checks are
+ * really just an extra safety net. We kinda assume that the UID range we allocate from is
+ * really ours. */
+
+ etc_passwd_lock = take_etc_passwd_lock(NULL);
+ if (etc_passwd_lock < 0 && etc_passwd_lock != -EROFS)
+ return log_error_errno(etc_passwd_lock, "Failed to take /etc/passwd lock: %m");
+ }
+
+ r = barrier_create(&barrier);
+ if (r < 0)
+ return log_error_errno(r, "Cannot initialize IPC barrier: %m");
+
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create kmsg socket pair: %m");
+
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, rtnl_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create rtnl socket pair: %m");
+
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pid_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create pid socket pair: %m");
+
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uuid_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create id socket pair: %m");
+
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, notify_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create notify socket pair: %m");
+
+ if (arg_userns_mode != USER_NAMESPACE_NO)
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create uid shift socket pair: %m");
+
+ /* Child can be killed before execv(), so handle SIGCHLD in order to interrupt
+ * parent's blocking calls and give it a chance to call wait() and terminate. */
+ r = sigprocmask(SIG_UNBLOCK, &mask_chld, NULL);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to change the signal mask: %m");
+
+ r = sigaction(SIGCHLD, &sa, NULL);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
+
+ *pid = raw_clone(SIGCHLD|CLONE_NEWNS);
+ if (*pid < 0)
+ return log_error_errno(errno, "clone() failed%s: %m",
+ errno == EINVAL ?
+ ", do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in)" : "");
+
+ if (*pid == 0) {
+ /* The outer child only has a file system namespace. */
+ barrier_set_role(&barrier, BARRIER_CHILD);
+
+ master = safe_close(master);
+
+ kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]);
+ rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
+ pid_socket_pair[0] = safe_close(pid_socket_pair[0]);
+ uuid_socket_pair[0] = safe_close(uuid_socket_pair[0]);
+ notify_socket_pair[0] = safe_close(notify_socket_pair[0]);
+ uid_shift_socket_pair[0] = safe_close(uid_shift_socket_pair[0]);
+
+ (void) reset_all_signal_handlers();
+ (void) reset_signal_mask();
+
+ r = outer_child(&barrier,
+ arg_directory,
+ console,
+ root_device, root_device_rw,
+ home_device, home_device_rw,
+ srv_device, srv_device_rw,
+ esp_device,
+ interactive,
+ secondary,
+ pid_socket_pair[1],
+ uuid_socket_pair[1],
+ notify_socket_pair[1],
+ kmsg_socket_pair[1],
+ rtnl_socket_pair[1],
+ uid_shift_socket_pair[1],
+ fds);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ barrier_set_role(&barrier, BARRIER_PARENT);
+
+ fds = fdset_free(fds);
+
+ kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]);
+ rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]);
+ pid_socket_pair[1] = safe_close(pid_socket_pair[1]);
+ uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]);
+ notify_socket_pair[1] = safe_close(notify_socket_pair[1]);
+ uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]);
+
+ if (arg_userns_mode != USER_NAMESPACE_NO) {
+ /* The child just let us know the UID shift it might have read from the image. */
+ l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, 0);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to read UID shift: %m");
+
+ if (l != sizeof arg_uid_shift) {
+ log_error("Short read while reading UID shift.");
+ return -EIO;
+ }
+
+ if (arg_userns_mode == USER_NAMESPACE_PICK) {
+ /* If we are supposed to pick the UID shift, let's try to use the shift read from the
+ * image, but if that's already in use, pick a new one, and report back to the child,
+ * which one we now picked. */
+
+ r = uid_shift_pick(&arg_uid_shift, &uid_shift_lock);
+ if (r < 0)
+ return log_error_errno(r, "Failed to pick suitable UID/GID range: %m");
+
+ l = send(uid_shift_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, MSG_NOSIGNAL);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to send UID shift: %m");
+ if (l != sizeof arg_uid_shift) {
+ log_error("Short write while writing UID shift.");
+ return -EIO;
+ }
+ }
+ }
+
+ /* Wait for the outer child. */
+ r = wait_for_terminate_and_warn("namespace helper", *pid, NULL);
+ if (r != 0)
+ return r < 0 ? r : -EIO;
+
+ /* And now retrieve the PID of the inner child. */
+ l = recv(pid_socket_pair[0], pid, sizeof *pid, 0);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to read inner child PID: %m");
+ if (l != sizeof *pid) {
+ log_error("Short read while reading inner child PID.");
+ return -EIO;
+ }
+
+ /* We also retrieve container UUID in case it was generated by outer child */
+ l = recv(uuid_socket_pair[0], &arg_uuid, sizeof arg_uuid, 0);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to read container machine ID: %m");
+ if (l != sizeof(arg_uuid)) {
+ log_error("Short read while reading container machined ID.");
+ return -EIO;
+ }
+
+ /* We also retrieve the socket used for notifications generated by outer child */
+ notify_socket = receive_one_fd(notify_socket_pair[0], 0);
+ if (notify_socket < 0)
+ return log_error_errno(notify_socket,
+ "Failed to receive notification socket from the outer child: %m");
+
+ log_debug("Init process invoked as PID "PID_FMT, *pid);
+
+ if (arg_userns_mode != USER_NAMESPACE_NO) {
+ if (!barrier_place_and_sync(&barrier)) { /* #1 */
+ log_error("Child died too early.");
+ return -ESRCH;
+ }
+
+ r = setup_uid_map(*pid);
+ if (r < 0)
+ return r;
+
+ (void) barrier_place(&barrier); /* #2 */
+ }
+
+ if (arg_private_network) {
+
+ r = move_network_interfaces(*pid, arg_network_interfaces);
+ if (r < 0)
+ return r;
+
+ if (arg_network_veth) {
+ r = setup_veth(arg_machine, *pid, veth_name,
+ arg_network_bridge || arg_network_zone);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ ifi = r;
+
+ if (arg_network_bridge) {
+ /* Add the interface to a bridge */
+ r = setup_bridge(veth_name, arg_network_bridge, false);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ ifi = r;
+ } else if (arg_network_zone) {
+ /* Add the interface to a bridge, possibly creating it */
+ r = setup_bridge(veth_name, arg_network_zone, true);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ ifi = r;
+ }
+ }
+
+ r = setup_veth_extra(arg_machine, *pid, arg_network_veth_extra);
+ if (r < 0)
+ return r;
+
+ /* We created the primary and extra veth links now; let's remember this, so that we know to
+ remove them later on. Note that we don't bother with removing veth links that were created
+ here when their setup failed half-way, because in that case the kernel should be able to
+ remove them on its own, since they cannot be referenced by anything yet. */
+ *veth_created = true;
+
+ r = setup_macvlan(arg_machine, *pid, arg_network_macvlan);
+ if (r < 0)
+ return r;
+
+ r = setup_ipvlan(arg_machine, *pid, arg_network_ipvlan);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_register) {
+ r = register_machine(
+ arg_machine,
+ *pid,
+ arg_directory,
+ arg_uuid,
+ ifi,
+ arg_slice,
+ arg_custom_mounts, arg_n_custom_mounts,
+ arg_kill_signal,
+ arg_property,
+ arg_keep_unit,
+ arg_container_service_name);
+ if (r < 0)
+ return r;
+ }
+
+ r = sync_cgroup(*pid, arg_unified_cgroup_hierarchy);
+ if (r < 0)
+ return r;
+
+ if (arg_keep_unit) {
+ r = create_subcgroup(*pid, arg_unified_cgroup_hierarchy);
+ if (r < 0)
+ return r;
+ }
+
+ r = chown_cgroup(*pid, arg_uid_shift);
+ if (r < 0)
+ return r;
+
+ /* Notify the child that the parent is ready with all
+ * its setup (including cgroup-ification), and that
+ * the child can now hand over control to the code to
+ * run inside the container. */
+ (void) barrier_place(&barrier); /* #3 */
+
+ /* Block SIGCHLD here, before notifying child.
+ * process_pty() will handle it with the other signals. */
+ assert_se(sigprocmask(SIG_BLOCK, &mask_chld, NULL) >= 0);
+
+ /* Reset signal to default */
+ r = default_signals(SIGCHLD, -1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to reset SIGCHLD: %m");
+
+ r = sd_event_new(&event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get default event source: %m");
+
+ r = setup_sd_notify_parent(event, notify_socket, PID_TO_PTR(*pid));
+ if (r < 0)
+ return r;
+
+ /* Let the child know that we are ready and wait that the child is completely ready now. */
+ if (!barrier_place_and_sync(&barrier)) { /* #4 */
+ log_error("Child died too early.");
+ return -ESRCH;
+ }
+
+ /* At this point we have made use of the UID we picked, and thus nss-mymachines
+ * will make them appear in getpwuid(), thus we can release the /etc/passwd lock. */
+ etc_passwd_lock = safe_close(etc_passwd_lock);
+
+ sd_notifyf(false,
+ "STATUS=Container running.\n"
+ "X_NSPAWN_LEADER_PID=" PID_FMT, *pid);
+ if (!arg_notify_ready)
+ sd_notify(false, "READY=1\n");
+
+ if (arg_kill_signal > 0) {
+ /* Try to kill the init system on SIGINT or SIGTERM */
+ sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, PID_TO_PTR(*pid));
+ sd_event_add_signal(event, NULL, SIGTERM, on_orderly_shutdown, PID_TO_PTR(*pid));
+ } else {
+ /* Immediately exit */
+ sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
+ sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+ }
+
+ /* simply exit on sigchld */
+ sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL);
+
+ if (arg_expose_ports) {
+ r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, exposed, &rtnl);
+ if (r < 0)
+ return r;
+
+ (void) expose_port_execute(rtnl, arg_expose_ports, exposed);
+ }
+
+ rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
+
+ r = pty_forward_new(event, master,
+ PTY_FORWARD_IGNORE_VHANGUP | (interactive ? 0 : PTY_FORWARD_READ_ONLY),
+ &forward);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create PTY forwarder: %m");
+
+ r = sd_event_loop(event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
+
+ pty_forward_get_last_char(forward, &last_char);
+
+ forward = pty_forward_free(forward);
+
+ if (!arg_quiet && last_char != '\n')
+ putc('\n', stdout);
+
+ /* Kill if it is not dead yet anyway */
+ if (arg_register && !arg_keep_unit)
+ terminate_machine(*pid);
+
+ /* Normally redundant, but better safe than sorry */
+ kill(*pid, SIGKILL);
+
+ r = wait_for_container(*pid, &container_status);
+ *pid = 0;
+
+ if (r < 0)
+ /* We failed to wait for the container, or the container exited abnormally. */
+ return r;
+ if (r > 0 || container_status == CONTAINER_TERMINATED) {
+ /* r > 0 → The container exited with a non-zero status.
+ * As a special case, we need to replace 133 with a different value,
+ * because 133 is special-cased in the service file to reboot the container.
+ * otherwise → The container exited with zero status and a reboot was not requested.
+ */
+ if (r == 133)
+ r = EXIT_FAILURE; /* replace 133 with the general failure code */
+ *ret = r;
+ return 0; /* finito */
+ }
+
+ /* CONTAINER_REBOOTED, loop again */
+
+ if (arg_keep_unit) {
+ /* Special handling if we are running as a service: instead of simply
+ * restarting the machine we want to restart the entire service, so let's
+ * inform systemd about this with the special exit code 133. The service
+ * file uses RestartForceExitStatus=133 so that this results in a full
+ * nspawn restart. This is necessary since we might have cgroup parameters
+ * set we want to have flushed out. */
+ *ret = 0;
+ return 133;
+ }
+
+ expose_port_flush(arg_expose_ports, exposed);
+
+ (void) remove_veth_links(veth_name, arg_network_veth_extra);
+ *veth_created = false;
+ return 1; /* loop again */
+}
+
int main(int argc, char *argv[]) {
_cleanup_free_ char *device_path = NULL, *root_device = NULL, *home_device = NULL, *srv_device = NULL, *esp_device = NULL, *console = NULL;
bool root_device_rw = true, home_device_rw = true, srv_device_rw = true;
_cleanup_close_ int master = -1, image_fd = -1;
_cleanup_fdset_free_ FDSet *fds = NULL;
- int r, n_fd_passed, loop_nr = -1;
+ int r, n_fd_passed, loop_nr = -1, ret = EXIT_FAILURE;
char veth_name[IFNAMSIZ] = "";
bool secondary = false, remove_subvol = false;
- sigset_t mask_chld;
pid_t pid = 0;
- int ret = EXIT_SUCCESS;
union in_addr_union exposed = {};
_cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT;
bool interactive, veth_created = false;
@@ -3802,470 +4246,25 @@ int main(int argc, char *argv[]) {
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
- assert_se(sigemptyset(&mask_chld) == 0);
- assert_se(sigaddset(&mask_chld, SIGCHLD) == 0);
-
if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) {
r = log_error_errno(errno, "Failed to become subreaper: %m");
goto finish;
}
for (;;) {
- static const struct sigaction sa = {
- .sa_handler = nop_signal_handler,
- .sa_flags = SA_NOCLDSTOP,
- };
-
- _cleanup_release_lock_file_ LockFile uid_shift_lock = LOCK_FILE_INIT;
- _cleanup_close_ int etc_passwd_lock = -1;
- _cleanup_close_pair_ int
- kmsg_socket_pair[2] = { -1, -1 },
- rtnl_socket_pair[2] = { -1, -1 },
- pid_socket_pair[2] = { -1, -1 },
- uuid_socket_pair[2] = { -1, -1 },
- notify_socket_pair[2] = { -1, -1 },
- uid_shift_socket_pair[2] = { -1, -1 };
- _cleanup_close_ int notify_socket= -1;
- _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
- _cleanup_(sd_event_unrefp) sd_event *event = NULL;
- _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
- _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
- ContainerStatus container_status = 0;
- char last_char = 0;
- int ifi = 0;
- ssize_t l;
-
- if (arg_userns_mode == USER_NAMESPACE_PICK) {
- /* When we shall pick the UID/GID range, let's first lock /etc/passwd, so that we can safely
- * check with getpwuid() if the specific user already exists. Note that /etc might be
- * read-only, in which case this will fail with EROFS. But that's really OK, as in that case we
- * can be reasonably sure that no users are going to be added. Note that getpwuid() checks are
- * really just an extra safety net. We kinda assume that the UID range we allocate from is
- * really ours. */
-
- etc_passwd_lock = take_etc_passwd_lock(NULL);
- if (etc_passwd_lock < 0 && etc_passwd_lock != -EROFS) {
- log_error_errno(r, "Failed to take /etc/passwd lock: %m");
- goto finish;
- }
- }
-
- r = barrier_create(&barrier);
- if (r < 0) {
- log_error_errno(r, "Cannot initialize IPC barrier: %m");
- goto finish;
- }
-
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) {
- r = log_error_errno(errno, "Failed to create kmsg socket pair: %m");
- goto finish;
- }
-
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, rtnl_socket_pair) < 0) {
- r = log_error_errno(errno, "Failed to create rtnl socket pair: %m");
- goto finish;
- }
-
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pid_socket_pair) < 0) {
- r = log_error_errno(errno, "Failed to create pid socket pair: %m");
- goto finish;
- }
-
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uuid_socket_pair) < 0) {
- r = log_error_errno(errno, "Failed to create id socket pair: %m");
- goto finish;
- }
-
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, notify_socket_pair) < 0) {
- r = log_error_errno(errno, "Failed to create notify socket pair: %m");
- goto finish;
- }
-
- if (arg_userns_mode != USER_NAMESPACE_NO)
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0) {
- r = log_error_errno(errno, "Failed to create uid shift socket pair: %m");
- goto finish;
- }
-
- /* Child can be killed before execv(), so handle SIGCHLD
- * in order to interrupt parent's blocking calls and
- * give it a chance to call wait() and terminate. */
- r = sigprocmask(SIG_UNBLOCK, &mask_chld, NULL);
- if (r < 0) {
- r = log_error_errno(errno, "Failed to change the signal mask: %m");
- goto finish;
- }
-
- r = sigaction(SIGCHLD, &sa, NULL);
- if (r < 0) {
- r = log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
- goto finish;
- }
-
- pid = raw_clone(SIGCHLD|CLONE_NEWNS);
- if (pid < 0) {
- if (errno == EINVAL)
- r = log_error_errno(errno, "clone() failed, do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in): %m");
- else
- r = log_error_errno(errno, "clone() failed: %m");
-
- goto finish;
- }
-
- if (pid == 0) {
- /* The outer child only has a file system namespace. */
- barrier_set_role(&barrier, BARRIER_CHILD);
-
- master = safe_close(master);
-
- kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]);
- rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
- pid_socket_pair[0] = safe_close(pid_socket_pair[0]);
- uuid_socket_pair[0] = safe_close(uuid_socket_pair[0]);
- notify_socket_pair[0] = safe_close(notify_socket_pair[0]);
- uid_shift_socket_pair[0] = safe_close(uid_shift_socket_pair[0]);
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
- r = outer_child(&barrier,
- arg_directory,
- console,
- root_device, root_device_rw,
- home_device, home_device_rw,
- srv_device, srv_device_rw,
- esp_device,
- interactive,
- secondary,
- pid_socket_pair[1],
- uuid_socket_pair[1],
- notify_socket_pair[1],
- kmsg_socket_pair[1],
- rtnl_socket_pair[1],
- uid_shift_socket_pair[1],
- fds);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
- _exit(EXIT_SUCCESS);
- }
-
- barrier_set_role(&barrier, BARRIER_PARENT);
-
- fds = fdset_free(fds);
-
- kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]);
- rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]);
- pid_socket_pair[1] = safe_close(pid_socket_pair[1]);
- uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]);
- notify_socket_pair[1] = safe_close(notify_socket_pair[1]);
- uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]);
-
- if (arg_userns_mode != USER_NAMESPACE_NO) {
- /* The child just let us know the UID shift it might have read from the image. */
- l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), 0);
- if (l < 0) {
- r = log_error_errno(errno, "Failed to read UID shift: %m");
- goto finish;
- }
- if (l != sizeof(arg_uid_shift)) {
- log_error("Short read while reading UID shift.");
- r = EIO;
- goto finish;
- }
-
- if (arg_userns_mode == USER_NAMESPACE_PICK) {
- /* If we are supposed to pick the UID shift, let's try to use the shift read from the
- * image, but if that's already in use, pick a new one, and report back to the child,
- * which one we now picked. */
-
- r = uid_shift_pick(&arg_uid_shift, &uid_shift_lock);
- if (r < 0) {
- log_error_errno(r, "Failed to pick suitable UID/GID range: %m");
- goto finish;
- }
-
- l = send(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL);
- if (l < 0) {
- r = log_error_errno(errno, "Failed to send UID shift: %m");
- goto finish;
- }
- if (l != sizeof(arg_uid_shift)) {
- log_error("Short write while writing UID shift.");
- r = -EIO;
- goto finish;
- }
- }
- }
-
- /* Wait for the outer child. */
- r = wait_for_terminate_and_warn("namespace helper", pid, NULL);
- if (r < 0)
- goto finish;
- if (r != 0) {
- r = -EIO;
- goto finish;
- }
- pid = 0;
-
- /* And now retrieve the PID of the inner child. */
- l = recv(pid_socket_pair[0], &pid, sizeof(pid), 0);
- if (l < 0) {
- r = log_error_errno(errno, "Failed to read inner child PID: %m");
- goto finish;
- }
- if (l != sizeof(pid)) {
- log_error("Short read while reading inner child PID.");
- r = EIO;
- goto finish;
- }
-
- /* We also retrieve container UUID in case it was generated by outer child */
- l = recv(uuid_socket_pair[0], &arg_uuid, sizeof(arg_uuid), 0);
- if (l < 0) {
- r = log_error_errno(errno, "Failed to read container machine ID: %m");
- goto finish;
- }
- if (l != sizeof(arg_uuid)) {
- log_error("Short read while reading container machined ID.");
- r = EIO;
- goto finish;
- }
-
- /* We also retrieve the socket used for notifications generated by outer child */
- notify_socket = receive_one_fd(notify_socket_pair[0], 0);
- if (notify_socket < 0) {
- r = log_error_errno(errno, "Failed to receive notification socket from the outer child: %m");
- goto finish;
- }
-
- log_debug("Init process invoked as PID " PID_FMT, pid);
-
- if (arg_userns_mode != USER_NAMESPACE_NO) {
- if (!barrier_place_and_sync(&barrier)) { /* #1 */
- log_error("Child died too early.");
- r = -ESRCH;
- goto finish;
- }
-
- r = setup_uid_map(pid);
- if (r < 0)
- goto finish;
-
- (void) barrier_place(&barrier); /* #2 */
- }
-
- if (arg_private_network) {
-
- r = move_network_interfaces(pid, arg_network_interfaces);
- if (r < 0)
- goto finish;
-
- if (arg_network_veth) {
- r = setup_veth(arg_machine, pid, veth_name,
- arg_network_bridge || arg_network_zone);
- if (r < 0)
- goto finish;
- else if (r > 0)
- ifi = r;
-
- if (arg_network_bridge) {
- /* Add the interface to a bridge */
- r = setup_bridge(veth_name, arg_network_bridge, false);
- if (r < 0)
- goto finish;
- if (r > 0)
- ifi = r;
- } else if (arg_network_zone) {
- /* Add the interface to a bridge, possibly creating it */
- r = setup_bridge(veth_name, arg_network_zone, true);
- if (r < 0)
- goto finish;
- if (r > 0)
- ifi = r;
- }
- }
-
- r = setup_veth_extra(arg_machine, pid, arg_network_veth_extra);
- if (r < 0)
- goto finish;
-
- /* We created the primary and extra veth links now; let's remember this, so that we know to
- remove them later on. Note that we don't bother with removing veth links that were created
- here when their setup failed half-way, because in that case the kernel should be able to
- remove them on its own, since they cannot be referenced by anything yet. */
- veth_created = true;
-
- r = setup_macvlan(arg_machine, pid, arg_network_macvlan);
- if (r < 0)
- goto finish;
-
- r = setup_ipvlan(arg_machine, pid, arg_network_ipvlan);
- if (r < 0)
- goto finish;
- }
-
- if (arg_register) {
- r = register_machine(
- arg_machine,
- pid,
- arg_directory,
- arg_uuid,
- ifi,
- arg_slice,
- arg_custom_mounts, arg_n_custom_mounts,
- arg_kill_signal,
- arg_property,
- arg_keep_unit,
- arg_container_service_name);
- if (r < 0)
- goto finish;
- }
-
- r = sync_cgroup(pid, arg_unified_cgroup_hierarchy);
- if (r < 0)
- goto finish;
-
- if (arg_keep_unit) {
- r = create_subcgroup(pid, arg_unified_cgroup_hierarchy);
- if (r < 0)
- goto finish;
- }
-
- r = chown_cgroup(pid, arg_uid_shift);
- if (r < 0)
- goto finish;
-
- /* Notify the child that the parent is ready with all
- * its setup (including cgroup-ification), and that
- * the child can now hand over control to the code to
- * run inside the container. */
- (void) barrier_place(&barrier); /* #3 */
-
- /* Block SIGCHLD here, before notifying child.
- * process_pty() will handle it with the other signals. */
- assert_se(sigprocmask(SIG_BLOCK, &mask_chld, NULL) >= 0);
-
- /* Reset signal to default */
- r = default_signals(SIGCHLD, -1);
- if (r < 0) {
- log_error_errno(r, "Failed to reset SIGCHLD: %m");
- goto finish;
- }
-
- r = sd_event_new(&event);
- if (r < 0) {
- log_error_errno(r, "Failed to get default event source: %m");
- goto finish;
- }
-
- r = setup_sd_notify_parent(event, notify_socket, PID_TO_PTR(pid));
- if (r < 0)
- goto finish;
-
- /* Let the child know that we are ready and wait that the child is completely ready now. */
- if (!barrier_place_and_sync(&barrier)) { /* #4 */
- log_error("Child died too early.");
- r = -ESRCH;
- goto finish;
- }
-
- /* At this point we have made use of the UID we picked, and thus nss-mymachines will make them appear
- * in getpwuid(), thus we can release the /etc/passwd lock. */
- etc_passwd_lock = safe_close(etc_passwd_lock);
-
- sd_notifyf(false,
- "STATUS=Container running.\n"
- "X_NSPAWN_LEADER_PID=" PID_FMT, pid);
- if (!arg_notify_ready)
- sd_notify(false, "READY=1\n");
-
- if (arg_kill_signal > 0) {
- /* Try to kill the init system on SIGINT or SIGTERM */
- sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, PID_TO_PTR(pid));
- sd_event_add_signal(event, NULL, SIGTERM, on_orderly_shutdown, PID_TO_PTR(pid));
- } else {
- /* Immediately exit */
- sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
- sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
- }
-
- /* simply exit on sigchld */
- sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL);
-
- if (arg_expose_ports) {
- r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, &exposed, &rtnl);
- if (r < 0)
- goto finish;
-
- (void) expose_port_execute(rtnl, arg_expose_ports, &exposed);
- }
-
- rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
-
- r = pty_forward_new(event, master, PTY_FORWARD_IGNORE_VHANGUP | (interactive ? 0 : PTY_FORWARD_READ_ONLY), &forward);
- if (r < 0) {
- log_error_errno(r, "Failed to create PTY forwarder: %m");
- goto finish;
- }
-
- r = sd_event_loop(event);
- if (r < 0) {
- log_error_errno(r, "Failed to run event loop: %m");
- goto finish;
- }
-
- pty_forward_get_last_char(forward, &last_char);
-
- forward = pty_forward_free(forward);
-
- if (!arg_quiet && last_char != '\n')
- putc('\n', stdout);
-
- /* Kill if it is not dead yet anyway */
- if (arg_register && !arg_keep_unit)
- terminate_machine(pid);
-
- /* Normally redundant, but better safe than sorry */
- kill(pid, SIGKILL);
-
- r = wait_for_container(pid, &container_status);
- pid = 0;
-
- if (r < 0)
- /* We failed to wait for the container, or the
- * container exited abnormally */
- goto finish;
- else if (r > 0 || container_status == CONTAINER_TERMINATED) {
- /* The container exited with a non-zero
- * status, or with zero status and no reboot
- * was requested. */
- ret = r;
- break;
- }
-
- /* CONTAINER_REBOOTED, loop again */
-
- if (arg_keep_unit) {
- /* Special handling if we are running as a
- * service: instead of simply restarting the
- * machine we want to restart the entire
- * service, so let's inform systemd about this
- * with the special exit code 133. The service
- * file uses RestartForceExitStatus=133 so
- * that this results in a full nspawn
- * restart. This is necessary since we might
- * have cgroup parameters set we want to have
- * flushed out. */
- ret = 133;
- r = 0;
+ r = run(master,
+ console,
+ root_device, root_device_rw,
+ home_device, home_device_rw,
+ srv_device, srv_device_rw,
+ esp_device,
+ interactive, secondary,
+ fds,
+ veth_name, &veth_created,
+ &exposed,
+ &pid, &ret);
+ if (r <= 0)
break;
- }
-
- expose_port_flush(arg_expose_ports, &exposed);
-
- (void) remove_veth_links(veth_name, arg_network_veth_extra);
- veth_created = false;
}
finish:
diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c
index 6468d1eecd..c3bdcaf1da 100644
--- a/src/remount-fs/remount-fs.c
+++ b/src/remount-fs/remount-fs.c
@@ -137,7 +137,7 @@ int main(int argc, char *argv[]) {
s = hashmap_remove(pids, PID_TO_PTR(si.si_pid));
if (s) {
- if (!is_clean_exit(si.si_code, si.si_status, NULL)) {
+ if (!is_clean_exit(si.si_code, si.si_status, EXIT_CLEAN_COMMAND, NULL)) {
if (si.si_code == CLD_EXITED)
log_error(MOUNT_PATH " for %s exited with exit status %i.", s, si.si_status);
else
diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c
index 07e4cd7d1d..8aa79049b6 100644
--- a/src/resolve/resolve-tool.c
+++ b/src/resolve/resolve-tool.c
@@ -395,7 +395,7 @@ static int output_rr_packet(const void *d, size_t l, int ifindex) {
return 0;
}
-static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type) {
+static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type, bool warn_missing) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char ifname[IF_NAMESIZE] = "";
@@ -430,7 +430,8 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_
r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
- log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
+ if (warn_missing || r != -ENXIO)
+ log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
return r;
}
@@ -488,7 +489,8 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_
return bus_log_parse_error(r);
if (n == 0) {
- log_error("%s: no records found", name);
+ if (warn_missing)
+ log_error("%s: no records found", name);
return -ESRCH;
}
@@ -618,7 +620,7 @@ static int resolve_rfc4501(sd_bus *bus, const char *name) {
if (type == 0)
type = arg_type ?: DNS_TYPE_A;
- return resolve_record(bus, n, class, type);
+ return resolve_record(bus, n, class, type, true);
invalid:
log_error("Invalid DNS URI: %s", name);
@@ -840,16 +842,34 @@ static int resolve_openpgp(sd_bus *bus, const char *address) {
}
domain++;
- r = string_hashsum_sha224(address, domain - 1 - address, &hashed);
+ r = string_hashsum_sha256(address, domain - 1 - address, &hashed);
if (r < 0)
return log_error_errno(r, "Hashing failed: %m");
+ strshorten(hashed, 56);
+
full = strjoina(hashed, "._openpgpkey.", domain);
log_debug("Looking up \"%s\".", full);
- return resolve_record(bus, full,
- arg_class ?: DNS_CLASS_IN,
- arg_type ?: DNS_TYPE_OPENPGPKEY);
+ r = resolve_record(bus, full,
+ arg_class ?: DNS_CLASS_IN,
+ arg_type ?: DNS_TYPE_OPENPGPKEY, false);
+
+ if (IN_SET(r, -ENXIO, -ESRCH)) { /* NXDOMAIN or NODATA? */
+ hashed = NULL;
+ r = string_hashsum_sha224(address, domain - 1 - address, &hashed);
+ if (r < 0)
+ return log_error_errno(r, "Hashing failed: %m");
+
+ full = strjoina(hashed, "._openpgpkey.", domain);
+ log_debug("Looking up \"%s\".", full);
+
+ return resolve_record(bus, full,
+ arg_class ?: DNS_CLASS_IN,
+ arg_type ?: DNS_TYPE_OPENPGPKEY, true);
+ }
+
+ return r;
}
static int resolve_tlsa(sd_bus *bus, const char *address) {
@@ -881,7 +901,7 @@ static int resolve_tlsa(sd_bus *bus, const char *address) {
return resolve_record(bus, full,
arg_class ?: DNS_CLASS_IN,
- arg_type ?: DNS_TYPE_TLSA);
+ arg_type ?: DNS_TYPE_TLSA, true);
}
static int show_statistics(sd_bus *bus) {
@@ -1877,7 +1897,7 @@ int main(int argc, char **argv) {
while (argv[optind]) {
int k;
- k = resolve_record(bus, argv[optind], arg_class, arg_type);
+ k = resolve_record(bus, argv[optind], arg_class, arg_type, true);
if (r == 0)
r = k;
diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c
index 83f7596ac8..abf3263178 100644
--- a/src/resolve/resolved-conf.c
+++ b/src/resolve/resolved-conf.c
@@ -23,8 +23,19 @@
#include "extract-word.h"
#include "parse-util.h"
#include "resolved-conf.h"
+#include "string-table.h"
#include "string-util.h"
+DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_stub_listener_mode, dns_stub_listener_mode, DnsStubListenerMode, "Failed to parse DNS stub listener mode setting");
+
+static const char* const dns_stub_listener_mode_table[_DNS_STUB_LISTENER_MODE_MAX] = {
+ [DNS_STUB_LISTENER_NO] = "no",
+ [DNS_STUB_LISTENER_UDP] = "udp",
+ [DNS_STUB_LISTENER_TCP] = "tcp",
+ [DNS_STUB_LISTENER_YES] = "yes",
+};
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_stub_listener_mode, DnsStubListenerMode, DNS_STUB_LISTENER_YES);
+
int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) {
union in_addr_union address;
int family, r, ifindex = 0;
diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h
index e1fd2cceec..fc425a36b2 100644
--- a/src/resolve/resolved-conf.h
+++ b/src/resolve/resolved-conf.h
@@ -19,7 +19,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+typedef enum DnsStubListenerMode DnsStubListenerMode;
+
+enum DnsStubListenerMode {
+ DNS_STUB_LISTENER_NO,
+ DNS_STUB_LISTENER_UDP,
+ DNS_STUB_LISTENER_TCP,
+ DNS_STUB_LISTENER_YES,
+ _DNS_STUB_LISTENER_MODE_MAX,
+ _DNS_STUB_LISTENER_MODE_INVALID = -1
+};
+
#include "resolved-manager.h"
+#include "resolved-dns-server.h"
int manager_parse_config_file(Manager *m);
@@ -33,4 +45,7 @@ const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned len
int config_parse_dns_servers(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_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_dnssec(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_dns_stub_listener_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);
+
+const char* dns_stub_listener_mode_to_string(DnsStubListenerMode p) _const_;
+DnsStubListenerMode dns_stub_listener_mode_from_string(const char *s) _pure_;
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index 538bc61f81..01a83a76b2 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -26,7 +26,10 @@ typedef struct DnsScope DnsScope;
#include "resolved-dns-cache.h"
#include "resolved-dns-dnssec.h"
#include "resolved-dns-packet.h"
+#include "resolved-dns-query.h"
+#include "resolved-dns-search-domain.h"
#include "resolved-dns-server.h"
+#include "resolved-dns-stream.h"
#include "resolved-dns-zone.h"
#include "resolved-link.h"
diff --git a/src/resolve/resolved-dns-stream.h b/src/resolve/resolved-dns-stream.h
index e6569678fa..4cdb4f6806 100644
--- a/src/resolve/resolved-dns-stream.h
+++ b/src/resolve/resolved-dns-stream.h
@@ -25,6 +25,7 @@ typedef struct DnsStream DnsStream;
#include "resolved-dns-packet.h"
#include "resolved-dns-transaction.h"
+#include "resolved-manager.h"
/* Streams are used by three subsystems:
*
diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c
index d263cedcd9..e76de6c06a 100644
--- a/src/resolve/resolved-dns-stub.c
+++ b/src/resolve/resolved-dns-stub.c
@@ -25,6 +25,9 @@
* IP and UDP header sizes */
#define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U)
+static int manager_dns_stub_udp_fd(Manager *m);
+static int manager_dns_stub_tcp_fd(Manager *m);
+
static int dns_stub_make_reply_packet(
uint16_t id,
int rcode,
@@ -354,66 +357,48 @@ static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
-int manager_dns_stub_udp_fd(Manager *m) {
+static int manager_dns_stub_udp_fd(Manager *m) {
static const int one = 1;
-
union sockaddr_union sa = {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(53),
.in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
};
-
+ _cleanup_close_ int fd = -1;
int r;
if (m->dns_stub_udp_fd >= 0)
return m->dns_stub_udp_fd;
- m->dns_stub_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->dns_stub_udp_fd < 0)
+ fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0)
return -errno;
- r = setsockopt(m->dns_stub_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0)
+ return -errno;
- r = setsockopt(m->dns_stub_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0)
+ return -errno;
- r = setsockopt(m->dns_stub_udp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0)
+ return -errno;
/* Make sure no traffic from outside the local host can leak to onto this socket */
- r = setsockopt(m->dns_stub_udp_fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3);
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3) < 0)
+ return -errno;
- r = bind(m->dns_stub_udp_fd, &sa.sa, sizeof(sa.in));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
+ return -errno;
- r = sd_event_add_io(m->event, &m->dns_stub_udp_event_source, m->dns_stub_udp_fd, EPOLLIN, on_dns_stub_packet, m);
+ r = sd_event_add_io(m->event, &m->dns_stub_udp_event_source, fd, EPOLLIN, on_dns_stub_packet, m);
if (r < 0)
- goto fail;
+ return r;
(void) sd_event_source_set_description(m->dns_stub_udp_event_source, "dns-stub-udp");
+ m->dns_stub_udp_fd = fd;
+ fd = -1;
return m->dns_stub_udp_fd;
-
-fail:
- m->dns_stub_udp_fd = safe_close(m->dns_stub_udp_fd);
- return r;
}
static int on_dns_stub_stream_packet(DnsStream *s) {
@@ -461,102 +446,83 @@ static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
-int manager_dns_stub_tcp_fd(Manager *m) {
+static int manager_dns_stub_tcp_fd(Manager *m) {
static const int one = 1;
-
union sockaddr_union sa = {
.in.sin_family = AF_INET,
.in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
.in.sin_port = htobe16(53),
};
-
+ _cleanup_close_ int fd = -1;
int r;
if (m->dns_stub_tcp_fd >= 0)
return m->dns_stub_tcp_fd;
- m->dns_stub_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->dns_stub_tcp_fd < 0)
+ fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0)
return -errno;
- r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof one) < 0)
+ return -errno;
- r = setsockopt(m->dns_stub_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0)
+ return -errno;
- r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0)
+ return -errno;
- r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0)
+ return -errno;
/* Make sure no traffic from outside the local host can leak to onto this socket */
- r = setsockopt(m->dns_stub_tcp_fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3);
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3) < 0)
+ return -errno;
- r = bind(m->dns_stub_tcp_fd, &sa.sa, sizeof(sa.in));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
+ return -errno;
- r = listen(m->dns_stub_tcp_fd, SOMAXCONN);
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (listen(fd, SOMAXCONN) < 0)
+ return -errno;
- r = sd_event_add_io(m->event, &m->dns_stub_tcp_event_source, m->dns_stub_tcp_fd, EPOLLIN, on_dns_stub_stream, m);
+ r = sd_event_add_io(m->event, &m->dns_stub_tcp_event_source, fd, EPOLLIN, on_dns_stub_stream, m);
if (r < 0)
- goto fail;
+ return r;
(void) sd_event_source_set_description(m->dns_stub_tcp_event_source, "dns-stub-tcp");
+ m->dns_stub_tcp_fd = fd;
+ fd = -1;
return m->dns_stub_tcp_fd;
-
-fail:
- m->dns_stub_tcp_fd = safe_close(m->dns_stub_tcp_fd);
- return r;
}
int manager_dns_stub_start(Manager *m) {
- int r;
+ const char *t = "UDP";
+ int r = 0;
assert(m);
- r = manager_dns_stub_udp_fd(m);
- if (r == -EADDRINUSE)
- goto eaddrinuse;
- if (r < 0)
- return r;
-
- r = manager_dns_stub_tcp_fd(m);
- if (r == -EADDRINUSE)
- goto eaddrinuse;
- if (r < 0)
- return r;
+ if (IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_UDP))
+ r = manager_dns_stub_udp_fd(m);
- return 0;
+ if (r >= 0 &&
+ IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_TCP)) {
+ t = "TCP";
+ r = manager_dns_stub_tcp_fd(m);
+ }
-eaddrinuse:
- log_warning("Another process is already listening on 127.0.0.53:53. Turning off local DNS stub support.");
- manager_dns_stub_stop(m);
+ if (IN_SET(r, -EADDRINUSE, -EPERM)) {
+ if (r == -EADDRINUSE)
+ log_warning_errno(r,
+ "Another process is already listening on %s socket 127.0.0.53:53.\n"
+ "Turning off local DNS stub support.", t);
+ else
+ log_warning_errno(r,
+ "Failed to listen on %s socket 127.0.0.53:53: %m.\n"
+ "Turning off local DNS stub support.", t);
+ manager_dns_stub_stop(m);
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to listen on %s socket 127.0.0.53:53: %m", t);
return 0;
}
diff --git a/src/resolve/resolved-dns-stub.h b/src/resolve/resolved-dns-stub.h
index fce4d25ede..12b86f6753 100644
--- a/src/resolve/resolved-dns-stub.h
+++ b/src/resolve/resolved-dns-stub.h
@@ -24,8 +24,5 @@
/* 127.0.0.53 in native endian */
#define INADDR_DNS_STUB ((in_addr_t) 0x7f000035U)
-int manager_dns_stub_udp_fd(Manager *m);
-int manager_dns_stub_tcp_fd(Manager *m);
-
void manager_dns_stub_stop(Manager *m);
int manager_dns_stub_start(Manager *m);
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index 96b066845d..5a1df70422 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -59,6 +59,8 @@ enum DnsTransactionSource {
#include "resolved-dns-packet.h"
#include "resolved-dns-question.h"
#include "resolved-dns-scope.h"
+#include "resolved-dns-server.h"
+#include "resolved-dns-stream.h"
struct DnsTransaction {
DnsScope *scope;
diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf
index 2fd56bce26..446f85cdf4 100644
--- a/src/resolve/resolved-gperf.gperf
+++ b/src/resolve/resolved-gperf.gperf
@@ -14,9 +14,10 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0
-Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0
-Resolve.Domains, config_parse_search_domains, 0, 0
-Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support)
-Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode)
-Resolve.Cache, config_parse_bool, 0, offsetof(Manager, enable_cache)
+Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0
+Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0
+Resolve.Domains, config_parse_search_domains, 0, 0
+Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support)
+Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode)
+Resolve.Cache, config_parse_bool, 0, offsetof(Manager, enable_cache)
+Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0, offsetof(Manager, dns_stub_listener_mode)
diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h
index 6a2343f9f7..c9b2a58c34 100644
--- a/src/resolve/resolved-link.h
+++ b/src/resolve/resolved-link.h
@@ -29,6 +29,7 @@ typedef struct Link Link;
typedef struct LinkAddress LinkAddress;
#include "resolved-dns-rr.h"
+#include "resolved-dns-scope.h"
#include "resolved-dns-search-domain.h"
#include "resolved-dns-server.h"
#include "resolved-manager.h"
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 92ade820ac..40f08e8044 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -501,6 +501,7 @@ int manager_new(Manager **ret) {
m->mdns_support = RESOLVE_SUPPORT_NO;
m->dnssec_mode = DEFAULT_DNSSEC_MODE;
m->enable_cache = true;
+ m->dns_stub_listener_mode = DNS_STUB_LISTENER_UDP;
m->read_resolv_conf = true;
m->need_builtin_fallbacks = true;
m->etc_hosts_last = m->etc_hosts_mtime = USEC_INFINITY;
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index deebd8e484..6b2208ed94 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -30,6 +30,7 @@
typedef struct Manager Manager;
+#include "resolved-conf.h"
#include "resolved-dns-query.h"
#include "resolved-dns-search-domain.h"
#include "resolved-dns-server.h"
@@ -47,6 +48,7 @@ struct Manager {
ResolveSupport mdns_support;
DnssecMode dnssec_mode;
bool enable_cache;
+ DnsStubListenerMode dns_stub_listener_mode;
/* Network */
Hashmap *links;
diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in
index 3bd8389c88..60afa151e3 100644
--- a/src/resolve/resolved.conf.in
+++ b/src/resolve/resolved.conf.in
@@ -18,3 +18,4 @@
#LLMNR=yes
#DNSSEC=@DEFAULT_DNSSEC_MODE@
#Cache=yes
+#DNSStubListener=udp
diff --git a/src/shared/gcrypt-util.h b/src/shared/gcrypt-util.h
index cf33b3c59c..1da12a32be 100644
--- a/src/shared/gcrypt-util.h
+++ b/src/shared/gcrypt-util.h
@@ -37,3 +37,11 @@ static inline int string_hashsum_sha224(const char *s, size_t len, char **out) {
return -EOPNOTSUPP;
#endif
}
+
+static inline int string_hashsum_sha256(const char *s, size_t len, char **out) {
+#ifdef HAVE_GCRYPT
+ return string_hashsum(s, len, GCRY_MD_SHA256, out);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
diff --git a/src/shared/install.c b/src/shared/install.c
index 5f12fb447f..60a6d1312d 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -1020,7 +1020,7 @@ static int config_parse_alias(
type = unit_name_to_type(name);
if (!unit_type_may_alias(type))
return log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Aliases are not allowed for %s units, ignoring.",
+ "Alias= is not allowed for %s units, ignoring.",
unit_type_to_string(type));
return config_parse_strv(unit, filename, line, section, section_line,
@@ -1098,7 +1098,7 @@ static int config_parse_default_instance(
return 0;
if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE))
return log_syntax(unit, LOG_WARNING, filename, line, 0,
- "DefaultInstance only makes sense for template units, ignoring.");
+ "DefaultInstance= only makes sense for template units, ignoring.");
r = install_full_printf(i, rvalue, &printed);
if (r < 0)
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index bb6002e8ef..18a8a4f248 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -3936,7 +3936,7 @@ static void print_status_info(
argv = strv_join(p->argv, " ");
printf(" Process: "PID_FMT" %s=%s ", p->pid, p->name, strna(argv));
- good = is_clean_exit_lsb(p->code, p->status, NULL);
+ good = is_clean_exit(p->code, p->status, EXIT_CLEAN_DAEMON, NULL);
if (!good) {
on = ansi_highlight_red();
off = ansi_normal();
diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c
index 39821687b9..c2c80175a2 100644
--- a/src/sysv-generator/sysv-generator.c
+++ b/src/sysv-generator/sysv-generator.c
@@ -25,6 +25,7 @@
#include "alloc-util.h"
#include "dirent-util.h"
+#include "exit-status.h"
#include "fd-util.h"
#include "fileio.h"
#include "hashmap.h"
@@ -199,6 +200,13 @@ static int generate_unit_file(SysvStub *s) {
if (s->pid_file)
fprintf(f, "PIDFile=%s\n", s->pid_file);
+ /* Consider two special LSB exit codes a clean exit */
+ if (s->has_lsb)
+ fprintf(f,
+ "SuccessExitStatus=%i %i\n",
+ EXIT_NOTINSTALLED,
+ EXIT_NOTCONFIGURED);
+
fprintf(f,
"ExecStart=%s start\n"
"ExecStop=%s stop\n",
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
index 164a10d8a8..0b10d8e25e 100644
--- a/src/test/test-path-util.c
+++ b/src/test/test-path-util.c
@@ -511,7 +511,24 @@ static void test_hidden_or_backup_file(void) {
assert_se(!hidden_or_backup_file("test.dpkg-old.foo"));
}
+static void test_systemd_installation_has_version(const char *path) {
+ int r;
+ const unsigned versions[] = {0, 231, atoi(PACKAGE_VERSION), 999};
+ unsigned i;
+
+ for (i = 0; i < ELEMENTSOF(versions); i++) {
+ r = systemd_installation_has_version(path, versions[i]);
+ assert_se(r >= 0);
+ log_info("%s has systemd >= %u: %s",
+ path ?: "Current installation", versions[i], yes_no(r));
+ }
+}
+
int main(int argc, char **argv) {
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
test_path();
test_find_binary(argv[0]);
test_prefixes();
@@ -526,5 +543,7 @@ int main(int argc, char **argv) {
test_filename_is_valid();
test_hidden_or_backup_file();
+ test_systemd_installation_has_version(argv[1]); /* NULL is OK */
+
return 0;
}
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
index 8851af449d..b45490be1a 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -827,7 +827,7 @@ static int ask_on_consoles(int argc, char *argv[]) {
break;
}
- if (!is_clean_exit(status.si_code, status.si_status, NULL))
+ if (!is_clean_exit(status.si_code, status.si_status, EXIT_CLEAN_DAEMON, NULL))
log_error("Password agent failed with: %d", status.si_status);
terminate_agents(pids);
diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c
index 989decbe95..6f8e96a123 100644
--- a/src/udev/udevadm-control.c
+++ b/src/udev/udevadm-control.c
@@ -20,6 +20,7 @@
#include <string.h>
#include <unistd.h>
+#include "time-util.h"
#include "udev-util.h"
#include "udev.h"
@@ -60,7 +61,7 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) {
};
if (getuid() != 0) {
- fprintf(stderr, "root privileges required\n");
+ log_error("root privileges required");
return 1;
}
@@ -81,7 +82,7 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) {
i = util_log_priority(optarg);
if (i < 0) {
- fprintf(stderr, "invalid number '%s'\n", optarg);
+ log_error("invalid number '%s'", optarg);
return rc;
}
if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg), timeout) < 0)
@@ -110,7 +111,7 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) {
break;
case 'p':
if (strchr(optarg, '=') == NULL) {
- fprintf(stderr, "expect <KEY>=<value> instead of '%s'\n", optarg);
+ log_error("expect <KEY>=<value> instead of '%s'", optarg);
return rc;
}
if (udev_ctrl_send_set_env(uctrl, optarg, timeout) < 0)
@@ -124,7 +125,7 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) {
i = strtoul(optarg, &endp, 0);
if (endp[0] != '\0' || i < 1) {
- fprintf(stderr, "invalid number '%s'\n", optarg);
+ log_error("invalid number '%s'", optarg);
return rc;
}
if (udev_ctrl_send_set_children_max(uctrl, i, timeout) < 0)
@@ -134,13 +135,21 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) {
break;
}
case 't': {
+ usec_t s;
int seconds;
+ int r;
- seconds = atoi(optarg);
- if (seconds >= 0)
+ r = parse_sec(optarg, &s);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse timeout value '%s'.", optarg);
+
+ if (((s + USEC_PER_SEC - 1) / USEC_PER_SEC) > INT_MAX)
+ log_error("Timeout value is out of range.");
+ else {
+ seconds = s != USEC_INFINITY ? (int) ((s + USEC_PER_SEC - 1) / USEC_PER_SEC) : INT_MAX;
timeout = seconds;
- else
- fprintf(stderr, "invalid timeout value\n");
+ rc = 0;
+ }
break;
}
case 'h':
@@ -150,9 +159,9 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) {
}
if (optind < argc)
- fprintf(stderr, "Extraneous argument: %s\n", argv[optind]);
+ log_error("Extraneous argument: %s", argv[optind]);
else if (optind == 1)
- fprintf(stderr, "Option missing\n");
+ log_error("Option missing");
return rc;
}
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index 19f1c29198..535d317c27 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -1738,8 +1738,13 @@ int main(int argc, char *argv[]) {
log_info("starting version " VERSION);
/* connect /dev/null to stdin, stdout, stderr */
- if (log_get_max_level() < LOG_DEBUG)
- (void) make_null_stdio();
+ if (log_get_max_level() < LOG_DEBUG) {
+ r = make_null_stdio();
+ if (r < 0)
+ log_warning_errno(r, "Failed to redirect standard streams to /dev/null: %m");
+ }
+
+
pid = fork();
switch (pid) {
diff --git a/system-preset/90-systemd.preset b/system-preset/90-systemd.preset
index 0f494b7552..6b5349dc8d 100644
--- a/system-preset/90-systemd.preset
+++ b/system-preset/90-systemd.preset
@@ -18,7 +18,6 @@ enable systemd-resolved.service
enable systemd-networkd-wait-online.service
disable console-getty.service
-disable console-shell.service
disable debug-shell.service
disable halt.target
diff --git a/units/.gitignore b/units/.gitignore
index 47e99154ee..8f4949258e 100644
--- a/units/.gitignore
+++ b/units/.gitignore
@@ -1,8 +1,6 @@
/user@.service.m4
/console-getty.service
/console-getty.service.m4
-/console-shell.service
-/console-shell.service.m4
/container-getty@.service
/container-getty@.service.m4
/debug-shell.service
diff --git a/units/console-shell.service.m4.in b/units/console-shell.service.m4.in
deleted file mode 100644
index a345ec25d4..0000000000
--- a/units/console-shell.service.m4.in
+++ /dev/null
@@ -1,31 +0,0 @@
-# This file is part of systemd.
-#
-# 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.
-
-[Unit]
-Description=Console Shell
-Documentation=man:sulogin(8)
-After=systemd-user-sessions.service plymouth-quit-wait.service
-m4_ifdef(`HAVE_SYSV_COMPAT',
-After=rc-local.service
-)m4_dnl
-Before=getty.target
-
-[Service]
-Environment=HOME=/root
-WorkingDirectory=-/root
-ExecStart=-@SULOGIN@
-ExecStopPost=-@SYSTEMCTL@ poweroff
-Type=idle
-StandardInput=tty-force
-StandardOutput=inherit
-StandardError=inherit
-KillMode=process
-IgnoreSIGPIPE=no
-SendSIGHUP=yes
-
-[Install]
-WantedBy=getty.target
diff --git a/units/initrd-switch-root.target b/units/initrd-switch-root.target
index f34768790b..934d82f667 100644
--- a/units/initrd-switch-root.target
+++ b/units/initrd-switch-root.target
@@ -12,5 +12,5 @@ DefaultDependencies=no
Requires=initrd-switch-root.service
Before=initrd-switch-root.service
AllowIsolate=yes
-Wants=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target systemd-journald.service
+Wants=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target systemd-journald.service initrd-cleanup.service
After=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target emergency.service emergency.target