Compare commits

..

No commits in common. "e1da60e430e881b3d8d40e5b1ab36bda3ed5a054" and "66032ef489e19292b06773e5f44531a7b44c23c5" have entirely different histories.

45 changed files with 599 additions and 1138 deletions

View File

@ -13,14 +13,13 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-20.04 runs-on: ubuntu-18.04
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
env: env:
- { COMPILER: "gcc", COMPILER_VERSION: "10" } - { COMPILER: "gcc", COMPILER_VERSION: "10" }
- { COMPILER: "clang", COMPILER_VERSION: "10" } - { COMPILER: "clang", COMPILER_VERSION: "10" }
- { COMPILER: "clang", COMPILER_VERSION: "11" }
env: ${{ matrix.env }} env: ${{ matrix.env }}
steps: steps:
- name: Repository checkout - name: Repository checkout

View File

@ -19,6 +19,7 @@ PACKAGES=(
expect expect
fdisk fdisk
gettext gettext
iptables-dev
iputils-ping iputils-ping
isc-dhcp-client isc-dhcp-client
itstool itstool
@ -27,7 +28,6 @@ PACKAGES=(
libcap-dev libcap-dev
libcurl4-gnutls-dev libcurl4-gnutls-dev
libfdisk-dev libfdisk-dev
libfido2-dev
libgpg-error-dev libgpg-error-dev
liblz4-dev liblz4-dev
liblzma-dev liblzma-dev
@ -38,7 +38,6 @@ PACKAGES=(
libqrencode-dev libqrencode-dev
libssl-dev libssl-dev
libxkbcommon-dev libxkbcommon-dev
libxtables-dev
libzstd-dev libzstd-dev
mount mount
net-tools net-tools

3
TODO
View File

@ -813,6 +813,9 @@ Features:
* systemctl: if some operation fails, show log output? * systemctl: if some operation fails, show log output?
* systemctl edit: use equivalent of cat() to insert existing config as a comment, prepended with #.
Upon editor exit, lines with one # are removed, lines with two # are left with one #, etc.
* exponential backoff in timesyncd when we cannot reach a server * exponential backoff in timesyncd when we cannot reach a server
* timesyncd: add ugly bus calls to set NTP servers per-interface, for usage by NM * timesyncd: add ugly bus calls to set NTP servers per-interface, for usage by NM

View File

@ -138,17 +138,7 @@
and follow the same overriding rules. They are text files with the and follow the same overriding rules. They are text files with the
<filename>.negative</filename> suffix. Empty lines and lines whose first character is <filename>.negative</filename> suffix. Empty lines and lines whose first character is
<literal>;</literal> are ignored. Each line specifies one domain name which is the root of a DNS <literal>;</literal> are ignored. Each line specifies one domain name which is the root of a DNS
subtree where validation shall be disabled. For example:</para> subtree where validation shall be disabled.</para>
<programlisting># Reverse IPv4 mappings
10.in-addr.arpa
16.172.in-addr.arpa
168.192.in-addr.arpa
...
# Some custom domains
prod
stag
</programlisting>
<para>Negative trust anchors are useful to support private DNS <para>Negative trust anchors are useful to support private DNS
subtrees that are not referenced from the Internet DNS hierarchy, subtrees that are not referenced from the Internet DNS hierarchy,

View File

@ -207,7 +207,7 @@ if dbus_docs.length() > 0
'@INPUT@'], '@INPUT@'],
input : dbus_docs) input : dbus_docs)
if conf.get('BUILD_MODE') == 'BUILD_MODE_DEVELOPER' if conf.get('DEVELOPER_MODE') == 1
test('dbus-docs-fresh', test('dbus-docs-fresh',
update_dbus_docs_py, update_dbus_docs_py,
args : ['--build-dir=@0@'.format(project_build_root), args : ['--build-dir=@0@'.format(project_build_root),

View File

@ -156,6 +156,12 @@ node /org/freedesktop/resolve1 {
}; };
</programlisting> </programlisting>
<!--method SetLinkDNSEx is not documented!-->
<!--method SetLinkDefaultRoute is not documented!-->
<!--method SetLinkDNSOverTLS is not documented!-->
<!--method RegisterService is not documented!--> <!--method RegisterService is not documented!-->
<!--method UnregisterService is not documented!--> <!--method UnregisterService is not documented!-->
@ -164,8 +170,28 @@ node /org/freedesktop/resolve1 {
<!--method ResetServerFeatures is not documented!--> <!--method ResetServerFeatures is not documented!-->
<!--property LLMNR is not documented!-->
<!--property MulticastDNS is not documented!-->
<!--property DNSOverTLS is not documented!-->
<!--property DNSEx is not documented!-->
<!--property FallbackDNS is not documented!-->
<!--property FallbackDNSEx is not documented!-->
<!--property CurrentDNSServer is not documented!-->
<!--property CurrentDNSServerEx is not documented!-->
<!--property DNSSEC is not documented!-->
<!--property DNSSECNegativeTrustAnchors is not documented!--> <!--property DNSSECNegativeTrustAnchors is not documented!-->
<!--property DNSStubListener is not documented!-->
<!--Autogenerated cross-references for systemd.directives, do not edit--> <!--Autogenerated cross-references for systemd.directives, do not edit-->
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.resolve1.Manager"/> <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.resolve1.Manager"/>
@ -255,28 +281,26 @@ node /org/freedesktop/resolve1 {
<refsect2> <refsect2>
<title>Methods</title> <title>Methods</title>
<para><function>ResolveHostname()</function> takes a hostname and resolves it to one or more IP <para><function>ResolveHostname()</function> takes a hostname and resolves it to one or more IP addresses.
addresses. As parameters it takes the Linux network interface index to execute the query on, or 0 if As parameters it takes the Linux network interface index to execute the query on, or 0 if it may be
it may be done on any suitable interface. The <varname>name</varname> parameter specifies the hostname done on any suitable interface. The <varname>name</varname> parameter specifies the hostname to
to resolve. Note that if required, IDNA conversion is applied to this name unless it is resolved via resolve. Note that if required, IDNA conversion is applied to this name unless it is resolved via LLMNR or MulticastDNS. The <varname>family</varname> parameter
LLMNR or MulticastDNS. The <varname>family</varname> parameter limits the results to a specific address limits the results to a specific address family. It may be <constant>AF_INET</constant>,
family. It may be <constant>AF_INET</constant>, <constant>AF_INET6</constant> or <constant>AF_INET6</constant> or <constant>AF_UNSPEC</constant>. If <constant>AF_UNSPEC</constant> is specified (recommended), both kinds are retrieved, subject
<constant>AF_UNSPEC</constant>. If <constant>AF_UNSPEC</constant> is specified (recommended), both to local network configuration (i.e. if no local, routable IPv6 address is found, no IPv6 address is
kinds are retrieved, subject to local network configuration (i.e. if no local, routable IPv6 address is retrieved; and similarly for IPv4). A 64-bit <varname>flags</varname> field may be used to alter the
found, no IPv6 address is retrieved; and similarly for IPv4). A 64-bit <varname>flags</varname> field behaviour of the resolver operation (see below). The method returns an array of address records. Each
may be used to alter the behaviour of the resolver operation (see below). The method returns an array address record consists of the interface index the address belongs to, an address family as well as a
of address records. Each address record consists of the interface index the address belongs to, an byte array with the actual IP address data (which either has 4 or 16 elements, depending on the address
address family as well as a byte array with the actual IP address data (which either has 4 or 16 family). The returned address family will be one of <constant>AF_INET</constant> or
elements, depending on the address family). The returned address family will be one of <constant>AF_INET6</constant>. For IPv6, the returned address interface index should be used to
<constant>AF_INET</constant> or <constant>AF_INET6</constant>. For IPv6, the returned address interface initialize the .sin6_scope_id field of a <structname>struct sockaddr_in6</structname> instance to permit
index should be used to initialize the .sin6_scope_id field of a support for resolution to link-local IP addresses. The address array is followed by the canonical name
<structname>struct sockaddr_in6</structname> instance to permit support for resolution to link-local IP of the host, which may or may not be identical to the resolved hostname. Finally, a 64-bit
addresses. The address array is followed by the canonical name of the host, which may or may not be <varname>flags</varname> field is returned that is defined similarly to the <varname>flags</varname>
identical to the resolved hostname. Finally, a 64-bit <varname>flags</varname> field is returned that field that was passed in, but contains information about the resolved data (see below). If the hostname
is defined similarly to the <varname>flags</varname> field that was passed in, but contains information passed in is an IPv4 or IPv6 address formatted as string, it is parsed, and the result is returned. In
about the resolved data (see below). If the hostname passed in is an IPv4 or IPv6 address formatted as this case, no network communication is done.</para>
string, it is parsed, and the result is returned. In this case, no network communication is
done.</para>
<para><function>ResolveAddress()</function> executes the reverse operation: it takes an IP address and <para><function>ResolveAddress()</function> executes the reverse operation: it takes an IP address and
acquires one or more hostnames for it. As parameters it takes the interface index to execute the query acquires one or more hostnames for it. As parameters it takes the interface index to execute the query
@ -363,19 +387,15 @@ node /org/freedesktop/resolve1 {
<constant>AF_INET6</constant>), followed by a 4-byte or 16-byte array with the raw address data. This <constant>AF_INET6</constant>), followed by a 4-byte or 16-byte array with the raw address data. This
method is a one-step shortcut for retrieving the Link object for a network interface using method is a one-step shortcut for retrieving the Link object for a network interface using
<function>GetLink()</function> (see above) and then invoking the <function>SetDNS()</function> method <function>GetLink()</function> (see above) and then invoking the <function>SetDNS()</function> method
(see below) on it.</para> (see below) on it.
<para><function>SetLinkDNSEx()</function> is similar to <function>SetLinkDNS()</function>, but allows
an IP port (instead of the default 53) and DNS name to be specified for each DNS server. The server
name is used for Server Name Indication (SNI), which is useful when DNS-over-TLS is
used. C.f. <varname>DNS=</varname> in
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para> </para>
<para><function>SetLinkDefaultRoute()</function> specifies whether the link shall be used as the <para>Network management software integrating with <filename>systemd-resolved</filename> should
default route for name queries. See the description of name routing in call this method (and the five below) after the interface appeared in the kernel (and thus after a
<citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> network interface index has been assigned), but before the network interfaces is activated
for details.</para> (<constant>IFF_UP</constant> set) so that all settings take effect during the full time the network
interface is up. It is safe to alter settings while the interface is up, however. Use
<function>RevertLink()</function> (described below) to reset all per-interface settings.</para>
<para>The <function>SetLinkDomains()</function> method sets the search and routing domains to use on a <para>The <function>SetLinkDomains()</function> method sets the search and routing domains to use on a
specific network interface for DNS look-ups. It takes a network interface index and an array of domains, specific network interface for DNS look-ups. It takes a network interface index and an array of domains,
@ -412,22 +432,8 @@ node /org/freedesktop/resolve1 {
Negative Trust Anchors (NTAs) for a specific network interface. It takes a network interface index and a Negative Trust Anchors (NTAs) for a specific network interface. It takes a network interface index and a
list of domains as arguments.</para> list of domains as arguments.</para>
<para>The <function>SetLinkDNSOverTLS()</function> method enables or disables DNS-over-TLS. <para>The <function>RevertLink()</function> method may be used to revert all per-link settings done with
C.f. <varname>DNSOverTLS=</varname> in the six methods described above to the defaults again.</para>
<citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for details.</para>
<para>Network management software integrating with <filename>systemd-resolved</filename> should call
<function>SetLinkDNS()</function> or <function>SetLinkDNSEx()</function>,
<function>SetLinkDefaultRoute()</function>, <function>SetLinkDomains()</function> and others after the
interface appeared in the kernel (and thus after a network interface index has been assigned), but
before the network interfaces is activated (<constant>IFF_UP</constant> set) so that all settings take
effect during the full time the network interface is up. It is safe to alter settings while the
interface is up, however. Use <function>RevertLink()</function> (described below) to reset all
per-interface settings.</para>
<para>The <function>RevertLink()</function> method may be used to revert all per-link settings
described above to the defaults.</para>
<refsect3> <refsect3>
<title>The Flags Parameter</title> <title>The Flags Parameter</title>
@ -452,11 +458,11 @@ node /org/freedesktop/resolve1 {
classic unicast DNS, LLMNR via IPv4/UDP and IPv6/UDP respectively, as well as MulticastDNS via classic unicast DNS, LLMNR via IPv4/UDP and IPv6/UDP respectively, as well as MulticastDNS via
IPv4/UDP and IPv6/UDP. If all of these five bits are off on input (which is strongly recommended) the IPv4/UDP and IPv6/UDP. If all of these five bits are off on input (which is strongly recommended) the
look-up will be done via all suitable protocols for the specific look-up. Note that these flags look-up will be done via all suitable protocols for the specific look-up. Note that these flags
operate as filter only, but cannot force a look-up to be done via a protocol. Specifically, operate as filter only, but cannot force a look-up to be done via a protocol. Specifically, <filename>systemd-resolved</filename>
<filename>systemd-resolved</filename> will only route look-ups within the .local TLD to MulticastDNS will only route look-ups within the .local TLD to MulticastDNS (plus some reverse look-up address
(plus some reverse look-up address domains), and single-label names to LLMNR (plus some reverse domains), and single-label names to LLMNR (plus some reverse address lookup domains). It will route
address lookup domains). It will route neither of these to Unicast DNS servers. Also, it will do neither of these to Unicast DNS servers. Also, it will do LLMNR and Multicast DNS only on interfaces
LLMNR and Multicast DNS only on interfaces suitable for multicast.</para> suitable for multicast.</para>
<para>On output, these five flags indicate which protocol was used to execute the operation, and hence <para>On output, these five flags indicate which protocol was used to execute the operation, and hence
where the data was found.</para> where the data was found.</para>
@ -492,50 +498,34 @@ node /org/freedesktop/resolve1 {
the data is "rightfully" unauthenticated (which includes cases where the underlying protocol or server the data is "rightfully" unauthenticated (which includes cases where the underlying protocol or server
does not support authenticating data).</para> does not support authenticating data).</para>
</refsect3> </refsect3>
</refsect2> </refsect2>
<refsect2> <refsect2>
<title>Properties</title> <title>Properties</title>
<para>The <varname>LLMNR</varname> and <varname>MulticastDNS</varname> properties report whether LLMNR
and MulticastDNS are (globally) enabled. Each may be one of <literal>yes</literal>,
<literal>no</literal>, and <literal>resolve</literal>. See <function>SetLinkLLMNR()</function>
and <function>SetLinkMulticastDNS()</function> above.</para>
<para><varname>LLMNRHostname</varname> contains the hostname currently exposed on the network via <para><varname>LLMNRHostname</varname> contains the hostname currently exposed on the network via
LLMNR. It usually follows the system hostname as may be queried via LLMNR. It usually follows the system hostname as may be queried via
<citerefentry project="man-pages"><refentrytitle>gethostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry project="man-pages"><refentrytitle>gethostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
but may differ if a conflict is detected on the network.</para> but may differ if a conflict is detected on the network.</para>
<para><varname>DNS</varname> and <varname>DNSEx</varname> contain arrays of all DNS servers currently <para><varname>DNS</varname> contains an array of all DNS servers currently used by
used by <filename>systemd-resolved</filename>. <varname>DNS</varname> contains information similar to <filename>systemd-resolved</filename>. It contains similar information as the DNS server data written to
the DNS server data in <filename>/run/systemd/resolve/resolv.conf</filename>. Each structure in the /run/systemd/resolve/resolv.conf. Each structure in the array consists of a numeric network interface
array consists of a numeric network interface index, an address family, and a byte array containing the index, an address family, and a byte array containing the DNS server address (either 4 bytes in length
DNS server address (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6). for IPv4 or 16 bytes in lengths for IPv6). The array contains DNS servers configured system-wide,
<varname>DNSEx</varname> is similar, but additionally contains the IP port and server name (used for including those possibly read from a foreign <filename>/etc/resolv.conf</filename> or the
Server Name Indication, SNI). Both arrays contain DNS servers configured system-wide, including those <varname>DNS=</varname> setting in <filename>/etc/systemd/resolved.conf</filename>, as well as
possibly read from a foreign <filename>/etc/resolv.conf</filename> or the <varname>DNS=</varname> per-interface DNS server information either retrieved from
setting in <filename>/etc/systemd/resolved.conf</filename>, as well as per-interface DNS server
information either retrieved from
<citerefentry><refentrytitle>systemd-networkd</refentrytitle><manvolnum>8</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemd-networkd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
or configured by external software via <function>SetLinkDNS()</function> or or configured by external software via <function>SetLinkDNS()</function> (see above). The network
<function>SetLinkDNSEx()</function> (see above). The network interface index will be 0 for the interface index will be 0 for the system-wide configured services and non-zero for the per-link
system-wide configured services and non-zero for the per-link servers.</para> servers.</para>
<para><varname>FallbackDNS</varname> and <varname>FallbackDNSEx</varname> contain arrays of all DNS <para>Similarly, the <varname>Domains</varname> property contains an array of all search and
servers configured as fallback servers, if any, using the same format as <varname>DNS</varname> and routing domains currently used by <filename>systemd-resolved</filename>. Each entry consists of a network interface index (again, 0
<varname>DNSEx</varname> described above. See the description of <varname>FallbackDNS=</varname> in encodes system-wide entries), the actual domain name, and whether the entry is used only for routing
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> for (true) or for both routing and searching (false).</para>
the description of when those servers are used.</para>
<para><varname>CurrentDNSServer</varname> and <varname>CurrentDNSServerEx</varname> specify the server
that is currently used for query resolution, in the same format as a single entry in the
<varname>DNS</varname> and <varname>DNSEx</varname> arrays described above.</para>
<para>Similarly, the <varname>Domains</varname> property contains an array of all search and routing
domains currently used by <filename>systemd-resolved</filename>. Each entry consists of a network
interface index (again, 0 encodes system-wide entries), the actual domain name, and whether the entry
is used only for routing (true) or for both routing and searching (false).</para>
<para>The <varname>TransactionStatistics</varname> property contains information about the number of <para>The <varname>TransactionStatistics</varname> property contains information about the number of
transactions <filename>systemd-resolved</filename> has processed. It contains a pair of unsigned 64-bit counters, the first transactions <filename>systemd-resolved</filename> has processed. It contains a pair of unsigned 64-bit counters, the first
@ -550,14 +540,7 @@ node /org/freedesktop/resolve1 {
operations so far. It exposes three 64-bit counters: the first being the total number of current cache operations so far. It exposes three 64-bit counters: the first being the total number of current cache
entries (both positive and negative), the second the number of cache hits, and the third the number of entries (both positive and negative), the second the number of cache hits, and the third the number of
cache misses. The latter counters may be reset using <function>ResetStatistics()</function> (see cache misses. The latter counters may be reset using <function>ResetStatistics()</function> (see
above).</para> above). </para>
<para>The <varname>DNSSEC</varname> property specifies current status of DNSSEC validation. It is one
of <literal>yes</literal> (validation is enforced), <literal>no</literal> (no validation is done),
<literal>allow-downgrade</literal> (validation is done if the current DNS server supports it). See the
description of <varname>DNSSEC=</varname> in
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
<para>The <varname>DNSSECStatistics</varname> property contains information about the DNSSEC <para>The <varname>DNSSECStatistics</varname> property contains information about the DNSSEC
validations executed so far. It contains four 64-bit counters: the number of secure, insecure, bogus, validations executed so far. It contains four 64-bit counters: the number of secure, insecure, bogus,
@ -576,20 +559,12 @@ node /org/freedesktop/resolve1 {
DNSSEC is supported by DNS servers until it verifies that this is not the case. Thus, the reported DNSSEC is supported by DNS servers until it verifies that this is not the case. Thus, the reported
value may initially be true, until the first transactions are executed.</para> value may initially be true, until the first transactions are executed.</para>
<para>The <varname>DNSOverTLS</varname> boolean property reports whether DNS-over-TLS is enabled.
</para>
<para>The <varname>ResolvConfMode</varname> property exposes how <filename>/etc/resolv.conf</filename> <para>The <varname>ResolvConfMode</varname> property exposes how <filename>/etc/resolv.conf</filename>
is managed on the host. Currently, the values <literal>uplink</literal>, <literal>stub</literal>, is managed on the host. Currently, the values <literal>uplink</literal>, <literal>stub</literal>,
<literal>static</literal> (these three correspond to the three different files <literal>static</literal> (these three correspond to the three different files
<filename>systemd-resolved.service</filename> provides), <literal>foreign</literal> (the file is <filename>systemd-resolved.service</filename> provides), <literal>foreign</literal> (the file is
managed by admin or another service, <filename>systemd-resolved.service</filename> just consumes it), managed by admin or another service, <filename>systemd-resolved.service</filename> just consumes it),
<literal>missing</literal> (<filename>/etc/resolv.conf</filename> is missing).</para> <literal>missing</literal> (<filename>/etc/resolv.conf</filename> is missing).</para>
<para>The <varname>DNSStubListener</varname> property reports whether the stub listener on port 53 is
enabled. Possible values are <literal>yes</literal> (enabled), <literal>no</literal> (disabled),
<literal>udp</literal> (only the UDP listener is enabled), and <literal>tcp</literal> (only the TCP
listener is enabled).</para>
</refsect2> </refsect2>
</refsect1> </refsect1>
@ -644,6 +619,40 @@ node /org/freedesktop/resolve1/link/_1 {
}; };
</programlisting> </programlisting>
<!--method SetDNSEx is not documented!-->
<!--method SetDomains is not documented!-->
<!--method SetDefaultRoute is not documented!-->
<!--method SetLLMNR is not documented!-->
<!--method SetMulticastDNS is not documented!-->
<!--method SetDNSOverTLS is not documented!-->
<!--method SetDNSSEC is not documented!-->
<!--method SetDNSSECNegativeTrustAnchors is not documented!-->
<!--method Revert is not documented!-->
<!--property DNSEx is not documented!-->
<!--property CurrentDNSServer is not documented!-->
<!--property CurrentDNSServerEx is not documented!-->
<!--property DefaultRoute is not documented!-->
<!--property LLMNR is not documented!-->
<!--property MulticastDNS is not documented!-->
<!--property DNSOverTLS is not documented!-->
<!--property DNSSEC is not documented!-->
<!--property DNSSECNegativeTrustAnchors is not documented!--> <!--property DNSSECNegativeTrustAnchors is not documented!-->
<!--Autogenerated cross-references for systemd.directives, do not edit--> <!--Autogenerated cross-references for systemd.directives, do not edit-->
@ -712,13 +721,8 @@ node /org/freedesktop/resolve1/link/_1 {
<function>SetLinkDNS()</function> on the Manager object, the main difference being that the later <function>SetLinkDNS()</function> on the Manager object, the main difference being that the later
expects an interface index to be specified. Invoking the methods on the Manager interface has the expects an interface index to be specified. Invoking the methods on the Manager interface has the
benefit of reducing roundtrips, as it is not necessary to first request the Link object path via benefit of reducing roundtrips, as it is not necessary to first request the Link object path via
<function>GetLink()</function> before invoking the methods. The same relationship holds for <function>GetLink()</function> before invoking the methods. For further details on these methods see
<function>SetDNSEx()</function>, <function>SetDomains()</function>, the <interfacename>Manager</interfacename> documentation above.</para>
<function>SetDefaultRoute()</function>, <function>SetLLMNR()</function>,
<function>SetMulticastDNS()</function>, <function>SetDNSOverTLS()</function>,
<function>SetDNSSEC()</function>, <function>SetDNSSECNegativeTrustAnchors()</function>, and
<function>Revert()</function>. For further details on these methods see the
<interfacename>Manager</interfacename> documentation above.</para>
</refsect2> </refsect2>
<refsect2> <refsect2>
@ -740,12 +744,8 @@ node /org/freedesktop/resolve1/link/_1 {
assumed available until it is detected that the configured server does not actually support it. Thus, assumed available until it is detected that the configured server does not actually support it. Thus,
this property may initially report that DNSSEC is supported on an interface.</para> this property may initially report that DNSSEC is supported on an interface.</para>
<para><varname>DefaultRoute</varname> exposes a boolean field that indicates whether the interface will
be used as default route for name queries. See <function>SetLinkDefaultRoute()</function> above.</para>
<para>The other properties reflect the state of the various configuration settings for the link which <para>The other properties reflect the state of the various configuration settings for the link which
may be set with the various methods calls such as <function>SetDNS()</function> or may be set with the various methods calls such as SetDNS() or SetLLMNR().</para>
<function>SetLLMNR()</function>.</para>
</refsect2> </refsect2>
</refsect1> </refsect1>

View File

@ -87,17 +87,17 @@
<refsect1> <refsect1>
<title>Synthetic Records</title> <title>Synthetic Records</title>
<para><command>systemd-resolved</command> synthetizes DNS resource records (RRs) for the following <para><command>systemd-resolved</command> synthesizes DNS resource records (RRs) for the following
cases:</para> cases:</para>
<itemizedlist> <itemizedlist>
<listitem><para>The local, configured hostname is resolved to all locally configured IP addresses <listitem><para>The local, configured hostname is resolved to all locally configured IP addresses
ordered by their scope, or — if none are configured — the IPv4 address 127.0.0.2 (which is on the local ordered by their scope, or — if none are configured — the IPv4 address 127.0.0.2 (which is on the local
loopback interface) and the IPv6 address ::1 (which is the local host).</para></listitem> loopback) and the IPv6 address ::1 (which is the local host).</para></listitem>
<listitem><para>The hostnames <literal>localhost</literal> and <literal>localhost.localdomain</literal> <listitem><para>The hostnames <literal>localhost</literal> and <literal>localhost.localdomain</literal>
as well as any hostname ending in <literal>.localhost</literal> or (as well as any hostname ending in <literal>.localhost</literal> or
<literal>.localhost.localdomain</literal> are resolved to the IP addresses 127.0.0.1 and ::1. <literal>.localhost.localdomain</literal>) are resolved to the IP addresses 127.0.0.1 and ::1.
</para></listitem> </para></listitem>
<listitem><para>The hostname <literal>_gateway</literal> is resolved to all current default routing <listitem><para>The hostname <literal>_gateway</literal> is resolved to all current default routing
@ -119,162 +119,104 @@
according to the following rules:</para> according to the following rules:</para>
<itemizedlist> <itemizedlist>
<listitem><para>Names for which synthetic records are generated (the local hostname, <listitem><para>Names for which synthetic records are generated (as listed in the previous section) are
<literal>localhost</literal> and <literal>localdomain</literal>, local gateway, as listed in the never routed to the network and a reply is sent immediately. In particular this means that lookups for
previous section) and addresses configured in <filename>/etc/hosts</filename> are never routed to the <literal>localhost</literal> are never routed to the network.</para></listitem>
network and a reply is sent immediately.</para></listitem>
<listitem><para>Single-label names are resolved using LLMNR on all local interfaces where LLMNR is <listitem><para>Single-label names are routed to all local interfaces capable of IP multicasting, where
enabled. Lookups for IPv4 addresses are only sent via LLMNR on IPv4, and lookups for IPv6 addresses are LLMNR is not disabled, using the LLMNR protocol. Lookups for IPv4 addresses are only sent via LLMNR on
only sent via LLMNR on IPv6. Note that lookups for single-label synthetized names are not routed to IPv4, and lookups for IPv6 addresses are only sent via LLMNR on IPv6. Lookups for the locally
LLMNR, MulticastDNS or unicast DNS.</para></listitem> configured hostname and the <literal>_gateway</literal> hostname are never routed to LLMNR.
</para></listitem>
<listitem><para>Queries for the address records (A and AAAA) of single-label non-synthetized names are <listitem><para>Multi-label names with the domain suffix <literal>.local</literal> are routed to all
resolved via unicast DNS using search domains. For any interface which defines search domains, such local interfaces capable of IP multicasting, where MulticastDNS is not disabled, using the MulticastDNS
look-ups are routed to that interface, suffixed with each of the search domains defined on that protocol. As with LLMNR, IPv4 address lookups are sent via IPv4 and IPv6 address lookups are sent via
interface in turn. When global search domains are defined, such look-ups are routed to all interfaces, IPv6.</para></listitem>
suffixed by each of the global search domains in turn. Additionally, lookup of single-label names via
unicast DNS may be enabled with the <varname>ResolveUnicastSingleLabel=yes</varname> setting. The
details of which servers are queried and how the final reply is chosen are described below. Note that
this means that address queries for single-label names are never sent out to remote DNS servers by
default, and resoulution is only possible if search domains are defined.</para></listitem>
<listitem><para>Multi-label names with the domain suffix <literal>.local</literal> are resolved using <listitem><para>Resolution of address records (A and AAAA) via unicast DNS (i.e. not LLMNR or
MulticastDNS on all local interfaces where MulticastDNS is enabled. As with LLMNR, IPv4 address lookups MulticastDNS) for non-synthesized single-label names is allowed for non-top-level domains. This means
are sent via IPv4 and IPv6 address lookups are sent via IPv6.</para></listitem> that such records can be resolved when search domains are defined. For any interface which defines
search domains, such look-ups are routed to that interface, suffixed with each of the search domains
defined on that interface in turn. When global search domains are defined, such look-ups are routed to
all interfaces, suffixed by each of the global search domains in turn. Additionally, lookup of
single-label names via unicast DNS may be enabled with the
<varname>ResolveUnicastSingleLabel=yes</varname> setting. The details of which servers are queried and
how the final reply is chosen are described below. Note that this means that address queries for
single-label names are never sent out to remote DNS servers by default, and if no search domains are
defined, resolution will fail.</para></listitem>
<listitem><para>Queries for multi-label names are routed via unicast DNS on local interfaces that have <listitem><para>Other multi-label names are routed to all local interfaces that have a DNS server
a DNS server configured, plus the globally configured DNS servers if there are any. Which interfaces configured, plus the globally configured DNS servers if there are any. Note that by default, lookups for
are used is determined by the routing logic based on search and route-only domains, described below. domains with the <literal>.local</literal> suffix are not routed to DNS servers, unless the domain is
Note that by default, lookups for domains with the <literal>.local</literal> suffix are not routed to specified explicitly as routing or search domain for the DNS server and interface. This means that on
DNS servers, unless the domain is specified explicitly as routing or search domain for the DNS server networks where the <literal>.local</literal> domain is defined in a site-specific DNS server, explicit
and interface. This means that on networks where the <literal>.local</literal> domain is defined in a search or routing domains need to be configured to make lookups within this DNS domain work. Note that
site-specific DNS server, explicit search or routing domains need to be configured to make lookups work these days, it's generally recommended to avoid defining <literal>.local</literal> in a DNS server, as
within this DNS domain. Note that these days, it's generally recommended to avoid defining <ulink url="https://tools.ietf.org/html/rfc6762">RFC6762</ulink> reserves this domain for exclusive
<literal>.local</literal> in a DNS server, as <ulink
url="https://tools.ietf.org/html/rfc6762">RFC6762</ulink> reserves this domain for exclusive
MulticastDNS use.</para></listitem> MulticastDNS use.</para></listitem>
<listitem><para>Address lookups (reverse lookups) are routed similarly to multi-label names, with the <listitem><para>Address lookups are routed similarly to multi-label names, with the exception that
exception that addresses from the link-local address range are never routed to unicast DNS and are only addresses from the link-local address range are never routed to unicast DNS and are only resolved using
resolved using LLMNR and MulticastDNS (when enabled).</para></listitem> LLMNR and MulticastDNS (when enabled).</para></listitem>
</itemizedlist> </itemizedlist>
<para>If lookups are routed to multiple interfaces, the first successful response is returned (thus <para>If lookups are routed to multiple interfaces, the first successful response is returned (thus
effectively merging the lookup zones on all matching interfaces). If the lookup failed on all interfaces, effectively merging the lookup zones on all matching interfaces). If the lookup failed on all interfaces,
the last failing response is returned.</para> the last failing response is returned.</para>
<para>Routing of lookups is determined by the per-interface routing domains (search and route-only) and <para>Routing of lookups may be influenced by configuring per-interface domain names and other
global search domains. See settings. See
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry> and <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
<citerefentry><refentrytitle>resolvectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for a <citerefentry><refentrytitle>resolvectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
description how those settings are set dynamically and the discussion of <varname>Domains=</varname> in details. The following query routing logic applies for unicast DNS traffic:</para>
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> for a
description of globally configured DNS settings.</para>
<para>The following query routing logic applies for unicast DNS traffic:</para>
<itemizedlist> <itemizedlist>
<listitem><para>If a name to look up matches (that is: is equal to or has as suffix) any of the <listitem><para>If a name to look up matches (that is: is equal to or has as suffix) any of the
configured routing domains (search or route-only) of any link, or the globally configured DNS settings, configured search or route-only domains of any link (see
"best matching" routing domain is determined: the matching one with the most labels. The query is then <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>),
sent to all DNS servers of any links or the globally configured DNS servers associated with this "best or the globally configured DNS settings (see the discussion of <varname>Domains=</varname> in
matching" routing domain. (Note that more than one link might have this same "best matching" routing <citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>),
domain configured, in which case the query is sent to all of them in parallel).</para> "best matching" search/route-only domain is determined: the matching one with the most labels. The
query is then sent to all DNS servers of any links or the globally configured DNS servers associated
with this "best matching" search/route-only domain. (Note that more than one link might have this same
"best matching" search/route-only domain configured, in which case the query is sent to all of them in
parallel).</para>
<para>In case of single-label names, when search domains are defined, the same logic applies, except <para>In case of single-label names, when search domains are defined, the same logic applies, except
that the name is first suffixed by each of the search domains in turn. Note that this search logic that the name is first suffixed by the search domain.</para></listitem>
doesn't apply to any names with at least one dot. Also see the discussion about compatiblity with
the traditional glibc resolver below.</para></listitem>
<listitem><para>If a query does not match any configured routing domain (either per-link or global), it <listitem><para>If a query does not match any configured search/route-only domain (neither per-link nor
is sent to all DNS servers that are configured on links with the <varname>DefaultRoute=</varname> global), it is sent to all DNS servers that are configured on links with the "DNS default route" option
option set, as well as the globally configured DNS server.</para></listitem> set, as well as the globally configured DNS server.</para></listitem>
<listitem><para>If there is no link configured as <varname>DefaultRoute=</varname> and no global DNS <listitem><para>If there is no link configured as "DNS default route" and no global DNS server
server configured, one of the compiled-in fallback DNS servers is used.</para></listitem> configured, the compiled-in fallback DNS server is used.</para></listitem>
<listitem><para>Otherwise the unicast DNS query fails, as no suitable DNS servers can be determined. <listitem><para>Otherwise the query is failed as no suitable DNS servers could be determined.
</para></listitem> </para></listitem>
</itemizedlist> </itemizedlist>
<para>The <varname>DefaultRoute=</varname> option is a boolean setting configurable with <para>The "DNS default route" option is a boolean setting configurable with <command>resolvectl</command>
<command>resolvectl</command> or in <filename>.network</filename> files. If not set, it is implicitly or in <filename>.network</filename> files. If not set, it is implicitly determined based on the
determined based on the configured DNS domains for a link: if there's a route-only domain other than configured DNS domains for a link: if there's any route-only domain (not matching <literal>~.</literal>)
<literal>~.</literal>, it defaults to false, otherwise to true.</para> it defaults to false, otherwise to true.</para>
<para>Effectively this means: in order to support single-label non-synthetized names, define appropriate <para>Effectively this means: in order to support single-label non-synthetized names, define appropriate
search domains. In order to preferably route all DNS queries not explicitly matched by routing domain search domains. In order to preferably route all DNS queries not explicitly matched by search/route-only
configuration to a specific link, configure a <literal>~.</literal> route-only domain on it. This will domain configuration to a specific link, configure a <literal>~.</literal> route-only domain on it. This
ensure that other links will not be considered for these queries (unless they too carry such a routing will ensure that other links will not be considered for these queries (unless they too carry such a
domain). In order to route all such DNS queries to a specific link only if no other link is preferred, route-only domain). In order to route all such DNS queries to a specific link only if no other link
set the <varname>DefaultRoute=</varname> option for the link to true and do not configure a is preferable, set the "DNS default route" option for the link to true and do not configure a
<literal>~.</literal> route-only domain on it. Finally, in order to ensure that a specific link never <literal>~.</literal> route-only domain on it. Finally, in order to ensure that a specific link never
receives any DNS traffic not matching any of its configured routing domains, set the receives any DNS traffic not matching any of its configured search/route-only domains, set the "DNS
<varname>DefaultRoute=</varname> option for it to false.</para> default route" option for it to false.</para>
<para>See <para>See
<citerefentry><refentrytitle>org.freedesktop.resolve1</refentrytitle><manvolnum>5</manvolnum></citerefentry> <citerefentry><refentrytitle>org.freedesktop.resolve1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for information about the D-Bus APIs <filename>systemd-resolved</filename> provides.</para> for information about the D-Bus APIs <filename>systemd-resolved</filename> provides.</para>
</refsect1> </refsect1>
<refsect1>
<title>Compatibility with the traditional glibc stub resolver</title>
<para>This section provides a short summary of differences in the stub resolver implemented by
<citerefentry><refentrytitle>nss-resolve</refentrytitle><manvolnum>8</manvolnum></citerefentry> together
with <command>systemd-resolved</command> and the tranditional stub resolver implemented in
<citerefentry><refentrytitle>nss-dns</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<itemizedlist>
<listitem><para>Some names are always resolved internally (see Synthetic Records above). Traditionally
they would be resolved by <filename>nss-files</filename>, and only if provided in
<filename>/etc/hosts</filename>.</para></listitem>
<listitem><para>Single-label names are not resolved for A and AAAA records using unicast DNS (unless
overriden with <varname>ResolveUnicastSingleLabel=</varname>, see
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
This is similar to the <option>no-tld-query</option> option being set in
<citerefentry><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para></listitem>
<listitem><para>Search domains are not used for <emphasis>suffixing</emphasis> of multi-label names.
(Search domains are nevertheless used for lookup <emphasis>routing</emphasis>, for names that were
originally specified as single-label or multi-label.) Any name with at least one dot is always
interpreted as a FQDN. <filename>nss-dns</filename> would resolve names both as relative (using search
domains) and absolute FQDN names. Some names would be resolved as relative first, and after that query
has failed, as absolute, while other names would be resolved in opposite order. The
<varname>ndots</varname> option in <filename>/etc/resolv.conf</filename> was used to control how many
dots the name needs to have to be resolved as relative first. This stub resolver does not implement
this at all: multi-label names are only resolved as FQDNs. (There are currently more than 1500
top-level domain names defined, and new ones are added regularly, often using "attractive" names that
are also likely to be used locally. Not looking up multi-label names in this fashion avoids fragility
in both directions: a valid global name could be obscured by a local name, and resolution of a relative
local name could suddenly break when a new top-level domain is created, or when a new subdomain of a
top-level domain in registered. Resolving any given name as either relative or absolute avoids this
ambiguity.)</para></listitem>
<listitem><para>This resolver has a notion of the special <literal>.local</literal> domain used for
MulticastDNS, and will not route queries with that suffix to unicast DNS servers unless explicitly
configured, see above. Also, reverse lookups for link-local addresses are not sent to unicast DNS
servers.</para></listitem>
<listitem><para>This resolver reads and caches <filename>/etc/hosts</filename> internally. (In other
words, <filename>nss-resolve</filename> replaces <filename>nss-files</filename> in addition to
<filename>nss-dns</filename>). Entries in <filename>/etc/hosts</filename> have highest priority.</para>
</listitem>
<listitem><para>This resolver also implements LLMNR and MulticastDNS in addition to the classic unicast
DNS protocol, and will resolve single-label names using LLMNR (when enabled) and names ending in
<literal>.local</literal> using MulticastDNS (when enabled).</para></listitem>
<listitem><para>Environment variables <varname>$LOCALDOMAIN</varname> and
<varname>$RES_OPTIONS</varname> described in
<citerefentry><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> are not
supported currently.</para></listitem>
</itemizedlist>
</refsect1>
<refsect1> <refsect1>
<title><filename>/etc/resolv.conf</filename></title> <title><filename>/etc/resolv.conf</filename></title>
@ -361,6 +303,7 @@
synchronous way.</para></listitem> synchronous way.</para></listitem>
</varlistentry> </varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>
<refsect1> <refsect1>

View File

@ -38,8 +38,8 @@ relative_source_path = run_command('realpath',
project_source_root).stdout().strip() project_source_root).stdout().strip()
conf.set_quoted('RELATIVE_SOURCE_PATH', relative_source_path) conf.set_quoted('RELATIVE_SOURCE_PATH', relative_source_path)
conf.set('BUILD_MODE', 'BUILD_MODE_' + get_option('mode').to_upper(), conf.set10('DEVELOPER_MODE', get_option('mode') == 'developer',
description : 'tailor build to development or release builds') description : 'enable additional checks only suitable in development')
want_ossfuzz = get_option('oss-fuzz') want_ossfuzz = get_option('oss-fuzz')
want_libfuzzer = get_option('llvm-fuzz') want_libfuzzer = get_option('llvm-fuzz')

View File

@ -5,7 +5,7 @@ option('version-tag', type : 'string',
description : 'override the git version string') description : 'override the git version string')
option('mode', type : 'combo', choices : ['developer', 'release'], option('mode', type : 'combo', choices : ['developer', 'release'],
description : 'autoenable features suitable for systemd development/release builds') description : 'enable additional checks suitable for systemd development')
option('split-usr', type : 'combo', choices : ['auto', 'true', 'false'], option('split-usr', type : 'combo', choices : ['auto', 'true', 'false'],
description : '''/bin, /sbin aren't symlinks into /usr''') description : '''/bin, /sbin aren't symlinks into /usr''')

View File

@ -161,8 +161,3 @@
_IDN_FEATURE_ " " \ _IDN_FEATURE_ " " \
_PCRE2_FEATURE_ " " \ _PCRE2_FEATURE_ " " \
_CGROUP_HIERARCHY_ _CGROUP_HIERARCHY_
enum {
BUILD_MODE_DEVELOPER,
BUILD_MODE_RELEASE,
};

View File

@ -252,8 +252,7 @@ int write_string_file_ts(
/* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */ /* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */
fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY | fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY |
(FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) | (FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) | (FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0),
(FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0),
(FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666)); (FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
if (fd < 0) { if (fd < 0) {
r = -errno; r = -errno;

View File

@ -16,15 +16,14 @@
typedef enum { typedef enum {
WRITE_STRING_FILE_CREATE = 1 << 0, WRITE_STRING_FILE_CREATE = 1 << 0,
WRITE_STRING_FILE_TRUNCATE = 1 << 1, WRITE_STRING_FILE_ATOMIC = 1 << 1,
WRITE_STRING_FILE_ATOMIC = 1 << 2, WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 2,
WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3, WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 3,
WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4, WRITE_STRING_FILE_SYNC = 1 << 4,
WRITE_STRING_FILE_SYNC = 1 << 5, WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 5,
WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 6, WRITE_STRING_FILE_NOFOLLOW = 1 << 6,
WRITE_STRING_FILE_NOFOLLOW = 1 << 7, WRITE_STRING_FILE_MKDIR_0755 = 1 << 7,
WRITE_STRING_FILE_MKDIR_0755 = 1 << 8, WRITE_STRING_FILE_MODE_0600 = 1 << 8,
WRITE_STRING_FILE_MODE_0600 = 1 << 9,
/* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file() more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()

View File

@ -650,8 +650,4 @@ static inline int __coverity_check_and_return__(int condition) {
_copy; \ _copy; \
}) })
static inline size_t size_add(size_t x, size_t y) {
return y >= SIZE_MAX - x ? SIZE_MAX : x + y;
}
#include "log.h" #include "log.h"

View File

@ -27,7 +27,7 @@
#ifdef CAP_LAST_CAP #ifdef CAP_LAST_CAP
# if CAP_LAST_CAP > SYSTEMD_CAP_LAST_CAP # if CAP_LAST_CAP > SYSTEMD_CAP_LAST_CAP
# if BUILD_MODE == BUILD_MODE_DEVELOPER && defined(TEST_CAPABILITY_C) # if DEVELOPER_MODE && defined(TEST_CAPABILITY_C)
# warning "The capability list here is outdated" # warning "The capability list here is outdated"
# endif # endif
# else # else

View File

@ -33,12 +33,6 @@ static inline bool streq_ptr(const char *a, const char *b) {
return strcmp_ptr(a, b) == 0; return strcmp_ptr(a, b) == 0;
} }
static inline char* strstr_ptr(const char *haystack, const char *needle) {
if (!haystack || !needle)
return NULL;
return strstr(haystack, needle);
}
static inline const char* strempty(const char *s) { static inline const char* strempty(const char *s) {
return s ?: ""; return s ?: "";
} }
@ -59,10 +53,6 @@ static inline const char* true_false(bool b) {
return b ? "true" : "false"; return b ? "true" : "false";
} }
static inline const char* plus_minus(bool b) {
return b ? "+" : "-";
}
static inline const char* one_zero(bool b) { static inline const char* one_zero(bool b) {
return b ? "1" : "0"; return b ? "1" : "0";
} }

View File

@ -537,19 +537,6 @@ int strv_consume_prepend(char ***l, char *value) {
return r; return r;
} }
int strv_prepend(char ***l, const char *value) {
char *v;
if (!value)
return 0;
v = strdup(value);
if (!v)
return -ENOMEM;
return strv_consume_prepend(l, v);
}
int strv_extend(char ***l, const char *value) { int strv_extend(char ***l, const char *value) {
char *v; char *v;

View File

@ -34,7 +34,6 @@ size_t strv_length(char * const *l) _pure_;
int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates); int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates);
int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix); int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix);
int strv_prepend(char ***l, const char *value);
int strv_extend(char ***l, const char *value); int strv_extend(char ***l, const char *value);
int strv_extendf(char ***l, const char *format, ...) _printf_(2,0); int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
int strv_extend_front(char ***l, const char *value); int strv_extend_front(char ***l, const char *value);

View File

@ -87,7 +87,7 @@ struct ExecStatus {
dual_timestamp exit_timestamp; dual_timestamp exit_timestamp;
pid_t pid; pid_t pid;
int code; /* as in siginfo_t::si_code */ int code; /* as in siginfo_t::si_code */
int status; /* as in siginfo_t::si_status */ int status; /* as in sigingo_t::si_status */
}; };
/* Stores information about commands we execute. Covers both configuration settings as well as runtime data. */ /* Stores information about commands we execute. Covers both configuration settings as well as runtime data. */

View File

@ -35,7 +35,6 @@
#include "string-table.h" #include "string-table.h"
#include "strv.h" #include "strv.h"
#include "terminal-util.h" #include "terminal-util.h"
#include "utf8.h"
#include "verbs.h" #include "verbs.h"
static int arg_family = AF_UNSPEC; static int arg_family = AF_UNSPEC;
@ -1297,46 +1296,24 @@ static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m,
if (r < 0) if (r < 0)
return r; return r;
strv_sort(*l);
return 0; return 0;
} }
static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p) { static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p) {
const unsigned indent = strlen("Global: "); /* Use the same indentation everywhere to make things nice */
int pos1, pos2;
if (ifname)
printf("%s%nLink %i (%s)%n%s:", ansi_highlight(), &pos1, ifindex, ifname, &pos2, ansi_normal());
else
printf("%s%nGlobal%n%s:", ansi_highlight(), &pos1, &pos2, ansi_normal());
size_t cols = columns(), position = pos2 - pos1 + 2;
char **i; char **i;
STRV_FOREACH(i, p) { printf("%sLink %i (%s)%s:",
size_t our_len = utf8_console_width(*i); /* This returns -1 on invalid utf-8 (which shouldn't happen). ansi_highlight(), ifindex, ifname, ansi_normal());
* If that happens, we'll just print one item per line. */
if (position <= indent || size_add(size_add(position, 1), our_len) < cols) { STRV_FOREACH(i, p)
printf(" %s", *i); printf(" %s", *i);
position = size_add(size_add(position, 1), our_len);
} else {
printf("\n%*s%s", indent, "", *i);
position = size_add(our_len, indent);
}
}
printf("\n"); printf("\n");
return 0; return 0;
} }
static int status_print_strv_global(char **p) { struct link_info {
return status_print_strv_ifindex(0, NULL, p);
}
typedef struct LinkInfo {
uint64_t scopes_mask; uint64_t scopes_mask;
const char *llmnr; const char *llmnr;
const char *mdns; const char *mdns;
@ -1350,26 +1327,9 @@ typedef struct LinkInfo {
char **ntas; char **ntas;
bool dnssec_supported; bool dnssec_supported;
bool default_route; bool default_route;
} LinkInfo; };
typedef struct GlobalInfo { static void link_info_clear(struct link_info *p) {
char *current_dns;
char *current_dns_ex;
char **dns;
char **dns_ex;
char **fallback_dns;
char **fallback_dns_ex;
char **domains;
char **ntas;
const char *llmnr;
const char *mdns;
const char *dns_over_tls;
const char *dnssec;
const char *resolv_conf_mode;
bool dnssec_supported;
} GlobalInfo;
static void link_info_clear(LinkInfo *p) {
free(p->current_dns); free(p->current_dns);
free(p->current_dns_ex); free(p->current_dns_ex);
strv_free(p->dns); strv_free(p->dns);
@ -1378,17 +1338,6 @@ static void link_info_clear(LinkInfo *p) {
strv_free(p->ntas); strv_free(p->ntas);
} }
static void global_info_clear(GlobalInfo *p) {
free(p->current_dns);
free(p->current_dns_ex);
strv_free(p->dns);
strv_free(p->dns_ex);
strv_free(p->fallback_dns);
strv_free(p->fallback_dns_ex);
strv_free(p->domains);
strv_free(p->ntas);
}
static int dump_list(Table *table, const char *prefix, char * const *l) { static int dump_list(Table *table, const char *prefix, char * const *l) {
int r; int r;
@ -1397,88 +1346,33 @@ static int dump_list(Table *table, const char *prefix, char * const *l) {
r = table_add_many(table, r = table_add_many(table,
TABLE_STRING, prefix, TABLE_STRING, prefix,
TABLE_STRV_WRAPPED, l); TABLE_STRV, l);
if (r < 0) if (r < 0)
return table_log_add_error(r); return table_log_add_error(r);
return 0; return 0;
} }
static int strv_extend_extended_bool(char ***strv, const char *name, const char *value) {
int r;
if (value) {
r = parse_boolean(value);
if (r >= 0)
return strv_extendf(strv, "%s%s", plus_minus(r), name);
}
return strv_extendf(strv, "%s=%s", name, value ?: "???");
}
static char** link_protocol_status(const LinkInfo *info) {
_cleanup_strv_free_ char **s = NULL;
if (strv_extendf(&s, "%sDefaultRoute", plus_minus(info->default_route)) < 0)
return NULL;
if (strv_extend_extended_bool(&s, "LLMNR", info->llmnr) < 0)
return NULL;
if (strv_extend_extended_bool(&s, "mDNS", info->mdns) < 0)
return NULL;
if (strv_extend_extended_bool(&s, "DNSOverTLS", info->dns_over_tls) < 0)
return NULL;
if (strv_extendf(&s, "DNSSEC=%s/%s",
info->dnssec ?: "???",
info->dnssec_supported ? "supported" : "unsupported") < 0)
return NULL;
return TAKE_PTR(s);
}
static char** global_protocol_status(const GlobalInfo *info) {
_cleanup_strv_free_ char **s = NULL;
if (strv_extend_extended_bool(&s, "LLMNR", info->llmnr) < 0)
return NULL;
if (strv_extend_extended_bool(&s, "mDNS", info->mdns) < 0)
return NULL;
if (strv_extend_extended_bool(&s, "DNSOverTLS", info->dns_over_tls) < 0)
return NULL;
if (strv_extendf(&s, "DNSSEC=%s/%s",
info->dnssec ?: "???",
info->dnssec_supported ? "supported" : "unsupported") < 0)
return NULL;
return TAKE_PTR(s);
}
static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode mode, bool *empty_line) { static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode mode, bool *empty_line) {
static const struct bus_properties_map property_map[] = { static const struct bus_properties_map property_map[] = {
{ "ScopesMask", "t", NULL, offsetof(LinkInfo, scopes_mask) }, { "ScopesMask", "t", NULL, offsetof(struct link_info, scopes_mask) },
{ "DNS", "a(iay)", map_link_dns_servers, offsetof(LinkInfo, dns) }, { "DNS", "a(iay)", map_link_dns_servers, offsetof(struct link_info, dns) },
{ "DNSEx", "a(iayqs)", map_link_dns_servers_ex, offsetof(LinkInfo, dns_ex) }, { "DNSEx", "a(iayqs)", map_link_dns_servers_ex, offsetof(struct link_info, dns_ex) },
{ "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(LinkInfo, current_dns) }, { "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(struct link_info, current_dns) },
{ "CurrentDNSServerEx", "(iayqs)", map_link_current_dns_server_ex, offsetof(LinkInfo, current_dns_ex) }, { "CurrentDNSServerEx", "(iayqs)", map_link_current_dns_server_ex, offsetof(struct link_info, current_dns_ex) },
{ "Domains", "a(sb)", map_link_domains, offsetof(LinkInfo, domains) }, { "Domains", "a(sb)", map_link_domains, offsetof(struct link_info, domains) },
{ "DefaultRoute", "b", NULL, offsetof(LinkInfo, default_route) }, { "DefaultRoute", "b", NULL, offsetof(struct link_info, default_route) },
{ "LLMNR", "s", NULL, offsetof(LinkInfo, llmnr) }, { "LLMNR", "s", NULL, offsetof(struct link_info, llmnr) },
{ "MulticastDNS", "s", NULL, offsetof(LinkInfo, mdns) }, { "MulticastDNS", "s", NULL, offsetof(struct link_info, mdns) },
{ "DNSOverTLS", "s", NULL, offsetof(LinkInfo, dns_over_tls) }, { "DNSOverTLS", "s", NULL, offsetof(struct link_info, dns_over_tls) },
{ "DNSSEC", "s", NULL, offsetof(LinkInfo, dnssec) }, { "DNSSEC", "s", NULL, offsetof(struct link_info, dnssec) },
{ "DNSSECNegativeTrustAnchors", "as", bus_map_strv_sort, offsetof(LinkInfo, ntas) }, { "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct link_info, ntas) },
{ "DNSSECSupported", "b", NULL, offsetof(LinkInfo, dnssec_supported) }, { "DNSSECSupported", "b", NULL, offsetof(struct link_info, dnssec_supported) },
{} {}
}; };
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_(link_info_clear) LinkInfo link_info = {}; _cleanup_(link_info_clear) struct link_info link_info = {};
_cleanup_(table_unrefp) Table *table = NULL; _cleanup_(table_unrefp) Table *table = NULL;
_cleanup_free_ char *p = NULL; _cleanup_free_ char *p = NULL;
char ifi[DECIMAL_STR_MAX(int)], ifname[IF_NAMESIZE + 1] = ""; char ifi[DECIMAL_STR_MAX(int)], ifname[IF_NAMESIZE + 1] = "";
@ -1602,13 +1496,19 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
if (r < 0) if (r < 0)
return table_log_add_error(r); return table_log_add_error(r);
_cleanup_strv_free_ char **pstatus = link_protocol_status(&link_info);
if (!pstatus)
return log_oom();
r = table_add_many(table, r = table_add_many(table,
TABLE_STRING, "Protocols:", TABLE_STRING, "DefaultRoute setting:",
TABLE_STRV_WRAPPED, pstatus); TABLE_BOOLEAN, link_info.default_route,
TABLE_STRING, "LLMNR setting:",
TABLE_STRING, strna(link_info.llmnr),
TABLE_STRING, "MulticastDNS setting:",
TABLE_STRING, strna(link_info.mdns),
TABLE_STRING, "DNSOverTLS setting:",
TABLE_STRING, strna(link_info.dns_over_tls),
TABLE_STRING, "DNSSEC setting:",
TABLE_STRING, strna(link_info.dnssec),
TABLE_STRING, "DNSSEC supported:",
TABLE_BOOLEAN, link_info.dnssec_supported);
if (r < 0) if (r < 0)
return table_log_add_error(r); return table_log_add_error(r);
@ -1731,32 +1631,71 @@ static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m
if (r < 0) if (r < 0)
return r; return r;
strv_sort(*l); return 0;
}
static int status_print_strv_global(char **p) {
char **i;
printf("%sGlobal%s:", ansi_highlight(), ansi_normal());
STRV_FOREACH(i, p)
printf(" %s", *i);
printf("\n");
return 0; return 0;
} }
struct global_info {
char *current_dns;
char *current_dns_ex;
char **dns;
char **dns_ex;
char **fallback_dns;
char **fallback_dns_ex;
char **domains;
char **ntas;
const char *llmnr;
const char *mdns;
const char *dns_over_tls;
const char *dnssec;
const char *resolv_conf_mode;
bool dnssec_supported;
};
static void global_info_clear(struct global_info *p) {
free(p->current_dns);
free(p->current_dns_ex);
strv_free(p->dns);
strv_free(p->dns_ex);
strv_free(p->fallback_dns);
strv_free(p->fallback_dns_ex);
strv_free(p->domains);
strv_free(p->ntas);
}
static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) { static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
static const struct bus_properties_map property_map[] = { static const struct bus_properties_map property_map[] = {
{ "DNS", "a(iiay)", map_global_dns_servers, offsetof(GlobalInfo, dns) }, { "DNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, dns) },
{ "DNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(GlobalInfo, dns_ex) }, { "DNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(struct global_info, dns_ex) },
{ "FallbackDNS", "a(iiay)", map_global_dns_servers, offsetof(GlobalInfo, fallback_dns) }, { "FallbackDNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, fallback_dns) },
{ "FallbackDNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(GlobalInfo, fallback_dns_ex) }, { "FallbackDNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(struct global_info, fallback_dns_ex) },
{ "CurrentDNSServer", "(iiay)", map_global_current_dns_server, offsetof(GlobalInfo, current_dns) }, { "CurrentDNSServer", "(iiay)", map_global_current_dns_server, offsetof(struct global_info, current_dns) },
{ "CurrentDNSServerEx", "(iiayqs)", map_global_current_dns_server_ex, offsetof(GlobalInfo, current_dns_ex) }, { "CurrentDNSServerEx", "(iiayqs)", map_global_current_dns_server_ex, offsetof(struct global_info, current_dns_ex) },
{ "Domains", "a(isb)", map_global_domains, offsetof(GlobalInfo, domains) }, { "Domains", "a(isb)", map_global_domains, offsetof(struct global_info, domains) },
{ "DNSSECNegativeTrustAnchors", "as", bus_map_strv_sort, offsetof(GlobalInfo, ntas) }, { "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct global_info, ntas) },
{ "LLMNR", "s", NULL, offsetof(GlobalInfo, llmnr) }, { "LLMNR", "s", NULL, offsetof(struct global_info, llmnr) },
{ "MulticastDNS", "s", NULL, offsetof(GlobalInfo, mdns) }, { "MulticastDNS", "s", NULL, offsetof(struct global_info, mdns) },
{ "DNSOverTLS", "s", NULL, offsetof(GlobalInfo, dns_over_tls) }, { "DNSOverTLS", "s", NULL, offsetof(struct global_info, dns_over_tls) },
{ "DNSSEC", "s", NULL, offsetof(GlobalInfo, dnssec) }, { "DNSSEC", "s", NULL, offsetof(struct global_info, dnssec) },
{ "DNSSECSupported", "b", NULL, offsetof(GlobalInfo, dnssec_supported) }, { "DNSSECSupported", "b", NULL, offsetof(struct global_info, dnssec_supported) },
{ "ResolvConfMode", "s", NULL, offsetof(GlobalInfo, resolv_conf_mode) }, { "ResolvConfMode", "s", NULL, offsetof(struct global_info, resolv_conf_mode) },
{} {}
}; };
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_(global_info_clear) GlobalInfo global_info = {}; _cleanup_(global_info_clear) struct global_info global_info = {};
_cleanup_(table_unrefp) Table *table = NULL; _cleanup_(table_unrefp) Table *table = NULL;
int r; int r;
@ -1821,14 +1760,18 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
table_set_header(table, false); table_set_header(table, false);
_cleanup_strv_free_ char **pstatus = global_protocol_status(&global_info);
if (!pstatus)
return log_oom();
r = table_add_many(table, r = table_add_many(table,
TABLE_STRING, "Protocols:", TABLE_STRING, "LLMNR setting:",
TABLE_SET_ALIGN_PERCENT, 100, TABLE_SET_ALIGN_PERCENT, 100,
TABLE_STRV_WRAPPED, pstatus); TABLE_STRING, strna(global_info.llmnr),
TABLE_STRING, "MulticastDNS setting:",
TABLE_STRING, strna(global_info.mdns),
TABLE_STRING, "DNSOverTLS setting:",
TABLE_STRING, strna(global_info.dns_over_tls),
TABLE_STRING, "DNSSEC setting:",
TABLE_STRING, strna(global_info.dnssec),
TABLE_STRING, "DNSSEC supported:",
TABLE_BOOLEAN, global_info.dnssec_supported);
if (r < 0) if (r < 0)
return table_log_add_error(r); return table_log_add_error(r);

View File

@ -273,7 +273,7 @@ static int write_uplink_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSe
} }
if (ordered_set_isempty(domains)) if (ordered_set_isempty(domains))
fputs("search .\n", f); /* Make sure that if the local hostname is chosen as fqdn this does not fputs("search .", f); /* Make sure that if the local hostname is chosen as fqdn this does not
* imply a search domain */ * imply a search domain */
else else
write_resolv_conf_search(domains, f); write_resolv_conf_search(domains, f);
@ -302,7 +302,7 @@ static int write_stub_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet
"options edns0 trust-ad\n", f); "options edns0 trust-ad\n", f);
if (ordered_set_isempty(domains)) if (ordered_set_isempty(domains))
fputs("search .\n", f); /* Make sure that if the local hostname is chosen as fqdn this does not fputs("search .", f); /* Make sure that if the local hostname is chosen as fqdn this does not
* imply a search domain */ * imply a search domain */
else else
write_resolv_conf_search(domains, f); write_resolv_conf_search(domains, f);

View File

@ -25,6 +25,5 @@
#LLMNR=@DEFAULT_LLMNR_MODE@ #LLMNR=@DEFAULT_LLMNR_MODE@
#Cache=yes #Cache=yes
#DNSStubListener=yes #DNSStubListener=yes
#DNSStubListenerExtra=
#ReadEtcHosts=yes #ReadEtcHosts=yes
#ResolveUnicastSingleLabel=no #ResolveUnicastSingleLabel=no

View File

@ -25,23 +25,6 @@ int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_err
return 0; return 0;
} }
int bus_map_strv_sort(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
_cleanup_strv_free_ char **l = NULL;
char ***p = userdata;
int r;
r = bus_message_read_strv_extend(m, &l);
if (r < 0)
return r;
r = strv_extend_strv(p, l, false);
if (r < 0)
return r;
strv_sort(*p);
return 0;
}
static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, unsigned flags, sd_bus_error *error, void *userdata) { static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, unsigned flags, sd_bus_error *error, void *userdata) {
char type; char type;
int r; int r;

View File

@ -18,7 +18,6 @@ enum {
}; };
int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata); int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
int bus_map_strv_sort(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, unsigned flags, sd_bus_error *error, void *userdata); int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, unsigned flags, sd_bus_error *error, void *userdata);
int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map, int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map,

View File

@ -66,7 +66,6 @@ typedef struct TableData {
size_t minimum_width; /* minimum width for the column */ size_t minimum_width; /* minimum width for the column */
size_t maximum_width; /* maximum width for the column */ size_t maximum_width; /* maximum width for the column */
size_t formatted_for_width; /* the width we tried to format for */
unsigned weight; /* the horizontal weight for this column, in case the table is expanded/compressed */ unsigned weight; /* the horizontal weight for this column, in case the table is expanded/compressed */
unsigned ellipsize_percent; /* 0 … 100, where to place the ellipsis when compression is needed */ unsigned ellipsize_percent; /* 0 … 100, where to place the ellipsis when compression is needed */
unsigned align_percent; /* 0 … 100, where to pad with spaces when expanding is needed. 0: left-aligned, 100: right-aligned */ unsigned align_percent; /* 0 … 100, where to pad with spaces when expanding is needed. 0: left-aligned, 100: right-aligned */
@ -165,6 +164,7 @@ Table *table_new_raw(size_t n_columns) {
Table *table_new_internal(const char *first_header, ...) { Table *table_new_internal(const char *first_header, ...) {
_cleanup_(table_unrefp) Table *t = NULL; _cleanup_(table_unrefp) Table *t = NULL;
size_t n_columns = 1; size_t n_columns = 1;
const char *h;
va_list ap; va_list ap;
int r; int r;
@ -172,7 +172,8 @@ Table *table_new_internal(const char *first_header, ...) {
va_start(ap, first_header); va_start(ap, first_header);
for (;;) { for (;;) {
if (!va_arg(ap, const char*)) h = va_arg(ap, const char*);
if (!h)
break; break;
n_columns++; n_columns++;
@ -184,7 +185,7 @@ Table *table_new_internal(const char *first_header, ...) {
return NULL; return NULL;
va_start(ap, first_header); va_start(ap, first_header);
for (const char *h = first_header; h; h = va_arg(ap, const char*)) { for (h = first_header; h; h = va_arg(ap, const char*)) {
TableCell *cell; TableCell *cell;
r = table_add_cell(t, &cell, TABLE_STRING, h); r = table_add_cell(t, &cell, TABLE_STRING, h);
@ -212,7 +213,7 @@ static TableData *table_data_free(TableData *d) {
free(d->formatted); free(d->formatted);
free(d->url); free(d->url);
if (IN_SET(d->type, TABLE_STRV, TABLE_STRV_WRAPPED)) if (d->type == TABLE_STRV)
strv_free(d->strv); strv_free(d->strv);
return mfree(d); return mfree(d);
@ -222,10 +223,12 @@ DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(TableData, table_data, table_data_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(TableData*, table_data_unref); DEFINE_TRIVIAL_CLEANUP_FUNC(TableData*, table_data_unref);
Table *table_unref(Table *t) { Table *table_unref(Table *t) {
size_t i;
if (!t) if (!t)
return NULL; return NULL;
for (size_t i = 0; i < t->n_cells; i++) for (i = 0; i < t->n_cells; i++)
table_data_unref(t->data[i]); table_data_unref(t->data[i]);
free(t->data); free(t->data);
@ -249,7 +252,6 @@ static size_t table_data_size(TableDataType type, const void *data) {
return strlen(data) + 1; return strlen(data) + 1;
case TABLE_STRV: case TABLE_STRV:
case TABLE_STRV_WRAPPED:
return sizeof(char **); return sizeof(char **);
case TABLE_BOOLEAN: case TABLE_BOOLEAN:
@ -374,7 +376,7 @@ static TableData *table_data_new(
d->align_percent = align_percent; d->align_percent = align_percent;
d->ellipsize_percent = ellipsize_percent; d->ellipsize_percent = ellipsize_percent;
if (IN_SET(type, TABLE_STRV, TABLE_STRV_WRAPPED)) { if (type == TABLE_STRV) {
d->strv = strv_copy(data); d->strv = strv_copy(data);
if (!d->strv) if (!d->strv)
return NULL; return NULL;
@ -815,7 +817,6 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
break; break;
case TABLE_STRV: case TABLE_STRV:
case TABLE_STRV_WRAPPED:
data = va_arg(ap, char * const *); data = va_arg(ap, char * const *);
break; break;
@ -1046,9 +1047,11 @@ int table_set_empty_string(Table *t, const char *empty) {
} }
int table_set_display_all(Table *t) { int table_set_display_all(Table *t) {
size_t allocated;
assert(t); assert(t);
size_t allocated = t->n_display_map; allocated = t->n_display_map;
if (!GREEDY_REALLOC(t->display_map, allocated, MAX(t->n_columns, allocated))) if (!GREEDY_REALLOC(t->display_map, allocated, MAX(t->n_columns, allocated)))
return -ENOMEM; return -ENOMEM;
@ -1121,6 +1124,7 @@ int table_set_sort(Table *t, size_t first_column, ...) {
} }
int table_hide_column_from_display(Table *t, size_t column) { int table_hide_column_from_display(Table *t, size_t column) {
size_t allocated, cur = 0;
int r; int r;
assert(t); assert(t);
@ -1133,7 +1137,7 @@ int table_hide_column_from_display(Table *t, size_t column) {
return r; return r;
} }
size_t allocated = t->n_display_map, cur = 0; allocated = t->n_display_map;
for (size_t i = 0; i < allocated; i++) { for (size_t i = 0; i < allocated; i++) {
if (t->display_map[i] == column) if (t->display_map[i] == column)
@ -1165,7 +1169,6 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
return path_compare(a->string, b->string); return path_compare(a->string, b->string);
case TABLE_STRV: case TABLE_STRV:
case TABLE_STRV_WRAPPED:
return strv_compare(a->strv, b->strv); return strv_compare(a->strv, b->strv);
case TABLE_BOOLEAN: case TABLE_BOOLEAN:
@ -1244,6 +1247,7 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
} }
static int table_data_compare(const size_t *a, const size_t *b, Table *t) { static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
size_t i;
int r; int r;
assert(t); assert(t);
@ -1258,7 +1262,7 @@ static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
return 1; return 1;
/* Order other lines by the sorting map */ /* Order other lines by the sorting map */
for (size_t i = 0; i < t->n_sort_map; i++) { for (i = 0; i < t->n_sort_map; i++) {
TableData *d, *dd; TableData *d, *dd;
d = t->data[*a + t->sort_map[i]]; d = t->data[*a + t->sort_map[i]];
@ -1273,46 +1277,10 @@ static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
return CMP(*a, *b); return CMP(*a, *b);
} }
static char* format_strv_width(char **strv, size_t column_width) { static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercasing) {
_cleanup_fclose_ FILE *f = NULL;
size_t sz = 0;
_cleanup_free_ char *buf = NULL;
f = open_memstream_unlocked(&buf, &sz);
if (!f)
return NULL;
size_t position = 0;
char **p;
STRV_FOREACH(p, strv) {
size_t our_len = utf8_console_width(*p); /* This returns -1 on invalid utf-8 (which shouldn't happen).
* If that happens, we'll just print one item per line. */
if (position == 0) {
fputs(*p, f);
position = our_len;
} else if (size_add(size_add(position, 1), our_len) <= column_width) {
fprintf(f, " %s", *p);
position = size_add(size_add(position, 1), our_len);
} else {
fprintf(f, "\n%s", *p);
position = our_len;
}
}
if (fflush_and_check(f) < 0)
return NULL;
f = safe_fclose(f);
return TAKE_PTR(buf);
}
static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercasing, size_t column_width, bool *have_soft) {
assert(d); assert(d);
if (d->formatted && if (d->formatted)
/* Only TABLE_STRV_WRAPPED adjust based on column_width so far… */
(d->type != TABLE_STRV_WRAPPED || d->formatted_for_width == column_width))
return d->formatted; return d->formatted;
switch (d->type) { switch (d->type) {
@ -1322,12 +1290,13 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
case TABLE_STRING: case TABLE_STRING:
case TABLE_PATH: case TABLE_PATH:
if (d->uppercase && !avoid_uppercasing) { if (d->uppercase && !avoid_uppercasing) {
char *p, *q;
d->formatted = new(char, strlen(d->string) + 1); d->formatted = new(char, strlen(d->string) + 1);
if (!d->formatted) if (!d->formatted)
return NULL; return NULL;
char *q = d->formatted; for (p = d->string, q = d->formatted; *p; p++, q++)
for (char *p = d->string; *p; p++, q++)
*q = (char) toupper((unsigned char) *p); *q = (char) toupper((unsigned char) *p);
*q = 0; *q = 0;
@ -1336,28 +1305,17 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
return d->string; return d->string;
case TABLE_STRV: case TABLE_STRV: {
char *p;
if (strv_isempty(d->strv)) if (strv_isempty(d->strv))
return strempty(t->empty_string); return strempty(t->empty_string);
d->formatted = strv_join(d->strv, "\n"); p = strv_join(d->strv, "\n");
if (!d->formatted) if (!p)
return NULL;
break;
case TABLE_STRV_WRAPPED: {
if (strv_isempty(d->strv))
return strempty(t->empty_string);
char *buf = format_strv_width(d->strv, column_width);
if (!buf)
return NULL; return NULL;
free_and_replace(d->formatted, buf); d->formatted = p;
d->formatted_for_width = column_width;
if (have_soft)
*have_soft = true;
break; break;
} }
@ -1674,19 +1632,16 @@ static int console_width_height(
static int table_data_requested_width_height( static int table_data_requested_width_height(
Table *table, Table *table,
TableData *d, TableData *d,
size_t available_width,
size_t *ret_width, size_t *ret_width,
size_t *ret_height, size_t *ret_height) {
bool *have_soft) {
_cleanup_free_ char *truncated = NULL; _cleanup_free_ char *truncated = NULL;
bool truncation_applied = false; bool truncation_applied = false;
size_t width, height; size_t width, height;
const char *t; const char *t;
int r; int r;
bool soft = false;
t = table_data_format(table, d, false, available_width, &soft); t = table_data_format(table, d, false);
if (!t) if (!t)
return -ENOMEM; return -ENOMEM;
@ -1714,8 +1669,6 @@ static int table_data_requested_width_height(
*ret_width = width; *ret_width = width;
if (ret_height) if (ret_height)
*ret_height = height; *ret_height = height;
if (have_soft && soft)
*have_soft = true;
return truncation_applied; return truncation_applied;
} }
@ -1725,6 +1678,7 @@ static char *align_string_mem(const char *str, const char *url, size_t new_lengt
_cleanup_free_ char *clickable = NULL; _cleanup_free_ char *clickable = NULL;
const char *p; const char *p;
char *ret; char *ret;
size_t i;
int r; int r;
/* As with ellipsize_mem(), 'old_length' is a byte size while 'new_length' is a width in character cells */ /* As with ellipsize_mem(), 'old_length' is a byte size while 'new_length' is a width in character cells */
@ -1769,10 +1723,10 @@ static char *align_string_mem(const char *str, const char *url, size_t new_lengt
if (!ret) if (!ret)
return NULL; return NULL;
for (size_t i = 0; i < lspace; i++) for (i = 0; i < lspace; i++)
ret[i] = ' '; ret[i] = ' ';
memcpy(ret + lspace, clickable ?: str, clickable_length); memcpy(ret + lspace, clickable ?: str, clickable_length);
for (size_t i = lspace + clickable_length; i < space + clickable_length; i++) for (i = lspace + clickable_length; i < space + clickable_length; i++)
ret[i] = ' '; ret[i] = ' ';
ret[space + clickable_length] = 0; ret[space + clickable_length] = 0;
@ -1786,7 +1740,7 @@ static bool table_data_isempty(TableData *d) {
return true; return true;
/* Let's also consider an empty strv as truly empty. */ /* Let's also consider an empty strv as truly empty. */
if (IN_SET(d->type, TABLE_STRV, TABLE_STRV_WRAPPED)) if (d->type == TABLE_STRV)
return strv_isempty(d->strv); return strv_isempty(d->strv);
/* Note that an empty string we do not consider empty here! */ /* Note that an empty string we do not consider empty here! */
@ -1817,8 +1771,8 @@ static const char* table_data_rgap_color(TableData *d) {
int table_print(Table *t, FILE *f) { int table_print(Table *t, FILE *f) {
size_t n_rows, *minimum_width, *maximum_width, display_columns, *requested_width, size_t n_rows, *minimum_width, *maximum_width, display_columns, *requested_width,
table_minimum_width, table_maximum_width, table_requested_width, table_effective_width, i, j, table_minimum_width, table_maximum_width, table_requested_width, table_effective_width,
*width = NULL; *width;
_cleanup_free_ size_t *sorted = NULL; _cleanup_free_ size_t *sorted = NULL;
uint64_t *column_weight, weight_sum; uint64_t *column_weight, weight_sum;
int r; int r;
@ -1841,7 +1795,7 @@ int table_print(Table *t, FILE *f) {
if (!sorted) if (!sorted)
return -ENOMEM; return -ENOMEM;
for (size_t i = 0; i < n_rows; i++) for (i = 0; i < n_rows; i++)
sorted[i] = i * t->n_columns; sorted[i] = i * t->n_columns;
typesafe_qsort_r(sorted, n_rows, table_data_compare, t); typesafe_qsort_r(sorted, n_rows, table_data_compare, t);
@ -1857,37 +1811,30 @@ int table_print(Table *t, FILE *f) {
minimum_width = newa(size_t, display_columns); minimum_width = newa(size_t, display_columns);
maximum_width = newa(size_t, display_columns); maximum_width = newa(size_t, display_columns);
requested_width = newa(size_t, display_columns); requested_width = newa(size_t, display_columns);
width = newa(size_t, display_columns);
column_weight = newa0(uint64_t, display_columns); column_weight = newa0(uint64_t, display_columns);
for (size_t j = 0; j < display_columns; j++) { for (j = 0; j < display_columns; j++) {
minimum_width[j] = 1; minimum_width[j] = 1;
maximum_width[j] = (size_t) -1; maximum_width[j] = (size_t) -1;
requested_width[j] = (size_t) -1;
} }
for (unsigned pass = 0; pass < 2; pass++) {
/* First pass: determine column sizes */ /* First pass: determine column sizes */
for (i = t->header ? 0 : 1; i < n_rows; i++) {
for (size_t j = 0; j < display_columns; j++)
requested_width[j] = (size_t) -1;
bool any_soft = false;
for (size_t i = t->header ? 0 : 1; i < n_rows; i++) {
TableData **row; TableData **row;
/* Note that we don't care about ordering at this time, as we just want to determine column sizes, /* Note that we don't care about ordering at this time, as we just want to determine column sizes,
* hence we don't care for sorted[] during the first pass. */ * hence we don't care for sorted[] during the first pass. */
row = t->data + i * t->n_columns; row = t->data + i * t->n_columns;
for (size_t j = 0; j < display_columns; j++) { for (j = 0; j < display_columns; j++) {
TableData *d; TableData *d;
size_t req_width, req_height; size_t req_width, req_height;
assert_se(d = row[t->display_map ? t->display_map[j] : j]); assert_se(d = row[t->display_map ? t->display_map[j] : j]);
r = table_data_requested_width_height(t, d, r = table_data_requested_width_height(t, d, &req_width, &req_height);
width ? width[j] : SIZE_MAX,
&req_width, &req_height, &any_soft);
if (r < 0) if (r < 0)
return r; return r;
if (r > 0) { /* Truncated because too many lines? */ if (r > 0) { /* Truncated because too many lines? */
@ -1899,9 +1846,7 @@ int table_print(Table *t, FILE *f) {
* ellipsis. Hence, let's figure out the last line, and account for its * ellipsis. Hence, let's figure out the last line, and account for its
* length plus ellipsis. */ * length plus ellipsis. */
field = table_data_format(t, d, false, field = table_data_format(t, d, false);
width ? width[j] : SIZE_MAX,
&any_soft);
if (!field) if (!field)
return -ENOMEM; return -ENOMEM;
@ -1940,7 +1885,7 @@ int table_print(Table *t, FILE *f) {
/* Calculate the total weight for all columns, plus the minimum, maximum and requested width for the table. */ /* Calculate the total weight for all columns, plus the minimum, maximum and requested width for the table. */
weight_sum = 0; weight_sum = 0;
for (size_t j = 0; j < display_columns; j++) { for (j = 0; j < display_columns; j++) {
weight_sum += column_weight[j]; weight_sum += column_weight[j];
table_minimum_width += minimum_width[j]; table_minimum_width += minimum_width[j];
@ -1956,8 +1901,7 @@ int table_print(Table *t, FILE *f) {
/* Calculate effective table width */ /* Calculate effective table width */
if (t->width != 0 && t->width != (size_t) -1) if (t->width != 0 && t->width != (size_t) -1)
table_effective_width = t->width; table_effective_width = t->width;
else if (t->width == 0 || else if (t->width == 0 || pager_have() || !isatty(STDOUT_FILENO))
((pass > 0 || !any_soft) && (pager_have() || !isatty(STDOUT_FILENO))))
table_effective_width = table_requested_width; table_effective_width = table_requested_width;
else else
table_effective_width = MIN(table_requested_width, columns()); table_effective_width = MIN(table_requested_width, columns());
@ -1968,9 +1912,6 @@ int table_print(Table *t, FILE *f) {
if (table_effective_width < table_minimum_width) if (table_effective_width < table_minimum_width)
table_effective_width = table_minimum_width; table_effective_width = table_minimum_width;
if (!width)
width = newa(size_t, display_columns);
if (table_effective_width >= table_requested_width) { if (table_effective_width >= table_requested_width) {
size_t extra; size_t extra;
@ -1979,7 +1920,7 @@ int table_print(Table *t, FILE *f) {
extra = table_effective_width - table_requested_width; extra = table_effective_width - table_requested_width;
for (size_t j = 0; j < display_columns; j++) { for (j = 0; j < display_columns; j++) {
size_t delta; size_t delta;
if (weight_sum == 0) if (weight_sum == 0)
@ -2006,7 +1947,6 @@ int table_print(Table *t, FILE *f) {
weight_sum -= column_weight[j]; weight_sum -= column_weight[j];
} }
break; /* Every column should be happy, no need to repeat calculations. */
} else { } else {
/* We need to compress the table, columns can't get what they asked for. We first provide each column /* We need to compress the table, columns can't get what they asked for. We first provide each column
* with the minimum they need, and then distribute anything left. */ * with the minimum they need, and then distribute anything left. */
@ -2015,13 +1955,13 @@ int table_print(Table *t, FILE *f) {
extra = table_effective_width - table_minimum_width; extra = table_effective_width - table_minimum_width;
for (size_t j = 0; j < display_columns; j++) for (j = 0; j < display_columns; j++)
width[j] = (size_t) -1; width[j] = (size_t) -1;
for (;;) { for (;;) {
bool restart = false; bool restart = false;
for (size_t j = 0; j < display_columns; j++) { for (j = 0; j < display_columns; j++) {
size_t delta, w; size_t delta, w;
/* Did this column already get something assigned? If so, let's skip to the next */ /* Did this column already get something assigned? If so, let's skip to the next */
@ -2066,16 +2006,10 @@ int table_print(Table *t, FILE *f) {
if (!restart) if (!restart)
finalize = true; finalize = true;
} }
if (!any_soft) /* Some columns got less than requested. If some cells were "soft",
* let's try to reformat them with the new widths. Otherwise, let's
* move on. */
break;
}
} }
/* Second pass: show output */ /* Second pass: show output */
for (size_t i = t->header ? 0 : 1; i < n_rows; i++) { for (i = t->header ? 0 : 1; i < n_rows; i++) {
size_t n_subline = 0; size_t n_subline = 0;
bool more_sublines; bool more_sublines;
TableData **row; TableData **row;
@ -2089,7 +2023,7 @@ int table_print(Table *t, FILE *f) {
const char *gap_color = NULL; const char *gap_color = NULL;
more_sublines = false; more_sublines = false;
for (size_t j = 0; j < display_columns; j++) { for (j = 0; j < display_columns; j++) {
_cleanup_free_ char *buffer = NULL, *extracted = NULL; _cleanup_free_ char *buffer = NULL, *extracted = NULL;
bool lines_truncated = false; bool lines_truncated = false;
const char *field, *color = NULL; const char *field, *color = NULL;
@ -2098,7 +2032,7 @@ int table_print(Table *t, FILE *f) {
assert_se(d = row[t->display_map ? t->display_map[j] : j]); assert_se(d = row[t->display_map ? t->display_map[j] : j]);
field = table_data_format(t, d, false, width[j], NULL); field = table_data_format(t, d, false);
if (!field) if (!field)
return -ENOMEM; return -ENOMEM;
@ -2313,7 +2247,6 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
return json_variant_new_string(ret, d->string); return json_variant_new_string(ret, d->string);
case TABLE_STRV: case TABLE_STRV:
case TABLE_STRV_WRAPPED:
return json_variant_new_array_strv(ret, d->strv); return json_variant_new_array_strv(ret, d->strv);
case TABLE_BOOLEAN: case TABLE_BOOLEAN:
@ -2399,15 +2332,17 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
} }
static char* string_to_json_field_name(const char *f) { static char* string_to_json_field_name(const char *f) {
char *c, *x;
/* Tries to make a string more suitable as JSON field name. There are no strict rules defined what a /* Tries to make a string more suitable as JSON field name. There are no strict rules defined what a
* field name can be hence this is a bit vague and black magic. Right now we only convert spaces to * field name can be hence this is a bit vague and black magic. Right now we only convert spaces to
* underscores and leave everything as is. */ * underscores and leave everything as is. */
char *c = strdup(f); c = strdup(f);
if (!c) if (!c)
return NULL; return NULL;
for (char *x = c; *x; x++) for (x = c; *x; x++)
if (isspace(*x)) if (isspace(*x))
*x = '_'; *x = '_';
@ -2417,7 +2352,7 @@ static char* string_to_json_field_name(const char *f) {
int table_to_json(Table *t, JsonVariant **ret) { int table_to_json(Table *t, JsonVariant **ret) {
JsonVariant **rows = NULL, **elements = NULL; JsonVariant **rows = NULL, **elements = NULL;
_cleanup_free_ size_t *sorted = NULL; _cleanup_free_ size_t *sorted = NULL;
size_t n_rows, display_columns; size_t n_rows, i, j, display_columns;
int r; int r;
assert(t); assert(t);
@ -2437,7 +2372,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
goto finish; goto finish;
} }
for (size_t i = 0; i < n_rows; i++) for (i = 0; i < n_rows; i++)
sorted[i] = i * t->n_columns; sorted[i] = i * t->n_columns;
typesafe_qsort_r(sorted, n_rows, table_data_compare, t); typesafe_qsort_r(sorted, n_rows, table_data_compare, t);
@ -2455,7 +2390,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
goto finish; goto finish;
} }
for (size_t j = 0; j < display_columns; j++) { for (j = 0; j < display_columns; j++) {
_cleanup_free_ char *mangled = NULL; _cleanup_free_ char *mangled = NULL;
const char *formatted; const char *formatted;
TableData *d; TableData *d;
@ -2463,7 +2398,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
assert_se(d = t->data[t->display_map ? t->display_map[j] : j]); assert_se(d = t->data[t->display_map ? t->display_map[j] : j]);
/* Field names must be strings, hence format whatever we got here as a string first */ /* Field names must be strings, hence format whatever we got here as a string first */
formatted = table_data_format(t, d, true, SIZE_MAX, NULL); formatted = table_data_format(t, d, true);
if (!formatted) { if (!formatted) {
r = -ENOMEM; r = -ENOMEM;
goto finish; goto finish;
@ -2487,7 +2422,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
goto finish; goto finish;
} }
for (size_t i = 1; i < n_rows; i++) { for (i = 1; i < n_rows; i++) {
TableData **row; TableData **row;
if (sorted) if (sorted)
@ -2495,7 +2430,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
else else
row = t->data + i * t->n_columns; row = t->data + i * t->n_columns;
for (size_t j = 0; j < display_columns; j++) { for (j = 0; j < display_columns; j++) {
TableData *d; TableData *d;
size_t k; size_t k;

View File

@ -12,7 +12,6 @@ typedef enum TableDataType {
TABLE_EMPTY, TABLE_EMPTY,
TABLE_STRING, TABLE_STRING,
TABLE_STRV, TABLE_STRV,
TABLE_STRV_WRAPPED,
TABLE_PATH, TABLE_PATH,
TABLE_BOOLEAN, TABLE_BOOLEAN,
TABLE_TIMESTAMP, TABLE_TIMESTAMP,

View File

@ -301,43 +301,3 @@ int enter_cgroup_subroot(char **ret_cgroup) {
int enter_cgroup_root(char **ret_cgroup) { int enter_cgroup_root(char **ret_cgroup) {
return enter_cgroup(ret_cgroup, false); return enter_cgroup(ret_cgroup, false);
} }
const char *ci_environment(void) {
/* We return a string because we might want to provide multiple bits of information later on: not
* just the general CI environment type, but also whether we're sanitizing or not, etc. The caller is
* expected to use strstr on the returned value. */
static const char *ans = POINTER_MAX;
const char *p;
int r;
if (ans != POINTER_MAX)
return ans;
/* We allow specifying the environment with $CITYPE. Nobody uses this so far, but we are ready. */
p = getenv("CITYPE");
if (!isempty(p))
return (ans = p);
if (getenv_bool("TRAVIS") > 0)
return (ans = "travis");
if (getenv_bool("SEMAPHORE") > 0)
return (ans = "semaphore");
if (getenv_bool("GITHUB_ACTIONS") > 0)
return (ans = "github-actions");
if (getenv("AUTOPKGTEST_ARTIFACTS") || getenv("AUTOPKGTEST_TMP"))
return (ans = "autopkgtest");
FOREACH_STRING(p, "CI", "CONTINOUS_INTEGRATION") {
/* Those vars are booleans according to Semaphore and Travis docs:
* https://docs.travis-ci.com/user/environment-variables/#default-environment-variables
* https://docs.semaphoreci.com/ci-cd-environment/environment-variables/#ci
*/
r = getenv_bool(p);
if (r > 0)
return (ans = "unknown"); /* Some other unknown thing */
if (r == 0)
return (ans = NULL);
}
return (ans = NULL);
}

View File

@ -40,6 +40,3 @@ bool can_memlock(void);
} else { \ } else { \
printf("systemd not booted skipping '%s'\n", #x); \ printf("systemd not booted skipping '%s'\n", #x); \
} }
/* Provide a convenient way to check if we're running in CI. */
const char *ci_environment(void);

View File

@ -2,8 +2,6 @@
#include "bus-error.h" #include "bus-error.h"
#include "copy.h" #include "copy.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h" #include "fs-util.h"
#include "mkdir.h" #include "mkdir.h"
#include "pager.h" #include "pager.h"
@ -19,9 +17,6 @@
#include "terminal-util.h" #include "terminal-util.h"
#include "tmpfile-util.h" #include "tmpfile-util.h"
#define EDIT_MARKER_START "### Anything between here and the comment below will become the new contents of the file"
#define EDIT_MARKER_END "### Lines below this comment will be discarded"
int cat(int argc, char *argv[], void *userdata) { int cat(int argc, char *argv[], void *userdata) {
_cleanup_(hashmap_freep) Hashmap *cached_name_map = NULL, *cached_id_map = NULL; _cleanup_(hashmap_freep) Hashmap *cached_name_map = NULL, *cached_id_map = NULL;
_cleanup_(lookup_paths_free) LookupPaths lp = {}; _cleanup_(lookup_paths_free) LookupPaths lp = {};
@ -111,11 +106,12 @@ int cat(int argc, char *argv[], void *userdata) {
return rc; return rc;
} }
static int create_edit_temp_file(const char *new_path, const char *original_path, char ** const original_unit_paths, char **ret_tmp_fn) { static int create_edit_temp_file(const char *new_path, const char *original_path, char **ret_tmp_fn) {
_cleanup_free_ char *t = NULL; _cleanup_free_ char *t = NULL;
int r; int r;
assert(new_path); assert(new_path);
assert(original_path);
assert(ret_tmp_fn); assert(ret_tmp_fn);
r = tempfn_random(new_path, NULL, &t); r = tempfn_random(new_path, NULL, &t);
@ -126,79 +122,26 @@ static int create_edit_temp_file(const char *new_path, const char *original_path
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to create directories for \"%s\": %m", new_path); return log_error_errno(r, "Failed to create directories for \"%s\": %m", new_path);
if (original_path) { r = mac_selinux_create_file_prepare(original_path, S_IFREG);
r = mac_selinux_create_file_prepare(new_path, S_IFREG);
if (r < 0) if (r < 0)
return r; return r;
r = copy_file(original_path, t, 0, 0644, 0, 0, COPY_REFLINK); r = copy_file(original_path, t, 0, 0644, 0, 0, COPY_REFLINK);
if (r == -ENOENT) { if (r == -ENOENT) {
r = touch(t); r = touch(t);
mac_selinux_create_file_clear(); mac_selinux_create_file_clear();
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to create temporary file \"%s\": %m", t); return log_error_errno(r, "Failed to create temporary file \"%s\": %m", t);
} else { } else {
mac_selinux_create_file_clear(); mac_selinux_create_file_clear();
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to create temporary file for \"%s\": %m", new_path); return log_error_errno(r, "Failed to create temporary file for \"%s\": %m", new_path);
} }
} else if (original_unit_paths) {
_cleanup_free_ char *new_contents = NULL;
_cleanup_fclose_ FILE *f = NULL;
char **path;
size_t size;
r = mac_selinux_create_file_prepare(new_path, S_IFREG);
if (r < 0)
return r;
f = fopen(t, "we");
mac_selinux_create_file_clear();
if (!f)
return log_error_errno(errno, "Failed to open \"%s\": %m", t);
r = fchmod(fileno(f), 0644);
if (r < 0)
return log_error_errno(errno, "Failed to change mode of \"%s\": %m", t);
r = read_full_file(new_path, &new_contents, &size);
if (r < 0 && r != -ENOENT)
return log_error_errno(r, "Failed to read \"%s\": %m", new_path);
fprintf(f,
"### Editing %s\n"
EDIT_MARKER_START
"\n\n%s%s\n"
EDIT_MARKER_END,
new_path,
strempty(new_contents),
new_contents && endswith(new_contents, "\n") ? "" : "\n");
/* Add a comment with the contents of the original unit files */
STRV_FOREACH(path, original_unit_paths) {
_cleanup_free_ char *contents = NULL;
/* Skip the file that's being edited */
if (path_equal(*path, new_path))
continue;
r = read_full_file(*path, &contents, &size);
if (r < 0)
return log_error_errno(r, "Failed to read \"%s\": %m", *path);
fprintf(f, "\n\n### %s", *path);
if (!isempty(contents)) {
contents = strreplace(strstrip(contents), "\n", "\n# ");
if (!contents)
return log_oom();
fprintf(f, "\n# %s", contents);
}
}
r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to create temporary file \"%s\": %m", t);
}
*ret_tmp_fn = TAKE_PTR(t); *ret_tmp_fn = TAKE_PTR(t);
@ -242,7 +185,6 @@ static int unit_file_create_new(
const LookupPaths *paths, const LookupPaths *paths,
const char *unit_name, const char *unit_name,
const char *suffix, const char *suffix,
char ** const original_unit_paths,
char **ret_new_path, char **ret_new_path,
char **ret_tmp_path) { char **ret_tmp_path) {
@ -259,7 +201,7 @@ static int unit_file_create_new(
if (r < 0) if (r < 0)
return r; return r;
r = create_edit_temp_file(new_path, NULL, original_unit_paths, &tmp_path); r = create_edit_temp_file(new_path, new_path, &tmp_path);
if (r < 0) if (r < 0)
return r; return r;
@ -298,7 +240,7 @@ static int unit_file_create_copy(
return log_warning_errno(SYNTHETIC_ERRNO(EKEYREJECTED), "%s skipped.", unit_name); return log_warning_errno(SYNTHETIC_ERRNO(EKEYREJECTED), "%s skipped.", unit_name);
} }
r = create_edit_temp_file(new_path, fragment_path, NULL, &tmp_path); r = create_edit_temp_file(new_path, fragment_path, &tmp_path);
if (r < 0) if (r < 0)
return r; return r;
@ -390,10 +332,9 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
STRV_FOREACH(name, names) { STRV_FOREACH(name, names) {
_cleanup_free_ char *path = NULL, *new_path = NULL, *tmp_path = NULL, *tmp_name = NULL; _cleanup_free_ char *path = NULL, *new_path = NULL, *tmp_path = NULL, *tmp_name = NULL;
_cleanup_strv_free_ char **unit_paths = NULL;
const char *unit_name; const char *unit_name;
r = unit_find_paths(bus, *name, &lp, false, &cached_name_map, &cached_id_map, &path, &unit_paths); r = unit_find_paths(bus, *name, &lp, false, &cached_name_map, &cached_id_map, &path, NULL);
if (r == -EKEYREJECTED) { if (r == -EKEYREJECTED) {
/* If loading of the unit failed server side complete, then the server won't tell us /* If loading of the unit failed server side complete, then the server won't tell us
* the unit file path. In that case, find the file client side. */ * the unit file path. In that case, find the file client side. */
@ -420,7 +361,7 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
unit_name = *name; unit_name = *name;
r = unit_file_create_new(&lp, unit_name, r = unit_file_create_new(&lp, unit_name,
arg_full ? NULL : ".d/override.conf", arg_full ? NULL : ".d/override.conf",
NULL, &new_path, &tmp_path); &new_path, &tmp_path);
} else { } else {
assert(path); assert(path);
@ -443,13 +384,8 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
if (arg_full) if (arg_full)
r = unit_file_create_copy(&lp, unit_name, path, &new_path, &tmp_path); r = unit_file_create_copy(&lp, unit_name, path, &new_path, &tmp_path);
else { else
r = strv_prepend(&unit_paths, path); r = unit_file_create_new(&lp, unit_name, ".d/override.conf", &new_path, &tmp_path);
if (r < 0)
return log_oom();
r = unit_file_create_new(&lp, unit_name, ".d/override.conf", unit_paths, &new_path, &tmp_path);
}
} }
if (r < 0) if (r < 0)
return r; return r;
@ -464,40 +400,6 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
return 0; return 0;
} }
static int trim_edit_markers(const char *path) {
_cleanup_free_ char *contents = NULL;
char *contents_start = NULL;
const char *contents_end = NULL;
size_t size;
int r;
/* Trim out the lines between the two markers */
r = read_full_file(path, &contents, &size);
if (r < 0)
return log_error_errno(r, "Failed to read temporary file \"%s\": %m", path);
contents_start = strstr(contents, EDIT_MARKER_START);
if (contents_start)
contents_start += strlen(EDIT_MARKER_START);
else
contents_start = contents;
contents_end = strstr(contents_start, EDIT_MARKER_END);
if (contents_end)
strshorten(contents_start, contents_end - contents_start);
contents_start = strstrip(contents_start);
/* Write new contents if the trimming actually changed anything */
if (strlen(contents) != size) {
r = write_string_file(path, contents_start, WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_TRUNCATE | WRITE_STRING_FILE_AVOID_NEWLINE);
if (r < 0)
return log_error_errno(r, "Failed to modify temporary file \"%s\": %m", path);
}
return 0;
}
int edit(int argc, char *argv[], void *userdata) { int edit(int argc, char *argv[], void *userdata) {
_cleanup_(lookup_paths_free) LookupPaths lp = {}; _cleanup_(lookup_paths_free) LookupPaths lp = {};
_cleanup_strv_free_ char **names = NULL; _cleanup_strv_free_ char **names = NULL;
@ -550,10 +452,6 @@ int edit(int argc, char *argv[], void *userdata) {
STRV_FOREACH_PAIR(original, tmp, paths) { STRV_FOREACH_PAIR(original, tmp, paths) {
/* If the temporary file is empty we ignore it. This allows the user to cancel the /* If the temporary file is empty we ignore it. This allows the user to cancel the
* modification. */ * modification. */
r = trim_edit_markers(*tmp);
if (r < 0)
continue;
if (null_or_empty_path(*tmp)) { if (null_or_empty_path(*tmp)) {
log_warning("Editing \"%s\" canceled: temporary file is empty.", *original); log_warning("Editing \"%s\" canceled: temporary file is empty.", *original);
continue; continue;

View File

@ -264,7 +264,6 @@ static void test_env_clean(void) {
"xyz=xyz\n", "xyz=xyz\n",
"another=one", "another=one",
"another=final one", "another=final one",
"CRLF=\r\n",
"BASH_FUNC_foo%%=() { echo foo\n}"); "BASH_FUNC_foo%%=() { echo foo\n}");
assert_se(e); assert_se(e);
assert_se(!strv_env_is_valid(e)); assert_se(!strv_env_is_valid(e));
@ -307,8 +306,6 @@ static void test_env_value_is_valid(void) {
assert_se(env_value_is_valid("printf \"\\x1b]0;<mock-chroot>\\x07<mock-chroot>\"")); assert_se(env_value_is_valid("printf \"\\x1b]0;<mock-chroot>\\x07<mock-chroot>\""));
assert_se(env_value_is_valid("tab\tcharacter")); assert_se(env_value_is_valid("tab\tcharacter"));
assert_se(env_value_is_valid("new\nline")); assert_se(env_value_is_valid("new\nline"));
assert_se(!env_value_is_valid("Show this?\rNope. Show that!"));
assert_se(!env_value_is_valid("new DOS\r\nline"));
} }
static void test_env_assignment_is_valid(void) { static void test_env_assignment_is_valid(void) {

View File

@ -36,6 +36,11 @@ static int cld_dumped_to_killed(int code) {
return code == CLD_DUMPED ? CLD_KILLED : code; return code == CLD_DUMPED ? CLD_KILLED : code;
} }
_unused_ static bool is_run_on_travis_ci(void) {
/* https://docs.travis-ci.com/user/environment-variables#default-environment-variables */
return streq_ptr(getenv("TRAVIS"), "true");
}
static void wait_for_service_finish(Manager *m, Unit *unit) { static void wait_for_service_finish(Manager *m, Unit *unit) {
Service *service = NULL; Service *service = NULL;
usec_t ts; usec_t ts;
@ -892,7 +897,7 @@ int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG); test_setup_logging(LOG_DEBUG);
#if HAS_FEATURE_ADDRESS_SANITIZER #if HAS_FEATURE_ADDRESS_SANITIZER
if (strstr_ptr(ci_environment(), "travis")) { if (is_run_on_travis_ci()) {
log_notice("Running on TravisCI under ASan, skipping, see https://github.com/systemd/systemd/issues/10696"); log_notice("Running on TravisCI under ASan, skipping, see https://github.com/systemd/systemd/issues/10696");
return EXIT_TEST_SKIP; return EXIT_TEST_SKIP;
} }

View File

@ -12,8 +12,6 @@ static void test_issue_9549(void) {
_cleanup_(table_unrefp) Table *table = NULL; _cleanup_(table_unrefp) Table *table = NULL;
_cleanup_free_ char *formatted = NULL; _cleanup_free_ char *formatted = NULL;
log_info("/* %s */", __func__);
assert_se(table = table_new("name", "type", "ro", "usage", "created", "modified")); assert_se(table = table_new("name", "type", "ro", "usage", "created", "modified"));
assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(3), 100) >= 0); assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(3), 100) >= 0);
assert_se(table_add_many(table, assert_se(table_add_many(table,
@ -38,8 +36,6 @@ static void test_multiline(void) {
_cleanup_(table_unrefp) Table *table = NULL; _cleanup_(table_unrefp) Table *table = NULL;
_cleanup_free_ char *formatted = NULL; _cleanup_free_ char *formatted = NULL;
log_info("/* %s */", __func__);
assert_se(table = table_new("foo", "bar")); assert_se(table = table_new("foo", "bar"));
assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(1), 100) >= 0); assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(1), 100) >= 0);
@ -152,8 +148,6 @@ static void test_strv(void) {
_cleanup_(table_unrefp) Table *table = NULL; _cleanup_(table_unrefp) Table *table = NULL;
_cleanup_free_ char *formatted = NULL; _cleanup_free_ char *formatted = NULL;
log_info("/* %s */", __func__);
assert_se(table = table_new("foo", "bar")); assert_se(table = table_new("foo", "bar"));
assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(1), 100) >= 0); assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(1), 100) >= 0);
@ -262,111 +256,8 @@ static void test_strv(void) {
formatted = mfree(formatted); formatted = mfree(formatted);
} }
static void test_strv_wrapped(void) {
_cleanup_(table_unrefp) Table *table = NULL;
_cleanup_free_ char *formatted = NULL;
log_info("/* %s */", __func__);
assert_se(table = table_new("foo", "bar"));
assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(1), 100) >= 0);
assert_se(table_add_many(table,
TABLE_STRV_WRAPPED, STRV_MAKE("three", "different", "lines"),
TABLE_STRV_WRAPPED, STRV_MAKE("two", "lines")) >= 0);
table_set_cell_height_max(table, 1);
assert_se(table_format(table, &formatted) >= 0);
fputs(formatted, stdout);
assert_se(streq(formatted,
"FOO BAR\n"
"three different lines two lines\n"));
formatted = mfree(formatted);
table_set_cell_height_max(table, 2);
assert_se(table_format(table, &formatted) >= 0);
fputs(formatted, stdout);
assert_se(streq(formatted,
"FOO BAR\n"
"three different lines two lines\n"));
formatted = mfree(formatted);
table_set_cell_height_max(table, 3);
assert_se(table_format(table, &formatted) >= 0);
fputs(formatted, stdout);
assert_se(streq(formatted,
"FOO BAR\n"
"three different lines two lines\n"));
formatted = mfree(formatted);
table_set_cell_height_max(table, (size_t) -1);
assert_se(table_format(table, &formatted) >= 0);
fputs(formatted, stdout);
assert_se(streq(formatted,
"FOO BAR\n"
"three different lines two lines\n"));
formatted = mfree(formatted);
assert_se(table_add_many(table,
TABLE_STRING, "short",
TABLE_STRV_WRAPPED, STRV_MAKE("a", "pair")) >= 0);
assert_se(table_add_many(table,
TABLE_STRV_WRAPPED, STRV_MAKE("short2"),
TABLE_STRV_WRAPPED, STRV_MAKE("a", "eight", "line", "ćęłł",
"___5___", "___6___", "___7___", "___8___")) >= 0);
table_set_cell_height_max(table, 1);
assert_se(table_format(table, &formatted) >= 0);
fputs(formatted, stdout);
assert_se(streq(formatted,
"FOO BAR\n"
"three different… two lines\n"
"short a pair\n"
"short2 a eight line ćęłł…\n"));
formatted = mfree(formatted);
table_set_cell_height_max(table, 2);
assert_se(table_format(table, &formatted) >= 0);
fputs(formatted, stdout);
assert_se(streq(formatted,
"FOO BAR\n"
"three different two lines\n"
"lines \n"
"short a pair\n"
"short2 a eight line ćęłł\n"
" ___5___ ___6___…\n"));
formatted = mfree(formatted);
table_set_cell_height_max(table, 3);
assert_se(table_format(table, &formatted) >= 0);
fputs(formatted, stdout);
assert_se(streq(formatted,
"FOO BAR\n"
"three different two lines\n"
"lines \n"
"short a pair\n"
"short2 a eight line ćęłł\n"
" ___5___ ___6___\n"
" ___7___ ___8___\n"));
formatted = mfree(formatted);
table_set_cell_height_max(table, (size_t) -1);
assert_se(table_format(table, &formatted) >= 0);
fputs(formatted, stdout);
assert_se(streq(formatted,
"FOO BAR\n"
"three different two lines\n"
"lines \n"
"short a pair\n"
"short2 a eight line ćęłł\n"
" ___5___ ___6___\n"
" ___7___ ___8___\n"));
formatted = mfree(formatted);
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
_cleanup_(table_unrefp) Table *t = NULL; _cleanup_(table_unrefp) Table *t = NULL;
_cleanup_free_ char *formatted = NULL; _cleanup_free_ char *formatted = NULL;
@ -508,7 +399,6 @@ int main(int argc, char *argv[]) {
test_issue_9549(); test_issue_9549();
test_multiline(); test_multiline();
test_strv(); test_strv();
test_strv_wrapped();
return 0; return 0;
} }

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */ /* SPDX-License-Identifier: LGPL-2.1+ */
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
@ -77,52 +78,32 @@ static Service *service_for_path(Manager *m, Path *path, const char *service_nam
return SERVICE(service_unit); return SERVICE(service_unit);
} }
static int _check_states(unsigned line, static void check_states(Manager *m, Path *path, Service *service, PathState path_state, ServiceState service_state) {
Manager *m, Path *path, Service *service, PathState path_state, ServiceState service_state) {
assert_se(m); assert_se(m);
assert_se(service); assert_se(service);
usec_t end = now(CLOCK_MONOTONIC) + 30 * USEC_PER_SEC; usec_t end = now(CLOCK_MONOTONIC) + 30 * USEC_PER_SEC;
while (path->state != path_state || service->state != service_state || while (path->result != PATH_SUCCESS || service->result != SERVICE_SUCCESS ||
path->result != PATH_SUCCESS || service->result != SERVICE_SUCCESS) { path->state != path_state || service->state != service_state) {
assert_se(sd_event_run(m->event, 100 * USEC_PER_MSEC) >= 0); assert_se(sd_event_run(m->event, 100 * USEC_PER_MSEC) >= 0);
usec_t n = now(CLOCK_MONOTONIC); printf("%s: state = %s; result = %s \n",
log_info("line %u: %s: state = %s; result = %s (left: %" PRIi64 ")",
line,
UNIT(path)->id, UNIT(path)->id,
path_state_to_string(path->state), path_state_to_string(path->state),
path_result_to_string(path->result), path_result_to_string(path->result));
end - n); printf("%s: state = %s; result = %s \n",
log_info("line %u: %s: state = %s; result = %s",
line,
UNIT(service)->id, UNIT(service)->id,
service_state_to_string(service->state), service_state_to_string(service->state),
service_result_to_string(service->result)); service_result_to_string(service->result));
if (service->state == SERVICE_FAILED && if (now(CLOCK_MONOTONIC) >= end) {
service->main_exec_status.status == EXIT_CGROUP &&
!ci_environment())
/* On a general purpose system we may fail to start the service for reasons which are
* not under our control: permission limits, resource exhaustion, etc. Let's skip the
* test in those cases. On developer machines we require proper setup. */
return log_notice_errno(SYNTHETIC_ERRNO(ECANCELED),
"Failed to start service %s, aborting test: %s/%s",
UNIT(service)->id,
service_state_to_string(service->state),
service_result_to_string(service->result));
if (n >= end) {
log_error("Test timeout when testing %s", UNIT(path)->id); log_error("Test timeout when testing %s", UNIT(path)->id);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
return 0;
} }
#define check_states(...) _check_states(__LINE__, __VA_ARGS__)
static void test_path_exists(Manager *m) { static void test_path_exists(Manager *m) {
const char *test_path = "/tmp/test-path_exists"; const char *test_path = "/tmp/test-path_exists";
@ -138,22 +119,18 @@ static void test_path_exists(Manager *m) {
service = service_for_path(m, path, NULL); service = service_for_path(m, path, NULL);
assert_se(unit_start(unit) >= 0); assert_se(unit_start(unit) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
return;
assert_se(touch(test_path) >= 0); assert_se(touch(test_path) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
return;
/* Service restarts if file still exists */ /* Service restarts if file still exists */
assert_se(unit_stop(UNIT(service)) >= 0); assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
return;
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
assert_se(unit_stop(UNIT(service)) >= 0); assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
return;
assert_se(unit_stop(unit) >= 0); assert_se(unit_stop(unit) >= 0);
} }
@ -172,22 +149,18 @@ static void test_path_existsglob(Manager *m) {
service = service_for_path(m, path, NULL); service = service_for_path(m, path, NULL);
assert_se(unit_start(unit) >= 0); assert_se(unit_start(unit) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
return;
assert_se(touch(test_path) >= 0); assert_se(touch(test_path) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
return;
/* Service restarts if file still exists */ /* Service restarts if file still exists */
assert_se(unit_stop(UNIT(service)) >= 0); assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
return;
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
assert_se(unit_stop(UNIT(service)) >= 0); assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
return;
assert_se(unit_stop(unit) >= 0); assert_se(unit_stop(unit) >= 0);
} }
@ -207,28 +180,23 @@ static void test_path_changed(Manager *m) {
service = service_for_path(m, path, NULL); service = service_for_path(m, path, NULL);
assert_se(unit_start(unit) >= 0); assert_se(unit_start(unit) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
return;
assert_se(touch(test_path) >= 0); assert_se(touch(test_path) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
return;
/* Service does not restart if file still exists */ /* Service does not restart if file still exists */
assert_se(unit_stop(UNIT(service)) >= 0); assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
return;
f = fopen(test_path, "w"); f = fopen(test_path, "w");
assert_se(f); assert_se(f);
fclose(f); fclose(f);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
return;
assert_se(unit_stop(UNIT(service)) >= 0); assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
return;
(void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL); (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
assert_se(unit_stop(unit) >= 0); assert_se(unit_stop(unit) >= 0);
@ -249,28 +217,23 @@ static void test_path_modified(Manager *m) {
service = service_for_path(m, path, NULL); service = service_for_path(m, path, NULL);
assert_se(unit_start(unit) >= 0); assert_se(unit_start(unit) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
return;
assert_se(touch(test_path) >= 0); assert_se(touch(test_path) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
return;
/* Service does not restart if file still exists */ /* Service does not restart if file still exists */
assert_se(unit_stop(UNIT(service)) >= 0); assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
return;
f = fopen(test_path, "w"); f = fopen(test_path, "w");
assert_se(f); assert_se(f);
fputs("test", f); fputs("test", f);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
return;
assert_se(unit_stop(UNIT(service)) >= 0); assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
return;
(void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL); (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
assert_se(unit_stop(unit) >= 0); assert_se(unit_stop(unit) >= 0);
@ -290,17 +253,14 @@ static void test_path_unit(Manager *m) {
service = service_for_path(m, path, "path-mycustomunit.service"); service = service_for_path(m, path, "path-mycustomunit.service");
assert_se(unit_start(unit) >= 0); assert_se(unit_start(unit) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
return;
assert_se(touch(test_path) >= 0); assert_se(touch(test_path) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
return;
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
assert_se(unit_stop(UNIT(service)) >= 0); assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
return;
assert_se(unit_stop(unit) >= 0); assert_se(unit_stop(unit) >= 0);
} }
@ -321,26 +281,22 @@ static void test_path_directorynotempty(Manager *m) {
assert_se(access(test_path, F_OK) < 0); assert_se(access(test_path, F_OK) < 0);
assert_se(unit_start(unit) >= 0); assert_se(unit_start(unit) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
return;
/* MakeDirectory default to no */ /* MakeDirectory default to no */
assert_se(access(test_path, F_OK) < 0); assert_se(access(test_path, F_OK) < 0);
assert_se(mkdir_p(test_path, 0755) >= 0); assert_se(mkdir_p(test_path, 0755) >= 0);
assert_se(touch(strjoina(test_path, "test_file")) >= 0); assert_se(touch(strjoina(test_path, "test_file")) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
return;
/* Service restarts if directory is still not empty */ /* Service restarts if directory is still not empty */
assert_se(unit_stop(UNIT(service)) >= 0); assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
return;
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
assert_se(unit_stop(UNIT(service)) >= 0); assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
return;
assert_se(unit_stop(unit) >= 0); assert_se(unit_stop(unit) >= 0);
} }

View File

@ -604,7 +604,7 @@ int xdg_autostart_service_generate_unit(
fprintf(f, fprintf(f,
"\n[Service]\n" "\n[Service]\n"
"Type=exec\n" "Type=simple\n"
"ExecStart=:%s\n" "ExecStart=:%s\n"
"Restart=no\n" "Restart=no\n"
"TimeoutSec=5s\n" "TimeoutSec=5s\n"

View File

@ -2,6 +2,6 @@
Description=Service Test for Path units Description=Service Test for Path units
[Service] [Service]
ExecStart=sleep infinity ExecStart=/bin/true
Type=exec Type=simple
RemainAfterExit=true RemainAfterExit=true

View File

@ -2,6 +2,6 @@
Description=Service Test for Path units Description=Service Test for Path units
[Service] [Service]
ExecStart=sleep infinity ExecStart=/bin/true
Type=exec Type=simple
RemainAfterExit=true RemainAfterExit=true

View File

@ -2,6 +2,6 @@
Description=Service Test for Path units Description=Service Test for Path units
[Service] [Service]
ExecStart=sleep infinity ExecStart=/bin/true
Type=exec Type=simple
RemainAfterExit=true RemainAfterExit=true

View File

@ -2,6 +2,6 @@
Description=Service Test for Path units Description=Service Test for Path units
[Service] [Service]
ExecStart=sleep infinity ExecStart=/bin/true
Type=exec Type=simple
RemainAfterExit=true RemainAfterExit=true

View File

@ -2,6 +2,6 @@
Description=Service Test for Path units Description=Service Test for Path units
[Service] [Service]
ExecStart=sleep infinity ExecStart=/bin/true
Type=exec Type=simple
RemainAfterExit=true RemainAfterExit=true

View File

@ -2,6 +2,6 @@
Description=Service Test for Path units Description=Service Test for Path units
[Service] [Service]
ExecStart=sleep infinity ExecStart=/bin/true
Type=exec Type=simple
RemainAfterExit=true RemainAfterExit=true

View File

@ -2,6 +2,6 @@
Description=Service Test Path Unit Description=Service Test Path Unit
[Service] [Service]
ExecStart=sleep infinity ExecStart=/bin/true
Type=exec Type=simple
RemainAfterExit=true RemainAfterExit=true

View File

@ -0,0 +1,6 @@
[Unit]
Description=Service Test for Path units
[Service]
ExecStart=/bin/true
Type=oneshot

View File

@ -2,5 +2,5 @@
Description=ForeverPrintHola service Description=ForeverPrintHola service
[Service] [Service]
Type=exec Type=simple
ExecStart=sh -x -c 'while :; do printf "Hola\n" || touch /i-lose-my-logs; sleep 1; done' ExecStart=sh -x -c 'while :; do printf "Hola\n" || touch /i-lose-my-logs; sleep 1; done'

View File

@ -4,6 +4,6 @@ StartLimitIntervalSec=1m
StartLimitBurst=3 StartLimitBurst=3
[Service] [Service]
Type=exec Type=simple
ExecStart=false ExecStart=false
Restart=always Restart=always

View File

@ -13,7 +13,7 @@ StopWhenUnneeded=yes
[Service] [Service]
ExecStartPre=rm -f /failed /testok ExecStartPre=rm -f /failed /testok
Type=exec Type=simple
TimeoutStartSec=infinity TimeoutStartSec=infinity
ExecStartPre=/usr/lib/systemd/tests/testdata/units/%N.sh ExecStartPre=/usr/lib/systemd/tests/testdata/units/%N.sh
ExecStart=true ExecStart=true

View File

@ -11,13 +11,8 @@
# control writes into pstore. # control writes into pstore.
# #
# The crash_kexec_post_notifiers parameter enables the kernel to write # The crash_kexec_post_notifiers parameter enables the kernel to write
# dmesg (including stack trace) into pstore upon a panic even if kdump # dmesg (including stack trace) into pstore upon a panic, and
# is loaded, only needed if you want to use pstore with kdump. Without # printk.always_kmsg_dump parameter enables the kernel to write dmesg
# this parameter, kdump could block writing to pstore for stability
# reason. Note this increases the risk of kdump failure even if pstore
# is not available.
#
# The printk.always_kmsg_dump parameter enables the kernel to write dmesg
# upon a normal shutdown (shutdown, reboot, halt). # upon a normal shutdown (shutdown, reboot, halt).
# #
# To configure the kernel parameters, uncomment the appropriate # To configure the kernel parameters, uncomment the appropriate
@ -31,4 +26,4 @@
d /var/lib/systemd/pstore 0755 root root 14d d /var/lib/systemd/pstore 0755 root root 14d
#w- /sys/module/printk/parameters/always_kmsg_dump - - - - Y #w- /sys/module/printk/parameters/always_kmsg_dump - - - - Y
#w- /sys/module/kernel/parameters/crash_kexec_post_notifiers - - - - Y w- /sys/module/kernel/parameters/crash_kexec_post_notifiers - - - - Y