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