Compare commits
37 Commits
66032ef489
...
e1da60e430
Author | SHA1 | Date |
---|---|---|
Lennart Poettering | e1da60e430 | |
Hans Ulrich Niedermann | e1e8c60af7 | |
Lennart Poettering | d975310342 | |
Lennart Poettering | 44f88e7050 | |
Zbigniew Jędrzejewski-Szmek | 7d1e1afe28 | |
Zbigniew Jędrzejewski-Szmek | f08a64c5e1 | |
Zbigniew Jędrzejewski-Szmek | b0e3d79989 | |
Zbigniew Jędrzejewski-Szmek | 16e925c6bb | |
Zbigniew Jędrzejewski-Szmek | 4eb0c875f8 | |
Zbigniew Jędrzejewski-Szmek | 31619e2f61 | |
Zbigniew Jędrzejewski-Szmek | 9c0780af5d | |
Zbigniew Jędrzejewski-Szmek | bace688394 | |
Zbigniew Jędrzejewski-Szmek | 2ce493672c | |
Zbigniew Jędrzejewski-Szmek | 9e1804b24c | |
Lennart Poettering | 349bbd8331 | |
Kairui Song | edb8c98446 | |
Lennart Poettering | dbb74162b2 | |
Zbigniew Jędrzejewski-Szmek | b8ee3493a5 | |
Zbigniew Jędrzejewski-Szmek | 4cce6f2524 | |
Zbigniew Jędrzejewski-Szmek | 28c48f4d78 | |
Zbigniew Jędrzejewski-Szmek | 67b2edb21f | |
Zbigniew Jędrzejewski-Szmek | 333d102c64 | |
Zbigniew Jędrzejewski-Szmek | fcb7138ca7 | |
Zbigniew Jędrzejewski-Szmek | 75edb0b0d6 | |
williamvds | 85c5d313b5 | |
williamvds | 6797a74f78 | |
williamvds | 82443be506 | |
Frantisek Sumsal | 5310fc624e | |
Frantisek Sumsal | 0273f6e7f2 | |
Frantisek Sumsal | f1150eddf6 | |
Zbigniew Jędrzejewski-Szmek | 6f8ca84c9b | |
Zbigniew Jędrzejewski-Szmek | fe37e5a5d1 | |
Zbigniew Jędrzejewski-Szmek | af781878d5 | |
Zbigniew Jędrzejewski-Szmek | 80b8c3d7fd | |
Zbigniew Jędrzejewski-Szmek | 2c91906e25 | |
Zbigniew Jędrzejewski-Szmek | 7c50230378 | |
Zbigniew Jędrzejewski-Szmek | 60de93e744 |
|
@ -13,13 +13,14 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-20.04
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
env:
|
env:
|
||||||
- { COMPILER: "gcc", COMPILER_VERSION: "10" }
|
- { COMPILER: "gcc", COMPILER_VERSION: "10" }
|
||||||
- { COMPILER: "clang", COMPILER_VERSION: "10" }
|
- { COMPILER: "clang", COMPILER_VERSION: "10" }
|
||||||
|
- { COMPILER: "clang", COMPILER_VERSION: "11" }
|
||||||
env: ${{ matrix.env }}
|
env: ${{ matrix.env }}
|
||||||
steps:
|
steps:
|
||||||
- name: Repository checkout
|
- name: Repository checkout
|
||||||
|
|
|
@ -19,7 +19,6 @@ PACKAGES=(
|
||||||
expect
|
expect
|
||||||
fdisk
|
fdisk
|
||||||
gettext
|
gettext
|
||||||
iptables-dev
|
|
||||||
iputils-ping
|
iputils-ping
|
||||||
isc-dhcp-client
|
isc-dhcp-client
|
||||||
itstool
|
itstool
|
||||||
|
@ -28,6 +27,7 @@ PACKAGES=(
|
||||||
libcap-dev
|
libcap-dev
|
||||||
libcurl4-gnutls-dev
|
libcurl4-gnutls-dev
|
||||||
libfdisk-dev
|
libfdisk-dev
|
||||||
|
libfido2-dev
|
||||||
libgpg-error-dev
|
libgpg-error-dev
|
||||||
liblz4-dev
|
liblz4-dev
|
||||||
liblzma-dev
|
liblzma-dev
|
||||||
|
@ -38,6 +38,7 @@ PACKAGES=(
|
||||||
libqrencode-dev
|
libqrencode-dev
|
||||||
libssl-dev
|
libssl-dev
|
||||||
libxkbcommon-dev
|
libxkbcommon-dev
|
||||||
|
libxtables-dev
|
||||||
libzstd-dev
|
libzstd-dev
|
||||||
mount
|
mount
|
||||||
net-tools
|
net-tools
|
||||||
|
|
3
TODO
3
TODO
|
@ -813,9 +813,6 @@ Features:
|
||||||
|
|
||||||
* systemctl: if some operation fails, show log output?
|
* systemctl: if some operation fails, show log output?
|
||||||
|
|
||||||
* systemctl edit: use equivalent of cat() to insert existing config as a comment, prepended with #.
|
|
||||||
Upon editor exit, lines with one # are removed, lines with two # are left with one #, etc.
|
|
||||||
|
|
||||||
* exponential backoff in timesyncd when we cannot reach a server
|
* exponential backoff in timesyncd when we cannot reach a server
|
||||||
|
|
||||||
* timesyncd: add ugly bus calls to set NTP servers per-interface, for usage by NM
|
* timesyncd: add ugly bus calls to set NTP servers per-interface, for usage by NM
|
||||||
|
|
|
@ -138,7 +138,17 @@
|
||||||
and follow the same overriding rules. They are text files with the
|
and follow the same overriding rules. They are text files with the
|
||||||
<filename>.negative</filename> suffix. Empty lines and lines whose first character is
|
<filename>.negative</filename> suffix. Empty lines and lines whose first character is
|
||||||
<literal>;</literal> are ignored. Each line specifies one domain name which is the root of a DNS
|
<literal>;</literal> are ignored. Each line specifies one domain name which is the root of a DNS
|
||||||
subtree where validation shall be disabled.</para>
|
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>
|
||||||
|
|
||||||
<para>Negative trust anchors are useful to support private DNS
|
<para>Negative trust anchors are useful to support private DNS
|
||||||
subtrees that are not referenced from the Internet DNS hierarchy,
|
subtrees that are not referenced from the Internet DNS hierarchy,
|
||||||
|
|
|
@ -207,7 +207,7 @@ if dbus_docs.length() > 0
|
||||||
'@INPUT@'],
|
'@INPUT@'],
|
||||||
input : dbus_docs)
|
input : dbus_docs)
|
||||||
|
|
||||||
if conf.get('DEVELOPER_MODE') == 1
|
if conf.get('BUILD_MODE') == 'BUILD_MODE_DEVELOPER'
|
||||||
test('dbus-docs-fresh',
|
test('dbus-docs-fresh',
|
||||||
update_dbus_docs_py,
|
update_dbus_docs_py,
|
||||||
args : ['--build-dir=@0@'.format(project_build_root),
|
args : ['--build-dir=@0@'.format(project_build_root),
|
||||||
|
|
|
@ -156,12 +156,6 @@ node /org/freedesktop/resolve1 {
|
||||||
};
|
};
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
|
||||||
<!--method SetLinkDNSEx is not documented!-->
|
|
||||||
|
|
||||||
<!--method SetLinkDefaultRoute is not documented!-->
|
|
||||||
|
|
||||||
<!--method SetLinkDNSOverTLS is not documented!-->
|
|
||||||
|
|
||||||
<!--method RegisterService is not documented!-->
|
<!--method RegisterService is not documented!-->
|
||||||
|
|
||||||
<!--method UnregisterService is not documented!-->
|
<!--method UnregisterService is not documented!-->
|
||||||
|
@ -170,28 +164,8 @@ node /org/freedesktop/resolve1 {
|
||||||
|
|
||||||
<!--method ResetServerFeatures is not documented!-->
|
<!--method ResetServerFeatures is not documented!-->
|
||||||
|
|
||||||
<!--property LLMNR is not documented!-->
|
|
||||||
|
|
||||||
<!--property MulticastDNS is not documented!-->
|
|
||||||
|
|
||||||
<!--property DNSOverTLS is not documented!-->
|
|
||||||
|
|
||||||
<!--property DNSEx is not documented!-->
|
|
||||||
|
|
||||||
<!--property FallbackDNS is not documented!-->
|
|
||||||
|
|
||||||
<!--property FallbackDNSEx is not documented!-->
|
|
||||||
|
|
||||||
<!--property CurrentDNSServer is not documented!-->
|
|
||||||
|
|
||||||
<!--property CurrentDNSServerEx is not documented!-->
|
|
||||||
|
|
||||||
<!--property DNSSEC is not documented!-->
|
|
||||||
|
|
||||||
<!--property DNSSECNegativeTrustAnchors is not documented!-->
|
<!--property DNSSECNegativeTrustAnchors is not documented!-->
|
||||||
|
|
||||||
<!--property DNSStubListener is not documented!-->
|
|
||||||
|
|
||||||
<!--Autogenerated cross-references for systemd.directives, do not edit-->
|
<!--Autogenerated cross-references for systemd.directives, do not edit-->
|
||||||
|
|
||||||
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.resolve1.Manager"/>
|
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.resolve1.Manager"/>
|
||||||
|
@ -281,26 +255,28 @@ node /org/freedesktop/resolve1 {
|
||||||
<refsect2>
|
<refsect2>
|
||||||
<title>Methods</title>
|
<title>Methods</title>
|
||||||
|
|
||||||
<para><function>ResolveHostname()</function> takes a hostname and resolves it to one or more IP addresses.
|
<para><function>ResolveHostname()</function> takes a hostname and resolves it to one or more IP
|
||||||
As parameters it takes the Linux network interface index to execute the query on, or 0 if it may be
|
addresses. As parameters it takes the Linux network interface index to execute the query on, or 0 if
|
||||||
done on any suitable interface. The <varname>name</varname> parameter specifies the hostname to
|
it may be done on any suitable interface. The <varname>name</varname> parameter specifies the hostname
|
||||||
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
|
to resolve. Note that if required, IDNA conversion is applied to this name unless it is resolved via
|
||||||
limits the results to a specific address family. It may be <constant>AF_INET</constant>,
|
LLMNR or MulticastDNS. The <varname>family</varname> parameter limits the results to a specific address
|
||||||
<constant>AF_INET6</constant> or <constant>AF_UNSPEC</constant>. If <constant>AF_UNSPEC</constant> is specified (recommended), both kinds are retrieved, subject
|
family. It may be <constant>AF_INET</constant>, <constant>AF_INET6</constant> or
|
||||||
to local network configuration (i.e. if no local, routable IPv6 address is found, no IPv6 address is
|
<constant>AF_UNSPEC</constant>. If <constant>AF_UNSPEC</constant> is specified (recommended), both
|
||||||
retrieved; and similarly for IPv4). A 64-bit <varname>flags</varname> field may be used to alter the
|
kinds are retrieved, subject to local network configuration (i.e. if no local, routable IPv6 address is
|
||||||
behaviour of the resolver operation (see below). The method returns an array of address records. Each
|
found, no IPv6 address is retrieved; and similarly for IPv4). A 64-bit <varname>flags</varname> field
|
||||||
address record consists of the interface index the address belongs to, an address family as well as a
|
may be used to alter the behaviour of the resolver operation (see below). The method returns an array
|
||||||
byte array with the actual IP address data (which either has 4 or 16 elements, depending on the address
|
of address records. Each address record consists of the interface index the address belongs to, an
|
||||||
family). The returned address family will be one of <constant>AF_INET</constant> or
|
address family as well as a byte array with the actual IP address data (which either has 4 or 16
|
||||||
<constant>AF_INET6</constant>. For IPv6, the returned address interface index should be used to
|
elements, depending on the address family). The returned address family will be one of
|
||||||
initialize the .sin6_scope_id field of a <structname>struct sockaddr_in6</structname> instance to permit
|
<constant>AF_INET</constant> or <constant>AF_INET6</constant>. For IPv6, the returned address interface
|
||||||
support for resolution to link-local IP addresses. The address array is followed by the canonical name
|
index should be used to initialize the .sin6_scope_id field of a
|
||||||
of the host, which may or may not be identical to the resolved hostname. Finally, a 64-bit
|
<structname>struct sockaddr_in6</structname> instance to permit support for resolution to link-local IP
|
||||||
<varname>flags</varname> field is returned that is defined similarly to the <varname>flags</varname>
|
addresses. The address array is followed by the canonical name of the host, which may or may not be
|
||||||
field that was passed in, but contains information about the resolved data (see below). If the hostname
|
identical to the resolved hostname. Finally, a 64-bit <varname>flags</varname> field is returned that
|
||||||
passed in is an IPv4 or IPv6 address formatted as string, it is parsed, and the result is returned. In
|
is defined similarly to the <varname>flags</varname> field that was passed in, but contains information
|
||||||
this case, no network communication is done.</para>
|
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
|
<para><function>ResolveAddress()</function> executes the reverse operation: it takes an IP address and
|
||||||
acquires one or more hostnames for it. As parameters it takes the interface index to execute the query
|
acquires one or more hostnames for it. As parameters it takes the interface index to execute the query
|
||||||
|
@ -387,15 +363,19 @@ node /org/freedesktop/resolve1 {
|
||||||
<constant>AF_INET6</constant>), followed by a 4-byte or 16-byte array with the raw address data. This
|
<constant>AF_INET6</constant>), followed by a 4-byte or 16-byte array with the raw address data. This
|
||||||
method is a one-step shortcut for retrieving the Link object for a network interface using
|
method is a one-step shortcut for retrieving the Link object for a network interface using
|
||||||
<function>GetLink()</function> (see above) and then invoking the <function>SetDNS()</function> method
|
<function>GetLink()</function> (see above) and then invoking the <function>SetDNS()</function> method
|
||||||
(see below) on it.
|
(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>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>Network management software integrating with <filename>systemd-resolved</filename> should
|
<para><function>SetLinkDefaultRoute()</function> specifies whether the link shall be used as the
|
||||||
call this method (and the five below) after the interface appeared in the kernel (and thus after a
|
default route for name queries. See the description of name routing in
|
||||||
network interface index has been assigned), but before the network interfaces is activated
|
<citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||||
(<constant>IFF_UP</constant> set) so that all settings take effect during the full time the network
|
for details.</para>
|
||||||
interface is up. It is safe to alter settings while the interface is up, however. Use
|
|
||||||
<function>RevertLink()</function> (described below) to reset all per-interface settings.</para>
|
|
||||||
|
|
||||||
<para>The <function>SetLinkDomains()</function> method sets the search and routing domains to use on a
|
<para>The <function>SetLinkDomains()</function> method sets the search and routing domains to use on a
|
||||||
specific network interface for DNS look-ups. It takes a network interface index and an array of domains,
|
specific network interface for DNS look-ups. It takes a network interface index and an array of domains,
|
||||||
|
@ -432,8 +412,22 @@ node /org/freedesktop/resolve1 {
|
||||||
Negative Trust Anchors (NTAs) for a specific network interface. It takes a network interface index and a
|
Negative Trust Anchors (NTAs) for a specific network interface. It takes a network interface index and a
|
||||||
list of domains as arguments.</para>
|
list of domains as arguments.</para>
|
||||||
|
|
||||||
<para>The <function>RevertLink()</function> method may be used to revert all per-link settings done with
|
<para>The <function>SetLinkDNSOverTLS()</function> method enables or disables DNS-over-TLS.
|
||||||
the six methods described above to the defaults again.</para>
|
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>
|
||||||
|
|
||||||
<refsect3>
|
<refsect3>
|
||||||
<title>The Flags Parameter</title>
|
<title>The Flags Parameter</title>
|
||||||
|
@ -458,11 +452,11 @@ node /org/freedesktop/resolve1 {
|
||||||
classic unicast DNS, LLMNR via IPv4/UDP and IPv6/UDP respectively, as well as MulticastDNS via
|
classic unicast DNS, LLMNR via IPv4/UDP and IPv6/UDP respectively, as well as MulticastDNS via
|
||||||
IPv4/UDP and IPv6/UDP. If all of these five bits are off on input (which is strongly recommended) the
|
IPv4/UDP and IPv6/UDP. If all of these five bits are off on input (which is strongly recommended) the
|
||||||
look-up will be done via all suitable protocols for the specific look-up. Note that these flags
|
look-up will be done via all suitable protocols for the specific look-up. Note that these flags
|
||||||
operate as filter only, but cannot force a look-up to be done via a protocol. Specifically, <filename>systemd-resolved</filename>
|
operate as filter only, but cannot force a look-up to be done via a protocol. Specifically,
|
||||||
will only route look-ups within the .local TLD to MulticastDNS (plus some reverse look-up address
|
<filename>systemd-resolved</filename> will only route look-ups within the .local TLD to MulticastDNS
|
||||||
domains), and single-label names to LLMNR (plus some reverse address lookup domains). It will route
|
(plus some reverse look-up address domains), and single-label names to LLMNR (plus some reverse
|
||||||
neither of these to Unicast DNS servers. Also, it will do LLMNR and Multicast DNS only on interfaces
|
address lookup domains). It will route neither of these to Unicast DNS servers. Also, it will do
|
||||||
suitable for multicast.</para>
|
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
|
<para>On output, these five flags indicate which protocol was used to execute the operation, and hence
|
||||||
where the data was found.</para>
|
where the data was found.</para>
|
||||||
|
@ -498,34 +492,50 @@ node /org/freedesktop/resolve1 {
|
||||||
the data is "rightfully" unauthenticated (which includes cases where the underlying protocol or server
|
the data is "rightfully" unauthenticated (which includes cases where the underlying protocol or server
|
||||||
does not support authenticating data).</para>
|
does not support authenticating data).</para>
|
||||||
</refsect3>
|
</refsect3>
|
||||||
|
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
|
||||||
<refsect2>
|
<refsect2>
|
||||||
<title>Properties</title>
|
<title>Properties</title>
|
||||||
|
|
||||||
|
<para>The <varname>LLMNR</varname> and <varname>MulticastDNS</varname> properties report whether LLMNR
|
||||||
|
and MulticastDNS are (globally) enabled. Each may be one of <literal>yes</literal>,
|
||||||
|
<literal>no</literal>, and <literal>resolve</literal>. See <function>SetLinkLLMNR()</function>
|
||||||
|
and <function>SetLinkMulticastDNS()</function> above.</para>
|
||||||
|
|
||||||
<para><varname>LLMNRHostname</varname> contains the hostname currently exposed on the network via
|
<para><varname>LLMNRHostname</varname> contains the hostname currently exposed on the network via
|
||||||
LLMNR. It usually follows the system hostname as may be queried via
|
LLMNR. It usually follows the system hostname as may be queried via
|
||||||
<citerefentry project="man-pages"><refentrytitle>gethostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
|
<citerefentry project="man-pages"><refentrytitle>gethostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
|
||||||
but may differ if a conflict is detected on the network.</para>
|
but may differ if a conflict is detected on the network.</para>
|
||||||
|
|
||||||
<para><varname>DNS</varname> contains an array of all DNS servers currently used by
|
<para><varname>DNS</varname> and <varname>DNSEx</varname> contain arrays of all DNS servers currently
|
||||||
<filename>systemd-resolved</filename>. It contains similar information as the DNS server data written to
|
used by <filename>systemd-resolved</filename>. <varname>DNS</varname> contains information similar to
|
||||||
/run/systemd/resolve/resolv.conf. Each structure in the array consists of a numeric network interface
|
the DNS server data in <filename>/run/systemd/resolve/resolv.conf</filename>. Each structure in the
|
||||||
index, an address family, and a byte array containing the DNS server address (either 4 bytes in length
|
array consists of a numeric network interface index, an address family, and a byte array containing the
|
||||||
for IPv4 or 16 bytes in lengths for IPv6). The array contains DNS servers configured system-wide,
|
DNS server address (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6).
|
||||||
including those possibly read from a foreign <filename>/etc/resolv.conf</filename> or the
|
<varname>DNSEx</varname> is similar, but additionally contains the IP port and server name (used for
|
||||||
<varname>DNS=</varname> setting in <filename>/etc/systemd/resolved.conf</filename>, as well as
|
Server Name Indication, SNI). Both arrays contain DNS servers configured system-wide, including those
|
||||||
per-interface DNS server information either retrieved from
|
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>,
|
<citerefentry><refentrytitle>systemd-networkd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||||
or configured by external software via <function>SetLinkDNS()</function> (see above). The network
|
or configured by external software via <function>SetLinkDNS()</function> or
|
||||||
interface index will be 0 for the system-wide configured services and non-zero for the per-link
|
<function>SetLinkDNSEx()</function> (see above). The network interface index will be 0 for the
|
||||||
servers.</para>
|
system-wide configured services and non-zero for the per-link servers.</para>
|
||||||
|
|
||||||
<para>Similarly, the <varname>Domains</varname> property contains an array of all search and
|
<para><varname>FallbackDNS</varname> and <varname>FallbackDNSEx</varname> contain arrays of all DNS
|
||||||
routing domains currently used by <filename>systemd-resolved</filename>. Each entry consists of a network interface index (again, 0
|
servers configured as fallback servers, if any, using the same format as <varname>DNS</varname> and
|
||||||
encodes system-wide entries), the actual domain name, and whether the entry is used only for routing
|
<varname>DNSEx</varname> described above. See the description of <varname>FallbackDNS=</varname> in
|
||||||
(true) or for both routing and searching (false).</para>
|
<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>The <varname>TransactionStatistics</varname> property contains information about the number of
|
<para>The <varname>TransactionStatistics</varname> property contains information about the number of
|
||||||
transactions <filename>systemd-resolved</filename> has processed. It contains a pair of unsigned 64-bit counters, the first
|
transactions <filename>systemd-resolved</filename> has processed. It contains a pair of unsigned 64-bit counters, the first
|
||||||
|
@ -540,7 +550,14 @@ node /org/freedesktop/resolve1 {
|
||||||
operations so far. It exposes three 64-bit counters: the first being the total number of current cache
|
operations so far. It exposes three 64-bit counters: the first being the total number of current cache
|
||||||
entries (both positive and negative), the second the number of cache hits, and the third the number of
|
entries (both positive and negative), the second the number of cache hits, and the third the number of
|
||||||
cache misses. The latter counters may be reset using <function>ResetStatistics()</function> (see
|
cache misses. The latter counters may be reset using <function>ResetStatistics()</function> (see
|
||||||
above). </para>
|
above).</para>
|
||||||
|
|
||||||
|
<para>The <varname>DNSSEC</varname> property specifies current status of DNSSEC validation. It is one
|
||||||
|
of <literal>yes</literal> (validation is enforced), <literal>no</literal> (no validation is done),
|
||||||
|
<literal>allow-downgrade</literal> (validation is done if the current DNS server supports it). See the
|
||||||
|
description of <varname>DNSSEC=</varname> in
|
||||||
|
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>The <varname>DNSSECStatistics</varname> property contains information about the DNSSEC
|
<para>The <varname>DNSSECStatistics</varname> property contains information about the DNSSEC
|
||||||
validations executed so far. It contains four 64-bit counters: the number of secure, insecure, bogus,
|
validations executed so far. It contains four 64-bit counters: the number of secure, insecure, bogus,
|
||||||
|
@ -559,12 +576,20 @@ node /org/freedesktop/resolve1 {
|
||||||
DNSSEC is supported by DNS servers until it verifies that this is not the case. Thus, the reported
|
DNSSEC is supported by DNS servers until it verifies that this is not the case. Thus, the reported
|
||||||
value may initially be true, until the first transactions are executed.</para>
|
value may initially be true, until the first transactions are executed.</para>
|
||||||
|
|
||||||
|
<para>The <varname>DNSOverTLS</varname> boolean property reports whether DNS-over-TLS is enabled.
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>The <varname>ResolvConfMode</varname> property exposes how <filename>/etc/resolv.conf</filename>
|
<para>The <varname>ResolvConfMode</varname> property exposes how <filename>/etc/resolv.conf</filename>
|
||||||
is managed on the host. Currently, the values <literal>uplink</literal>, <literal>stub</literal>,
|
is managed on the host. Currently, the values <literal>uplink</literal>, <literal>stub</literal>,
|
||||||
<literal>static</literal> (these three correspond to the three different files
|
<literal>static</literal> (these three correspond to the three different files
|
||||||
<filename>systemd-resolved.service</filename> provides), <literal>foreign</literal> (the file is
|
<filename>systemd-resolved.service</filename> provides), <literal>foreign</literal> (the file is
|
||||||
managed by admin or another service, <filename>systemd-resolved.service</filename> just consumes it),
|
managed by admin or another service, <filename>systemd-resolved.service</filename> just consumes it),
|
||||||
<literal>missing</literal> (<filename>/etc/resolv.conf</filename> is missing).</para>
|
<literal>missing</literal> (<filename>/etc/resolv.conf</filename> is missing).</para>
|
||||||
|
|
||||||
|
<para>The <varname>DNSStubListener</varname> property reports whether the stub listener on port 53 is
|
||||||
|
enabled. Possible values are <literal>yes</literal> (enabled), <literal>no</literal> (disabled),
|
||||||
|
<literal>udp</literal> (only the UDP listener is enabled), and <literal>tcp</literal> (only the TCP
|
||||||
|
listener is enabled).</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
@ -619,40 +644,6 @@ node /org/freedesktop/resolve1/link/_1 {
|
||||||
};
|
};
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
|
||||||
<!--method SetDNSEx is not documented!-->
|
|
||||||
|
|
||||||
<!--method SetDomains is not documented!-->
|
|
||||||
|
|
||||||
<!--method SetDefaultRoute is not documented!-->
|
|
||||||
|
|
||||||
<!--method SetLLMNR is not documented!-->
|
|
||||||
|
|
||||||
<!--method SetMulticastDNS is not documented!-->
|
|
||||||
|
|
||||||
<!--method SetDNSOverTLS is not documented!-->
|
|
||||||
|
|
||||||
<!--method SetDNSSEC is not documented!-->
|
|
||||||
|
|
||||||
<!--method SetDNSSECNegativeTrustAnchors is not documented!-->
|
|
||||||
|
|
||||||
<!--method Revert is not documented!-->
|
|
||||||
|
|
||||||
<!--property DNSEx is not documented!-->
|
|
||||||
|
|
||||||
<!--property CurrentDNSServer is not documented!-->
|
|
||||||
|
|
||||||
<!--property CurrentDNSServerEx is not documented!-->
|
|
||||||
|
|
||||||
<!--property DefaultRoute is not documented!-->
|
|
||||||
|
|
||||||
<!--property LLMNR is not documented!-->
|
|
||||||
|
|
||||||
<!--property MulticastDNS is not documented!-->
|
|
||||||
|
|
||||||
<!--property DNSOverTLS is not documented!-->
|
|
||||||
|
|
||||||
<!--property DNSSEC is not documented!-->
|
|
||||||
|
|
||||||
<!--property DNSSECNegativeTrustAnchors is not documented!-->
|
<!--property DNSSECNegativeTrustAnchors is not documented!-->
|
||||||
|
|
||||||
<!--Autogenerated cross-references for systemd.directives, do not edit-->
|
<!--Autogenerated cross-references for systemd.directives, do not edit-->
|
||||||
|
@ -721,8 +712,13 @@ node /org/freedesktop/resolve1/link/_1 {
|
||||||
<function>SetLinkDNS()</function> on the Manager object, the main difference being that the later
|
<function>SetLinkDNS()</function> on the Manager object, the main difference being that the later
|
||||||
expects an interface index to be specified. Invoking the methods on the Manager interface has the
|
expects an interface index to be specified. Invoking the methods on the Manager interface has the
|
||||||
benefit of reducing roundtrips, as it is not necessary to first request the Link object path via
|
benefit of reducing roundtrips, as it is not necessary to first request the Link object path via
|
||||||
<function>GetLink()</function> before invoking the methods. For further details on these methods see
|
<function>GetLink()</function> before invoking the methods. The same relationship holds for
|
||||||
the <interfacename>Manager</interfacename> documentation above.</para>
|
<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>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
|
||||||
<refsect2>
|
<refsect2>
|
||||||
|
@ -744,8 +740,12 @@ node /org/freedesktop/resolve1/link/_1 {
|
||||||
assumed available until it is detected that the configured server does not actually support it. Thus,
|
assumed available until it is detected that the configured server does not actually support it. Thus,
|
||||||
this property may initially report that DNSSEC is supported on an interface.</para>
|
this property may initially report that DNSSEC is supported on an interface.</para>
|
||||||
|
|
||||||
|
<para><varname>DefaultRoute</varname> exposes a boolean field that indicates whether the interface will
|
||||||
|
be used as default route for name queries. See <function>SetLinkDefaultRoute()</function> above.</para>
|
||||||
|
|
||||||
<para>The other properties reflect the state of the various configuration settings for the link which
|
<para>The other properties reflect the state of the various configuration settings for the link which
|
||||||
may be set with the various methods calls such as SetDNS() or SetLLMNR().</para>
|
may be set with the various methods calls such as <function>SetDNS()</function> or
|
||||||
|
<function>SetLLMNR()</function>.</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
|
|
@ -87,17 +87,17 @@
|
||||||
<refsect1>
|
<refsect1>
|
||||||
<title>Synthetic Records</title>
|
<title>Synthetic Records</title>
|
||||||
|
|
||||||
<para><command>systemd-resolved</command> synthesizes DNS resource records (RRs) for the following
|
<para><command>systemd-resolved</command> synthetizes DNS resource records (RRs) for the following
|
||||||
cases:</para>
|
cases:</para>
|
||||||
|
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
<listitem><para>The local, configured hostname is resolved to all locally configured IP addresses
|
<listitem><para>The local, configured hostname is resolved to all locally configured IP addresses
|
||||||
ordered by their scope, or — if none are configured — the IPv4 address 127.0.0.2 (which is on the local
|
ordered by their scope, or — if none are configured — the IPv4 address 127.0.0.2 (which is on the local
|
||||||
loopback) and the IPv6 address ::1 (which is the local host).</para></listitem>
|
loopback interface) and the IPv6 address ::1 (which is the local host).</para></listitem>
|
||||||
|
|
||||||
<listitem><para>The hostnames <literal>localhost</literal> and <literal>localhost.localdomain</literal>
|
<listitem><para>The hostnames <literal>localhost</literal> and <literal>localhost.localdomain</literal>
|
||||||
(as well as any hostname ending in <literal>.localhost</literal> or
|
as well as any hostname ending in <literal>.localhost</literal> or
|
||||||
<literal>.localhost.localdomain</literal>) are resolved to the IP addresses 127.0.0.1 and ::1.
|
<literal>.localhost.localdomain</literal> are resolved to the IP addresses 127.0.0.1 and ::1.
|
||||||
</para></listitem>
|
</para></listitem>
|
||||||
|
|
||||||
<listitem><para>The hostname <literal>_gateway</literal> is resolved to all current default routing
|
<listitem><para>The hostname <literal>_gateway</literal> is resolved to all current default routing
|
||||||
|
@ -119,104 +119,162 @@
|
||||||
according to the following rules:</para>
|
according to the following rules:</para>
|
||||||
|
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
<listitem><para>Names for which synthetic records are generated (as listed in the previous section) are
|
<listitem><para>Names for which synthetic records are generated (the local hostname,
|
||||||
never routed to the network and a reply is sent immediately. In particular this means that lookups for
|
<literal>localhost</literal> and <literal>localdomain</literal>, local gateway, as listed in the
|
||||||
<literal>localhost</literal> are never routed to the network.</para></listitem>
|
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>Single-label names are routed to all local interfaces capable of IP multicasting, where
|
<listitem><para>Single-label names are resolved using LLMNR on all local interfaces where LLMNR is
|
||||||
LLMNR is not disabled, using the LLMNR protocol. Lookups for IPv4 addresses are only sent via LLMNR on
|
enabled. Lookups for IPv4 addresses are only sent via LLMNR on IPv4, and lookups for IPv6 addresses are
|
||||||
IPv4, and lookups for IPv6 addresses are only sent via LLMNR on IPv6. Lookups for the locally
|
only sent via LLMNR on IPv6. Note that lookups for single-label synthetized names are not routed to
|
||||||
configured hostname and the <literal>_gateway</literal> hostname are never routed to LLMNR.
|
LLMNR, MulticastDNS or unicast DNS.</para></listitem>
|
||||||
</para></listitem>
|
|
||||||
|
|
||||||
<listitem><para>Multi-label names with the domain suffix <literal>.local</literal> are routed to all
|
<listitem><para>Queries for the address records (A and AAAA) of single-label non-synthetized names are
|
||||||
local interfaces capable of IP multicasting, where MulticastDNS is not disabled, using the MulticastDNS
|
resolved via unicast DNS using search domains. For any interface which defines search domains, such
|
||||||
protocol. As with LLMNR, IPv4 address lookups are sent via IPv4 and IPv6 address lookups are sent via
|
look-ups are routed to that interface, suffixed with each of the search domains defined on that
|
||||||
IPv6.</para></listitem>
|
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>Resolution of address records (A and AAAA) via unicast DNS (i.e. not LLMNR or
|
<listitem><para>Multi-label names with the domain suffix <literal>.local</literal> are resolved using
|
||||||
MulticastDNS) for non-synthesized single-label names is allowed for non-top-level domains. This means
|
MulticastDNS on all local interfaces where MulticastDNS is enabled. As with LLMNR, IPv4 address lookups
|
||||||
that such records can be resolved when search domains are defined. For any interface which defines
|
are sent via IPv4 and IPv6 address lookups are sent via IPv6.</para></listitem>
|
||||||
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>Other multi-label names are routed to all local interfaces that have a DNS server
|
<listitem><para>Queries for multi-label names are routed via unicast DNS on local interfaces that have
|
||||||
configured, plus the globally configured DNS servers if there are any. Note that by default, lookups for
|
a DNS server configured, plus the globally configured DNS servers if there are any. Which interfaces
|
||||||
domains with the <literal>.local</literal> suffix are not routed to DNS servers, unless the domain is
|
are used is determined by the routing logic based on search and route-only domains, described below.
|
||||||
specified explicitly as routing or search domain for the DNS server and interface. This means that on
|
Note that by default, lookups for domains with the <literal>.local</literal> suffix are not routed to
|
||||||
networks where the <literal>.local</literal> domain is defined in a site-specific DNS server, explicit
|
DNS servers, unless the domain is specified explicitly as routing or search domain for the DNS server
|
||||||
search or routing domains need to be configured to make lookups within this DNS domain work. Note that
|
and interface. This means that on networks where the <literal>.local</literal> domain is defined in a
|
||||||
these days, it's generally recommended to avoid defining <literal>.local</literal> in a DNS server, as
|
site-specific DNS server, explicit search or routing domains need to be configured to make lookups work
|
||||||
<ulink url="https://tools.ietf.org/html/rfc6762">RFC6762</ulink> reserves this domain for exclusive
|
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
|
||||||
MulticastDNS use.</para></listitem>
|
MulticastDNS use.</para></listitem>
|
||||||
|
|
||||||
<listitem><para>Address lookups are routed similarly to multi-label names, with the exception that
|
<listitem><para>Address lookups (reverse lookups) are routed similarly to multi-label names, with the
|
||||||
addresses from the link-local address range are never routed to unicast DNS and are only resolved using
|
exception that addresses from the link-local address range are never routed to unicast DNS and are only
|
||||||
LLMNR and MulticastDNS (when enabled).</para></listitem>
|
resolved using LLMNR and MulticastDNS (when enabled).</para></listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
|
||||||
<para>If lookups are routed to multiple interfaces, the first successful response is returned (thus
|
<para>If lookups are routed to multiple interfaces, the first successful response is returned (thus
|
||||||
effectively merging the lookup zones on all matching interfaces). If the lookup failed on all interfaces,
|
effectively merging the lookup zones on all matching interfaces). If the lookup failed on all interfaces,
|
||||||
the last failing response is returned.</para>
|
the last failing response is returned.</para>
|
||||||
|
|
||||||
<para>Routing of lookups may be influenced by configuring per-interface domain names and other
|
<para>Routing of lookups is determined by the per-interface routing domains (search and route-only) and
|
||||||
settings. See
|
global search domains. See
|
||||||
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
|
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
|
||||||
<citerefentry><refentrytitle>resolvectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
|
<citerefentry><refentrytitle>resolvectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for a
|
||||||
details. The following query routing logic applies for unicast DNS traffic:</para>
|
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>
|
||||||
|
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
<listitem><para>If a name to look up matches (that is: is equal to or has as suffix) any of the
|
<listitem><para>If a name to look up matches (that is: is equal to or has as suffix) any of the
|
||||||
configured search or route-only domains of any link (see
|
configured routing domains (search or route-only) of any link, or the globally configured DNS settings,
|
||||||
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>),
|
"best matching" routing domain is determined: the matching one with the most labels. The query is then
|
||||||
or the globally configured DNS settings (see the discussion of <varname>Domains=</varname> in
|
sent to all DNS servers of any links or the globally configured DNS servers associated with this "best
|
||||||
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>),
|
matching" routing domain. (Note that more than one link might have this same "best matching" routing
|
||||||
"best matching" search/route-only domain is determined: the matching one with the most labels. The
|
domain configured, in which case the query is sent to all of them in parallel).</para>
|
||||||
query is then sent to all DNS servers of any links or the globally configured DNS servers associated
|
|
||||||
with this "best matching" search/route-only domain. (Note that more than one link might have this same
|
|
||||||
"best matching" search/route-only domain configured, in which case the query is sent to all of them in
|
|
||||||
parallel).</para>
|
|
||||||
|
|
||||||
<para>In case of single-label names, when search domains are defined, the same logic applies, except
|
<para>In case of single-label names, when search domains are defined, the same logic applies, except
|
||||||
that the name is first suffixed by the search domain.</para></listitem>
|
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>
|
||||||
|
|
||||||
<listitem><para>If a query does not match any configured search/route-only domain (neither per-link nor
|
<listitem><para>If a query does not match any configured routing domain (either per-link or global), it
|
||||||
global), it is sent to all DNS servers that are configured on links with the "DNS default route" option
|
is sent to all DNS servers that are configured on links with the <varname>DefaultRoute=</varname>
|
||||||
set, as well as the globally configured DNS server.</para></listitem>
|
option set, as well as the globally configured DNS server.</para></listitem>
|
||||||
|
|
||||||
<listitem><para>If there is no link configured as "DNS default route" and no global DNS server
|
<listitem><para>If there is no link configured as <varname>DefaultRoute=</varname> and no global DNS
|
||||||
configured, the compiled-in fallback DNS server is used.</para></listitem>
|
server configured, one of the compiled-in fallback DNS servers is used.</para></listitem>
|
||||||
|
|
||||||
<listitem><para>Otherwise the query is failed as no suitable DNS servers could be determined.
|
<listitem><para>Otherwise the unicast DNS query fails, as no suitable DNS servers can be determined.
|
||||||
</para></listitem>
|
</para></listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
|
||||||
<para>The "DNS default route" option is a boolean setting configurable with <command>resolvectl</command>
|
<para>The <varname>DefaultRoute=</varname> option is a boolean setting configurable with
|
||||||
or in <filename>.network</filename> files. If not set, it is implicitly determined based on the
|
<command>resolvectl</command> or in <filename>.network</filename> files. If not set, it is implicitly
|
||||||
configured DNS domains for a link: if there's any route-only domain (not matching <literal>~.</literal>)
|
determined based on the configured DNS domains for a link: if there's a route-only domain other than
|
||||||
it defaults to false, otherwise to true.</para>
|
<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
|
<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 search/route-only
|
search domains. In order to preferably route all DNS queries not explicitly matched by routing domain
|
||||||
domain configuration to a specific link, configure a <literal>~.</literal> route-only domain on it. This
|
configuration to a specific link, configure a <literal>~.</literal> route-only domain on it. This will
|
||||||
will ensure that other links will not be considered for these queries (unless they too carry such a
|
ensure that other links will not be considered for these queries (unless they too carry such a routing
|
||||||
route-only domain). In order to route all such DNS queries to a specific link only if no other link
|
domain). In order to route all such DNS queries to a specific link only if no other link is preferred,
|
||||||
is preferable, set the "DNS default route" option for the link to true and do not configure a
|
set the <varname>DefaultRoute=</varname> option for the link to true and do not configure a
|
||||||
<literal>~.</literal> route-only domain on it. Finally, in order to ensure that a specific link never
|
<literal>~.</literal> route-only domain on it. Finally, in order to ensure that a specific link never
|
||||||
receives any DNS traffic not matching any of its configured search/route-only domains, set the "DNS
|
receives any DNS traffic not matching any of its configured routing domains, set the
|
||||||
default route" option for it to false.</para>
|
<varname>DefaultRoute=</varname> option for it to false.</para>
|
||||||
|
|
||||||
<para>See
|
<para>See
|
||||||
<citerefentry><refentrytitle>org.freedesktop.resolve1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
<citerefentry><refentrytitle>org.freedesktop.resolve1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||||
for information about the D-Bus APIs <filename>systemd-resolved</filename> provides.</para>
|
for information about the D-Bus APIs <filename>systemd-resolved</filename> provides.</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Compatibility with the traditional glibc stub resolver</title>
|
||||||
|
|
||||||
|
<para>This section provides a short summary of differences in the stub resolver implemented by
|
||||||
|
<citerefentry><refentrytitle>nss-resolve</refentrytitle><manvolnum>8</manvolnum></citerefentry> together
|
||||||
|
with <command>systemd-resolved</command> and the tranditional stub resolver implemented in
|
||||||
|
<citerefentry><refentrytitle>nss-dns</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para>Some names are always resolved internally (see Synthetic Records above). Traditionally
|
||||||
|
they would be resolved by <filename>nss-files</filename>, and only if provided in
|
||||||
|
<filename>/etc/hosts</filename>.</para></listitem>
|
||||||
|
|
||||||
|
<listitem><para>Single-label names are not resolved for A and AAAA records using unicast DNS (unless
|
||||||
|
overriden with <varname>ResolveUnicastSingleLabel=</varname>, see
|
||||||
|
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
|
||||||
|
This is similar to the <option>no-tld-query</option> option being set in
|
||||||
|
<citerefentry><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||||
|
</para></listitem>
|
||||||
|
|
||||||
|
<listitem><para>Search domains are not used for <emphasis>suffixing</emphasis> of multi-label names.
|
||||||
|
(Search domains are nevertheless used for lookup <emphasis>routing</emphasis>, for names that were
|
||||||
|
originally specified as single-label or multi-label.) Any name with at least one dot is always
|
||||||
|
interpreted as a FQDN. <filename>nss-dns</filename> would resolve names both as relative (using search
|
||||||
|
domains) and absolute FQDN names. Some names would be resolved as relative first, and after that query
|
||||||
|
has failed, as absolute, while other names would be resolved in opposite order. The
|
||||||
|
<varname>ndots</varname> option in <filename>/etc/resolv.conf</filename> was used to control how many
|
||||||
|
dots the name needs to have to be resolved as relative first. This stub resolver does not implement
|
||||||
|
this at all: multi-label names are only resolved as FQDNs. (There are currently more than 1500
|
||||||
|
top-level domain names defined, and new ones are added regularly, often using "attractive" names that
|
||||||
|
are also likely to be used locally. Not looking up multi-label names in this fashion avoids fragility
|
||||||
|
in both directions: a valid global name could be obscured by a local name, and resolution of a relative
|
||||||
|
local name could suddenly break when a new top-level domain is created, or when a new subdomain of a
|
||||||
|
top-level domain in registered. Resolving any given name as either relative or absolute avoids this
|
||||||
|
ambiguity.)</para></listitem>
|
||||||
|
|
||||||
|
<listitem><para>This resolver has a notion of the special <literal>.local</literal> domain used for
|
||||||
|
MulticastDNS, and will not route queries with that suffix to unicast DNS servers unless explicitly
|
||||||
|
configured, see above. Also, reverse lookups for link-local addresses are not sent to unicast DNS
|
||||||
|
servers.</para></listitem>
|
||||||
|
|
||||||
|
<listitem><para>This resolver reads and caches <filename>/etc/hosts</filename> internally. (In other
|
||||||
|
words, <filename>nss-resolve</filename> replaces <filename>nss-files</filename> in addition to
|
||||||
|
<filename>nss-dns</filename>). Entries in <filename>/etc/hosts</filename> have highest priority.</para>
|
||||||
|
</listitem>
|
||||||
|
|
||||||
|
<listitem><para>This resolver also implements LLMNR and MulticastDNS in addition to the classic unicast
|
||||||
|
DNS protocol, and will resolve single-label names using LLMNR (when enabled) and names ending in
|
||||||
|
<literal>.local</literal> using MulticastDNS (when enabled).</para></listitem>
|
||||||
|
|
||||||
|
<listitem><para>Environment variables <varname>$LOCALDOMAIN</varname> and
|
||||||
|
<varname>$RES_OPTIONS</varname> described in
|
||||||
|
<citerefentry><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> are not
|
||||||
|
supported currently.</para></listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
<title><filename>/etc/resolv.conf</filename></title>
|
<title><filename>/etc/resolv.conf</filename></title>
|
||||||
|
|
||||||
|
@ -303,7 +361,6 @@
|
||||||
synchronous way.</para></listitem>
|
synchronous way.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
|
|
|
@ -38,8 +38,8 @@ relative_source_path = run_command('realpath',
|
||||||
project_source_root).stdout().strip()
|
project_source_root).stdout().strip()
|
||||||
conf.set_quoted('RELATIVE_SOURCE_PATH', relative_source_path)
|
conf.set_quoted('RELATIVE_SOURCE_PATH', relative_source_path)
|
||||||
|
|
||||||
conf.set10('DEVELOPER_MODE', get_option('mode') == 'developer',
|
conf.set('BUILD_MODE', 'BUILD_MODE_' + get_option('mode').to_upper(),
|
||||||
description : 'enable additional checks only suitable in development')
|
description : 'tailor build to development or release builds')
|
||||||
|
|
||||||
want_ossfuzz = get_option('oss-fuzz')
|
want_ossfuzz = get_option('oss-fuzz')
|
||||||
want_libfuzzer = get_option('llvm-fuzz')
|
want_libfuzzer = get_option('llvm-fuzz')
|
||||||
|
|
|
@ -5,7 +5,7 @@ option('version-tag', type : 'string',
|
||||||
description : 'override the git version string')
|
description : 'override the git version string')
|
||||||
|
|
||||||
option('mode', type : 'combo', choices : ['developer', 'release'],
|
option('mode', type : 'combo', choices : ['developer', 'release'],
|
||||||
description : 'enable additional checks suitable for systemd development')
|
description : 'autoenable features suitable for systemd development/release builds')
|
||||||
|
|
||||||
option('split-usr', type : 'combo', choices : ['auto', 'true', 'false'],
|
option('split-usr', type : 'combo', choices : ['auto', 'true', 'false'],
|
||||||
description : '''/bin, /sbin aren't symlinks into /usr''')
|
description : '''/bin, /sbin aren't symlinks into /usr''')
|
||||||
|
|
|
@ -161,3 +161,8 @@
|
||||||
_IDN_FEATURE_ " " \
|
_IDN_FEATURE_ " " \
|
||||||
_PCRE2_FEATURE_ " " \
|
_PCRE2_FEATURE_ " " \
|
||||||
_CGROUP_HIERARCHY_
|
_CGROUP_HIERARCHY_
|
||||||
|
|
||||||
|
enum {
|
||||||
|
BUILD_MODE_DEVELOPER,
|
||||||
|
BUILD_MODE_RELEASE,
|
||||||
|
};
|
||||||
|
|
|
@ -252,7 +252,8 @@ int write_string_file_ts(
|
||||||
/* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */
|
/* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */
|
||||||
fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY |
|
fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY |
|
||||||
(FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) |
|
(FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) |
|
||||||
(FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0),
|
(FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) |
|
||||||
|
(FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0),
|
||||||
(FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
|
(FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
r = -errno;
|
r = -errno;
|
||||||
|
|
|
@ -16,14 +16,15 @@
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
WRITE_STRING_FILE_CREATE = 1 << 0,
|
WRITE_STRING_FILE_CREATE = 1 << 0,
|
||||||
WRITE_STRING_FILE_ATOMIC = 1 << 1,
|
WRITE_STRING_FILE_TRUNCATE = 1 << 1,
|
||||||
WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 2,
|
WRITE_STRING_FILE_ATOMIC = 1 << 2,
|
||||||
WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 3,
|
WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3,
|
||||||
WRITE_STRING_FILE_SYNC = 1 << 4,
|
WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4,
|
||||||
WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 5,
|
WRITE_STRING_FILE_SYNC = 1 << 5,
|
||||||
WRITE_STRING_FILE_NOFOLLOW = 1 << 6,
|
WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 6,
|
||||||
WRITE_STRING_FILE_MKDIR_0755 = 1 << 7,
|
WRITE_STRING_FILE_NOFOLLOW = 1 << 7,
|
||||||
WRITE_STRING_FILE_MODE_0600 = 1 << 8,
|
WRITE_STRING_FILE_MKDIR_0755 = 1 << 8,
|
||||||
|
WRITE_STRING_FILE_MODE_0600 = 1 << 9,
|
||||||
|
|
||||||
/* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
|
/* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
|
||||||
more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
|
more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
|
||||||
|
|
|
@ -650,4 +650,8 @@ static inline int __coverity_check_and_return__(int condition) {
|
||||||
_copy; \
|
_copy; \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
static inline size_t size_add(size_t x, size_t y) {
|
||||||
|
return y >= SIZE_MAX - x ? SIZE_MAX : x + y;
|
||||||
|
}
|
||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
|
|
||||||
#ifdef CAP_LAST_CAP
|
#ifdef CAP_LAST_CAP
|
||||||
# if CAP_LAST_CAP > SYSTEMD_CAP_LAST_CAP
|
# if CAP_LAST_CAP > SYSTEMD_CAP_LAST_CAP
|
||||||
# if DEVELOPER_MODE && defined(TEST_CAPABILITY_C)
|
# if BUILD_MODE == BUILD_MODE_DEVELOPER && defined(TEST_CAPABILITY_C)
|
||||||
# warning "The capability list here is outdated"
|
# warning "The capability list here is outdated"
|
||||||
# endif
|
# endif
|
||||||
# else
|
# else
|
||||||
|
|
|
@ -33,6 +33,12 @@ static inline bool streq_ptr(const char *a, const char *b) {
|
||||||
return strcmp_ptr(a, b) == 0;
|
return strcmp_ptr(a, b) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline char* strstr_ptr(const char *haystack, const char *needle) {
|
||||||
|
if (!haystack || !needle)
|
||||||
|
return NULL;
|
||||||
|
return strstr(haystack, needle);
|
||||||
|
}
|
||||||
|
|
||||||
static inline const char* strempty(const char *s) {
|
static inline const char* strempty(const char *s) {
|
||||||
return s ?: "";
|
return s ?: "";
|
||||||
}
|
}
|
||||||
|
@ -53,6 +59,10 @@ static inline const char* true_false(bool b) {
|
||||||
return b ? "true" : "false";
|
return b ? "true" : "false";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline const char* plus_minus(bool b) {
|
||||||
|
return b ? "+" : "-";
|
||||||
|
}
|
||||||
|
|
||||||
static inline const char* one_zero(bool b) {
|
static inline const char* one_zero(bool b) {
|
||||||
return b ? "1" : "0";
|
return b ? "1" : "0";
|
||||||
}
|
}
|
||||||
|
|
|
@ -537,6 +537,19 @@ int strv_consume_prepend(char ***l, char *value) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int strv_prepend(char ***l, const char *value) {
|
||||||
|
char *v;
|
||||||
|
|
||||||
|
if (!value)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
v = strdup(value);
|
||||||
|
if (!v)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
return strv_consume_prepend(l, v);
|
||||||
|
}
|
||||||
|
|
||||||
int strv_extend(char ***l, const char *value) {
|
int strv_extend(char ***l, const char *value) {
|
||||||
char *v;
|
char *v;
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ size_t strv_length(char * const *l) _pure_;
|
||||||
|
|
||||||
int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates);
|
int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates);
|
||||||
int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix);
|
int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix);
|
||||||
|
int strv_prepend(char ***l, const char *value);
|
||||||
int strv_extend(char ***l, const char *value);
|
int strv_extend(char ***l, const char *value);
|
||||||
int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
|
int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
|
||||||
int strv_extend_front(char ***l, const char *value);
|
int strv_extend_front(char ***l, const char *value);
|
||||||
|
|
|
@ -87,7 +87,7 @@ struct ExecStatus {
|
||||||
dual_timestamp exit_timestamp;
|
dual_timestamp exit_timestamp;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int code; /* as in siginfo_t::si_code */
|
int code; /* as in siginfo_t::si_code */
|
||||||
int status; /* as in sigingo_t::si_status */
|
int status; /* as in siginfo_t::si_status */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Stores information about commands we execute. Covers both configuration settings as well as runtime data. */
|
/* Stores information about commands we execute. Covers both configuration settings as well as runtime data. */
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include "string-table.h"
|
#include "string-table.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
#include "terminal-util.h"
|
#include "terminal-util.h"
|
||||||
|
#include "utf8.h"
|
||||||
#include "verbs.h"
|
#include "verbs.h"
|
||||||
|
|
||||||
static int arg_family = AF_UNSPEC;
|
static int arg_family = AF_UNSPEC;
|
||||||
|
@ -1296,24 +1297,46 @@ static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m,
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
strv_sort(*l);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p) {
|
static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p) {
|
||||||
|
const unsigned indent = strlen("Global: "); /* Use the same indentation everywhere to make things nice */
|
||||||
|
int pos1, pos2;
|
||||||
|
|
||||||
|
if (ifname)
|
||||||
|
printf("%s%nLink %i (%s)%n%s:", ansi_highlight(), &pos1, ifindex, ifname, &pos2, ansi_normal());
|
||||||
|
else
|
||||||
|
printf("%s%nGlobal%n%s:", ansi_highlight(), &pos1, &pos2, ansi_normal());
|
||||||
|
|
||||||
|
size_t cols = columns(), position = pos2 - pos1 + 2;
|
||||||
char **i;
|
char **i;
|
||||||
|
|
||||||
printf("%sLink %i (%s)%s:",
|
STRV_FOREACH(i, p) {
|
||||||
ansi_highlight(), ifindex, ifname, ansi_normal());
|
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. */
|
||||||
|
|
||||||
STRV_FOREACH(i, p)
|
if (position <= indent || size_add(size_add(position, 1), our_len) < cols) {
|
||||||
printf(" %s", *i);
|
printf(" %s", *i);
|
||||||
|
position = size_add(size_add(position, 1), our_len);
|
||||||
|
} else {
|
||||||
|
printf("\n%*s%s", indent, "", *i);
|
||||||
|
position = size_add(our_len, indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct link_info {
|
static int status_print_strv_global(char **p) {
|
||||||
|
return status_print_strv_ifindex(0, NULL, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct LinkInfo {
|
||||||
uint64_t scopes_mask;
|
uint64_t scopes_mask;
|
||||||
const char *llmnr;
|
const char *llmnr;
|
||||||
const char *mdns;
|
const char *mdns;
|
||||||
|
@ -1327,9 +1350,26 @@ struct link_info {
|
||||||
char **ntas;
|
char **ntas;
|
||||||
bool dnssec_supported;
|
bool dnssec_supported;
|
||||||
bool default_route;
|
bool default_route;
|
||||||
};
|
} LinkInfo;
|
||||||
|
|
||||||
static void link_info_clear(struct link_info *p) {
|
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) {
|
||||||
free(p->current_dns);
|
free(p->current_dns);
|
||||||
free(p->current_dns_ex);
|
free(p->current_dns_ex);
|
||||||
strv_free(p->dns);
|
strv_free(p->dns);
|
||||||
|
@ -1338,6 +1378,17 @@ static void link_info_clear(struct link_info *p) {
|
||||||
strv_free(p->ntas);
|
strv_free(p->ntas);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void global_info_clear(GlobalInfo *p) {
|
||||||
|
free(p->current_dns);
|
||||||
|
free(p->current_dns_ex);
|
||||||
|
strv_free(p->dns);
|
||||||
|
strv_free(p->dns_ex);
|
||||||
|
strv_free(p->fallback_dns);
|
||||||
|
strv_free(p->fallback_dns_ex);
|
||||||
|
strv_free(p->domains);
|
||||||
|
strv_free(p->ntas);
|
||||||
|
}
|
||||||
|
|
||||||
static int dump_list(Table *table, const char *prefix, char * const *l) {
|
static int dump_list(Table *table, const char *prefix, char * const *l) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
@ -1346,33 +1397,88 @@ static int dump_list(Table *table, const char *prefix, char * const *l) {
|
||||||
|
|
||||||
r = table_add_many(table,
|
r = table_add_many(table,
|
||||||
TABLE_STRING, prefix,
|
TABLE_STRING, prefix,
|
||||||
TABLE_STRV, l);
|
TABLE_STRV_WRAPPED, l);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return table_log_add_error(r);
|
return table_log_add_error(r);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int strv_extend_extended_bool(char ***strv, const char *name, const char *value) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
r = parse_boolean(value);
|
||||||
|
if (r >= 0)
|
||||||
|
return strv_extendf(strv, "%s%s", plus_minus(r), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return strv_extendf(strv, "%s=%s", name, value ?: "???");
|
||||||
|
}
|
||||||
|
|
||||||
|
static char** link_protocol_status(const LinkInfo *info) {
|
||||||
|
_cleanup_strv_free_ char **s = NULL;
|
||||||
|
|
||||||
|
if (strv_extendf(&s, "%sDefaultRoute", plus_minus(info->default_route)) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extend_extended_bool(&s, "LLMNR", info->llmnr) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extend_extended_bool(&s, "mDNS", info->mdns) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extend_extended_bool(&s, "DNSOverTLS", info->dns_over_tls) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extendf(&s, "DNSSEC=%s/%s",
|
||||||
|
info->dnssec ?: "???",
|
||||||
|
info->dnssec_supported ? "supported" : "unsupported") < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return TAKE_PTR(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char** global_protocol_status(const GlobalInfo *info) {
|
||||||
|
_cleanup_strv_free_ char **s = NULL;
|
||||||
|
|
||||||
|
if (strv_extend_extended_bool(&s, "LLMNR", info->llmnr) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extend_extended_bool(&s, "mDNS", info->mdns) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extend_extended_bool(&s, "DNSOverTLS", info->dns_over_tls) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (strv_extendf(&s, "DNSSEC=%s/%s",
|
||||||
|
info->dnssec ?: "???",
|
||||||
|
info->dnssec_supported ? "supported" : "unsupported") < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return TAKE_PTR(s);
|
||||||
|
}
|
||||||
|
|
||||||
static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode mode, bool *empty_line) {
|
static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode mode, bool *empty_line) {
|
||||||
static const struct bus_properties_map property_map[] = {
|
static const struct bus_properties_map property_map[] = {
|
||||||
{ "ScopesMask", "t", NULL, offsetof(struct link_info, scopes_mask) },
|
{ "ScopesMask", "t", NULL, offsetof(LinkInfo, scopes_mask) },
|
||||||
{ "DNS", "a(iay)", map_link_dns_servers, offsetof(struct link_info, dns) },
|
{ "DNS", "a(iay)", map_link_dns_servers, offsetof(LinkInfo, dns) },
|
||||||
{ "DNSEx", "a(iayqs)", map_link_dns_servers_ex, offsetof(struct link_info, dns_ex) },
|
{ "DNSEx", "a(iayqs)", map_link_dns_servers_ex, offsetof(LinkInfo, dns_ex) },
|
||||||
{ "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(struct link_info, current_dns) },
|
{ "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(LinkInfo, current_dns) },
|
||||||
{ "CurrentDNSServerEx", "(iayqs)", map_link_current_dns_server_ex, offsetof(struct link_info, current_dns_ex) },
|
{ "CurrentDNSServerEx", "(iayqs)", map_link_current_dns_server_ex, offsetof(LinkInfo, current_dns_ex) },
|
||||||
{ "Domains", "a(sb)", map_link_domains, offsetof(struct link_info, domains) },
|
{ "Domains", "a(sb)", map_link_domains, offsetof(LinkInfo, domains) },
|
||||||
{ "DefaultRoute", "b", NULL, offsetof(struct link_info, default_route) },
|
{ "DefaultRoute", "b", NULL, offsetof(LinkInfo, default_route) },
|
||||||
{ "LLMNR", "s", NULL, offsetof(struct link_info, llmnr) },
|
{ "LLMNR", "s", NULL, offsetof(LinkInfo, llmnr) },
|
||||||
{ "MulticastDNS", "s", NULL, offsetof(struct link_info, mdns) },
|
{ "MulticastDNS", "s", NULL, offsetof(LinkInfo, mdns) },
|
||||||
{ "DNSOverTLS", "s", NULL, offsetof(struct link_info, dns_over_tls) },
|
{ "DNSOverTLS", "s", NULL, offsetof(LinkInfo, dns_over_tls) },
|
||||||
{ "DNSSEC", "s", NULL, offsetof(struct link_info, dnssec) },
|
{ "DNSSEC", "s", NULL, offsetof(LinkInfo, dnssec) },
|
||||||
{ "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct link_info, ntas) },
|
{ "DNSSECNegativeTrustAnchors", "as", bus_map_strv_sort, offsetof(LinkInfo, ntas) },
|
||||||
{ "DNSSECSupported", "b", NULL, offsetof(struct link_info, dnssec_supported) },
|
{ "DNSSECSupported", "b", NULL, offsetof(LinkInfo, dnssec_supported) },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||||
_cleanup_(link_info_clear) struct link_info link_info = {};
|
_cleanup_(link_info_clear) LinkInfo link_info = {};
|
||||||
_cleanup_(table_unrefp) Table *table = NULL;
|
_cleanup_(table_unrefp) Table *table = NULL;
|
||||||
_cleanup_free_ char *p = NULL;
|
_cleanup_free_ char *p = NULL;
|
||||||
char ifi[DECIMAL_STR_MAX(int)], ifname[IF_NAMESIZE + 1] = "";
|
char ifi[DECIMAL_STR_MAX(int)], ifname[IF_NAMESIZE + 1] = "";
|
||||||
|
@ -1496,19 +1602,13 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return table_log_add_error(r);
|
return table_log_add_error(r);
|
||||||
|
|
||||||
|
_cleanup_strv_free_ char **pstatus = link_protocol_status(&link_info);
|
||||||
|
if (!pstatus)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
r = table_add_many(table,
|
r = table_add_many(table,
|
||||||
TABLE_STRING, "DefaultRoute setting:",
|
TABLE_STRING, "Protocols:",
|
||||||
TABLE_BOOLEAN, link_info.default_route,
|
TABLE_STRV_WRAPPED, pstatus);
|
||||||
TABLE_STRING, "LLMNR setting:",
|
|
||||||
TABLE_STRING, strna(link_info.llmnr),
|
|
||||||
TABLE_STRING, "MulticastDNS setting:",
|
|
||||||
TABLE_STRING, strna(link_info.mdns),
|
|
||||||
TABLE_STRING, "DNSOverTLS setting:",
|
|
||||||
TABLE_STRING, strna(link_info.dns_over_tls),
|
|
||||||
TABLE_STRING, "DNSSEC setting:",
|
|
||||||
TABLE_STRING, strna(link_info.dnssec),
|
|
||||||
TABLE_STRING, "DNSSEC supported:",
|
|
||||||
TABLE_BOOLEAN, link_info.dnssec_supported);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return table_log_add_error(r);
|
return table_log_add_error(r);
|
||||||
|
|
||||||
|
@ -1631,71 +1731,32 @@ static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
return 0;
|
strv_sort(*l);
|
||||||
}
|
|
||||||
|
|
||||||
static int status_print_strv_global(char **p) {
|
|
||||||
char **i;
|
|
||||||
|
|
||||||
printf("%sGlobal%s:", ansi_highlight(), ansi_normal());
|
|
||||||
|
|
||||||
STRV_FOREACH(i, p)
|
|
||||||
printf(" %s", *i);
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct global_info {
|
|
||||||
char *current_dns;
|
|
||||||
char *current_dns_ex;
|
|
||||||
char **dns;
|
|
||||||
char **dns_ex;
|
|
||||||
char **fallback_dns;
|
|
||||||
char **fallback_dns_ex;
|
|
||||||
char **domains;
|
|
||||||
char **ntas;
|
|
||||||
const char *llmnr;
|
|
||||||
const char *mdns;
|
|
||||||
const char *dns_over_tls;
|
|
||||||
const char *dnssec;
|
|
||||||
const char *resolv_conf_mode;
|
|
||||||
bool dnssec_supported;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void global_info_clear(struct global_info *p) {
|
|
||||||
free(p->current_dns);
|
|
||||||
free(p->current_dns_ex);
|
|
||||||
strv_free(p->dns);
|
|
||||||
strv_free(p->dns_ex);
|
|
||||||
strv_free(p->fallback_dns);
|
|
||||||
strv_free(p->fallback_dns_ex);
|
|
||||||
strv_free(p->domains);
|
|
||||||
strv_free(p->ntas);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
|
static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
|
||||||
static const struct bus_properties_map property_map[] = {
|
static const struct bus_properties_map property_map[] = {
|
||||||
{ "DNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, dns) },
|
{ "DNS", "a(iiay)", map_global_dns_servers, offsetof(GlobalInfo, dns) },
|
||||||
{ "DNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(struct global_info, dns_ex) },
|
{ "DNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(GlobalInfo, dns_ex) },
|
||||||
{ "FallbackDNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, fallback_dns) },
|
{ "FallbackDNS", "a(iiay)", map_global_dns_servers, offsetof(GlobalInfo, fallback_dns) },
|
||||||
{ "FallbackDNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(struct global_info, fallback_dns_ex) },
|
{ "FallbackDNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(GlobalInfo, fallback_dns_ex) },
|
||||||
{ "CurrentDNSServer", "(iiay)", map_global_current_dns_server, offsetof(struct global_info, current_dns) },
|
{ "CurrentDNSServer", "(iiay)", map_global_current_dns_server, offsetof(GlobalInfo, current_dns) },
|
||||||
{ "CurrentDNSServerEx", "(iiayqs)", map_global_current_dns_server_ex, offsetof(struct global_info, current_dns_ex) },
|
{ "CurrentDNSServerEx", "(iiayqs)", map_global_current_dns_server_ex, offsetof(GlobalInfo, current_dns_ex) },
|
||||||
{ "Domains", "a(isb)", map_global_domains, offsetof(struct global_info, domains) },
|
{ "Domains", "a(isb)", map_global_domains, offsetof(GlobalInfo, domains) },
|
||||||
{ "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct global_info, ntas) },
|
{ "DNSSECNegativeTrustAnchors", "as", bus_map_strv_sort, offsetof(GlobalInfo, ntas) },
|
||||||
{ "LLMNR", "s", NULL, offsetof(struct global_info, llmnr) },
|
{ "LLMNR", "s", NULL, offsetof(GlobalInfo, llmnr) },
|
||||||
{ "MulticastDNS", "s", NULL, offsetof(struct global_info, mdns) },
|
{ "MulticastDNS", "s", NULL, offsetof(GlobalInfo, mdns) },
|
||||||
{ "DNSOverTLS", "s", NULL, offsetof(struct global_info, dns_over_tls) },
|
{ "DNSOverTLS", "s", NULL, offsetof(GlobalInfo, dns_over_tls) },
|
||||||
{ "DNSSEC", "s", NULL, offsetof(struct global_info, dnssec) },
|
{ "DNSSEC", "s", NULL, offsetof(GlobalInfo, dnssec) },
|
||||||
{ "DNSSECSupported", "b", NULL, offsetof(struct global_info, dnssec_supported) },
|
{ "DNSSECSupported", "b", NULL, offsetof(GlobalInfo, dnssec_supported) },
|
||||||
{ "ResolvConfMode", "s", NULL, offsetof(struct global_info, resolv_conf_mode) },
|
{ "ResolvConfMode", "s", NULL, offsetof(GlobalInfo, resolv_conf_mode) },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||||
_cleanup_(global_info_clear) struct global_info global_info = {};
|
_cleanup_(global_info_clear) GlobalInfo global_info = {};
|
||||||
_cleanup_(table_unrefp) Table *table = NULL;
|
_cleanup_(table_unrefp) Table *table = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
@ -1760,18 +1821,14 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
|
||||||
|
|
||||||
table_set_header(table, false);
|
table_set_header(table, false);
|
||||||
|
|
||||||
|
_cleanup_strv_free_ char **pstatus = global_protocol_status(&global_info);
|
||||||
|
if (!pstatus)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
r = table_add_many(table,
|
r = table_add_many(table,
|
||||||
TABLE_STRING, "LLMNR setting:",
|
TABLE_STRING, "Protocols:",
|
||||||
TABLE_SET_ALIGN_PERCENT, 100,
|
TABLE_SET_ALIGN_PERCENT, 100,
|
||||||
TABLE_STRING, strna(global_info.llmnr),
|
TABLE_STRV_WRAPPED, pstatus);
|
||||||
TABLE_STRING, "MulticastDNS setting:",
|
|
||||||
TABLE_STRING, strna(global_info.mdns),
|
|
||||||
TABLE_STRING, "DNSOverTLS setting:",
|
|
||||||
TABLE_STRING, strna(global_info.dns_over_tls),
|
|
||||||
TABLE_STRING, "DNSSEC setting:",
|
|
||||||
TABLE_STRING, strna(global_info.dnssec),
|
|
||||||
TABLE_STRING, "DNSSEC supported:",
|
|
||||||
TABLE_BOOLEAN, global_info.dnssec_supported);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return table_log_add_error(r);
|
return table_log_add_error(r);
|
||||||
|
|
||||||
|
|
|
@ -273,7 +273,7 @@ static int write_uplink_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSe
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ordered_set_isempty(domains))
|
if (ordered_set_isempty(domains))
|
||||||
fputs("search .", f); /* Make sure that if the local hostname is chosen as fqdn this does not
|
fputs("search .\n", f); /* Make sure that if the local hostname is chosen as fqdn this does not
|
||||||
* imply a search domain */
|
* imply a search domain */
|
||||||
else
|
else
|
||||||
write_resolv_conf_search(domains, f);
|
write_resolv_conf_search(domains, f);
|
||||||
|
@ -302,7 +302,7 @@ static int write_stub_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet
|
||||||
"options edns0 trust-ad\n", f);
|
"options edns0 trust-ad\n", f);
|
||||||
|
|
||||||
if (ordered_set_isempty(domains))
|
if (ordered_set_isempty(domains))
|
||||||
fputs("search .", f); /* Make sure that if the local hostname is chosen as fqdn this does not
|
fputs("search .\n", f); /* Make sure that if the local hostname is chosen as fqdn this does not
|
||||||
* imply a search domain */
|
* imply a search domain */
|
||||||
else
|
else
|
||||||
write_resolv_conf_search(domains, f);
|
write_resolv_conf_search(domains, f);
|
||||||
|
|
|
@ -25,5 +25,6 @@
|
||||||
#LLMNR=@DEFAULT_LLMNR_MODE@
|
#LLMNR=@DEFAULT_LLMNR_MODE@
|
||||||
#Cache=yes
|
#Cache=yes
|
||||||
#DNSStubListener=yes
|
#DNSStubListener=yes
|
||||||
|
#DNSStubListenerExtra=
|
||||||
#ReadEtcHosts=yes
|
#ReadEtcHosts=yes
|
||||||
#ResolveUnicastSingleLabel=no
|
#ResolveUnicastSingleLabel=no
|
||||||
|
|
|
@ -25,6 +25,23 @@ int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_err
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int bus_map_strv_sort(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
|
||||||
|
_cleanup_strv_free_ char **l = NULL;
|
||||||
|
char ***p = userdata;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = bus_message_read_strv_extend(m, &l);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = strv_extend_strv(p, l, false);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
strv_sort(*p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, unsigned flags, sd_bus_error *error, void *userdata) {
|
static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, unsigned flags, sd_bus_error *error, void *userdata) {
|
||||||
char type;
|
char type;
|
||||||
int r;
|
int r;
|
||||||
|
|
|
@ -18,6 +18,7 @@ enum {
|
||||||
};
|
};
|
||||||
|
|
||||||
int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
|
int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
|
||||||
|
int bus_map_strv_sort(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
|
||||||
|
|
||||||
int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, unsigned flags, sd_bus_error *error, void *userdata);
|
int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, unsigned flags, sd_bus_error *error, void *userdata);
|
||||||
int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map,
|
int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map,
|
||||||
|
|
|
@ -66,6 +66,7 @@ typedef struct TableData {
|
||||||
|
|
||||||
size_t minimum_width; /* minimum width for the column */
|
size_t minimum_width; /* minimum width for the column */
|
||||||
size_t maximum_width; /* maximum width for the column */
|
size_t maximum_width; /* maximum width for the column */
|
||||||
|
size_t formatted_for_width; /* the width we tried to format for */
|
||||||
unsigned weight; /* the horizontal weight for this column, in case the table is expanded/compressed */
|
unsigned weight; /* the horizontal weight for this column, in case the table is expanded/compressed */
|
||||||
unsigned ellipsize_percent; /* 0 … 100, where to place the ellipsis when compression is needed */
|
unsigned ellipsize_percent; /* 0 … 100, where to place the ellipsis when compression is needed */
|
||||||
unsigned align_percent; /* 0 … 100, where to pad with spaces when expanding is needed. 0: left-aligned, 100: right-aligned */
|
unsigned align_percent; /* 0 … 100, where to pad with spaces when expanding is needed. 0: left-aligned, 100: right-aligned */
|
||||||
|
@ -164,7 +165,6 @@ Table *table_new_raw(size_t n_columns) {
|
||||||
Table *table_new_internal(const char *first_header, ...) {
|
Table *table_new_internal(const char *first_header, ...) {
|
||||||
_cleanup_(table_unrefp) Table *t = NULL;
|
_cleanup_(table_unrefp) Table *t = NULL;
|
||||||
size_t n_columns = 1;
|
size_t n_columns = 1;
|
||||||
const char *h;
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
@ -172,8 +172,7 @@ Table *table_new_internal(const char *first_header, ...) {
|
||||||
|
|
||||||
va_start(ap, first_header);
|
va_start(ap, first_header);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
h = va_arg(ap, const char*);
|
if (!va_arg(ap, const char*))
|
||||||
if (!h)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
n_columns++;
|
n_columns++;
|
||||||
|
@ -185,7 +184,7 @@ Table *table_new_internal(const char *first_header, ...) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
va_start(ap, first_header);
|
va_start(ap, first_header);
|
||||||
for (h = first_header; h; h = va_arg(ap, const char*)) {
|
for (const char *h = first_header; h; h = va_arg(ap, const char*)) {
|
||||||
TableCell *cell;
|
TableCell *cell;
|
||||||
|
|
||||||
r = table_add_cell(t, &cell, TABLE_STRING, h);
|
r = table_add_cell(t, &cell, TABLE_STRING, h);
|
||||||
|
@ -213,7 +212,7 @@ static TableData *table_data_free(TableData *d) {
|
||||||
free(d->formatted);
|
free(d->formatted);
|
||||||
free(d->url);
|
free(d->url);
|
||||||
|
|
||||||
if (d->type == TABLE_STRV)
|
if (IN_SET(d->type, TABLE_STRV, TABLE_STRV_WRAPPED))
|
||||||
strv_free(d->strv);
|
strv_free(d->strv);
|
||||||
|
|
||||||
return mfree(d);
|
return mfree(d);
|
||||||
|
@ -223,12 +222,10 @@ DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(TableData, table_data, table_data_free);
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC(TableData*, table_data_unref);
|
DEFINE_TRIVIAL_CLEANUP_FUNC(TableData*, table_data_unref);
|
||||||
|
|
||||||
Table *table_unref(Table *t) {
|
Table *table_unref(Table *t) {
|
||||||
size_t i;
|
|
||||||
|
|
||||||
if (!t)
|
if (!t)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (i = 0; i < t->n_cells; i++)
|
for (size_t i = 0; i < t->n_cells; i++)
|
||||||
table_data_unref(t->data[i]);
|
table_data_unref(t->data[i]);
|
||||||
|
|
||||||
free(t->data);
|
free(t->data);
|
||||||
|
@ -252,6 +249,7 @@ static size_t table_data_size(TableDataType type, const void *data) {
|
||||||
return strlen(data) + 1;
|
return strlen(data) + 1;
|
||||||
|
|
||||||
case TABLE_STRV:
|
case TABLE_STRV:
|
||||||
|
case TABLE_STRV_WRAPPED:
|
||||||
return sizeof(char **);
|
return sizeof(char **);
|
||||||
|
|
||||||
case TABLE_BOOLEAN:
|
case TABLE_BOOLEAN:
|
||||||
|
@ -376,7 +374,7 @@ static TableData *table_data_new(
|
||||||
d->align_percent = align_percent;
|
d->align_percent = align_percent;
|
||||||
d->ellipsize_percent = ellipsize_percent;
|
d->ellipsize_percent = ellipsize_percent;
|
||||||
|
|
||||||
if (type == TABLE_STRV) {
|
if (IN_SET(type, TABLE_STRV, TABLE_STRV_WRAPPED)) {
|
||||||
d->strv = strv_copy(data);
|
d->strv = strv_copy(data);
|
||||||
if (!d->strv)
|
if (!d->strv)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -817,6 +815,7 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TABLE_STRV:
|
case TABLE_STRV:
|
||||||
|
case TABLE_STRV_WRAPPED:
|
||||||
data = va_arg(ap, char * const *);
|
data = va_arg(ap, char * const *);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1047,11 +1046,9 @@ int table_set_empty_string(Table *t, const char *empty) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int table_set_display_all(Table *t) {
|
int table_set_display_all(Table *t) {
|
||||||
size_t allocated;
|
|
||||||
|
|
||||||
assert(t);
|
assert(t);
|
||||||
|
|
||||||
allocated = t->n_display_map;
|
size_t allocated = t->n_display_map;
|
||||||
|
|
||||||
if (!GREEDY_REALLOC(t->display_map, allocated, MAX(t->n_columns, allocated)))
|
if (!GREEDY_REALLOC(t->display_map, allocated, MAX(t->n_columns, allocated)))
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -1124,7 +1121,6 @@ int table_set_sort(Table *t, size_t first_column, ...) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int table_hide_column_from_display(Table *t, size_t column) {
|
int table_hide_column_from_display(Table *t, size_t column) {
|
||||||
size_t allocated, cur = 0;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(t);
|
assert(t);
|
||||||
|
@ -1137,7 +1133,7 @@ int table_hide_column_from_display(Table *t, size_t column) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
allocated = t->n_display_map;
|
size_t allocated = t->n_display_map, cur = 0;
|
||||||
|
|
||||||
for (size_t i = 0; i < allocated; i++) {
|
for (size_t i = 0; i < allocated; i++) {
|
||||||
if (t->display_map[i] == column)
|
if (t->display_map[i] == column)
|
||||||
|
@ -1169,6 +1165,7 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
|
||||||
return path_compare(a->string, b->string);
|
return path_compare(a->string, b->string);
|
||||||
|
|
||||||
case TABLE_STRV:
|
case TABLE_STRV:
|
||||||
|
case TABLE_STRV_WRAPPED:
|
||||||
return strv_compare(a->strv, b->strv);
|
return strv_compare(a->strv, b->strv);
|
||||||
|
|
||||||
case TABLE_BOOLEAN:
|
case TABLE_BOOLEAN:
|
||||||
|
@ -1247,7 +1244,6 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
|
||||||
}
|
}
|
||||||
|
|
||||||
static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
|
static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
|
||||||
size_t i;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(t);
|
assert(t);
|
||||||
|
@ -1262,7 +1258,7 @@ static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* Order other lines by the sorting map */
|
/* Order other lines by the sorting map */
|
||||||
for (i = 0; i < t->n_sort_map; i++) {
|
for (size_t i = 0; i < t->n_sort_map; i++) {
|
||||||
TableData *d, *dd;
|
TableData *d, *dd;
|
||||||
|
|
||||||
d = t->data[*a + t->sort_map[i]];
|
d = t->data[*a + t->sort_map[i]];
|
||||||
|
@ -1277,10 +1273,46 @@ static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
|
||||||
return CMP(*a, *b);
|
return CMP(*a, *b);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercasing) {
|
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) {
|
||||||
assert(d);
|
assert(d);
|
||||||
|
|
||||||
if (d->formatted)
|
if (d->formatted &&
|
||||||
|
/* Only TABLE_STRV_WRAPPED adjust based on column_width so far… */
|
||||||
|
(d->type != TABLE_STRV_WRAPPED || d->formatted_for_width == column_width))
|
||||||
return d->formatted;
|
return d->formatted;
|
||||||
|
|
||||||
switch (d->type) {
|
switch (d->type) {
|
||||||
|
@ -1290,13 +1322,12 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
|
||||||
case TABLE_STRING:
|
case TABLE_STRING:
|
||||||
case TABLE_PATH:
|
case TABLE_PATH:
|
||||||
if (d->uppercase && !avoid_uppercasing) {
|
if (d->uppercase && !avoid_uppercasing) {
|
||||||
char *p, *q;
|
|
||||||
|
|
||||||
d->formatted = new(char, strlen(d->string) + 1);
|
d->formatted = new(char, strlen(d->string) + 1);
|
||||||
if (!d->formatted)
|
if (!d->formatted)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (p = d->string, q = d->formatted; *p; p++, q++)
|
char *q = d->formatted;
|
||||||
|
for (char *p = d->string; *p; p++, q++)
|
||||||
*q = (char) toupper((unsigned char) *p);
|
*q = (char) toupper((unsigned char) *p);
|
||||||
*q = 0;
|
*q = 0;
|
||||||
|
|
||||||
|
@ -1305,17 +1336,28 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
|
||||||
|
|
||||||
return d->string;
|
return d->string;
|
||||||
|
|
||||||
case TABLE_STRV: {
|
case TABLE_STRV:
|
||||||
char *p;
|
|
||||||
|
|
||||||
if (strv_isempty(d->strv))
|
if (strv_isempty(d->strv))
|
||||||
return strempty(t->empty_string);
|
return strempty(t->empty_string);
|
||||||
|
|
||||||
p = strv_join(d->strv, "\n");
|
d->formatted = strv_join(d->strv, "\n");
|
||||||
if (!p)
|
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)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
d->formatted = p;
|
free_and_replace(d->formatted, buf);
|
||||||
|
d->formatted_for_width = column_width;
|
||||||
|
if (have_soft)
|
||||||
|
*have_soft = true;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1632,16 +1674,19 @@ static int console_width_height(
|
||||||
static int table_data_requested_width_height(
|
static int table_data_requested_width_height(
|
||||||
Table *table,
|
Table *table,
|
||||||
TableData *d,
|
TableData *d,
|
||||||
|
size_t available_width,
|
||||||
size_t *ret_width,
|
size_t *ret_width,
|
||||||
size_t *ret_height) {
|
size_t *ret_height,
|
||||||
|
bool *have_soft) {
|
||||||
|
|
||||||
_cleanup_free_ char *truncated = NULL;
|
_cleanup_free_ char *truncated = NULL;
|
||||||
bool truncation_applied = false;
|
bool truncation_applied = false;
|
||||||
size_t width, height;
|
size_t width, height;
|
||||||
const char *t;
|
const char *t;
|
||||||
int r;
|
int r;
|
||||||
|
bool soft = false;
|
||||||
|
|
||||||
t = table_data_format(table, d, false);
|
t = table_data_format(table, d, false, available_width, &soft);
|
||||||
if (!t)
|
if (!t)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -1669,6 +1714,8 @@ static int table_data_requested_width_height(
|
||||||
*ret_width = width;
|
*ret_width = width;
|
||||||
if (ret_height)
|
if (ret_height)
|
||||||
*ret_height = height;
|
*ret_height = height;
|
||||||
|
if (have_soft && soft)
|
||||||
|
*have_soft = true;
|
||||||
|
|
||||||
return truncation_applied;
|
return truncation_applied;
|
||||||
}
|
}
|
||||||
|
@ -1678,7 +1725,6 @@ static char *align_string_mem(const char *str, const char *url, size_t new_lengt
|
||||||
_cleanup_free_ char *clickable = NULL;
|
_cleanup_free_ char *clickable = NULL;
|
||||||
const char *p;
|
const char *p;
|
||||||
char *ret;
|
char *ret;
|
||||||
size_t i;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
/* As with ellipsize_mem(), 'old_length' is a byte size while 'new_length' is a width in character cells */
|
/* As with ellipsize_mem(), 'old_length' is a byte size while 'new_length' is a width in character cells */
|
||||||
|
@ -1723,10 +1769,10 @@ static char *align_string_mem(const char *str, const char *url, size_t new_lengt
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (i = 0; i < lspace; i++)
|
for (size_t i = 0; i < lspace; i++)
|
||||||
ret[i] = ' ';
|
ret[i] = ' ';
|
||||||
memcpy(ret + lspace, clickable ?: str, clickable_length);
|
memcpy(ret + lspace, clickable ?: str, clickable_length);
|
||||||
for (i = lspace + clickable_length; i < space + clickable_length; i++)
|
for (size_t i = lspace + clickable_length; i < space + clickable_length; i++)
|
||||||
ret[i] = ' ';
|
ret[i] = ' ';
|
||||||
|
|
||||||
ret[space + clickable_length] = 0;
|
ret[space + clickable_length] = 0;
|
||||||
|
@ -1740,7 +1786,7 @@ static bool table_data_isempty(TableData *d) {
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
/* Let's also consider an empty strv as truly empty. */
|
/* Let's also consider an empty strv as truly empty. */
|
||||||
if (d->type == TABLE_STRV)
|
if (IN_SET(d->type, TABLE_STRV, TABLE_STRV_WRAPPED))
|
||||||
return strv_isempty(d->strv);
|
return strv_isempty(d->strv);
|
||||||
|
|
||||||
/* Note that an empty string we do not consider empty here! */
|
/* Note that an empty string we do not consider empty here! */
|
||||||
|
@ -1771,8 +1817,8 @@ static const char* table_data_rgap_color(TableData *d) {
|
||||||
|
|
||||||
int table_print(Table *t, FILE *f) {
|
int table_print(Table *t, FILE *f) {
|
||||||
size_t n_rows, *minimum_width, *maximum_width, display_columns, *requested_width,
|
size_t n_rows, *minimum_width, *maximum_width, display_columns, *requested_width,
|
||||||
i, j, table_minimum_width, table_maximum_width, table_requested_width, table_effective_width,
|
table_minimum_width, table_maximum_width, table_requested_width, table_effective_width,
|
||||||
*width;
|
*width = NULL;
|
||||||
_cleanup_free_ size_t *sorted = NULL;
|
_cleanup_free_ size_t *sorted = NULL;
|
||||||
uint64_t *column_weight, weight_sum;
|
uint64_t *column_weight, weight_sum;
|
||||||
int r;
|
int r;
|
||||||
|
@ -1795,7 +1841,7 @@ int table_print(Table *t, FILE *f) {
|
||||||
if (!sorted)
|
if (!sorted)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
for (i = 0; i < n_rows; i++)
|
for (size_t i = 0; i < n_rows; i++)
|
||||||
sorted[i] = i * t->n_columns;
|
sorted[i] = i * t->n_columns;
|
||||||
|
|
||||||
typesafe_qsort_r(sorted, n_rows, table_data_compare, t);
|
typesafe_qsort_r(sorted, n_rows, table_data_compare, t);
|
||||||
|
@ -1811,30 +1857,37 @@ int table_print(Table *t, FILE *f) {
|
||||||
minimum_width = newa(size_t, display_columns);
|
minimum_width = newa(size_t, display_columns);
|
||||||
maximum_width = newa(size_t, display_columns);
|
maximum_width = newa(size_t, display_columns);
|
||||||
requested_width = newa(size_t, display_columns);
|
requested_width = newa(size_t, display_columns);
|
||||||
width = newa(size_t, display_columns);
|
|
||||||
column_weight = newa0(uint64_t, display_columns);
|
column_weight = newa0(uint64_t, display_columns);
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++) {
|
for (size_t j = 0; j < display_columns; j++) {
|
||||||
minimum_width[j] = 1;
|
minimum_width[j] = 1;
|
||||||
maximum_width[j] = (size_t) -1;
|
maximum_width[j] = (size_t) -1;
|
||||||
requested_width[j] = (size_t) -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (unsigned pass = 0; pass < 2; pass++) {
|
||||||
/* First pass: determine column sizes */
|
/* First pass: determine column sizes */
|
||||||
for (i = t->header ? 0 : 1; i < n_rows; i++) {
|
|
||||||
|
for (size_t j = 0; j < display_columns; j++)
|
||||||
|
requested_width[j] = (size_t) -1;
|
||||||
|
|
||||||
|
bool any_soft = false;
|
||||||
|
|
||||||
|
for (size_t i = t->header ? 0 : 1; i < n_rows; i++) {
|
||||||
TableData **row;
|
TableData **row;
|
||||||
|
|
||||||
/* Note that we don't care about ordering at this time, as we just want to determine column sizes,
|
/* Note that we don't care about ordering at this time, as we just want to determine column sizes,
|
||||||
* hence we don't care for sorted[] during the first pass. */
|
* hence we don't care for sorted[] during the first pass. */
|
||||||
row = t->data + i * t->n_columns;
|
row = t->data + i * t->n_columns;
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++) {
|
for (size_t j = 0; j < display_columns; j++) {
|
||||||
TableData *d;
|
TableData *d;
|
||||||
size_t req_width, req_height;
|
size_t req_width, req_height;
|
||||||
|
|
||||||
assert_se(d = row[t->display_map ? t->display_map[j] : j]);
|
assert_se(d = row[t->display_map ? t->display_map[j] : j]);
|
||||||
|
|
||||||
r = table_data_requested_width_height(t, d, &req_width, &req_height);
|
r = table_data_requested_width_height(t, d,
|
||||||
|
width ? width[j] : SIZE_MAX,
|
||||||
|
&req_width, &req_height, &any_soft);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r > 0) { /* Truncated because too many lines? */
|
if (r > 0) { /* Truncated because too many lines? */
|
||||||
|
@ -1846,7 +1899,9 @@ int table_print(Table *t, FILE *f) {
|
||||||
* ellipsis. Hence, let's figure out the last line, and account for its
|
* ellipsis. Hence, let's figure out the last line, and account for its
|
||||||
* length plus ellipsis. */
|
* length plus ellipsis. */
|
||||||
|
|
||||||
field = table_data_format(t, d, false);
|
field = table_data_format(t, d, false,
|
||||||
|
width ? width[j] : SIZE_MAX,
|
||||||
|
&any_soft);
|
||||||
if (!field)
|
if (!field)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -1885,7 +1940,7 @@ int table_print(Table *t, FILE *f) {
|
||||||
|
|
||||||
/* Calculate the total weight for all columns, plus the minimum, maximum and requested width for the table. */
|
/* Calculate the total weight for all columns, plus the minimum, maximum and requested width for the table. */
|
||||||
weight_sum = 0;
|
weight_sum = 0;
|
||||||
for (j = 0; j < display_columns; j++) {
|
for (size_t j = 0; j < display_columns; j++) {
|
||||||
weight_sum += column_weight[j];
|
weight_sum += column_weight[j];
|
||||||
|
|
||||||
table_minimum_width += minimum_width[j];
|
table_minimum_width += minimum_width[j];
|
||||||
|
@ -1901,7 +1956,8 @@ int table_print(Table *t, FILE *f) {
|
||||||
/* Calculate effective table width */
|
/* Calculate effective table width */
|
||||||
if (t->width != 0 && t->width != (size_t) -1)
|
if (t->width != 0 && t->width != (size_t) -1)
|
||||||
table_effective_width = t->width;
|
table_effective_width = t->width;
|
||||||
else if (t->width == 0 || pager_have() || !isatty(STDOUT_FILENO))
|
else if (t->width == 0 ||
|
||||||
|
((pass > 0 || !any_soft) && (pager_have() || !isatty(STDOUT_FILENO))))
|
||||||
table_effective_width = table_requested_width;
|
table_effective_width = table_requested_width;
|
||||||
else
|
else
|
||||||
table_effective_width = MIN(table_requested_width, columns());
|
table_effective_width = MIN(table_requested_width, columns());
|
||||||
|
@ -1912,6 +1968,9 @@ int table_print(Table *t, FILE *f) {
|
||||||
if (table_effective_width < table_minimum_width)
|
if (table_effective_width < table_minimum_width)
|
||||||
table_effective_width = table_minimum_width;
|
table_effective_width = table_minimum_width;
|
||||||
|
|
||||||
|
if (!width)
|
||||||
|
width = newa(size_t, display_columns);
|
||||||
|
|
||||||
if (table_effective_width >= table_requested_width) {
|
if (table_effective_width >= table_requested_width) {
|
||||||
size_t extra;
|
size_t extra;
|
||||||
|
|
||||||
|
@ -1920,7 +1979,7 @@ int table_print(Table *t, FILE *f) {
|
||||||
|
|
||||||
extra = table_effective_width - table_requested_width;
|
extra = table_effective_width - table_requested_width;
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++) {
|
for (size_t j = 0; j < display_columns; j++) {
|
||||||
size_t delta;
|
size_t delta;
|
||||||
|
|
||||||
if (weight_sum == 0)
|
if (weight_sum == 0)
|
||||||
|
@ -1947,6 +2006,7 @@ int table_print(Table *t, FILE *f) {
|
||||||
weight_sum -= column_weight[j];
|
weight_sum -= column_weight[j];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break; /* Every column should be happy, no need to repeat calculations. */
|
||||||
} else {
|
} else {
|
||||||
/* We need to compress the table, columns can't get what they asked for. We first provide each column
|
/* We need to compress the table, columns can't get what they asked for. We first provide each column
|
||||||
* with the minimum they need, and then distribute anything left. */
|
* with the minimum they need, and then distribute anything left. */
|
||||||
|
@ -1955,13 +2015,13 @@ int table_print(Table *t, FILE *f) {
|
||||||
|
|
||||||
extra = table_effective_width - table_minimum_width;
|
extra = table_effective_width - table_minimum_width;
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++)
|
for (size_t j = 0; j < display_columns; j++)
|
||||||
width[j] = (size_t) -1;
|
width[j] = (size_t) -1;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
bool restart = false;
|
bool restart = false;
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++) {
|
for (size_t j = 0; j < display_columns; j++) {
|
||||||
size_t delta, w;
|
size_t delta, w;
|
||||||
|
|
||||||
/* Did this column already get something assigned? If so, let's skip to the next */
|
/* Did this column already get something assigned? If so, let's skip to the next */
|
||||||
|
@ -2006,10 +2066,16 @@ int table_print(Table *t, FILE *f) {
|
||||||
if (!restart)
|
if (!restart)
|
||||||
finalize = true;
|
finalize = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!any_soft) /* Some columns got less than requested. If some cells were "soft",
|
||||||
|
* let's try to reformat them with the new widths. Otherwise, let's
|
||||||
|
* move on. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Second pass: show output */
|
/* Second pass: show output */
|
||||||
for (i = t->header ? 0 : 1; i < n_rows; i++) {
|
for (size_t i = t->header ? 0 : 1; i < n_rows; i++) {
|
||||||
size_t n_subline = 0;
|
size_t n_subline = 0;
|
||||||
bool more_sublines;
|
bool more_sublines;
|
||||||
TableData **row;
|
TableData **row;
|
||||||
|
@ -2023,7 +2089,7 @@ int table_print(Table *t, FILE *f) {
|
||||||
const char *gap_color = NULL;
|
const char *gap_color = NULL;
|
||||||
more_sublines = false;
|
more_sublines = false;
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++) {
|
for (size_t j = 0; j < display_columns; j++) {
|
||||||
_cleanup_free_ char *buffer = NULL, *extracted = NULL;
|
_cleanup_free_ char *buffer = NULL, *extracted = NULL;
|
||||||
bool lines_truncated = false;
|
bool lines_truncated = false;
|
||||||
const char *field, *color = NULL;
|
const char *field, *color = NULL;
|
||||||
|
@ -2032,7 +2098,7 @@ int table_print(Table *t, FILE *f) {
|
||||||
|
|
||||||
assert_se(d = row[t->display_map ? t->display_map[j] : j]);
|
assert_se(d = row[t->display_map ? t->display_map[j] : j]);
|
||||||
|
|
||||||
field = table_data_format(t, d, false);
|
field = table_data_format(t, d, false, width[j], NULL);
|
||||||
if (!field)
|
if (!field)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -2247,6 +2313,7 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
|
||||||
return json_variant_new_string(ret, d->string);
|
return json_variant_new_string(ret, d->string);
|
||||||
|
|
||||||
case TABLE_STRV:
|
case TABLE_STRV:
|
||||||
|
case TABLE_STRV_WRAPPED:
|
||||||
return json_variant_new_array_strv(ret, d->strv);
|
return json_variant_new_array_strv(ret, d->strv);
|
||||||
|
|
||||||
case TABLE_BOOLEAN:
|
case TABLE_BOOLEAN:
|
||||||
|
@ -2332,17 +2399,15 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static char* string_to_json_field_name(const char *f) {
|
static char* string_to_json_field_name(const char *f) {
|
||||||
char *c, *x;
|
|
||||||
|
|
||||||
/* Tries to make a string more suitable as JSON field name. There are no strict rules defined what a
|
/* Tries to make a string more suitable as JSON field name. There are no strict rules defined what a
|
||||||
* field name can be hence this is a bit vague and black magic. Right now we only convert spaces to
|
* field name can be hence this is a bit vague and black magic. Right now we only convert spaces to
|
||||||
* underscores and leave everything as is. */
|
* underscores and leave everything as is. */
|
||||||
|
|
||||||
c = strdup(f);
|
char *c = strdup(f);
|
||||||
if (!c)
|
if (!c)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (x = c; *x; x++)
|
for (char *x = c; *x; x++)
|
||||||
if (isspace(*x))
|
if (isspace(*x))
|
||||||
*x = '_';
|
*x = '_';
|
||||||
|
|
||||||
|
@ -2352,7 +2417,7 @@ static char* string_to_json_field_name(const char *f) {
|
||||||
int table_to_json(Table *t, JsonVariant **ret) {
|
int table_to_json(Table *t, JsonVariant **ret) {
|
||||||
JsonVariant **rows = NULL, **elements = NULL;
|
JsonVariant **rows = NULL, **elements = NULL;
|
||||||
_cleanup_free_ size_t *sorted = NULL;
|
_cleanup_free_ size_t *sorted = NULL;
|
||||||
size_t n_rows, i, j, display_columns;
|
size_t n_rows, display_columns;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(t);
|
assert(t);
|
||||||
|
@ -2372,7 +2437,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < n_rows; i++)
|
for (size_t i = 0; i < n_rows; i++)
|
||||||
sorted[i] = i * t->n_columns;
|
sorted[i] = i * t->n_columns;
|
||||||
|
|
||||||
typesafe_qsort_r(sorted, n_rows, table_data_compare, t);
|
typesafe_qsort_r(sorted, n_rows, table_data_compare, t);
|
||||||
|
@ -2390,7 +2455,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++) {
|
for (size_t j = 0; j < display_columns; j++) {
|
||||||
_cleanup_free_ char *mangled = NULL;
|
_cleanup_free_ char *mangled = NULL;
|
||||||
const char *formatted;
|
const char *formatted;
|
||||||
TableData *d;
|
TableData *d;
|
||||||
|
@ -2398,7 +2463,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
|
||||||
assert_se(d = t->data[t->display_map ? t->display_map[j] : j]);
|
assert_se(d = t->data[t->display_map ? t->display_map[j] : j]);
|
||||||
|
|
||||||
/* Field names must be strings, hence format whatever we got here as a string first */
|
/* Field names must be strings, hence format whatever we got here as a string first */
|
||||||
formatted = table_data_format(t, d, true);
|
formatted = table_data_format(t, d, true, SIZE_MAX, NULL);
|
||||||
if (!formatted) {
|
if (!formatted) {
|
||||||
r = -ENOMEM;
|
r = -ENOMEM;
|
||||||
goto finish;
|
goto finish;
|
||||||
|
@ -2422,7 +2487,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 1; i < n_rows; i++) {
|
for (size_t i = 1; i < n_rows; i++) {
|
||||||
TableData **row;
|
TableData **row;
|
||||||
|
|
||||||
if (sorted)
|
if (sorted)
|
||||||
|
@ -2430,7 +2495,7 @@ int table_to_json(Table *t, JsonVariant **ret) {
|
||||||
else
|
else
|
||||||
row = t->data + i * t->n_columns;
|
row = t->data + i * t->n_columns;
|
||||||
|
|
||||||
for (j = 0; j < display_columns; j++) {
|
for (size_t j = 0; j < display_columns; j++) {
|
||||||
TableData *d;
|
TableData *d;
|
||||||
size_t k;
|
size_t k;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ typedef enum TableDataType {
|
||||||
TABLE_EMPTY,
|
TABLE_EMPTY,
|
||||||
TABLE_STRING,
|
TABLE_STRING,
|
||||||
TABLE_STRV,
|
TABLE_STRV,
|
||||||
|
TABLE_STRV_WRAPPED,
|
||||||
TABLE_PATH,
|
TABLE_PATH,
|
||||||
TABLE_BOOLEAN,
|
TABLE_BOOLEAN,
|
||||||
TABLE_TIMESTAMP,
|
TABLE_TIMESTAMP,
|
||||||
|
|
|
@ -301,3 +301,43 @@ int enter_cgroup_subroot(char **ret_cgroup) {
|
||||||
int enter_cgroup_root(char **ret_cgroup) {
|
int enter_cgroup_root(char **ret_cgroup) {
|
||||||
return enter_cgroup(ret_cgroup, false);
|
return enter_cgroup(ret_cgroup, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *ci_environment(void) {
|
||||||
|
/* We return a string because we might want to provide multiple bits of information later on: not
|
||||||
|
* just the general CI environment type, but also whether we're sanitizing or not, etc. The caller is
|
||||||
|
* expected to use strstr on the returned value. */
|
||||||
|
static const char *ans = POINTER_MAX;
|
||||||
|
const char *p;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (ans != POINTER_MAX)
|
||||||
|
return ans;
|
||||||
|
|
||||||
|
/* We allow specifying the environment with $CITYPE. Nobody uses this so far, but we are ready. */
|
||||||
|
p = getenv("CITYPE");
|
||||||
|
if (!isempty(p))
|
||||||
|
return (ans = p);
|
||||||
|
|
||||||
|
if (getenv_bool("TRAVIS") > 0)
|
||||||
|
return (ans = "travis");
|
||||||
|
if (getenv_bool("SEMAPHORE") > 0)
|
||||||
|
return (ans = "semaphore");
|
||||||
|
if (getenv_bool("GITHUB_ACTIONS") > 0)
|
||||||
|
return (ans = "github-actions");
|
||||||
|
if (getenv("AUTOPKGTEST_ARTIFACTS") || getenv("AUTOPKGTEST_TMP"))
|
||||||
|
return (ans = "autopkgtest");
|
||||||
|
|
||||||
|
FOREACH_STRING(p, "CI", "CONTINOUS_INTEGRATION") {
|
||||||
|
/* Those vars are booleans according to Semaphore and Travis docs:
|
||||||
|
* https://docs.travis-ci.com/user/environment-variables/#default-environment-variables
|
||||||
|
* https://docs.semaphoreci.com/ci-cd-environment/environment-variables/#ci
|
||||||
|
*/
|
||||||
|
r = getenv_bool(p);
|
||||||
|
if (r > 0)
|
||||||
|
return (ans = "unknown"); /* Some other unknown thing */
|
||||||
|
if (r == 0)
|
||||||
|
return (ans = NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ans = NULL);
|
||||||
|
}
|
||||||
|
|
|
@ -40,3 +40,6 @@ bool can_memlock(void);
|
||||||
} else { \
|
} else { \
|
||||||
printf("systemd not booted skipping '%s'\n", #x); \
|
printf("systemd not booted skipping '%s'\n", #x); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Provide a convenient way to check if we're running in CI. */
|
||||||
|
const char *ci_environment(void);
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include "bus-error.h"
|
#include "bus-error.h"
|
||||||
#include "copy.h"
|
#include "copy.h"
|
||||||
|
#include "fd-util.h"
|
||||||
|
#include "fileio.h"
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
#include "mkdir.h"
|
#include "mkdir.h"
|
||||||
#include "pager.h"
|
#include "pager.h"
|
||||||
|
@ -17,6 +19,9 @@
|
||||||
#include "terminal-util.h"
|
#include "terminal-util.h"
|
||||||
#include "tmpfile-util.h"
|
#include "tmpfile-util.h"
|
||||||
|
|
||||||
|
#define EDIT_MARKER_START "### Anything between here and the comment below will become the new contents of the file"
|
||||||
|
#define EDIT_MARKER_END "### Lines below this comment will be discarded"
|
||||||
|
|
||||||
int cat(int argc, char *argv[], void *userdata) {
|
int cat(int argc, char *argv[], void *userdata) {
|
||||||
_cleanup_(hashmap_freep) Hashmap *cached_name_map = NULL, *cached_id_map = NULL;
|
_cleanup_(hashmap_freep) Hashmap *cached_name_map = NULL, *cached_id_map = NULL;
|
||||||
_cleanup_(lookup_paths_free) LookupPaths lp = {};
|
_cleanup_(lookup_paths_free) LookupPaths lp = {};
|
||||||
|
@ -106,12 +111,11 @@ int cat(int argc, char *argv[], void *userdata) {
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int create_edit_temp_file(const char *new_path, const char *original_path, char **ret_tmp_fn) {
|
static int create_edit_temp_file(const char *new_path, const char *original_path, char ** const original_unit_paths, char **ret_tmp_fn) {
|
||||||
_cleanup_free_ char *t = NULL;
|
_cleanup_free_ char *t = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(new_path);
|
assert(new_path);
|
||||||
assert(original_path);
|
|
||||||
assert(ret_tmp_fn);
|
assert(ret_tmp_fn);
|
||||||
|
|
||||||
r = tempfn_random(new_path, NULL, &t);
|
r = tempfn_random(new_path, NULL, &t);
|
||||||
|
@ -122,26 +126,79 @@ static int create_edit_temp_file(const char *new_path, const char *original_path
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to create directories for \"%s\": %m", new_path);
|
return log_error_errno(r, "Failed to create directories for \"%s\": %m", new_path);
|
||||||
|
|
||||||
r = mac_selinux_create_file_prepare(original_path, S_IFREG);
|
if (original_path) {
|
||||||
|
r = mac_selinux_create_file_prepare(new_path, S_IFREG);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = copy_file(original_path, t, 0, 0644, 0, 0, COPY_REFLINK);
|
r = copy_file(original_path, t, 0, 0644, 0, 0, COPY_REFLINK);
|
||||||
if (r == -ENOENT) {
|
if (r == -ENOENT) {
|
||||||
|
|
||||||
r = touch(t);
|
r = touch(t);
|
||||||
|
|
||||||
mac_selinux_create_file_clear();
|
mac_selinux_create_file_clear();
|
||||||
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to create temporary file \"%s\": %m", t);
|
return log_error_errno(r, "Failed to create temporary file \"%s\": %m", t);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
mac_selinux_create_file_clear();
|
mac_selinux_create_file_clear();
|
||||||
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to create temporary file for \"%s\": %m", new_path);
|
return log_error_errno(r, "Failed to create temporary file for \"%s\": %m", new_path);
|
||||||
}
|
}
|
||||||
|
} else if (original_unit_paths) {
|
||||||
|
_cleanup_free_ char *new_contents = NULL;
|
||||||
|
_cleanup_fclose_ FILE *f = NULL;
|
||||||
|
char **path;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
r = mac_selinux_create_file_prepare(new_path, S_IFREG);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
f = fopen(t, "we");
|
||||||
|
mac_selinux_create_file_clear();
|
||||||
|
if (!f)
|
||||||
|
return log_error_errno(errno, "Failed to open \"%s\": %m", t);
|
||||||
|
|
||||||
|
r = fchmod(fileno(f), 0644);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(errno, "Failed to change mode of \"%s\": %m", t);
|
||||||
|
|
||||||
|
r = read_full_file(new_path, &new_contents, &size);
|
||||||
|
if (r < 0 && r != -ENOENT)
|
||||||
|
return log_error_errno(r, "Failed to read \"%s\": %m", new_path);
|
||||||
|
|
||||||
|
fprintf(f,
|
||||||
|
"### Editing %s\n"
|
||||||
|
EDIT_MARKER_START
|
||||||
|
"\n\n%s%s\n"
|
||||||
|
EDIT_MARKER_END,
|
||||||
|
new_path,
|
||||||
|
strempty(new_contents),
|
||||||
|
new_contents && endswith(new_contents, "\n") ? "" : "\n");
|
||||||
|
|
||||||
|
/* Add a comment with the contents of the original unit files */
|
||||||
|
STRV_FOREACH(path, original_unit_paths) {
|
||||||
|
_cleanup_free_ char *contents = NULL;
|
||||||
|
|
||||||
|
/* Skip the file that's being edited */
|
||||||
|
if (path_equal(*path, new_path))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
r = read_full_file(*path, &contents, &size);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to read \"%s\": %m", *path);
|
||||||
|
|
||||||
|
fprintf(f, "\n\n### %s", *path);
|
||||||
|
if (!isempty(contents)) {
|
||||||
|
contents = strreplace(strstrip(contents), "\n", "\n# ");
|
||||||
|
if (!contents)
|
||||||
|
return log_oom();
|
||||||
|
fprintf(f, "\n# %s", contents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = fflush_and_check(f);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to create temporary file \"%s\": %m", t);
|
||||||
|
}
|
||||||
|
|
||||||
*ret_tmp_fn = TAKE_PTR(t);
|
*ret_tmp_fn = TAKE_PTR(t);
|
||||||
|
|
||||||
|
@ -185,6 +242,7 @@ static int unit_file_create_new(
|
||||||
const LookupPaths *paths,
|
const LookupPaths *paths,
|
||||||
const char *unit_name,
|
const char *unit_name,
|
||||||
const char *suffix,
|
const char *suffix,
|
||||||
|
char ** const original_unit_paths,
|
||||||
char **ret_new_path,
|
char **ret_new_path,
|
||||||
char **ret_tmp_path) {
|
char **ret_tmp_path) {
|
||||||
|
|
||||||
|
@ -201,7 +259,7 @@ static int unit_file_create_new(
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = create_edit_temp_file(new_path, new_path, &tmp_path);
|
r = create_edit_temp_file(new_path, NULL, original_unit_paths, &tmp_path);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
@ -240,7 +298,7 @@ static int unit_file_create_copy(
|
||||||
return log_warning_errno(SYNTHETIC_ERRNO(EKEYREJECTED), "%s skipped.", unit_name);
|
return log_warning_errno(SYNTHETIC_ERRNO(EKEYREJECTED), "%s skipped.", unit_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
r = create_edit_temp_file(new_path, fragment_path, &tmp_path);
|
r = create_edit_temp_file(new_path, fragment_path, NULL, &tmp_path);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
@ -332,9 +390,10 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
|
||||||
|
|
||||||
STRV_FOREACH(name, names) {
|
STRV_FOREACH(name, names) {
|
||||||
_cleanup_free_ char *path = NULL, *new_path = NULL, *tmp_path = NULL, *tmp_name = NULL;
|
_cleanup_free_ char *path = NULL, *new_path = NULL, *tmp_path = NULL, *tmp_name = NULL;
|
||||||
|
_cleanup_strv_free_ char **unit_paths = NULL;
|
||||||
const char *unit_name;
|
const char *unit_name;
|
||||||
|
|
||||||
r = unit_find_paths(bus, *name, &lp, false, &cached_name_map, &cached_id_map, &path, NULL);
|
r = unit_find_paths(bus, *name, &lp, false, &cached_name_map, &cached_id_map, &path, &unit_paths);
|
||||||
if (r == -EKEYREJECTED) {
|
if (r == -EKEYREJECTED) {
|
||||||
/* If loading of the unit failed server side complete, then the server won't tell us
|
/* If loading of the unit failed server side complete, then the server won't tell us
|
||||||
* the unit file path. In that case, find the file client side. */
|
* the unit file path. In that case, find the file client side. */
|
||||||
|
@ -361,7 +420,7 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
|
||||||
unit_name = *name;
|
unit_name = *name;
|
||||||
r = unit_file_create_new(&lp, unit_name,
|
r = unit_file_create_new(&lp, unit_name,
|
||||||
arg_full ? NULL : ".d/override.conf",
|
arg_full ? NULL : ".d/override.conf",
|
||||||
&new_path, &tmp_path);
|
NULL, &new_path, &tmp_path);
|
||||||
} else {
|
} else {
|
||||||
assert(path);
|
assert(path);
|
||||||
|
|
||||||
|
@ -384,8 +443,13 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
|
||||||
|
|
||||||
if (arg_full)
|
if (arg_full)
|
||||||
r = unit_file_create_copy(&lp, unit_name, path, &new_path, &tmp_path);
|
r = unit_file_create_copy(&lp, unit_name, path, &new_path, &tmp_path);
|
||||||
else
|
else {
|
||||||
r = unit_file_create_new(&lp, unit_name, ".d/override.conf", &new_path, &tmp_path);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -400,6 +464,40 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int trim_edit_markers(const char *path) {
|
||||||
|
_cleanup_free_ char *contents = NULL;
|
||||||
|
char *contents_start = NULL;
|
||||||
|
const char *contents_end = NULL;
|
||||||
|
size_t size;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* Trim out the lines between the two markers */
|
||||||
|
r = read_full_file(path, &contents, &size);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to read temporary file \"%s\": %m", path);
|
||||||
|
|
||||||
|
contents_start = strstr(contents, EDIT_MARKER_START);
|
||||||
|
if (contents_start)
|
||||||
|
contents_start += strlen(EDIT_MARKER_START);
|
||||||
|
else
|
||||||
|
contents_start = contents;
|
||||||
|
|
||||||
|
contents_end = strstr(contents_start, EDIT_MARKER_END);
|
||||||
|
if (contents_end)
|
||||||
|
strshorten(contents_start, contents_end - contents_start);
|
||||||
|
|
||||||
|
contents_start = strstrip(contents_start);
|
||||||
|
|
||||||
|
/* Write new contents if the trimming actually changed anything */
|
||||||
|
if (strlen(contents) != size) {
|
||||||
|
r = write_string_file(path, contents_start, WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_TRUNCATE | WRITE_STRING_FILE_AVOID_NEWLINE);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to modify temporary file \"%s\": %m", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int edit(int argc, char *argv[], void *userdata) {
|
int edit(int argc, char *argv[], void *userdata) {
|
||||||
_cleanup_(lookup_paths_free) LookupPaths lp = {};
|
_cleanup_(lookup_paths_free) LookupPaths lp = {};
|
||||||
_cleanup_strv_free_ char **names = NULL;
|
_cleanup_strv_free_ char **names = NULL;
|
||||||
|
@ -452,6 +550,10 @@ int edit(int argc, char *argv[], void *userdata) {
|
||||||
STRV_FOREACH_PAIR(original, tmp, paths) {
|
STRV_FOREACH_PAIR(original, tmp, paths) {
|
||||||
/* If the temporary file is empty we ignore it. This allows the user to cancel the
|
/* If the temporary file is empty we ignore it. This allows the user to cancel the
|
||||||
* modification. */
|
* modification. */
|
||||||
|
r = trim_edit_markers(*tmp);
|
||||||
|
if (r < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (null_or_empty_path(*tmp)) {
|
if (null_or_empty_path(*tmp)) {
|
||||||
log_warning("Editing \"%s\" canceled: temporary file is empty.", *original);
|
log_warning("Editing \"%s\" canceled: temporary file is empty.", *original);
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -264,6 +264,7 @@ static void test_env_clean(void) {
|
||||||
"xyz=xyz\n",
|
"xyz=xyz\n",
|
||||||
"another=one",
|
"another=one",
|
||||||
"another=final one",
|
"another=final one",
|
||||||
|
"CRLF=\r\n",
|
||||||
"BASH_FUNC_foo%%=() { echo foo\n}");
|
"BASH_FUNC_foo%%=() { echo foo\n}");
|
||||||
assert_se(e);
|
assert_se(e);
|
||||||
assert_se(!strv_env_is_valid(e));
|
assert_se(!strv_env_is_valid(e));
|
||||||
|
@ -306,6 +307,8 @@ static void test_env_value_is_valid(void) {
|
||||||
assert_se(env_value_is_valid("printf \"\\x1b]0;<mock-chroot>\\x07<mock-chroot>\""));
|
assert_se(env_value_is_valid("printf \"\\x1b]0;<mock-chroot>\\x07<mock-chroot>\""));
|
||||||
assert_se(env_value_is_valid("tab\tcharacter"));
|
assert_se(env_value_is_valid("tab\tcharacter"));
|
||||||
assert_se(env_value_is_valid("new\nline"));
|
assert_se(env_value_is_valid("new\nline"));
|
||||||
|
assert_se(!env_value_is_valid("Show this?\rNope. Show that!"));
|
||||||
|
assert_se(!env_value_is_valid("new DOS\r\nline"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_env_assignment_is_valid(void) {
|
static void test_env_assignment_is_valid(void) {
|
||||||
|
|
|
@ -36,11 +36,6 @@ static int cld_dumped_to_killed(int code) {
|
||||||
return code == CLD_DUMPED ? CLD_KILLED : code;
|
return code == CLD_DUMPED ? CLD_KILLED : code;
|
||||||
}
|
}
|
||||||
|
|
||||||
_unused_ static bool is_run_on_travis_ci(void) {
|
|
||||||
/* https://docs.travis-ci.com/user/environment-variables#default-environment-variables */
|
|
||||||
return streq_ptr(getenv("TRAVIS"), "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wait_for_service_finish(Manager *m, Unit *unit) {
|
static void wait_for_service_finish(Manager *m, Unit *unit) {
|
||||||
Service *service = NULL;
|
Service *service = NULL;
|
||||||
usec_t ts;
|
usec_t ts;
|
||||||
|
@ -897,7 +892,7 @@ int main(int argc, char *argv[]) {
|
||||||
test_setup_logging(LOG_DEBUG);
|
test_setup_logging(LOG_DEBUG);
|
||||||
|
|
||||||
#if HAS_FEATURE_ADDRESS_SANITIZER
|
#if HAS_FEATURE_ADDRESS_SANITIZER
|
||||||
if (is_run_on_travis_ci()) {
|
if (strstr_ptr(ci_environment(), "travis")) {
|
||||||
log_notice("Running on TravisCI under ASan, skipping, see https://github.com/systemd/systemd/issues/10696");
|
log_notice("Running on TravisCI under ASan, skipping, see https://github.com/systemd/systemd/issues/10696");
|
||||||
return EXIT_TEST_SKIP;
|
return EXIT_TEST_SKIP;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ static void test_issue_9549(void) {
|
||||||
_cleanup_(table_unrefp) Table *table = NULL;
|
_cleanup_(table_unrefp) Table *table = NULL;
|
||||||
_cleanup_free_ char *formatted = NULL;
|
_cleanup_free_ char *formatted = NULL;
|
||||||
|
|
||||||
|
log_info("/* %s */", __func__);
|
||||||
|
|
||||||
assert_se(table = table_new("name", "type", "ro", "usage", "created", "modified"));
|
assert_se(table = table_new("name", "type", "ro", "usage", "created", "modified"));
|
||||||
assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(3), 100) >= 0);
|
assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(3), 100) >= 0);
|
||||||
assert_se(table_add_many(table,
|
assert_se(table_add_many(table,
|
||||||
|
@ -36,6 +38,8 @@ static void test_multiline(void) {
|
||||||
_cleanup_(table_unrefp) Table *table = NULL;
|
_cleanup_(table_unrefp) Table *table = NULL;
|
||||||
_cleanup_free_ char *formatted = NULL;
|
_cleanup_free_ char *formatted = NULL;
|
||||||
|
|
||||||
|
log_info("/* %s */", __func__);
|
||||||
|
|
||||||
assert_se(table = table_new("foo", "bar"));
|
assert_se(table = table_new("foo", "bar"));
|
||||||
|
|
||||||
assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(1), 100) >= 0);
|
assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(1), 100) >= 0);
|
||||||
|
@ -148,6 +152,8 @@ static void test_strv(void) {
|
||||||
_cleanup_(table_unrefp) Table *table = NULL;
|
_cleanup_(table_unrefp) Table *table = NULL;
|
||||||
_cleanup_free_ char *formatted = NULL;
|
_cleanup_free_ char *formatted = NULL;
|
||||||
|
|
||||||
|
log_info("/* %s */", __func__);
|
||||||
|
|
||||||
assert_se(table = table_new("foo", "bar"));
|
assert_se(table = table_new("foo", "bar"));
|
||||||
|
|
||||||
assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(1), 100) >= 0);
|
assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(1), 100) >= 0);
|
||||||
|
@ -256,8 +262,111 @@ static void test_strv(void) {
|
||||||
formatted = mfree(formatted);
|
formatted = mfree(formatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
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_(table_unrefp) Table *t = NULL;
|
||||||
_cleanup_free_ char *formatted = NULL;
|
_cleanup_free_ char *formatted = NULL;
|
||||||
|
|
||||||
|
@ -399,6 +508,7 @@ int main(int argc, char *argv[]) {
|
||||||
test_issue_9549();
|
test_issue_9549();
|
||||||
test_multiline();
|
test_multiline();
|
||||||
test_strv();
|
test_strv();
|
||||||
|
test_strv_wrapped();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
@ -78,32 +77,52 @@ static Service *service_for_path(Manager *m, Path *path, const char *service_nam
|
||||||
return SERVICE(service_unit);
|
return SERVICE(service_unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void check_states(Manager *m, Path *path, Service *service, PathState path_state, ServiceState service_state) {
|
static int _check_states(unsigned line,
|
||||||
|
Manager *m, Path *path, Service *service, PathState path_state, ServiceState service_state) {
|
||||||
assert_se(m);
|
assert_se(m);
|
||||||
assert_se(service);
|
assert_se(service);
|
||||||
|
|
||||||
usec_t end = now(CLOCK_MONOTONIC) + 30 * USEC_PER_SEC;
|
usec_t end = now(CLOCK_MONOTONIC) + 30 * USEC_PER_SEC;
|
||||||
|
|
||||||
while (path->result != PATH_SUCCESS || service->result != SERVICE_SUCCESS ||
|
while (path->state != path_state || service->state != service_state ||
|
||||||
path->state != path_state || service->state != service_state) {
|
path->result != PATH_SUCCESS || service->result != SERVICE_SUCCESS) {
|
||||||
|
|
||||||
assert_se(sd_event_run(m->event, 100 * USEC_PER_MSEC) >= 0);
|
assert_se(sd_event_run(m->event, 100 * USEC_PER_MSEC) >= 0);
|
||||||
|
|
||||||
printf("%s: state = %s; result = %s \n",
|
usec_t n = now(CLOCK_MONOTONIC);
|
||||||
|
log_info("line %u: %s: state = %s; result = %s (left: %" PRIi64 ")",
|
||||||
|
line,
|
||||||
UNIT(path)->id,
|
UNIT(path)->id,
|
||||||
path_state_to_string(path->state),
|
path_state_to_string(path->state),
|
||||||
path_result_to_string(path->result));
|
path_result_to_string(path->result),
|
||||||
printf("%s: state = %s; result = %s \n",
|
end - n);
|
||||||
|
log_info("line %u: %s: state = %s; result = %s",
|
||||||
|
line,
|
||||||
UNIT(service)->id,
|
UNIT(service)->id,
|
||||||
service_state_to_string(service->state),
|
service_state_to_string(service->state),
|
||||||
service_result_to_string(service->result));
|
service_result_to_string(service->result));
|
||||||
|
|
||||||
if (now(CLOCK_MONOTONIC) >= end) {
|
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) {
|
||||||
log_error("Test timeout when testing %s", UNIT(path)->id);
|
log_error("Test timeout when testing %s", UNIT(path)->id);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
#define check_states(...) _check_states(__LINE__, __VA_ARGS__)
|
||||||
|
|
||||||
static void test_path_exists(Manager *m) {
|
static void test_path_exists(Manager *m) {
|
||||||
const char *test_path = "/tmp/test-path_exists";
|
const char *test_path = "/tmp/test-path_exists";
|
||||||
|
@ -119,18 +138,22 @@ static void test_path_exists(Manager *m) {
|
||||||
service = service_for_path(m, path, NULL);
|
service = service_for_path(m, path, NULL);
|
||||||
|
|
||||||
assert_se(unit_start(unit) >= 0);
|
assert_se(unit_start(unit) >= 0);
|
||||||
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
assert_se(touch(test_path) >= 0);
|
assert_se(touch(test_path) >= 0);
|
||||||
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Service restarts if file still exists */
|
/* Service restarts if file still exists */
|
||||||
assert_se(unit_stop(UNIT(service)) >= 0);
|
assert_se(unit_stop(UNIT(service)) >= 0);
|
||||||
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
assert_se(unit_stop(UNIT(service)) >= 0);
|
assert_se(unit_stop(UNIT(service)) >= 0);
|
||||||
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
assert_se(unit_stop(unit) >= 0);
|
assert_se(unit_stop(unit) >= 0);
|
||||||
}
|
}
|
||||||
|
@ -149,18 +172,22 @@ static void test_path_existsglob(Manager *m) {
|
||||||
service = service_for_path(m, path, NULL);
|
service = service_for_path(m, path, NULL);
|
||||||
|
|
||||||
assert_se(unit_start(unit) >= 0);
|
assert_se(unit_start(unit) >= 0);
|
||||||
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
assert_se(touch(test_path) >= 0);
|
assert_se(touch(test_path) >= 0);
|
||||||
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Service restarts if file still exists */
|
/* Service restarts if file still exists */
|
||||||
assert_se(unit_stop(UNIT(service)) >= 0);
|
assert_se(unit_stop(UNIT(service)) >= 0);
|
||||||
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
assert_se(unit_stop(UNIT(service)) >= 0);
|
assert_se(unit_stop(UNIT(service)) >= 0);
|
||||||
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
assert_se(unit_stop(unit) >= 0);
|
assert_se(unit_stop(unit) >= 0);
|
||||||
}
|
}
|
||||||
|
@ -180,23 +207,28 @@ static void test_path_changed(Manager *m) {
|
||||||
service = service_for_path(m, path, NULL);
|
service = service_for_path(m, path, NULL);
|
||||||
|
|
||||||
assert_se(unit_start(unit) >= 0);
|
assert_se(unit_start(unit) >= 0);
|
||||||
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
assert_se(touch(test_path) >= 0);
|
assert_se(touch(test_path) >= 0);
|
||||||
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Service does not restart if file still exists */
|
/* Service does not restart if file still exists */
|
||||||
assert_se(unit_stop(UNIT(service)) >= 0);
|
assert_se(unit_stop(UNIT(service)) >= 0);
|
||||||
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
f = fopen(test_path, "w");
|
f = fopen(test_path, "w");
|
||||||
assert_se(f);
|
assert_se(f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
assert_se(unit_stop(UNIT(service)) >= 0);
|
assert_se(unit_stop(UNIT(service)) >= 0);
|
||||||
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
(void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
|
(void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
|
||||||
assert_se(unit_stop(unit) >= 0);
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
@ -217,23 +249,28 @@ static void test_path_modified(Manager *m) {
|
||||||
service = service_for_path(m, path, NULL);
|
service = service_for_path(m, path, NULL);
|
||||||
|
|
||||||
assert_se(unit_start(unit) >= 0);
|
assert_se(unit_start(unit) >= 0);
|
||||||
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
assert_se(touch(test_path) >= 0);
|
assert_se(touch(test_path) >= 0);
|
||||||
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Service does not restart if file still exists */
|
/* Service does not restart if file still exists */
|
||||||
assert_se(unit_stop(UNIT(service)) >= 0);
|
assert_se(unit_stop(UNIT(service)) >= 0);
|
||||||
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
f = fopen(test_path, "w");
|
f = fopen(test_path, "w");
|
||||||
assert_se(f);
|
assert_se(f);
|
||||||
fputs("test", f);
|
fputs("test", f);
|
||||||
|
|
||||||
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
assert_se(unit_stop(UNIT(service)) >= 0);
|
assert_se(unit_stop(UNIT(service)) >= 0);
|
||||||
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
(void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
|
(void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
|
||||||
assert_se(unit_stop(unit) >= 0);
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
@ -253,14 +290,17 @@ static void test_path_unit(Manager *m) {
|
||||||
service = service_for_path(m, path, "path-mycustomunit.service");
|
service = service_for_path(m, path, "path-mycustomunit.service");
|
||||||
|
|
||||||
assert_se(unit_start(unit) >= 0);
|
assert_se(unit_start(unit) >= 0);
|
||||||
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
assert_se(touch(test_path) >= 0);
|
assert_se(touch(test_path) >= 0);
|
||||||
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
assert_se(unit_stop(UNIT(service)) >= 0);
|
assert_se(unit_stop(UNIT(service)) >= 0);
|
||||||
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
assert_se(unit_stop(unit) >= 0);
|
assert_se(unit_stop(unit) >= 0);
|
||||||
}
|
}
|
||||||
|
@ -281,22 +321,26 @@ static void test_path_directorynotempty(Manager *m) {
|
||||||
assert_se(access(test_path, F_OK) < 0);
|
assert_se(access(test_path, F_OK) < 0);
|
||||||
|
|
||||||
assert_se(unit_start(unit) >= 0);
|
assert_se(unit_start(unit) >= 0);
|
||||||
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
/* MakeDirectory default to no */
|
/* MakeDirectory default to no */
|
||||||
assert_se(access(test_path, F_OK) < 0);
|
assert_se(access(test_path, F_OK) < 0);
|
||||||
|
|
||||||
assert_se(mkdir_p(test_path, 0755) >= 0);
|
assert_se(mkdir_p(test_path, 0755) >= 0);
|
||||||
assert_se(touch(strjoina(test_path, "test_file")) >= 0);
|
assert_se(touch(strjoina(test_path, "test_file")) >= 0);
|
||||||
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Service restarts if directory is still not empty */
|
/* Service restarts if directory is still not empty */
|
||||||
assert_se(unit_stop(UNIT(service)) >= 0);
|
assert_se(unit_stop(UNIT(service)) >= 0);
|
||||||
check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
assert_se(unit_stop(UNIT(service)) >= 0);
|
assert_se(unit_stop(UNIT(service)) >= 0);
|
||||||
check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
assert_se(unit_stop(unit) >= 0);
|
assert_se(unit_stop(unit) >= 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -604,7 +604,7 @@ int xdg_autostart_service_generate_unit(
|
||||||
|
|
||||||
fprintf(f,
|
fprintf(f,
|
||||||
"\n[Service]\n"
|
"\n[Service]\n"
|
||||||
"Type=simple\n"
|
"Type=exec\n"
|
||||||
"ExecStart=:%s\n"
|
"ExecStart=:%s\n"
|
||||||
"Restart=no\n"
|
"Restart=no\n"
|
||||||
"TimeoutSec=5s\n"
|
"TimeoutSec=5s\n"
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
Description=Service Test for Path units
|
Description=Service Test for Path units
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/bin/true
|
ExecStart=sleep infinity
|
||||||
Type=simple
|
Type=exec
|
||||||
RemainAfterExit=true
|
RemainAfterExit=true
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
Description=Service Test for Path units
|
Description=Service Test for Path units
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/bin/true
|
ExecStart=sleep infinity
|
||||||
Type=simple
|
Type=exec
|
||||||
RemainAfterExit=true
|
RemainAfterExit=true
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
Description=Service Test for Path units
|
Description=Service Test for Path units
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/bin/true
|
ExecStart=sleep infinity
|
||||||
Type=simple
|
Type=exec
|
||||||
RemainAfterExit=true
|
RemainAfterExit=true
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
Description=Service Test for Path units
|
Description=Service Test for Path units
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/bin/true
|
ExecStart=sleep infinity
|
||||||
Type=simple
|
Type=exec
|
||||||
RemainAfterExit=true
|
RemainAfterExit=true
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
Description=Service Test for Path units
|
Description=Service Test for Path units
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/bin/true
|
ExecStart=sleep infinity
|
||||||
Type=simple
|
Type=exec
|
||||||
RemainAfterExit=true
|
RemainAfterExit=true
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
Description=Service Test for Path units
|
Description=Service Test for Path units
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/bin/true
|
ExecStart=sleep infinity
|
||||||
Type=simple
|
Type=exec
|
||||||
RemainAfterExit=true
|
RemainAfterExit=true
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
Description=Service Test Path Unit
|
Description=Service Test Path Unit
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/bin/true
|
ExecStart=sleep infinity
|
||||||
Type=simple
|
Type=exec
|
||||||
RemainAfterExit=true
|
RemainAfterExit=true
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
[Unit]
|
|
||||||
Description=Service Test for Path units
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
ExecStart=/bin/true
|
|
||||||
Type=oneshot
|
|
|
@ -2,5 +2,5 @@
|
||||||
Description=ForeverPrintHola service
|
Description=ForeverPrintHola service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=exec
|
||||||
ExecStart=sh -x -c 'while :; do printf "Hola\n" || touch /i-lose-my-logs; sleep 1; done'
|
ExecStart=sh -x -c 'while :; do printf "Hola\n" || touch /i-lose-my-logs; sleep 1; done'
|
||||||
|
|
|
@ -4,6 +4,6 @@ StartLimitIntervalSec=1m
|
||||||
StartLimitBurst=3
|
StartLimitBurst=3
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=exec
|
||||||
ExecStart=false
|
ExecStart=false
|
||||||
Restart=always
|
Restart=always
|
||||||
|
|
|
@ -13,7 +13,7 @@ StopWhenUnneeded=yes
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStartPre=rm -f /failed /testok
|
ExecStartPre=rm -f /failed /testok
|
||||||
Type=simple
|
Type=exec
|
||||||
TimeoutStartSec=infinity
|
TimeoutStartSec=infinity
|
||||||
ExecStartPre=/usr/lib/systemd/tests/testdata/units/%N.sh
|
ExecStartPre=/usr/lib/systemd/tests/testdata/units/%N.sh
|
||||||
ExecStart=true
|
ExecStart=true
|
||||||
|
|
|
@ -11,8 +11,13 @@
|
||||||
# control writes into pstore.
|
# control writes into pstore.
|
||||||
#
|
#
|
||||||
# The crash_kexec_post_notifiers parameter enables the kernel to write
|
# The crash_kexec_post_notifiers parameter enables the kernel to write
|
||||||
# dmesg (including stack trace) into pstore upon a panic, and
|
# dmesg (including stack trace) into pstore upon a panic even if kdump
|
||||||
# printk.always_kmsg_dump parameter enables the kernel to write dmesg
|
# 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
|
||||||
# upon a normal shutdown (shutdown, reboot, halt).
|
# upon a normal shutdown (shutdown, reboot, halt).
|
||||||
#
|
#
|
||||||
# To configure the kernel parameters, uncomment the appropriate
|
# To configure the kernel parameters, uncomment the appropriate
|
||||||
|
@ -26,4 +31,4 @@
|
||||||
|
|
||||||
d /var/lib/systemd/pstore 0755 root root 14d
|
d /var/lib/systemd/pstore 0755 root root 14d
|
||||||
#w- /sys/module/printk/parameters/always_kmsg_dump - - - - Y
|
#w- /sys/module/printk/parameters/always_kmsg_dump - - - - Y
|
||||||
w- /sys/module/kernel/parameters/crash_kexec_post_notifiers - - - - Y
|
#w- /sys/module/kernel/parameters/crash_kexec_post_notifiers - - - - Y
|
||||||
|
|
Loading…
Reference in New Issue