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:
build:
runs-on: ubuntu-20.04
runs-on: ubuntu-18.04
strategy:
fail-fast: false
matrix:
env:
- { COMPILER: "gcc", COMPILER_VERSION: "10" }
- { COMPILER: "clang", COMPILER_VERSION: "10" }
- { COMPILER: "clang", COMPILER_VERSION: "11" }
env: ${{ matrix.env }}
steps:
- name: Repository checkout

View File

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

3
TODO
View File

@ -813,6 +813,9 @@ Features:
* 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
* 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
<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
subtree where validation shall be disabled. For example:</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>
subtree where validation shall be disabled.</para>
<para>Negative trust anchors are useful to support private DNS
subtrees that are not referenced from the Internet DNS hierarchy,

View File

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

View File

@ -156,6 +156,12 @@ node /org/freedesktop/resolve1 {
};
</programlisting>
<!--method SetLinkDNSEx is not documented!-->
<!--method SetLinkDefaultRoute is not documented!-->
<!--method SetLinkDNSOverTLS is not documented!-->
<!--method RegisterService is not documented!-->
<!--method UnregisterService is not documented!-->
@ -164,8 +170,28 @@ node /org/freedesktop/resolve1 {
<!--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 DNSStubListener is not documented!-->
<!--Autogenerated cross-references for systemd.directives, do not edit-->
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.resolve1.Manager"/>
@ -255,28 +281,26 @@ node /org/freedesktop/resolve1 {
<refsect2>
<title>Methods</title>
<para><function>ResolveHostname()</function> takes a hostname and resolves it to one or more IP
addresses. As parameters it takes the Linux network interface index to execute the query on, or 0 if
it may be done on any suitable interface. The <varname>name</varname> parameter specifies the hostname
to 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 limits the results to a specific address
family. It may be <constant>AF_INET</constant>, <constant>AF_INET6</constant> or
<constant>AF_UNSPEC</constant>. If <constant>AF_UNSPEC</constant> is specified (recommended), both
kinds are retrieved, subject to local network configuration (i.e. if no local, routable IPv6 address is
found, no IPv6 address is retrieved; and similarly for IPv4). A 64-bit <varname>flags</varname> field
may be used to alter the behaviour of the resolver operation (see below). The method returns an array
of address records. Each address record consists of the interface index the address belongs to, an
address family as well as a byte array with the actual IP address data (which either has 4 or 16
elements, depending on the address family). The returned address family will be one of
<constant>AF_INET</constant> or <constant>AF_INET6</constant>. For IPv6, the returned address interface
index should be used to initialize the .sin6_scope_id field of a
<structname>struct sockaddr_in6</structname> instance to permit support for resolution to link-local IP
addresses. The address array is followed by the canonical name of the host, which may or may not be
identical to the resolved hostname. Finally, a 64-bit <varname>flags</varname> field is returned that
is defined similarly to the <varname>flags</varname> field that was passed in, but contains information
about the resolved data (see below). If the hostname passed in is an IPv4 or IPv6 address formatted as
string, it is parsed, and the result is returned. In this case, no network communication is
done.</para>
<para><function>ResolveHostname()</function> takes a hostname and resolves it to one or more IP addresses.
As parameters it takes the Linux network interface index to execute the query on, or 0 if it may be
done on any suitable interface. The <varname>name</varname> parameter specifies the hostname to
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
limits the results to a specific address family. It may be <constant>AF_INET</constant>,
<constant>AF_INET6</constant> or <constant>AF_UNSPEC</constant>. If <constant>AF_UNSPEC</constant> is specified (recommended), both kinds are retrieved, subject
to local network configuration (i.e. if no local, routable IPv6 address is found, no IPv6 address is
retrieved; and similarly for IPv4). A 64-bit <varname>flags</varname> field may be used to alter the
behaviour of the resolver operation (see below). The method returns an array of address records. Each
address record consists of the interface index the address belongs to, an address family as well as a
byte array with the actual IP address data (which either has 4 or 16 elements, depending on the address
family). The returned address family will be one of <constant>AF_INET</constant> or
<constant>AF_INET6</constant>. For IPv6, the returned address interface index should be used to
initialize the .sin6_scope_id field of a <structname>struct sockaddr_in6</structname> instance to permit
support for resolution to link-local IP addresses. The address array is followed by the canonical name
of the host, which may or may not be identical to the resolved hostname. Finally, a 64-bit
<varname>flags</varname> field is returned that is defined similarly to the <varname>flags</varname>
field that was passed in, but contains information about the resolved data (see below). If the hostname
passed in is an IPv4 or IPv6 address formatted as 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
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
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
(see below) on it.</para>
<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>.
(see below) on it.
</para>
<para><function>SetLinkDefaultRoute()</function> specifies whether the link shall be used as the
default route for name queries. See the description of name routing in
<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 this method (and the five below) 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>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,
@ -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
list of domains as arguments.</para>
<para>The <function>SetLinkDNSOverTLS()</function> method enables or disables DNS-over-TLS.
C.f. <varname>DNSOverTLS=</varname> in
<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>
<para>The <function>RevertLink()</function> method may be used to revert all per-link settings done with
the six methods described above to the defaults again.</para>
<refsect3>
<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
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
operate as filter only, but cannot force a look-up to be done via a protocol. Specifically,
<filename>systemd-resolved</filename> will only route look-ups within the .local TLD to MulticastDNS
(plus some reverse look-up address domains), and single-label names to LLMNR (plus some reverse
address lookup domains). It will route neither of these to Unicast DNS servers. Also, it will do
LLMNR and Multicast DNS only on interfaces suitable for multicast.</para>
operate as filter only, but cannot force a look-up to be done via a protocol. Specifically, <filename>systemd-resolved</filename>
will only route look-ups within the .local TLD to MulticastDNS (plus some reverse look-up address
domains), and single-label names to LLMNR (plus some reverse address lookup domains). It will route
neither of these to Unicast DNS servers. Also, it will do LLMNR and Multicast DNS only on interfaces
suitable for multicast.</para>
<para>On output, these five flags indicate which protocol was used to execute the operation, and hence
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
does not support authenticating data).</para>
</refsect3>
</refsect2>
</refsect2>
<refsect2>
<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
LLMNR. It usually follows the system hostname as may be queried via
<citerefentry project="man-pages"><refentrytitle>gethostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
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
used by <filename>systemd-resolved</filename>. <varname>DNS</varname> contains information similar to
the DNS server data in <filename>/run/systemd/resolve/resolv.conf</filename>. Each structure in the
array consists of a numeric network interface index, an address family, and a byte array containing the
DNS server address (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6).
<varname>DNSEx</varname> is similar, but additionally contains the IP port and server name (used for
Server Name Indication, SNI). Both arrays contain DNS servers configured system-wide, including those
possibly read from a foreign <filename>/etc/resolv.conf</filename> or the <varname>DNS=</varname>
setting in <filename>/etc/systemd/resolved.conf</filename>, as well as per-interface DNS server
information either retrieved from
<para><varname>DNS</varname> contains an array of all DNS servers currently used by
<filename>systemd-resolved</filename>. It contains similar information as the DNS server data written to
/run/systemd/resolve/resolv.conf. Each structure in the array consists of a numeric network interface
index, an address family, and a byte array containing the DNS server address (either 4 bytes in length
for IPv4 or 16 bytes in lengths for IPv6). The array contains DNS servers configured system-wide,
including those possibly read from a foreign <filename>/etc/resolv.conf</filename> or the
<varname>DNS=</varname> 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>,
or configured by external software via <function>SetLinkDNS()</function> or
<function>SetLinkDNSEx()</function> (see above). The network interface index will be 0 for the
system-wide configured services and non-zero for the per-link servers.</para>
or configured by external software via <function>SetLinkDNS()</function> (see above). The network
interface index will be 0 for the system-wide configured services and non-zero for the per-link
servers.</para>
<para><varname>FallbackDNS</varname> and <varname>FallbackDNSEx</varname> contain arrays of all DNS
servers configured as fallback servers, if any, using the same format as <varname>DNS</varname> and
<varname>DNSEx</varname> described above. See the description of <varname>FallbackDNS=</varname> in
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
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>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
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
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
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>
above). </para>
<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,
@ -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
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>
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
<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),
<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>
</refsect1>
@ -644,6 +619,40 @@ node /org/freedesktop/resolve1/link/_1 {
};
</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!-->
<!--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
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
<function>GetLink()</function> before invoking the methods. The same relationship holds for
<function>SetDNSEx()</function>, <function>SetDomains()</function>,
<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>
<function>GetLink()</function> before invoking the methods. For further details on these methods see
the <interfacename>Manager</interfacename> documentation above.</para>
</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,
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
may be set with the various methods calls such as <function>SetDNS()</function> or
<function>SetLLMNR()</function>.</para>
may be set with the various methods calls such as SetDNS() or SetLLMNR().</para>
</refsect2>
</refsect1>

View File

@ -87,17 +87,17 @@
<refsect1>
<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>
<itemizedlist>
<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
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>
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.
(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.
</para></listitem>
<listitem><para>The hostname <literal>_gateway</literal> is resolved to all current default routing
@ -119,162 +119,104 @@
according to the following rules:</para>
<itemizedlist>
<listitem><para>Names for which synthetic records are generated (the local hostname,
<literal>localhost</literal> and <literal>localdomain</literal>, local gateway, as listed in the
previous section) and addresses configured in <filename>/etc/hosts</filename> are never routed to the
network and a reply is sent immediately.</para></listitem>
<listitem><para>Names for which synthetic records are generated (as listed in the previous section) are
never routed to the network and a reply is sent immediately. In particular this means that lookups for
<literal>localhost</literal> are never routed to the network.</para></listitem>
<listitem><para>Single-label names are resolved using LLMNR on all local interfaces where LLMNR is
enabled. Lookups for IPv4 addresses are only sent via LLMNR on IPv4, and lookups for IPv6 addresses are
only sent via LLMNR on IPv6. Note that lookups for single-label synthetized names are not routed to
LLMNR, MulticastDNS or unicast DNS.</para></listitem>
<listitem><para>Single-label names are routed to all local interfaces capable of IP multicasting, where
LLMNR is not disabled, using the LLMNR protocol. Lookups for IPv4 addresses are only sent via LLMNR on
IPv4, and lookups for IPv6 addresses are only sent via LLMNR on IPv6. Lookups for the locally
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
resolved via unicast DNS using search domains. 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 resoulution is only possible if search domains are defined.</para></listitem>
<listitem><para>Multi-label names with the domain suffix <literal>.local</literal> are routed to all
local interfaces capable of IP multicasting, where MulticastDNS is not disabled, using the MulticastDNS
protocol. As with LLMNR, IPv4 address lookups are sent via IPv4 and IPv6 address lookups are sent via
IPv6.</para></listitem>
<listitem><para>Multi-label names with the domain suffix <literal>.local</literal> are resolved using
MulticastDNS on all local interfaces where MulticastDNS is enabled. As with LLMNR, IPv4 address lookups
are sent via IPv4 and IPv6 address lookups are sent via IPv6.</para></listitem>
<listitem><para>Resolution of address records (A and AAAA) via unicast DNS (i.e. not LLMNR or
MulticastDNS) for non-synthesized single-label names is allowed for non-top-level domains. This means
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
a DNS server configured, plus the globally configured DNS servers if there are any. Which interfaces
are used is determined by the routing logic based on search and route-only domains, described below.
Note that by default, lookups for domains with the <literal>.local</literal> suffix are not routed to
DNS servers, unless the domain is specified explicitly as routing or search domain for the DNS server
and interface. This means that on networks where the <literal>.local</literal> domain is defined in a
site-specific DNS server, explicit search or routing domains need to be configured to make lookups work
within this DNS domain. Note that these days, it's generally recommended to avoid defining
<literal>.local</literal> in a DNS server, as <ulink
url="https://tools.ietf.org/html/rfc6762">RFC6762</ulink> reserves this domain for exclusive
<listitem><para>Other multi-label names are routed to all local interfaces that have a DNS server
configured, plus the globally configured DNS servers if there are any. Note that by default, lookups for
domains with the <literal>.local</literal> suffix are not routed to DNS servers, unless the domain is
specified explicitly as routing or search domain for the DNS server and interface. This means that on
networks where the <literal>.local</literal> domain is defined in a site-specific DNS server, explicit
search or routing domains need to be configured to make lookups within this DNS domain work. Note that
these days, it's generally recommended to avoid defining <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>
<listitem><para>Address lookups (reverse lookups) are routed similarly to multi-label names, with the
exception that addresses from the link-local address range are never routed to unicast DNS and are only
resolved using LLMNR and MulticastDNS (when enabled).</para></listitem>
<listitem><para>Address lookups are routed similarly to multi-label names, with the exception that
addresses from the link-local address range are never routed to unicast DNS and are only resolved using
LLMNR and MulticastDNS (when enabled).</para></listitem>
</itemizedlist>
<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,
the last failing response is returned.</para>
<para>Routing of lookups is determined by the per-interface routing domains (search and route-only) and
global search domains. See
<para>Routing of lookups may be influenced by configuring per-interface domain names and other
settings. See
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
<citerefentry><refentrytitle>resolvectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for a
description how those settings are set dynamically and the discussion of <varname>Domains=</varname> in
<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>
<citerefentry><refentrytitle>resolvectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details. The following query routing logic applies for unicast DNS traffic:</para>
<itemizedlist>
<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,
"best matching" routing 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" routing domain. (Note that more than one link might have this same "best matching" routing
domain configured, in which case the query is sent to all of them in parallel).</para>
configured search or route-only domains of any link (see
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>),
or the globally configured DNS settings (see the discussion of <varname>Domains=</varname> in
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>),
"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
that the name is first suffixed by each of the search domains in turn. Note that this search logic
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>
that the name is first suffixed by the search domain.</para></listitem>
<listitem><para>If a query does not match any configured routing domain (either per-link or global), it
is sent to all DNS servers that are configured on links with the <varname>DefaultRoute=</varname>
option set, as well as the globally configured DNS server.</para></listitem>
<listitem><para>If a query does not match any configured search/route-only domain (neither per-link nor
global), it is sent to all DNS servers that are configured on links with the "DNS default route" option
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
server configured, one of the compiled-in fallback DNS servers is used.</para></listitem>
<listitem><para>If there is no link configured as "DNS default route" and no global DNS server
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>
</itemizedlist>
<para>The <varname>DefaultRoute=</varname> option is a boolean setting configurable with
<command>resolvectl</command> or in <filename>.network</filename> files. If not set, it is implicitly
determined based on the configured DNS domains for a link: if there's a route-only domain other than
<literal>~.</literal>, it defaults to false, otherwise to true.</para>
<para>The "DNS default route" option is a boolean setting configurable with <command>resolvectl</command>
or in <filename>.network</filename> files. If not set, it is implicitly determined based on the
configured DNS domains for a link: if there's any route-only domain (not matching <literal>~.</literal>)
it defaults to false, otherwise to true.</para>
<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
configuration to a specific link, configure a <literal>~.</literal> route-only domain on it. This will
ensure that other links will not be considered for these queries (unless they too carry such a routing
domain). In order to route all such DNS queries to a specific link only if no other link is preferred,
set the <varname>DefaultRoute=</varname> option for the link to true and do not configure a
search domains. In order to preferably route all DNS queries not explicitly matched by search/route-only
domain configuration to a specific link, configure a <literal>~.</literal> route-only domain on it. This
will ensure that other links will not be considered for these queries (unless they too carry such a
route-only domain). In order to route all such DNS queries to a specific link only if no other link
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
receives any DNS traffic not matching any of its configured routing domains, set the
<varname>DefaultRoute=</varname> option for it to false.</para>
receives any DNS traffic not matching any of its configured search/route-only domains, set the "DNS
default route" option for it to false.</para>
<para>See
<citerefentry><refentrytitle>org.freedesktop.resolve1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for information about the D-Bus APIs <filename>systemd-resolved</filename> provides.</para>
</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>
<title><filename>/etc/resolv.conf</filename></title>
@ -361,6 +303,7 @@
synchronous way.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>

View File

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

View File

@ -5,7 +5,7 @@ option('version-tag', type : 'string',
description : 'override the git version string')
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'],
description : '''/bin, /sbin aren't symlinks into /usr''')

View File

@ -161,8 +161,3 @@
_IDN_FEATURE_ " " \
_PCRE2_FEATURE_ " " \
_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. */
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_CREATE) ? O_CREAT : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0),
(FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0),
(FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
if (fd < 0) {
r = -errno;

View File

@ -16,15 +16,14 @@
typedef enum {
WRITE_STRING_FILE_CREATE = 1 << 0,
WRITE_STRING_FILE_TRUNCATE = 1 << 1,
WRITE_STRING_FILE_ATOMIC = 1 << 2,
WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3,
WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4,
WRITE_STRING_FILE_SYNC = 1 << 5,
WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 6,
WRITE_STRING_FILE_NOFOLLOW = 1 << 7,
WRITE_STRING_FILE_MKDIR_0755 = 1 << 8,
WRITE_STRING_FILE_MODE_0600 = 1 << 9,
WRITE_STRING_FILE_ATOMIC = 1 << 1,
WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 2,
WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 3,
WRITE_STRING_FILE_SYNC = 1 << 4,
WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 5,
WRITE_STRING_FILE_NOFOLLOW = 1 << 6,
WRITE_STRING_FILE_MKDIR_0755 = 1 << 7,
WRITE_STRING_FILE_MODE_0600 = 1 << 8,
/* 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()

View File

@ -650,8 +650,4 @@ static inline int __coverity_check_and_return__(int condition) {
_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"

View File

@ -27,7 +27,7 @@
#ifdef 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"
# endif
# else

View File

@ -33,12 +33,6 @@ static inline bool streq_ptr(const char *a, const char *b) {
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) {
return s ?: "";
}
@ -59,10 +53,6 @@ static inline const char* true_false(bool b) {
return b ? "true" : "false";
}
static inline const char* plus_minus(bool b) {
return b ? "+" : "-";
}
static inline const char* one_zero(bool b) {
return b ? "1" : "0";
}

View File

@ -537,19 +537,6 @@ int strv_consume_prepend(char ***l, char *value) {
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) {
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_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_extendf(char ***l, const char *format, ...) _printf_(2,0);
int strv_extend_front(char ***l, const char *value);

View File

@ -87,7 +87,7 @@ struct ExecStatus {
dual_timestamp exit_timestamp;
pid_t pid;
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. */

View File

@ -35,7 +35,6 @@
#include "string-table.h"
#include "strv.h"
#include "terminal-util.h"
#include "utf8.h"
#include "verbs.h"
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)
return r;
strv_sort(*l);
return 0;
}
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;
STRV_FOREACH(i, p) {
size_t our_len = utf8_console_width(*i); /* This returns -1 on invalid utf-8 (which shouldn't happen).
* If that happens, we'll just print one item per line. */
printf("%sLink %i (%s)%s:",
ansi_highlight(), ifindex, ifname, ansi_normal());
if (position <= indent || size_add(size_add(position, 1), our_len) < cols) {
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);
}
}
STRV_FOREACH(i, p)
printf(" %s", *i);
printf("\n");
return 0;
}
static int status_print_strv_global(char **p) {
return status_print_strv_ifindex(0, NULL, p);
}
typedef struct LinkInfo {
struct link_info {
uint64_t scopes_mask;
const char *llmnr;
const char *mdns;
@ -1350,26 +1327,9 @@ typedef struct LinkInfo {
char **ntas;
bool dnssec_supported;
bool default_route;
} LinkInfo;
};
typedef struct GlobalInfo {
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) {
static void link_info_clear(struct link_info *p) {
free(p->current_dns);
free(p->current_dns_ex);
strv_free(p->dns);
@ -1378,17 +1338,6 @@ static void link_info_clear(LinkInfo *p) {
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) {
int r;
@ -1397,88 +1346,33 @@ static int dump_list(Table *table, const char *prefix, char * const *l) {
r = table_add_many(table,
TABLE_STRING, prefix,
TABLE_STRV_WRAPPED, l);
TABLE_STRV, l);
if (r < 0)
return table_log_add_error(r);
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 const struct bus_properties_map property_map[] = {
{ "ScopesMask", "t", NULL, offsetof(LinkInfo, scopes_mask) },
{ "DNS", "a(iay)", map_link_dns_servers, offsetof(LinkInfo, dns) },
{ "DNSEx", "a(iayqs)", map_link_dns_servers_ex, offsetof(LinkInfo, dns_ex) },
{ "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(LinkInfo, current_dns) },
{ "CurrentDNSServerEx", "(iayqs)", map_link_current_dns_server_ex, offsetof(LinkInfo, current_dns_ex) },
{ "Domains", "a(sb)", map_link_domains, offsetof(LinkInfo, domains) },
{ "DefaultRoute", "b", NULL, offsetof(LinkInfo, default_route) },
{ "LLMNR", "s", NULL, offsetof(LinkInfo, llmnr) },
{ "MulticastDNS", "s", NULL, offsetof(LinkInfo, mdns) },
{ "DNSOverTLS", "s", NULL, offsetof(LinkInfo, dns_over_tls) },
{ "DNSSEC", "s", NULL, offsetof(LinkInfo, dnssec) },
{ "DNSSECNegativeTrustAnchors", "as", bus_map_strv_sort, offsetof(LinkInfo, ntas) },
{ "DNSSECSupported", "b", NULL, offsetof(LinkInfo, dnssec_supported) },
{ "ScopesMask", "t", NULL, offsetof(struct link_info, scopes_mask) },
{ "DNS", "a(iay)", map_link_dns_servers, offsetof(struct link_info, dns) },
{ "DNSEx", "a(iayqs)", map_link_dns_servers_ex, offsetof(struct link_info, dns_ex) },
{ "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(struct link_info, current_dns) },
{ "CurrentDNSServerEx", "(iayqs)", map_link_current_dns_server_ex, offsetof(struct link_info, current_dns_ex) },
{ "Domains", "a(sb)", map_link_domains, offsetof(struct link_info, domains) },
{ "DefaultRoute", "b", NULL, offsetof(struct link_info, default_route) },
{ "LLMNR", "s", NULL, offsetof(struct link_info, llmnr) },
{ "MulticastDNS", "s", NULL, offsetof(struct link_info, mdns) },
{ "DNSOverTLS", "s", NULL, offsetof(struct link_info, dns_over_tls) },
{ "DNSSEC", "s", NULL, offsetof(struct link_info, dnssec) },
{ "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct link_info, ntas) },
{ "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_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_free_ char *p = NULL;
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)
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,
TABLE_STRING, "Protocols:",
TABLE_STRV_WRAPPED, pstatus);
TABLE_STRING, "DefaultRoute setting:",
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)
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)
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;
}
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 const struct bus_properties_map property_map[] = {
{ "DNS", "a(iiay)", map_global_dns_servers, offsetof(GlobalInfo, dns) },
{ "DNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(GlobalInfo, dns_ex) },
{ "FallbackDNS", "a(iiay)", map_global_dns_servers, offsetof(GlobalInfo, fallback_dns) },
{ "FallbackDNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(GlobalInfo, fallback_dns_ex) },
{ "CurrentDNSServer", "(iiay)", map_global_current_dns_server, offsetof(GlobalInfo, current_dns) },
{ "CurrentDNSServerEx", "(iiayqs)", map_global_current_dns_server_ex, offsetof(GlobalInfo, current_dns_ex) },
{ "Domains", "a(isb)", map_global_domains, offsetof(GlobalInfo, domains) },
{ "DNSSECNegativeTrustAnchors", "as", bus_map_strv_sort, offsetof(GlobalInfo, ntas) },
{ "LLMNR", "s", NULL, offsetof(GlobalInfo, llmnr) },
{ "MulticastDNS", "s", NULL, offsetof(GlobalInfo, mdns) },
{ "DNSOverTLS", "s", NULL, offsetof(GlobalInfo, dns_over_tls) },
{ "DNSSEC", "s", NULL, offsetof(GlobalInfo, dnssec) },
{ "DNSSECSupported", "b", NULL, offsetof(GlobalInfo, dnssec_supported) },
{ "ResolvConfMode", "s", NULL, offsetof(GlobalInfo, resolv_conf_mode) },
{ "DNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, dns) },
{ "DNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(struct global_info, dns_ex) },
{ "FallbackDNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, fallback_dns) },
{ "FallbackDNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(struct global_info, fallback_dns_ex) },
{ "CurrentDNSServer", "(iiay)", map_global_current_dns_server, offsetof(struct global_info, current_dns) },
{ "CurrentDNSServerEx", "(iiayqs)", map_global_current_dns_server_ex, offsetof(struct global_info, current_dns_ex) },
{ "Domains", "a(isb)", map_global_domains, offsetof(struct global_info, domains) },
{ "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct global_info, ntas) },
{ "LLMNR", "s", NULL, offsetof(struct global_info, llmnr) },
{ "MulticastDNS", "s", NULL, offsetof(struct global_info, mdns) },
{ "DNSOverTLS", "s", NULL, offsetof(struct global_info, dns_over_tls) },
{ "DNSSEC", "s", NULL, offsetof(struct global_info, dnssec) },
{ "DNSSECSupported", "b", NULL, offsetof(struct global_info, dnssec_supported) },
{ "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_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;
int r;
@ -1821,14 +1760,18 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
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,
TABLE_STRING, "Protocols:",
TABLE_STRING, "LLMNR setting:",
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)
return table_log_add_error(r);

View File

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

View File

@ -25,6 +25,5 @@
#LLMNR=@DEFAULT_LLMNR_MODE@
#Cache=yes
#DNSStubListener=yes
#DNSStubListenerExtra=
#ReadEtcHosts=yes
#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;
}
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) {
char type;
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_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_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 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 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 */
@ -165,6 +164,7 @@ Table *table_new_raw(size_t n_columns) {
Table *table_new_internal(const char *first_header, ...) {
_cleanup_(table_unrefp) Table *t = NULL;
size_t n_columns = 1;
const char *h;
va_list ap;
int r;
@ -172,7 +172,8 @@ Table *table_new_internal(const char *first_header, ...) {
va_start(ap, first_header);
for (;;) {
if (!va_arg(ap, const char*))
h = va_arg(ap, const char*);
if (!h)
break;
n_columns++;
@ -184,7 +185,7 @@ Table *table_new_internal(const char *first_header, ...) {
return NULL;
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;
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->url);
if (IN_SET(d->type, TABLE_STRV, TABLE_STRV_WRAPPED))
if (d->type == TABLE_STRV)
strv_free(d->strv);
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);
Table *table_unref(Table *t) {
size_t i;
if (!t)
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]);
free(t->data);
@ -249,7 +252,6 @@ static size_t table_data_size(TableDataType type, const void *data) {
return strlen(data) + 1;
case TABLE_STRV:
case TABLE_STRV_WRAPPED:
return sizeof(char **);
case TABLE_BOOLEAN:
@ -374,7 +376,7 @@ static TableData *table_data_new(
d->align_percent = align_percent;
d->ellipsize_percent = ellipsize_percent;
if (IN_SET(type, TABLE_STRV, TABLE_STRV_WRAPPED)) {
if (type == TABLE_STRV) {
d->strv = strv_copy(data);
if (!d->strv)
return NULL;
@ -815,7 +817,6 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
break;
case TABLE_STRV:
case TABLE_STRV_WRAPPED:
data = va_arg(ap, char * const *);
break;
@ -1046,9 +1047,11 @@ int table_set_empty_string(Table *t, const char *empty) {
}
int table_set_display_all(Table *t) {
size_t allocated;
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)))
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) {
size_t allocated, cur = 0;
int r;
assert(t);
@ -1133,7 +1137,7 @@ int table_hide_column_from_display(Table *t, size_t column) {
return r;
}
size_t allocated = t->n_display_map, cur = 0;
allocated = t->n_display_map;
for (size_t i = 0; i < allocated; i++) {
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);
case TABLE_STRV:
case TABLE_STRV_WRAPPED:
return strv_compare(a->strv, b->strv);
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) {
size_t i;
int r;
assert(t);
@ -1258,7 +1262,7 @@ static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
return 1;
/* 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;
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);
}
static char* format_strv_width(char **strv, size_t column_width) {
_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) {
static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercasing) {
assert(d);
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))
if (d->formatted)
return d->formatted;
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_PATH:
if (d->uppercase && !avoid_uppercasing) {
char *p, *q;
d->formatted = new(char, strlen(d->string) + 1);
if (!d->formatted)
return NULL;
char *q = d->formatted;
for (char *p = d->string; *p; p++, q++)
for (p = d->string, q = d->formatted; *p; p++, q++)
*q = (char) toupper((unsigned char) *p);
*q = 0;
@ -1336,28 +1305,17 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
return d->string;
case TABLE_STRV:
case TABLE_STRV: {
char *p;
if (strv_isempty(d->strv))
return strempty(t->empty_string);
d->formatted = strv_join(d->strv, "\n");
if (!d->formatted)
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)
p = strv_join(d->strv, "\n");
if (!p)
return NULL;
free_and_replace(d->formatted, buf);
d->formatted_for_width = column_width;
if (have_soft)
*have_soft = true;
d->formatted = p;
break;
}
@ -1674,19 +1632,16 @@ static int console_width_height(
static int table_data_requested_width_height(
Table *table,
TableData *d,
size_t available_width,
size_t *ret_width,
size_t *ret_height,
bool *have_soft) {
size_t *ret_height) {
_cleanup_free_ char *truncated = NULL;
bool truncation_applied = false;
size_t width, height;
const char *t;
int r;
bool soft = false;
t = table_data_format(table, d, false, available_width, &soft);
t = table_data_format(table, d, false);
if (!t)
return -ENOMEM;
@ -1714,8 +1669,6 @@ static int table_data_requested_width_height(
*ret_width = width;
if (ret_height)
*ret_height = height;
if (have_soft && soft)
*have_soft = true;
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;
const char *p;
char *ret;
size_t i;
int r;
/* 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)
return NULL;
for (size_t i = 0; i < lspace; i++)
for (i = 0; i < lspace; i++)
ret[i] = ' ';
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[space + clickable_length] = 0;
@ -1786,7 +1740,7 @@ static bool table_data_isempty(TableData *d) {
return true;
/* 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);
/* 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) {
size_t n_rows, *minimum_width, *maximum_width, display_columns, *requested_width,
table_minimum_width, table_maximum_width, table_requested_width, table_effective_width,
*width = NULL;
i, j, table_minimum_width, table_maximum_width, table_requested_width, table_effective_width,
*width;
_cleanup_free_ size_t *sorted = NULL;
uint64_t *column_weight, weight_sum;
int r;
@ -1841,7 +1795,7 @@ int table_print(Table *t, FILE *f) {
if (!sorted)
return -ENOMEM;
for (size_t i = 0; i < n_rows; i++)
for (i = 0; i < n_rows; i++)
sorted[i] = i * t->n_columns;
typesafe_qsort_r(sorted, n_rows, table_data_compare, t);
@ -1857,225 +1811,205 @@ int table_print(Table *t, FILE *f) {
minimum_width = newa(size_t, display_columns);
maximum_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);
for (size_t j = 0; j < display_columns; j++) {
for (j = 0; j < display_columns; j++) {
minimum_width[j] = 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++) {
TableData **row;
for (size_t j = 0; j < display_columns; j++)
requested_width[j] = (size_t) -1;
/* 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. */
row = t->data + i * t->n_columns;
bool any_soft = false;
for (j = 0; j < display_columns; j++) {
TableData *d;
size_t req_width, req_height;
for (size_t i = t->header ? 0 : 1; i < n_rows; i++) {
TableData **row;
assert_se(d = row[t->display_map ? t->display_map[j] : j]);
/* 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. */
row = t->data + i * t->n_columns;
r = table_data_requested_width_height(t, d, &req_width, &req_height);
if (r < 0)
return r;
if (r > 0) { /* Truncated because too many lines? */
_cleanup_free_ char *last = NULL;
const char *field;
for (size_t j = 0; j < display_columns; j++) {
TableData *d;
size_t req_width, req_height;
/* If we are going to show only the first few lines of a cell that has
* multiple make sure that we have enough space horizontally to show an
* ellipsis. Hence, let's figure out the last line, and account for its
* length plus ellipsis. */
assert_se(d = row[t->display_map ? t->display_map[j] : j]);
field = table_data_format(t, d, false);
if (!field)
return -ENOMEM;
r = table_data_requested_width_height(t, d,
width ? width[j] : SIZE_MAX,
&req_width, &req_height, &any_soft);
assert_se(t->cell_height_max > 0);
r = string_extract_line(field, t->cell_height_max-1, &last);
if (r < 0)
return r;
if (r > 0) { /* Truncated because too many lines? */
_cleanup_free_ char *last = NULL;
const char *field;
/* If we are going to show only the first few lines of a cell that has
* multiple make sure that we have enough space horizontally to show an
* ellipsis. Hence, let's figure out the last line, and account for its
* length plus ellipsis. */
field = table_data_format(t, d, false,
width ? width[j] : SIZE_MAX,
&any_soft);
if (!field)
return -ENOMEM;
assert_se(t->cell_height_max > 0);
r = string_extract_line(field, t->cell_height_max-1, &last);
if (r < 0)
return r;
req_width = MAX(req_width,
utf8_console_width(last) +
utf8_console_width(special_glyph(SPECIAL_GLYPH_ELLIPSIS)));
}
/* Determine the biggest width that any cell in this column would like to have */
if (requested_width[j] == (size_t) -1 ||
requested_width[j] < req_width)
requested_width[j] = req_width;
/* Determine the minimum width any cell in this column needs */
if (minimum_width[j] < d->minimum_width)
minimum_width[j] = d->minimum_width;
/* Determine the maximum width any cell in this column needs */
if (d->maximum_width != (size_t) -1 &&
(maximum_width[j] == (size_t) -1 ||
maximum_width[j] > d->maximum_width))
maximum_width[j] = d->maximum_width;
/* Determine the full columns weight */
column_weight[j] += d->weight;
req_width = MAX(req_width,
utf8_console_width(last) +
utf8_console_width(special_glyph(SPECIAL_GLYPH_ELLIPSIS)));
}
/* Determine the biggest width that any cell in this column would like to have */
if (requested_width[j] == (size_t) -1 ||
requested_width[j] < req_width)
requested_width[j] = req_width;
/* Determine the minimum width any cell in this column needs */
if (minimum_width[j] < d->minimum_width)
minimum_width[j] = d->minimum_width;
/* Determine the maximum width any cell in this column needs */
if (d->maximum_width != (size_t) -1 &&
(maximum_width[j] == (size_t) -1 ||
maximum_width[j] > d->maximum_width))
maximum_width[j] = d->maximum_width;
/* Determine the full columns weight */
column_weight[j] += d->weight;
}
}
/* One space between each column */
table_requested_width = table_minimum_width = table_maximum_width = display_columns - 1;
/* One space between each column */
table_requested_width = table_minimum_width = table_maximum_width = display_columns - 1;
/* Calculate the total weight for all columns, plus the minimum, maximum and requested width for the table. */
weight_sum = 0;
for (size_t j = 0; j < display_columns; j++) {
weight_sum += column_weight[j];
/* Calculate the total weight for all columns, plus the minimum, maximum and requested width for the table. */
weight_sum = 0;
for (j = 0; j < display_columns; j++) {
weight_sum += column_weight[j];
table_minimum_width += minimum_width[j];
table_minimum_width += minimum_width[j];
if (maximum_width[j] == (size_t) -1)
table_maximum_width = (size_t) -1;
else
table_maximum_width += maximum_width[j];
table_requested_width += requested_width[j];
}
/* Calculate effective table width */
if (t->width != 0 && t->width != (size_t) -1)
table_effective_width = t->width;
else if (t->width == 0 ||
((pass > 0 || !any_soft) && (pager_have() || !isatty(STDOUT_FILENO))))
table_effective_width = table_requested_width;
if (maximum_width[j] == (size_t) -1)
table_maximum_width = (size_t) -1;
else
table_effective_width = MIN(table_requested_width, columns());
table_maximum_width += maximum_width[j];
if (table_maximum_width != (size_t) -1 && table_effective_width > table_maximum_width)
table_effective_width = table_maximum_width;
table_requested_width += requested_width[j];
}
if (table_effective_width < table_minimum_width)
table_effective_width = table_minimum_width;
/* Calculate effective table width */
if (t->width != 0 && t->width != (size_t) -1)
table_effective_width = t->width;
else if (t->width == 0 || pager_have() || !isatty(STDOUT_FILENO))
table_effective_width = table_requested_width;
else
table_effective_width = MIN(table_requested_width, columns());
if (!width)
width = newa(size_t, display_columns);
if (table_maximum_width != (size_t) -1 && table_effective_width > table_maximum_width)
table_effective_width = table_maximum_width;
if (table_effective_width >= table_requested_width) {
size_t extra;
if (table_effective_width < table_minimum_width)
table_effective_width = table_minimum_width;
/* We have extra room, let's distribute it among columns according to their weights. We first provide
* each column with what it asked for and the distribute the rest. */
if (table_effective_width >= table_requested_width) {
size_t extra;
extra = table_effective_width - table_requested_width;
/* We have extra room, let's distribute it among columns according to their weights. We first provide
* each column with what it asked for and the distribute the rest. */
for (size_t j = 0; j < display_columns; j++) {
size_t delta;
extra = table_effective_width - table_requested_width;
for (j = 0; j < display_columns; j++) {
size_t delta;
if (weight_sum == 0)
width[j] = requested_width[j] + extra / (display_columns - j); /* Avoid division by zero */
else
width[j] = requested_width[j] + (extra * column_weight[j]) / weight_sum;
if (maximum_width[j] != (size_t) -1 && width[j] > maximum_width[j])
width[j] = maximum_width[j];
if (width[j] < minimum_width[j])
width[j] = minimum_width[j];
assert(width[j] >= requested_width[j]);
delta = width[j] - requested_width[j];
/* Subtract what we just added from the rest */
if (extra > delta)
extra -= delta;
else
extra = 0;
assert(weight_sum >= column_weight[j]);
weight_sum -= column_weight[j];
}
} else {
/* 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. */
bool finalize = false;
size_t extra;
extra = table_effective_width - table_minimum_width;
for (j = 0; j < display_columns; j++)
width[j] = (size_t) -1;
for (;;) {
bool restart = false;
for (j = 0; j < display_columns; j++) {
size_t delta, w;
/* Did this column already get something assigned? If so, let's skip to the next */
if (width[j] != (size_t) -1)
continue;
if (weight_sum == 0)
width[j] = requested_width[j] + extra / (display_columns - j); /* Avoid division by zero */
w = minimum_width[j] + extra / (display_columns - j); /* avoid division by zero */
else
width[j] = requested_width[j] + (extra * column_weight[j]) / weight_sum;
w = minimum_width[j] + (extra * column_weight[j]) / weight_sum;
if (maximum_width[j] != (size_t) -1 && width[j] > maximum_width[j])
width[j] = maximum_width[j];
if (w >= requested_width[j]) {
/* Never give more than requested. If we hit a column like this, there's more
* space to allocate to other columns which means we need to restart the
* iteration. However, if we hit a column like this, let's assign it the space
* it wanted for good early.*/
if (width[j] < minimum_width[j])
width[j] = minimum_width[j];
w = requested_width[j];
restart = true;
assert(width[j] >= requested_width[j]);
delta = width[j] - requested_width[j];
} else if (!finalize)
continue;
/* Subtract what we just added from the rest */
if (extra > delta)
extra -= delta;
else
extra = 0;
width[j] = w;
assert(w >= minimum_width[j]);
delta = w - minimum_width[j];
assert(delta <= extra);
extra -= delta;
assert(weight_sum >= column_weight[j]);
weight_sum -= column_weight[j];
}
break; /* Every column should be happy, no need to repeat calculations. */
} else {
/* 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. */
bool finalize = false;
size_t extra;
extra = table_effective_width - table_minimum_width;
for (size_t j = 0; j < display_columns; j++)
width[j] = (size_t) -1;
for (;;) {
bool restart = false;
for (size_t j = 0; j < display_columns; j++) {
size_t delta, w;
/* Did this column already get something assigned? If so, let's skip to the next */
if (width[j] != (size_t) -1)
continue;
if (weight_sum == 0)
w = minimum_width[j] + extra / (display_columns - j); /* avoid division by zero */
else
w = minimum_width[j] + (extra * column_weight[j]) / weight_sum;
if (w >= requested_width[j]) {
/* Never give more than requested. If we hit a column like this, there's more
* space to allocate to other columns which means we need to restart the
* iteration. However, if we hit a column like this, let's assign it the space
* it wanted for good early.*/
w = requested_width[j];
restart = true;
} else if (!finalize)
continue;
width[j] = w;
assert(w >= minimum_width[j]);
delta = w - minimum_width[j];
assert(delta <= extra);
extra -= delta;
assert(weight_sum >= column_weight[j]);
weight_sum -= column_weight[j];
if (restart && !finalize)
break;
}
if (finalize)
if (restart && !finalize)
break;
if (!restart)
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. */
if (finalize)
break;
if (!restart)
finalize = true;
}
}
/* 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;
bool more_sublines;
TableData **row;
@ -2089,7 +2023,7 @@ int table_print(Table *t, FILE *f) {
const char *gap_color = NULL;
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;
bool lines_truncated = false;
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]);
field = table_data_format(t, d, false, width[j], NULL);
field = table_data_format(t, d, false);
if (!field)
return -ENOMEM;
@ -2313,7 +2247,6 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
return json_variant_new_string(ret, d->string);
case TABLE_STRV:
case TABLE_STRV_WRAPPED:
return json_variant_new_array_strv(ret, d->strv);
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) {
char *c, *x;
/* 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
* underscores and leave everything as is. */
char *c = strdup(f);
c = strdup(f);
if (!c)
return NULL;
for (char *x = c; *x; x++)
for (x = c; *x; x++)
if (isspace(*x))
*x = '_';
@ -2417,7 +2352,7 @@ static char* string_to_json_field_name(const char *f) {
int table_to_json(Table *t, JsonVariant **ret) {
JsonVariant **rows = NULL, **elements = NULL;
_cleanup_free_ size_t *sorted = NULL;
size_t n_rows, display_columns;
size_t n_rows, i, j, display_columns;
int r;
assert(t);
@ -2437,7 +2372,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
goto finish;
}
for (size_t i = 0; i < n_rows; i++)
for (i = 0; i < n_rows; i++)
sorted[i] = i * t->n_columns;
typesafe_qsort_r(sorted, n_rows, table_data_compare, t);
@ -2455,7 +2390,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
goto finish;
}
for (size_t j = 0; j < display_columns; j++) {
for (j = 0; j < display_columns; j++) {
_cleanup_free_ char *mangled = NULL;
const char *formatted;
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]);
/* 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) {
r = -ENOMEM;
goto finish;
@ -2487,7 +2422,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
goto finish;
}
for (size_t i = 1; i < n_rows; i++) {
for (i = 1; i < n_rows; i++) {
TableData **row;
if (sorted)
@ -2495,7 +2430,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
else
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;
size_t k;

View File

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

View File

@ -301,43 +301,3 @@ int enter_cgroup_subroot(char **ret_cgroup) {
int enter_cgroup_root(char **ret_cgroup) {
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 { \
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 "copy.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "mkdir.h"
#include "pager.h"
@ -19,9 +17,6 @@
#include "terminal-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) {
_cleanup_(hashmap_freep) Hashmap *cached_name_map = NULL, *cached_id_map = NULL;
_cleanup_(lookup_paths_free) LookupPaths lp = {};
@ -111,11 +106,12 @@ int cat(int argc, char *argv[], void *userdata) {
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;
int r;
assert(new_path);
assert(original_path);
assert(ret_tmp_fn);
r = tempfn_random(new_path, NULL, &t);
@ -126,78 +122,25 @@ static int create_edit_temp_file(const char *new_path, const char *original_path
if (r < 0)
return log_error_errno(r, "Failed to create directories for \"%s\": %m", new_path);
if (original_path) {
r = mac_selinux_create_file_prepare(new_path, S_IFREG);
if (r < 0)
return r;
r = mac_selinux_create_file_prepare(original_path, S_IFREG);
if (r < 0)
return r;
r = copy_file(original_path, t, 0, 0644, 0, 0, COPY_REFLINK);
if (r == -ENOENT) {
r = touch(t);
mac_selinux_create_file_clear();
if (r < 0)
return log_error_errno(r, "Failed to create temporary file \"%s\": %m", t);
} else {
mac_selinux_create_file_clear();
if (r < 0)
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 = copy_file(original_path, t, 0, 0644, 0, 0, COPY_REFLINK);
if (r == -ENOENT) {
r = mac_selinux_create_file_prepare(new_path, S_IFREG);
if (r < 0)
return r;
r = touch(t);
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);
} else {
mac_selinux_create_file_clear();
if (r < 0)
return log_error_errno(r, "Failed to create temporary file for \"%s\": %m", new_path);
}
*ret_tmp_fn = TAKE_PTR(t);
@ -242,7 +185,6 @@ static int unit_file_create_new(
const LookupPaths *paths,
const char *unit_name,
const char *suffix,
char ** const original_unit_paths,
char **ret_new_path,
char **ret_tmp_path) {
@ -259,7 +201,7 @@ static int unit_file_create_new(
if (r < 0)
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)
return r;
@ -298,7 +240,7 @@ static int unit_file_create_copy(
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)
return r;
@ -390,10 +332,9 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
STRV_FOREACH(name, names) {
_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;
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 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. */
@ -420,7 +361,7 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
unit_name = *name;
r = unit_file_create_new(&lp, unit_name,
arg_full ? NULL : ".d/override.conf",
NULL, &new_path, &tmp_path);
&new_path, &tmp_path);
} else {
assert(path);
@ -443,13 +384,8 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
if (arg_full)
r = unit_file_create_copy(&lp, unit_name, path, &new_path, &tmp_path);
else {
r = strv_prepend(&unit_paths, path);
if (r < 0)
return log_oom();
r = unit_file_create_new(&lp, unit_name, ".d/override.conf", unit_paths, &new_path, &tmp_path);
}
else
r = unit_file_create_new(&lp, unit_name, ".d/override.conf", &new_path, &tmp_path);
}
if (r < 0)
return r;
@ -464,40 +400,6 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
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) {
_cleanup_(lookup_paths_free) LookupPaths lp = {};
_cleanup_strv_free_ char **names = NULL;
@ -550,10 +452,6 @@ int edit(int argc, char *argv[], void *userdata) {
STRV_FOREACH_PAIR(original, tmp, paths) {
/* If the temporary file is empty we ignore it. This allows the user to cancel the
* modification. */
r = trim_edit_markers(*tmp);
if (r < 0)
continue;
if (null_or_empty_path(*tmp)) {
log_warning("Editing \"%s\" canceled: temporary file is empty.", *original);
continue;

View File

@ -264,7 +264,6 @@ static void test_env_clean(void) {
"xyz=xyz\n",
"another=one",
"another=final one",
"CRLF=\r\n",
"BASH_FUNC_foo%%=() { echo foo\n}");
assert_se(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("tab\tcharacter"));
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) {

View File

@ -36,6 +36,11 @@ static int cld_dumped_to_killed(int 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) {
Service *service = NULL;
usec_t ts;
@ -892,7 +897,7 @@ int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
#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");
return EXIT_TEST_SKIP;
}

View File

@ -12,8 +12,6 @@ static void test_issue_9549(void) {
_cleanup_(table_unrefp) Table *table = NULL;
_cleanup_free_ char *formatted = NULL;
log_info("/* %s */", __func__);
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_add_many(table,
@ -38,8 +36,6 @@ static void test_multiline(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);
@ -152,8 +148,6 @@ static void test_strv(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);
@ -262,111 +256,8 @@ static void test_strv(void) {
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[]) {
_cleanup_(table_unrefp) Table *t = NULL;
_cleanup_free_ char *formatted = NULL;
@ -508,7 +399,6 @@ int main(int argc, char *argv[]) {
test_issue_9549();
test_multiline();
test_strv();
test_strv_wrapped();
return 0;
}

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <stdbool.h>
#include <stdio.h>
#include <sys/stat.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);
}
static int _check_states(unsigned line,
Manager *m, Path *path, Service *service, PathState path_state, ServiceState service_state) {
static void check_states(Manager *m, Path *path, Service *service, PathState path_state, ServiceState service_state) {
assert_se(m);
assert_se(service);
usec_t end = now(CLOCK_MONOTONIC) + 30 * USEC_PER_SEC;
while (path->state != path_state || service->state != service_state ||
path->result != PATH_SUCCESS || service->result != SERVICE_SUCCESS) {
while (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);
usec_t n = now(CLOCK_MONOTONIC);
log_info("line %u: %s: state = %s; result = %s (left: %" PRIi64 ")",
line,
UNIT(path)->id,
path_state_to_string(path->state),
path_result_to_string(path->result),
end - n);
log_info("line %u: %s: state = %s; result = %s",
line,
UNIT(service)->id,
service_state_to_string(service->state),
service_result_to_string(service->result));
printf("%s: state = %s; result = %s \n",
UNIT(path)->id,
path_state_to_string(path->state),
path_result_to_string(path->result));
printf("%s: state = %s; result = %s \n",
UNIT(service)->id,
service_state_to_string(service->state),
service_result_to_string(service->result));
if (service->state == SERVICE_FAILED &&
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) {
if (now(CLOCK_MONOTONIC) >= end) {
log_error("Test timeout when testing %s", UNIT(path)->id);
exit(EXIT_FAILURE);
}
}
return 0;
}
#define check_states(...) _check_states(__LINE__, __VA_ARGS__)
static void test_path_exists(Manager *m) {
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);
assert_se(unit_start(unit) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
assert_se(touch(test_path) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
return;
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
/* Service restarts if file still exists */
assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
return;
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
assert_se(unit_stop(unit) >= 0);
}
@ -172,22 +149,18 @@ static void test_path_existsglob(Manager *m) {
service = service_for_path(m, path, NULL);
assert_se(unit_start(unit) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
assert_se(touch(test_path) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
return;
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
/* Service restarts if file still exists */
assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
return;
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
assert_se(unit_stop(unit) >= 0);
}
@ -207,28 +180,23 @@ static void test_path_changed(Manager *m) {
service = service_for_path(m, path, NULL);
assert_se(unit_start(unit) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
assert_se(touch(test_path) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
return;
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
/* Service does not restart if file still exists */
assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
f = fopen(test_path, "w");
assert_se(f);
fclose(f);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
return;
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
(void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
assert_se(unit_stop(unit) >= 0);
@ -249,28 +217,23 @@ static void test_path_modified(Manager *m) {
service = service_for_path(m, path, NULL);
assert_se(unit_start(unit) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
assert_se(touch(test_path) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
return;
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
/* Service does not restart if file still exists */
assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
f = fopen(test_path, "w");
assert_se(f);
fputs("test", f);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
return;
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
(void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
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");
assert_se(unit_start(unit) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
assert_se(touch(test_path) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
return;
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
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(unit_start(unit) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
/* MakeDirectory default to no */
assert_se(access(test_path, F_OK) < 0);
assert_se(mkdir_p(test_path, 0755) >= 0);
assert_se(touch(strjoina(test_path, "test_file")) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
return;
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
/* Service restarts if directory is still not empty */
assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
return;
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
assert_se(unit_stop(UNIT(service)) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
assert_se(unit_stop(unit) >= 0);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,6 @@
Description=Service Test Path Unit
[Service]
ExecStart=sleep infinity
Type=exec
ExecStart=/bin/true
Type=simple
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
[Service]
Type=exec
Type=simple
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
[Service]
Type=exec
Type=simple
ExecStart=false
Restart=always

View File

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

View File

@ -11,13 +11,8 @@
# control writes into pstore.
#
# The crash_kexec_post_notifiers parameter enables the kernel to write
# dmesg (including stack trace) into pstore upon a panic even if kdump
# is loaded, only needed if you want to use pstore with kdump. Without
# 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
# dmesg (including stack trace) into pstore upon a panic, and
# printk.always_kmsg_dump parameter enables the kernel to write dmesg
# upon a normal shutdown (shutdown, reboot, halt).
#
# To configure the kernel parameters, uncomment the appropriate
@ -31,4 +26,4 @@
d /var/lib/systemd/pstore 0755 root root 14d
#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