BinPackArguments: false
BinPackParameters: false
BraceWrapping:
- AfterEnum: true
+ AfterEnum: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
/.mkosi-*
/mkosi.builddir/
/mkosi.output/
+/mkosi.default
/tags
# Copyright © 2016 Zeal Jagannatha
# This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi).
-# Simply invoke "mkosi" in the project directory to build an OS image.
+# Symlink this file to mkosi.default in the project root directory and invoke "mkosi" to build an OS image.
[Distribution]
Distribution=arch
# SPDX-License-Identifier: LGPL-2.1+
# This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi).
-# Simply invoke "mkosi" in the project directory to build an OS image.
+# Symlink this file to mkosi.default in the project root directory and invoke "mkosi" to build an OS image.
[Distribution]
Distribution=debian
# SPDX-License-Identifier: LGPL-2.1+
# This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi).
-# Simply invoke "mkosi" in the project directory to build an OS image.
+# Symlink this file to mkosi.default in the project root directory and invoke "mkosi" to build an OS image.
[Distribution]
Distribution=fedora
# SPDX-License-Identifier: LGPL-2.1+
# This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi).
-# Simply invoke "mkosi" in the project directory to build an OS image.
+# Symlink this file to mkosi.default in the project root directory and invoke "mkosi" to build an OS image.
[Distribution]
Distribution=opensuse
# SPDX-License-Identifier: LGPL-2.1+
# This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi).
-# Simply invoke "mkosi" in the project directory to build an OS image.
+# Symlink this file to mkosi.default in the project root directory and invoke "mkosi" to build an OS image.
[Distribution]
Distribution=ubuntu
LogControl1 D-Bus API which allows clients to change log level +
target of the service during runtime.
+ * Various command line parameters and configuration file settings that
+ configure key or certificate files now optionally take paths to
+ AF_UNIX sockets in the file system. If configured that way a stream
+ connection is made to the socket and the required data read from
+ it. This is a simple and natural extension to the existing regular
+ file logic, and permits other software to provide keys or
+ certificates via simple IPC services, for example when unencrypted
+ storage on disk is not desired. Specifically, systemd-networkd's
+ Wireguard and MACSEC key file settings as well as
+ systemd-journal-gatewayd's and systemd-journal-remote's PEM
+ key/certificate parameters support this now.
+
Contributions from: 24bisquitz, Adam Nielsen, Alan Perry, Alexander
Malafeev, Alin Popa, Amos Bird, Andreas Rammhold, AndreRH, Andrew
Doran, Anita Zhang, Ankit Jain, antznin, Arnaud Ferraris, Arthur Moraes
Features:
+* cryptsetup: if keyfile specified in crypttab is AF_UNIX socket, connect to it
+ and read from it (like we do elsewhere with READ_FULL_FILE_CONNECT_SOCKET)
+
* repart: support setting up dm-integrity with HMAC
* add /etc/integritytab, to support dm-integrity setups. In particular those
## XDG autostart integration
-To allow XDG autostart integration, systemd will ship a cross-desktop generator
-to create appropriate units for the autostart directory.
-Desktop Environments will be able to make use of this simply by starting the
-appropriate XDG related targets (representing e.g. content of the
-`$XDG_CURRENT_DESKTOP` environment variable to handle `OnlyShowIn/NotShowIn`).
-The names and ordering rules for these targets are to be defined.
-
-This generator will likely never support certain desktop specific extensions.
-One such example is the GNOME specific feature to bind a service to a settings
-variable.
+To allow XDG autostart integration, systemd ships a cross-desktop generator
+to create appropriate units for the autostart directory
+(`systemd-xdg-autostart-generator`).
+Desktop Environments can opt-in to using this by starting
+`xdg-desktop-autostart.target`. The systemd generator correctly handles
+`OnlyShowIn=` and `NotShowin=`. It also handles the KDE and GNOME specific
+`X-KDE-autostart-condition=` and `AutostartCondition=` by using desktop
+environment provided binaries in an `ExecCondition=` line.
+
+However, this generator is somewhat limited in what it supports. For example,
+all generated units will have `After=graphical-session.target` set on them,
+it may therefore not be useful to start session services.
+
+Desktop files can be marked to be explicitly excluded from the generator using the line
+`X-systemd-skip=true`. This should be set if an application provides its own
+systemd service file for startup.
## Startup and shutdown best practices
fresh build of the project in the local working directory. To make use of this,
please acquire `mkosi` from https://github.com/systemd/mkosi first, unless your
distribution has packaged it already and you can get it from there. After the
-tool is installed it is sufficient to type `mkosi` in the systemd project
-directory to generate a disk image `image.raw` you can boot either in
-`systemd-nspawn` or in an UEFI-capable VM:
+tool is installed, symlink the settings file for your distribution of choice from
+.mkosi/ to mkosi.default in the project root directory (note that the package
+manager for this distro needs to be installed on your host system). After doing
+that, it is sufficient to type `mkosi` in the systemd project directory to
+generate a disk image `image.raw` you can boot either in `systemd-nspawn` or in
+an UEFI-capable VM:
```
# systemd-nspawn -bi image.raw
for systemd (this example is for Fedora):
```sh
-$ sudo dnf builddep systemd # install build dependencies
-$ sudo dnf install mkosi # install tool to quickly build images
+$ sudo dnf builddep systemd # install build dependencies
+$ sudo dnf install mkosi # install tool to quickly build images
$ git clone https://github.com/systemd/systemd.git
$ cd systemd
-$ vim src/core/main.c # or wherever you'd like to make your changes
-$ meson build # configure the build
-$ ninja -C build # build it locally, see if everything compiles fine
-$ ninja -C build test # run some simple regression tests
-$ (umask 077; echo 123 > mkosi.rootpw) # set root password used by mkosi
-$ sudo mkosi # build a test image
-$ sudo systemd-nspawn -bi image.raw # boot up the test image
-$ git add -p # interactively put together your patch
-$ git commit # commit it
+$ vim src/core/main.c # or wherever you'd like to make your changes
+$ meson build # configure the build
+$ ninja -C build # build it locally, see if everything compiles fine
+$ ninja -C build test # run some simple regression tests
+$ ln -s .mkosi/mkosi.fedora mkosi.default # Configure mkosi to build a fedora image
+$ (umask 077; echo 123 > mkosi.rootpw) # set root password used by mkosi
+$ sudo mkosi # build a test image
+$ sudo systemd-nspawn -bi image.raw # boot up the test image
+$ git add -p # interactively put together your patch
+$ git commit # commit it
$ git push REMOTE HEAD:refs/heads/BRANCH
- # where REMOTE is your "fork" on GitHub
- # and BRANCH is a branch name.
+ # where REMOTE is your "fork" on GitHub
+ # and BRANCH is a branch name.
```
And after that, head over to your repo on GitHub and click "Compare & pull request"
usb:v058Fp9540*
ID_AUTOSUSPEND=1
+#########################################
+# QEMU
+#########################################
+
+# Emulated USB HID devices
+usb:v0627p0001:*QEMU USB Keyboard*
+usb:v0627p0001:*QEMU USB Mouse*
+usb:v0627p0001:*QEMU USB Tablet*
+ ID_AUTOSUSPEND=1
+
#########################################
# Wacom
#########################################
'OUI',
}
+def upperhex_word(length):
+ return Word(nums + 'ABCDEF', exact=length)
+
@lru_cache()
def hwdb_grammar():
ParserElement.setDefaultWhitespaceChars('')
for category, conn in TYPES.items())
matchline_typed = Combine(prefix + Word(printables + ' ' + '®'))
- matchline_general = Combine(Or(GENERAL_MATCHES) + ':' + Word(printables))
+ matchline_general = Combine(Or(GENERAL_MATCHES) + ':' + Word(printables + ' ' + '®'))
matchline = (matchline_typed | matchline_general) + EOL
propertyline = (White(' ', exact=1).suppress() +
return []
return [convert_properties(g) for g in parsed.GROUPS]
-def check_match_uniqueness(groups):
+def check_matches(groups):
matches = sum((group[0] for group in groups), [])
+
+ # This is a partial check. The other cases could be also done, but those
+ # two are most commonly wrong.
+ grammars = { 'usb' : 'v' + upperhex_word(4) + Optional('p' + upperhex_word(4)),
+ 'pci' : 'v' + upperhex_word(8) + Optional('d' + upperhex_word(8)),
+ }
+
+ for match in matches:
+ prefix, rest = match.split(':', maxsplit=1)
+ gr = grammars.get(prefix)
+ if gr:
+ try:
+ gr.parseString(rest)
+ except ParseBaseException as e:
+ error('Pattern {!r} is invalid: {}', rest, e)
+ continue
+ if rest[-1] not in '*:':
+ error('pattern {} does not end with "*" or ":"', match)
+
matches.sort()
prev = None
for match in matches:
for fname in args:
groups = parse(fname)
print_summary(fname, groups)
- check_match_uniqueness(groups)
+ check_matches(groups)
check_properties(groups)
sys.exit(ERROR)
settings for network interfaces. These commands may be used to inform
<command>systemd-resolved</command> or <command>systemd-networkd</command> about per-interface DNS
configuration determined through external means. The <command>dns</command> command expects IPv4 or
- IPv6 address specifications of DNS servers to use. The <command>domain</command> command expects
- valid DNS domains, possibly prefixed with <literal>~</literal>, and configures a per-interface
- search or route-only domain. The <command>default-route</command> command expects a boolean
- parameter, and configures whether the link may be used as default route for DNS lookups, i.e. if it
- is suitable for lookups on domains no other link explicitly is configured for. The
- <command>llmnr</command>, <command>mdns</command>, <command>dnssec</command> and
- <command>dnsovertls</command> commands may be used to configure the per-interface LLMNR,
- MulticastDNS, DNSSEC and DNSOverTLS settings. Finally, <command>nta</command> command may be used
- to configure additional per-interface DNSSEC NTA domains.</para>
+ IPv6 address specifications of DNS servers to use. Each address can optionally take a port number
+ separated with <literal>:</literal>, a network interface name or index separated with
+ <literal>%</literal>, and a Server Name Indication (SNI) separated with <literal>#</literal>. When
+ IPv6 address is specified with a port number, then the address must be in the square brackets. That
+ is, the acceptable full formats are <literal>111.222.333.444:9953%ifname#example.com</literal> for
+ IPv4 and <literal>[1111:2222::3333]:9953%ifname#example.com</literal> for IPv6. The
+ <command>domain</command> command expects valid DNS domains, possibly prefixed with
+ <literal>~</literal>, and configures a per-interface search or route-only domain. The
+ <command>default-route</command> command expects a boolean parameter, and configures whether the
+ link may be used as default route for DNS lookups, i.e. if it is suitable for lookups on domains no
+ other link explicitly is configured for. The <command>llmnr</command>, <command>mdns</command>,
+ <command>dnssec</command> and <command>dnsovertls</command> commands may be used to configure the
+ per-interface LLMNR, MulticastDNS, DNSSEC and DNSOverTLS settings. Finally, <command>nta</command>
+ command may be used to configure additional per-interface DNSSEC NTA domains.</para>
<para>Commands <command>dns</command>, <command>domain</command> and <command>nta</command> can take
a single empty string argument to clear their respective value lists.</para>
<varlistentry>
<term><varname>DNS=</varname></term>
- <listitem><para>A space-separated list of IPv4 and IPv6 addresses to use as system DNS servers. DNS requests
- are sent to one of the listed DNS servers in parallel to suitable per-link DNS servers acquired from
+ <listitem><para>A space-separated list of IPv4 and IPv6 addresses to use as system DNS servers. Each address can
+ optionally take a port number separated with <literal>:</literal>, a network interface name or index separated with
+ <literal>%</literal>, and a Server Name Indication (SNI) separated with <literal>#</literal>. When IPv6 address is
+ specified with a port number, then the address must be in the square brackets. That is, the acceptable full formats
+ are <literal>111.222.333.444:9953%ifname#example.com</literal> for IPv4 and
+ <literal>[1111:2222::3333]:9953%ifname#example.com</literal> for IPv6. DNS requests are sent to one of the listed
+ DNS servers in parallel to suitable per-link DNS servers acquired from
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> or
set at runtime by external applications. For compatibility reasons, if this setting is not specified, the DNS
servers listed in <filename>/etc/resolv.conf</filename> are used instead, if that file exists and any servers
<varlistentry>
<term><varname>FallbackDNS=</varname></term>
- <listitem><para>A space-separated list of IPv4 and IPv6 addresses to use as the fallback DNS servers. Any
- per-link DNS servers obtained from
+ <listitem><para>A space-separated list of IPv4 and IPv6 addresses to use as the fallback DNS servers. Please see
+ <varname>DNS=</varname> for acceptable format of adddresses. Any per-link DNS servers obtained from
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
take precedence over this setting, as do any servers set via <varname>DNS=</varname> above or
<filename>/etc/resolv.conf</filename>. This setting is hence only used if no other DNS server information is
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1+ -->
-<refentry id="systemd-initctl.service">
+<refentry id="systemd-initctl.service" conditional='HAVE_SYSV_COMPAT'>
<refentryinfo>
<title>systemd-initctl.service</title>
<varlistentry>
<term><option>--cert=</option></term>
- <listitem><para>Specify the path to a file containing a server
- certificate in PEM format. This option switches
- <command>systemd-journal-gatewayd</command> into HTTPS mode
- and must be used together with
+ <listitem><para>Specify the path to a file or <constant>AF_UNIX</constant> stream socket to read the
+ server certificate from. The certificate must be in PEM format. This option switches
+ <command>systemd-journal-gatewayd</command> into HTTPS mode and must be used together with
<option>--key=</option>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--key=</option></term>
- <listitem><para>Specify the path to a file containing a server
- key in PEM format corresponding to the certificate specified
- with <option>--cert=</option>.</para></listitem>
+ <listitem><para>Specify the path to a file or <constant>AF_UNIX</constant> stream socket to read the
+ server key corresponding to the certificate specified with <option>--cert=</option> from. The key
+ must be in PEM format.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--trust=</option></term>
- <listitem><para>Specify the path to a file containing a
- CA certificate in PEM format.</para></listitem>
+ <listitem><para>Specify the path to a file or <constant>AF_UNIX</constant> stream socket to read a CA
+ certificate from. The certificate must be in PEM format.</para></listitem>
</varlistentry>
<varlistentry>
<varlistentry>
<term><option>--key=</option></term>
- <listitem><para>
- Takes a path to a SSL key file in PEM format.
- Defaults to <filename>&CERTIFICATE_ROOT;/private/journal-remote.pem</filename>.
- This option can be used with <option>--listen-https=</option>.
- </para></listitem>
+ <listitem><para> Takes a path to a SSL key file in PEM format. Defaults to
+ <filename>&CERTIFICATE_ROOT;/private/journal-remote.pem</filename>. This option can be used with
+ <option>--listen-https=</option>. If the path refers to an <constant>AF_UNIX</constant> stream socket
+ in the file system a connection is made to it and the key read from it.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--cert=</option></term>
- <listitem><para>
- Takes a path to a SSL certificate file in PEM format.
- Defaults to <filename>&CERTIFICATE_ROOT;/certs/journal-remote.pem</filename>.
- This option can be used with <option>--listen-https=</option>.
- </para></listitem>
+ <listitem><para> Takes a path to a SSL certificate file in PEM format. Defaults to
+ <filename>&CERTIFICATE_ROOT;/certs/journal-remote.pem</filename>. This option can be used with
+ <option>--listen-https=</option>. If the path refers to an <constant>AF_UNIX</constant> stream socket
+ in the file system a connection is made to it and the certificate read from it.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--trust=</option></term>
- <listitem><para>
- Takes a path to a SSL CA certificate file in PEM format,
- or <option>all</option>. If <option>all</option> is set,
- then certificate checking will be disabled.
- Defaults to <filename>&CERTIFICATE_ROOT;/ca/trusted.pem</filename>.
- This option can be used with <option>--listen-https=</option>.
- </para></listitem>
+ <listitem><para> Takes a path to a SSL CA certificate file in PEM format, or <option>all</option>. If
+ <option>all</option> is set, then certificate checking will be disabled. Defaults to
+ <filename>&CERTIFICATE_ROOT;/ca/trusted.pem</filename>. This option can be used with
+ <option>--listen-https=</option>. If the path refers to an <constant>AF_UNIX</constant> stream socket
+ in the file system a connection is made to it and the certificate read from it.</para></listitem>
</varlistentry>
<varlistentry>
<varlistentry>
<term><varname>KeyFile=</varname></term>
<listitem>
- <para>Takes a absolute path to a file which contains a 128-bit key encoded in a hexadecimal
- string, which will be used in the transmission channel. When this option is specified,
+ <para>Takes a absolute path to a file which contains a 128-bit key encoded in a hexadecimal string,
+ which will be used in the transmission channel. When this option is specified,
<varname>Key=</varname> is ignored. Note that the file must be readable by the user
<literal>systemd-network</literal>, so it should be, e.g., owned by
- <literal>root:systemd-network</literal> with a <literal>0640</literal> file mode.</para>
+ <literal>root:systemd-network</literal> with a <literal>0640</literal> file mode. If the path
+ refers to an <constant>AF_UNIX</constant> stream socket in the file system a connection is made to
+ it and the key read from it.</para>
</listitem>
</varlistentry>
<varlistentry>
<varlistentry>
<term><varname>PrivateKeyFile=</varname></term>
<listitem>
- <para>Takes an absolute path to a file which contains the Base64 encoded private key for the interface.
- When this option is specified, then <varname>PrivateKey=</varname> is ignored.
- Note that the file must be readable by the user <literal>systemd-network</literal>, so it
- should be, e.g., owned by <literal>root:systemd-network</literal> with a
- <literal>0640</literal> file mode.</para>
+ <para>Takes an absolute path to a file which contains the Base64 encoded private key for the
+ interface. When this option is specified, then <varname>PrivateKey=</varname> is ignored. Note
+ that the file must be readable by the user <literal>systemd-network</literal>, so it should be,
+ e.g., owned by <literal>root:systemd-network</literal> with a <literal>0640</literal> file mode. If
+ the path refers to an <constant>AF_UNIX</constant> stream socket in the file system a connection is
+ made to it and the key read from it.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>PresharedKeyFile=</varname></term>
<listitem>
<para>Takes an absolute path to a file which contains the Base64 encoded preshared key for the
- peer. When this option is specified, then <varname>PresharedKey=</varname> is ignored.
- Note that the file must be readable by the user <literal>systemd-network</literal>, so it
- should be, e.g., owned by <literal>root:systemd-network</literal> with a
- <literal>0640</literal> file mode.</para>
+ peer. When this option is specified, then <varname>PresharedKey=</varname> is ignored. Note that
+ the file must be readable by the user <literal>systemd-network</literal>, so it should be, e.g.,
+ owned by <literal>root:systemd-network</literal> with a <literal>0640</literal> file mode. If the
+ path refers to an <constant>AF_UNIX</constant> stream socket in the file system a connection is
+ made to it and the key read from it.</para>
</listitem>
</varlistentry>
<varlistentry>
<para>A DNS server address, which must be in the format
described in
<citerefentry project='man-pages'><refentrytitle>inet_pton</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
- This option may be specified more than once. This setting is read by
+ This option may be specified more than once. Each address can optionally take a port number
+ separated with <literal>:</literal>, a network interface name or index separated with
+ <literal>%</literal>, and a Server Name Indication (SNI) separated with <literal>#</literal>.
+ When IPv6 address is specified with a port number, then the address must be in the square
+ brackets. That is, the acceptable full formats are
+ <literal>111.222.333.444:9953%ifname#example.com</literal> for IPv4 and
+ <literal>[1111:2222::3333]:9953%ifname#example.com</literal> for IPv6. This setting can be
+ specified multiple times. If an empty string is assigned, then the all previous assignments
+ are cleared. This setting is read by
<citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
link_with : [libshared],
dependencies : [libaudit],
install_rpath : rootlibexecdir,
- install : true,
+ install : (conf.get('ENABLE_UTMP') == 1),
install_dir : rootlibexecdir)
if conf.get('HAVE_KMOD') == 1
+++ /dev/null
-.mkosi/mkosi.fedora
\ No newline at end of file
r = acquire_bus(&bus, &use_full_bus);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
n = acquire_boot_times(bus, &boot);
if (n < 0)
r = acquire_bus(&bus, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
n = acquire_time_data(bus, ×);
if (n <= 0)
r = acquire_bus(&bus, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
n = acquire_time_data(bus, ×);
if (n <= 0)
r = acquire_bus(&bus, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
r = pretty_boot_time(bus, &buf);
if (r < 0)
r = acquire_bus(&bus, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns);
if (r < 0)
r = acquire_bus(&bus, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
(void) pager_open(arg_pager_flags);
r = acquire_bus(&bus, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
r = bus_set_property(bus, bus_systemd_mgr, "LogLevel", &error, "s", argv[1]);
if (r < 0)
r = acquire_bus(&bus, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
r = bus_get_property_string(bus, bus_systemd_mgr, "LogLevel", &error, &level);
if (r < 0)
r = acquire_bus(&bus, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
r = bus_set_property(bus, bus_systemd_mgr, "LogTarget", &error, "s", argv[1]);
if (r < 0)
r = acquire_bus(&bus, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
r = bus_get_property_string(bus, bus_systemd_mgr, "LogTarget", &error, &target);
if (r < 0)
r = acquire_bus(&bus, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
if (argc == 1) {
/* get ServiceWatchdogs */
r = acquire_bus(&bus, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
(void) pager_open(arg_pager_flags);
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
+#include "socket-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "tmpfile-util.h"
assert(f);
assert(ret_contents);
assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX));
- assert(!(flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) || ret_size);
n_next = LINE_MAX; /* Start size */
fd = fileno(f);
- if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen(), let's
- * optimize our buffering) */
+ if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen()), let's
+ * optimize our buffering */
if (fstat(fd, &st) < 0)
return -errno;
if (st.st_size > 0)
n_next = st.st_size + 1;
- if (flags & READ_FULL_FILE_SECURE)
+ if (flags & READ_FULL_FILE_WARN_WORLD_READABLE)
(void) warn_file_is_world_accessible(filename, &st, NULL, 0);
}
}
errno = 0;
k = fread(buf + l, 1, n - l, f);
- if (k > 0)
- l += k;
+
+ assert(k <= n - l);
+ l += k;
if (ferror(f)) {
r = errno_or_else(EIO);
goto finalize;
}
-
if (feof(f))
break;
- /* We aren't expecting fread() to return a short read outside
- * of (error && eof), assert buffer is full and enlarge buffer.
- */
- assert(l == n);
+ assert(k > 0); /* we can't have read zero bytes because that would have been EOF */
/* Safety check */
if (n >= READ_FULL_BYTES_MAX) {
}
if (flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) {
+ _cleanup_free_ void *decoded = NULL;
+ size_t decoded_size;
+
buf[l++] = 0;
if (flags & READ_FULL_FILE_UNBASE64)
- r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size);
+ r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, &decoded, &decoded_size);
else
- r = unhexmem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size);
- goto finalize;
+ r = unhexmem_full(buf, l, flags & READ_FULL_FILE_SECURE, &decoded, &decoded_size);
+ if (r < 0)
+ goto finalize;
+
+ if (flags & READ_FULL_FILE_SECURE)
+ explicit_bzero_safe(buf, n);
+ free_and_replace(buf, decoded);
+ n = l = decoded_size;
}
if (!ret_size) {
assert(contents);
r = xfopenat(dir_fd, filename, "re", 0, &f);
- if (r < 0)
- return r;
+ if (r < 0) {
+ _cleanup_close_ int dfd = -1, sk = -1;
+ union sockaddr_union sa;
+
+ /* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */
+ if (r != -ENXIO)
+ return r;
+
+ /* If this is enabled, let's try to connect to it */
+ if (!FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET))
+ return -ENXIO;
+
+ if (dir_fd == AT_FDCWD)
+ r = sockaddr_un_set_path(&sa.un, filename);
+ else {
+ char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+
+ /* If we shall operate relative to some directory, then let's use O_PATH first to
+ * open the socket inode, and then connect to it via /proc/self/fd/. We have to do
+ * this since there's not connectat() that takes a directory fd as first arg. */
+
+ dfd = openat(dir_fd, filename, O_PATH|O_CLOEXEC);
+ if (dfd < 0)
+ return -errno;
+
+ xsprintf(procfs_path, "/proc/self/fd/%i", dfd);
+ r = sockaddr_un_set_path(&sa.un, procfs_path);
+ }
+ if (r < 0)
+ return r;
+
+ sk = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+ if (sk < 0)
+ return -errno;
+
+ if (connect(sk, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
+ return errno == ENOTSOCK ? -ENXIO : -errno; /* propagate original error if this is
+ * not a socket after all */
+
+ if (shutdown(sk, SHUT_WR) < 0)
+ return -errno;
+
+ f = fdopen(sk, "r");
+ if (!f)
+ return -errno;
+
+ TAKE_FD(sk);
+ }
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
} WriteStringFileFlags;
typedef enum {
- READ_FULL_FILE_SECURE = 1 << 0,
- READ_FULL_FILE_UNBASE64 = 1 << 1,
- READ_FULL_FILE_UNHEX = 1 << 2,
+ READ_FULL_FILE_SECURE = 1 << 0, /* erase any buffers we employ internally, after use */
+ READ_FULL_FILE_UNBASE64 = 1 << 1, /* base64 decode what we read */
+ READ_FULL_FILE_UNHEX = 1 << 2, /* hex decode what we read */
+ READ_FULL_FILE_WARN_WORLD_READABLE = 1 << 3, /* if regular file, log at LOG_WARNING level if access mode above 0700 */
+ READ_FULL_FILE_CONNECT_SOCKET = 1 << 4, /* if socket inode, connect to it and read off it */
} ReadFullFileFlags;
int fopen_unlocked(const char *path, const char *options, FILE **ret);
#include "macro.h"
#include "parse-util.h"
#include "random-util.h"
+#include "string-util.h"
#include "strxcpyx.h"
#include "util.h"
return in_addr_to_string(family, u, ret);
}
+int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret) {
+ _cleanup_free_ char *ip_str = NULL, *x = NULL;
+ int r;
+
+ assert(IN_SET(family, AF_INET, AF_INET6));
+ assert(u);
+ assert(ret);
+
+ /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly
+ * handle IPv6 link-local addresses. */
+
+ r = in_addr_to_string(family, u, &ip_str);
+ if (r < 0)
+ return r;
+
+ if (family == AF_INET6) {
+ r = in_addr_is_link_local(family, u);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ ifindex = 0;
+ } else
+ ifindex = 0; /* For IPv4 address, ifindex is always ignored. */
+
+ if (port == 0 && ifindex == 0 && isempty(server_name)) {
+ *ret = TAKE_PTR(ip_str);
+ return 0;
+ }
+
+ const char *separator = isempty(server_name) ? "" : "#";
+ server_name = strempty(server_name);
+
+ if (port > 0) {
+ if (family == AF_INET6) {
+ if (ifindex > 0)
+ r = asprintf(&x, "[%s]:%"PRIu16"%%%i%s%s", ip_str, port, ifindex, separator, server_name);
+ else
+ r = asprintf(&x, "[%s]:%"PRIu16"%s%s", ip_str, port, separator, server_name);
+ } else
+ r = asprintf(&x, "%s:%"PRIu16"%s%s", ip_str, port, separator, server_name);
+ } else {
+ if (ifindex > 0)
+ r = asprintf(&x, "%s%%%i%s%s", ip_str, ifindex, separator, server_name);
+ else {
+ x = strjoin(ip_str, separator, server_name);
+ r = x ? 0 : -ENOMEM;
+ }
+ }
+ if (r < 0)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(x);
+ return 0;
+}
+
int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
union in_addr_union buffer;
assert(s);
int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret);
int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret);
+int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret);
int in_addr_from_string(int family, const char *s, union in_addr_union *ret);
int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret);
(empty_or_root(p->pw_dir) ||
!path_is_valid(p->pw_dir) ||
!path_is_absolute(p->pw_dir)))
- *home = NULL; /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
+ *home = NULL; /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
else
*home = p->pw_dir;
}
arg_show_unit == SHOW_UNIT_USER,
&bus);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
}
q = show_cgroup_get_unit_path_and_warn(bus, *name, &cgroup);
static int fix_acl(int fd, uid_t uid) {
#if HAVE_ACL
- _cleanup_(acl_freep) acl_t acl = NULL;
- acl_entry_t entry;
- acl_permset_t permset;
int r;
assert(fd >= 0);
+ assert(uid_is_valid(uid));
if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY)
return 0;
- /* Make sure normal users can read (but not write or delete)
- * their own coredumps */
-
- acl = acl_get_fd(fd);
- if (!acl)
- return log_error_errno(errno, "Failed to get ACL: %m");
-
- if (acl_create_entry(&acl, &entry) < 0 ||
- acl_set_tag_type(entry, ACL_USER) < 0 ||
- acl_set_qualifier(entry, &uid) < 0)
- return log_error_errno(errno, "Failed to patch ACL: %m");
-
- if (acl_get_permset(entry, &permset) < 0 ||
- acl_add_perm(permset, ACL_READ) < 0)
- return log_warning_errno(errno, "Failed to patch ACL: %m");
-
- r = calc_acl_mask_if_needed(&acl);
+ /* Make sure normal users can read (but not write or delete) their own coredumps */
+ r = add_acls_for_user(fd, uid);
if (r < 0)
- return log_warning_errno(r, "Failed to patch ACL: %m");
-
- if (acl_set_fd(fd, acl) < 0)
- return log_error_errno(errno, "Failed to apply ACL: %m");
+ return log_error_errno(r, "Failed to adjust ACL of coredump: %m");
#endif
return 0;
r = bus_connect_transport(arg_transport, arg_host, false, &bus);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
return hostnamectl_main(bus, argc, argv);
}
if (r < 0)
return r;
if (r == 0) {
+ const char *cmdline[] = {
+ "tar",
+ "--numeric-owner",
+ "-C", path,
+ "-px",
+ "--xattrs",
+ "--xattrs-include=*",
+ use_selinux ? "--selinux" : "--no-selinux",
+ NULL
+ };
+
uint64_t retain =
(1ULL << CAP_CHOWN) |
(1ULL << CAP_FOWNER) |
if (r < 0)
log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
- execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", "--xattrs", "--xattrs-include=*",
- use_selinux ? "--selinux" : "--no-selinux", NULL);
+ /* Try "gtar" before "tar". We only test things upstream with GNU tar. Some distros appear to
+ * install a different implementation as "tar" (in particular some that do not support the
+ * same command line switches), but then provide "gtar" as alias for the real thing, hence
+ * let's prefer that. (Yes, it's a bad idea they do that, given they don't provide equivalent
+ * command line support, but we are not here to argue, let's just expose the same
+ * behaviour/implementation everywhere.) */
+ execvp("gtar", (char* const*) cmdline);
+ execvp("tar", (char* const*) cmdline);
+
log_error_errno(errno, "Failed to execute tar: %m");
_exit(EXIT_FAILURE);
}
if (r < 0)
return r;
if (r == 0) {
+ const char *cmdline[] = {
+ "tar",
+ "-C", path,
+ "-c",
+ "--xattrs",
+ "--xattrs-include=*",
+ use_selinux ? "--selinux" : "--no-selinux",
+ ".",
+ NULL
+ };
+
uint64_t retain = (1ULL << CAP_DAC_OVERRIDE);
/* Child */
if (r < 0)
log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
- execlp("tar", "tar", "-C", path, "-c", "--xattrs", "--xattrs-include=*",
- use_selinux ? "--selinux" : "--no-selinux", ".", NULL);
+ execvp("gtar", (char* const*) cmdline);
+ execvp("tar", (char* const*) cmdline);
+
log_error_errno(errno, "Failed to execute tar: %m");
_exit(EXIT_FAILURE);
}
if (arg_key_pem)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Key file specified twice");
- r = read_full_file(optarg, &arg_key_pem, NULL);
+ r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, &arg_key_pem, NULL);
if (r < 0)
return log_error_errno(r, "Failed to read key file: %m");
assert(arg_key_pem);
if (arg_cert_pem)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Certificate file specified twice");
- r = read_full_file(optarg, &arg_cert_pem, NULL);
+ r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, &arg_cert_pem, NULL);
if (r < 0)
return log_error_errno(r, "Failed to read certificate file: %m");
assert(arg_cert_pem);
if (arg_trust_pem)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"CA certificate file specified twice");
- r = read_full_file(optarg, &arg_trust_pem, NULL);
+ r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, &arg_trust_pem, NULL);
if (r < 0)
return log_error_errno(r, "Failed to read CA certificate file: %m");
assert(arg_trust_pem);
static int load_certificates(char **key, char **cert, char **trust) {
int r;
- r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL);
+ r = read_full_file_full(AT_FDCWD, arg_key ?: PRIV_KEY_FILE, READ_FULL_FILE_CONNECT_SOCKET, key, NULL);
if (r < 0)
return log_error_errno(r, "Failed to read key from file '%s': %m",
arg_key ?: PRIV_KEY_FILE);
- r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
+ r = read_full_file_full(AT_FDCWD, arg_cert ?: CERT_FILE, READ_FULL_FILE_CONNECT_SOCKET, cert, NULL);
if (r < 0)
return log_error_errno(r, "Failed to read certificate from file '%s': %m",
arg_cert ?: CERT_FILE);
if (arg_trust_all)
log_info("Certificate checking disabled.");
else {
- r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
+ r = read_full_file_full(AT_FDCWD, arg_trust ?: TRUST_FILE, READ_FULL_FILE_CONNECT_SOCKET, trust, NULL);
if (r < 0)
return log_error_errno(r, "Failed to read CA certificate file '%s': %m",
arg_trust ?: TRUST_FILE);
return ra->event;
}
+_public_ int sd_radv_is_running(sd_radv *ra) {
+ assert_return(ra, false);
+
+ return ra->state != SD_RADV_STATE_IDLE;
+}
+
static void radv_reset(sd_radv *ra) {
assert(ra);
#include "sd-netlink.h"
+#include "format-util.h"
#include "memory-util.h"
#include "netlink-internal.h"
#include "netlink-util.h"
int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
+ _cleanup_strv_free_ char **alternative_names = NULL;
+ char old_name[IF_NAMESIZE + 1] = {};
int r;
assert(rtnl);
if (!ifname_valid(name))
return -EINVAL;
- if (!*rtnl) {
- r = sd_netlink_open(rtnl);
+ r = rtnl_get_link_alternative_names(rtnl, ifindex, &alternative_names);
+ if (r < 0)
+ log_debug_errno(r, "Failed to get alternative names on network interface %i, ignoring: %m",
+ ifindex);
+
+ if (strv_contains(alternative_names, name)) {
+ r = rtnl_delete_link_alternative_names(rtnl, ifindex, STRV_MAKE(name));
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to remove '%s' from alternative names on network interface %i: %m",
+ name, ifindex);
+
+ format_ifname(ifindex, old_name);
}
r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex);
if (r < 0)
return r;
+ if (!isempty(old_name)) {
+ r = rtnl_set_link_alternative_names(rtnl, ifindex, STRV_MAKE(old_name));
+ if (r < 0)
+ log_debug_errno(r, "Failed to set '%s' as an alternative name on network interface %i, ignoring: %m",
+ old_name, ifindex);
+ }
+
return 0;
}
return 0;
}
-int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names) {
+int rtnl_get_link_alternative_names(sd_netlink **rtnl, int ifindex, char ***ret) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
+ _cleanup_strv_free_ char **names = NULL;
+ int r;
+
+ assert(rtnl);
+ assert(ifindex > 0);
+ assert(ret);
+
+ if (!*rtnl) {
+ r = sd_netlink_open(rtnl);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_call(*rtnl, message, 0, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_read_strv(reply, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &names);
+ if (r < 0 && r != -ENODATA)
+ return r;
+
+ *ret = TAKE_PTR(names);
+
+ return 0;
+}
+
+static int rtnl_update_link_alternative_names(sd_netlink **rtnl, uint16_t nlmsg_type, int ifindex, char * const *alternative_names) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
int r;
assert(rtnl);
assert(ifindex > 0);
+ assert(IN_SET(nlmsg_type, RTM_NEWLINKPROP, RTM_DELLINKPROP));
if (strv_isempty(alternative_names))
return 0;
return r;
}
- r = sd_rtnl_message_new_link(*rtnl, &message, RTM_NEWLINKPROP, ifindex);
+ r = sd_rtnl_message_new_link(*rtnl, &message, nlmsg_type, ifindex);
if (r < 0)
return r;
return 0;
}
+int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names) {
+ return rtnl_update_link_alternative_names(rtnl, RTM_NEWLINKPROP, ifindex, alternative_names);
+}
+
+int rtnl_delete_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names) {
+ return rtnl_update_link_alternative_names(rtnl, RTM_DELLINKPROP, ifindex, alternative_names);
+}
+
int rtnl_set_link_alternative_names_by_ifname(sd_netlink **rtnl, const char *ifname, char * const *alternative_names) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
int r;
if (r < 0)
return r;
+ rtnl_message_seal(*ret);
(*ret)->hdr->nlmsg_seq = serial;
err = NLMSG_DATA((*ret)->hdr);
-
err->error = error;
return 0;
int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name);
int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, const struct ether_addr *mac, uint32_t mtu);
+int rtnl_get_link_alternative_names(sd_netlink **rtnl, int ifindex, char ***ret);
int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names);
int rtnl_set_link_alternative_names_by_ifname(sd_netlink **rtnl, const char *ifname, char * const *alternative_names);
+int rtnl_delete_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names);
int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name);
int rtnl_get_link_iftype(sd_netlink **rtnl, int ifindex, unsigned short *ret);
r = bus_connect_transport(arg_transport, arg_host, false, &bus);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
return localectl_main(bus, argc, argv);
}
r = bus_connect_transport(arg_transport, arg_host, false, &bus);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
(void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
return r;
} else {
if (x == UINT64_MAX) {
- if (unlink("/run/systemd/reboot-to-loader-menu") < 0 && errno != ENOENT)
+ if (unlink("/run/systemd/reboot-to-boot-loader-menu") < 0 && errno != ENOENT)
return -errno;
} else {
char buf[DECIMAL_STR_MAX(uint64_t) + 1];
xsprintf(buf, "%" PRIu64, x); /* µs granularity */
- r = write_string_file_atomic_label("/run/systemd/reboot-to-loader-menu", buf);
+ r = write_string_file_atomic_label("/run/systemd/reboot-to-boot-loader-menu", buf);
if (r < 0)
return r;
}
r = bus_connect_transport(arg_transport, arg_host, false, &bus);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
(void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
if (arg_action == ACTION_UMOUNT)
return action_umount(bus, argc, argv);
(void) warn_file_is_world_accessible(sa->key_file, NULL, NULL, 0);
- r = read_full_file_full(AT_FDCWD, sa->key_file, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX, (char **) &key, &key_len);
+ r = read_full_file_full(
+ AT_FDCWD, sa->key_file,
+ READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
+ (char **) &key, &key_len);
if (r < 0)
return log_netdev_error_errno(netdev, r,
"Failed to read key from '%s', ignoring: %m",
(void) warn_file_is_world_accessible(filename, NULL, NULL, 0);
- r = read_full_file_full(AT_FDCWD, filename, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64, &key, &key_len);
+ r = read_full_file_full(
+ AT_FDCWD, filename,
+ READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
+ &key, &key_len);
if (r < 0)
return r;
return r;
r = sd_netlink_message_read_strv(m, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &altnames);
- if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENODATA))
+ if (r < 0 && r != -ENODATA)
return r;
if (patterns) {
if (r < 0)
return table_log_add_error(r);
+ strv_sort(info->alternative_names);
r = dump_list(table, "Alternative Names:", info->alternative_names);
if (r < 0)
return r;
return -ENOENT;
}
+static bool address_exists_internal(Set *addresses, int family, const union in_addr_union *in_addr) {
+ Address *address;
+ Iterator i;
+
+ SET_FOREACH(address, addresses, i) {
+ if (address->family != family)
+ continue;
+ if (in_addr_equal(address->family, &address->in_addr, in_addr))
+ return true;
+ }
+
+ return false;
+}
+
+bool address_exists(Link *link, int family, const union in_addr_union *in_addr) {
+ assert(link);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+ assert(in_addr);
+
+ if (address_exists_internal(link->addresses, family, in_addr))
+ return true;
+ if (address_exists_internal(link->addresses_foreign, family, in_addr))
+ return true;
+ return false;
+}
+
static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
int address_get(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
+bool address_exists(Link *link, int family, const union in_addr_union *in_addr);
int address_update(Address *address, unsigned char flags, unsigned char scope, const struct ifa_cacheinfo *cinfo);
int address_drop(Address *address);
int address_configure(Address *address, Link *link, link_netlink_message_handler_t callback, bool update);
struct in_addr ia;
/* Only look for IPv4 addresses */
- if (link->network->dns[i].family != AF_INET)
+ if (link->network->dns[i]->family != AF_INET)
continue;
- ia = link->network->dns[i].address.in;
+ ia = link->network->dns[i]->address.in;
/* Never propagate obviously borked data */
if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia))
#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-get-properties.h"
+#include "bus-message-util.h"
#include "bus-polkit.h"
#include "dns-domain.h"
#include "networkd-link-bus.h"
#include "networkd-manager.h"
#include "parse-util.h"
#include "resolve-util.h"
+#include "socket-netlink.h"
#include "strv.h"
#include "user-util.h"
return sd_bus_reply_method_return(message, NULL);
}
-int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_free_ struct in_addr_data *dns = NULL;
- size_t allocated = 0, n = 0;
+static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, void *userdata, sd_bus_error *error, bool extended) {
+ struct in_addr_full **dns;
Link *l = userdata;
+ size_t n;
int r;
assert(message);
if (r < 0)
return r;
- r = sd_bus_message_enter_container(message, 'a', "(iay)");
- if (r < 0)
- return r;
-
- for (;;) {
- int family;
- size_t sz;
- const void *d;
-
- assert_cc(sizeof(int) == sizeof(int32_t));
-
- r = sd_bus_message_enter_container(message, 'r', "iay");
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- r = sd_bus_message_read(message, "i", &family);
- if (r < 0)
- return r;
-
- if (!IN_SET(family, AF_INET, AF_INET6))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
-
- r = sd_bus_message_read_array(message, 'y', &d, &sz);
- if (r < 0)
- return r;
- if (sz != FAMILY_ADDRESS_SIZE(family))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
-
- if (!dns_server_address_valid(family, d))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
-
- r = sd_bus_message_exit_container(message);
- if (r < 0)
- return r;
-
- if (!GREEDY_REALLOC(dns, allocated, n+1))
- return -ENOMEM;
-
- dns[n].family = family;
- memcpy(&dns[n].address, d, sz);
- n++;
- }
-
- r = sd_bus_message_exit_container(message);
+ r = bus_message_read_dns_servers(message, error, extended, &dns, &n);
if (r < 0)
return r;
NULL, true, UID_INVALID,
&l->manager->polkit_registry, error);
if (r < 0)
- return r;
- if (r == 0)
- return 1; /* Polkit will call us back */
+ goto finalize;
+ if (r == 0) {
+ r = 1; /* Polkit will call us back */
+ goto finalize;
+ }
+
+ if (l->n_dns != (unsigned) -1)
+ for (unsigned i = 0; i < l->n_dns; i++)
+ in_addr_full_free(l->dns[i]);
free_and_replace(l->dns, dns);
l->n_dns = n;
(void) link_dirty(l);
return sd_bus_reply_method_return(message, NULL);
+
+finalize:
+ for (size_t i = 0; i < n; i++)
+ in_addr_full_free(dns[i]);
+ free(dns);
+
+ return r;
+}
+
+int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return bus_link_method_set_dns_servers_internal(message, userdata, error, false);
+}
+
+int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return bus_link_method_set_dns_servers_internal(message, userdata, error, true);
}
int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
SD_BUS_METHOD("SetNTP", "as", NULL, bus_link_method_set_ntp_servers, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetDNSEx", "a(iayqs)", NULL, bus_link_method_set_dns_servers_ex, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_domains, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetDefaultRoute", "b", NULL, bus_link_method_set_default_route, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, SD_BUS_VTABLE_UNPRIVILEGED),
int bus_link_method_set_ntp_servers(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error);
}
void link_dns_settings_clear(Link *link) {
+ if (link->n_dns != (unsigned) -1)
+ for (unsigned i = 0; i < link->n_dns; i++)
+ in_addr_full_free(link->dns[i]);
link->dns = mfree(link->dns);
link->n_dns = (unsigned) -1;
/* The kernel may broadcast NEWLINK messages without the MAC address
set, simply ignore them. */
r = sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &mac);
- if (r >= 0) {
- if (memcmp(link->mac.ether_addr_octet, mac.ether_addr_octet,
- ETH_ALEN)) {
-
- memcpy(link->mac.ether_addr_octet, mac.ether_addr_octet,
- ETH_ALEN);
-
- log_link_debug(link, "MAC address: "
- "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
- mac.ether_addr_octet[0],
- mac.ether_addr_octet[1],
- mac.ether_addr_octet[2],
- mac.ether_addr_octet[3],
- mac.ether_addr_octet[4],
- mac.ether_addr_octet[5]);
-
- if (link->ipv4ll) {
- r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
+ if (r >= 0 && memcmp(link->mac.ether_addr_octet, mac.ether_addr_octet, ETH_ALEN) != 0) {
+
+ memcpy(link->mac.ether_addr_octet, mac.ether_addr_octet, ETH_ALEN);
+
+ log_link_debug(link, "Gained new MAC address: "
+ "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+ mac.ether_addr_octet[0],
+ mac.ether_addr_octet[1],
+ mac.ether_addr_octet[2],
+ mac.ether_addr_octet[3],
+ mac.ether_addr_octet[4],
+ mac.ether_addr_octet[5]);
+
+ if (link->ipv4ll) {
+ bool restart = sd_ipv4ll_is_running(link->ipv4ll) > 0;
+
+ if (restart) {
+ r = sd_ipv4ll_stop(link->ipv4ll);
if (r < 0)
- return log_link_warning_errno(link, r, "Could not update MAC address in IPv4LL client: %m");
+ return log_link_warning_errno(link, r, "Could not stop IPv4LL client: %m");
}
- if (link->dhcp_client) {
- r = sd_dhcp_client_set_mac(link->dhcp_client,
- (const uint8_t *) &link->mac,
- sizeof (link->mac),
- ARPHRD_ETHER);
+ r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update MAC address in IPv4LL client: %m");
+
+ if (restart) {
+ r = sd_ipv4ll_start(link->ipv4ll);
if (r < 0)
- return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m");
+ return log_link_warning_errno(link, r, "Could not restart IPv4LL client: %m");
+ }
+ }
- r = dhcp4_set_client_identifier(link);
+ if (link->dhcp_client) {
+ r = sd_dhcp_client_set_mac(link->dhcp_client,
+ (const uint8_t *) &link->mac,
+ sizeof (link->mac),
+ ARPHRD_ETHER);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m");
+
+ r = dhcp4_set_client_identifier(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not set DHCP client identifier: %m");
+ }
+
+ if (link->dhcp6_client) {
+ const DUID* duid = link_get_duid(link);
+ bool restart = sd_dhcp6_client_is_running(link->dhcp6_client) > 0;
+
+ if (restart) {
+ r = sd_dhcp6_client_stop(link->dhcp6_client);
if (r < 0)
- return r;
+ return log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m");
}
- if (link->dhcp6_client) {
- const DUID* duid = link_get_duid(link);
+ r = sd_dhcp6_client_set_mac(link->dhcp6_client,
+ (const uint8_t *) &link->mac,
+ sizeof (link->mac),
+ ARPHRD_ETHER);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m");
- r = sd_dhcp6_client_set_mac(link->dhcp6_client,
- (const uint8_t *) &link->mac,
- sizeof (link->mac),
- ARPHRD_ETHER);
+ if (link->network->iaid_set) {
+ r = sd_dhcp6_client_set_iaid(link->dhcp6_client, link->network->iaid);
if (r < 0)
- return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m");
-
- if (link->network->iaid_set) {
- r = sd_dhcp6_client_set_iaid(link->dhcp6_client,
- link->network->iaid);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m");
- }
-
- r = sd_dhcp6_client_set_duid(link->dhcp6_client,
- duid->type,
- duid->raw_data_len > 0 ? duid->raw_data : NULL,
- duid->raw_data_len);
+ return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m");
+ }
+
+ r = sd_dhcp6_client_set_duid(link->dhcp6_client,
+ duid->type,
+ duid->raw_data_len > 0 ? duid->raw_data : NULL,
+ duid->raw_data_len);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update DHCPv6 DUID: %m");
+
+ if (restart) {
+ r = sd_dhcp6_client_start(link->dhcp6_client);
if (r < 0)
- return log_link_warning_errno(link, r, "Could not update DHCPv6 DUID: %m");
+ return log_link_warning_errno(link, r, "Could not restart DHCPv6 client: %m");
}
+ }
+
+ if (link->radv) {
+ bool restart = sd_radv_is_running(link->radv);
- if (link->radv) {
- r = sd_radv_set_mac(link->radv, &link->mac);
+ if (restart) {
+ r = sd_radv_stop(link->radv);
if (r < 0)
- return log_link_warning_errno(link, r, "Could not update MAC for Router Advertisement: %m");
+ return log_link_warning_errno(link, r, "Could not stop Router Advertisement: %m");
}
- if (link->ndisc) {
- r = sd_ndisc_set_mac(link->ndisc, &link->mac);
+ r = sd_radv_set_mac(link->radv, &link->mac);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update MAC for Router Advertisement: %m");
+
+ if (restart) {
+ r = sd_radv_start(link->radv);
if (r < 0)
- return log_link_warning_errno(link, r, "Could not update MAC for ndisc: %m");
+ return log_link_warning_errno(link, r, "Could not restart Router Advertisement: %m");
}
}
+
+ if (link->ndisc) {
+ r = sd_ndisc_set_mac(link->ndisc, &link->mac);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update MAC for NDisc: %m");
+ }
}
old_master = link->master_ifindex;
fputc('\n', f);
}
-static void link_save_dns(FILE *f, struct in_addr_data *dns, unsigned n_dns, bool *space) {
+static void link_save_dns(Link *link, FILE *f, struct in_addr_full **dns, unsigned n_dns, bool *space) {
for (unsigned j = 0; j < n_dns; j++) {
- _cleanup_free_ char *b = NULL;
- int r;
+ const char *str;
- r = in_addr_to_string(dns[j].family, &dns[j].address, &b);
- if (r < 0) {
- log_debug_errno(r, "Failed to format address, ignoring: %m");
+ if (dns[j]->ifindex != 0 && dns[j]->ifindex != link->ifindex)
+ continue;
+
+ str = in_addr_full_to_string(dns[j]);
+ if (!str)
continue;
- }
if (*space)
fputc(' ', f);
- fputs(b, f);
+ fputs(str, f);
*space = true;
}
}
fputs("DNS=", f);
space = false;
if (link->n_dns != (unsigned) -1)
- link_save_dns(f, link->dns, link->n_dns, &space);
+ link_save_dns(link, f, link->dns, link->n_dns, &space);
else
- link_save_dns(f, link->network->dns, link->network->n_dns, &space);
+ link_save_dns(link, f, link->network->dns, link->network->n_dns, &space);
serialize_addresses(f, NULL, &space,
NULL,
sd_dhcp6_lease_get_dns,
NULL);
- /* Make sure to flush out old entries before we use the NDISC data */
+ /* Make sure to flush out old entries before we use the NDisc data */
ndisc_vacuum(link);
if (link->network->ipv6_accept_ra_use_dns && link->ndisc_rdnss) {
bool stats_updated;
/* All kinds of DNS configuration the user configured via D-Bus */
- struct in_addr_data *dns;
+ struct in_addr_full **dns;
unsigned n_dns;
OrderedSet *search_domains, *route_domains;
#include "alloc-util.h"
#include "bus-common-errors.h"
+#include "bus-message-util.h"
#include "bus-polkit.h"
#include "networkd-link-bus.h"
#include "networkd-link.h"
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *path = NULL;
Manager *manager = userdata;
- int32_t index;
+ int ifindex, r;
Link *link;
- int r;
- r = sd_bus_message_read(message, "i", &index);
+ r = bus_message_read_ifindex(message, error, &ifindex);
if (r < 0)
return r;
- link = hashmap_get(manager->links, INT_TO_PTR((int) index));
+ link = hashmap_get(manager->links, INT_TO_PTR(ifindex));
if (!link)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %" PRIi32 " not known", index);
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex);
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
assert(message);
assert(handler);
- assert_cc(sizeof(int) == sizeof(int32_t));
- r = sd_bus_message_read(message, "i", &ifindex);
+ r = bus_message_read_ifindex(message, error, &ifindex);
if (r < 0)
return r;
- if (ifindex <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
-
l = hashmap_get(m->links, INT_TO_PTR(ifindex));
if (!l)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex);
return call_link_method(userdata, message, bus_link_method_set_dns_servers, error);
}
+static int bus_method_set_link_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_dns_servers_ex, error);
+}
+
static int bus_method_set_link_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return call_link_method(userdata, message, bus_link_method_set_domains, error);
}
SD_BUS_METHOD("GetLinkByIndex", "i", "so", method_get_link_by_index, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLinkNTP", "ias", NULL, bus_method_set_link_ntp_servers, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetLinkDNSEx", "ia(iayqs)", NULL, bus_method_set_link_dns_servers_ex, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_domains, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLinkDefaultRoute", "ib", NULL, bus_method_set_link_default_route, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, SD_BUS_VTABLE_UNPRIVILEGED),
log_link_debug(link,
"%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
- (!route && !link->manager->manage_foreign_routes) || type == RTM_DELROUTE ? "Forgetting" :
+ (!route && !link->manager->manage_foreign_routes) ? "Ignoring received foreign" :
+ type == RTM_DELROUTE ? "Forgetting" :
route ? "Received remembered" : "Remembering",
strna(buf_dst), strempty(buf_dst_prefixlen),
strna(buf_src), strna(buf_gw), strna(buf_prefsrc),
return 0;
}
-static int ordered_set_put_in_addr_data(OrderedSet *s, const struct in_addr_data *address) {
- char *p;
+static int ordered_set_put_dns_server(OrderedSet *s, int ifindex, struct in_addr_full *dns) {
+ const char *p;
int r;
assert(s);
- assert(address);
+ assert(dns);
- r = in_addr_to_string(address->family, &address->address, &p);
- if (r < 0)
- return r;
+ if (dns->ifindex != 0 && dns->ifindex != ifindex)
+ return 0;
- r = ordered_set_consume(s, p);
+ p = in_addr_full_to_string(dns);
+ if (!p)
+ return 0;
+
+ r = ordered_set_put_strdup(s, p);
if (r == -EEXIST)
return 0;
return r;
}
-static int ordered_set_put_in_addr_datav(OrderedSet *s, const struct in_addr_data *addresses, unsigned n) {
+static int ordered_set_put_dns_servers(OrderedSet *s, int ifindex, struct in_addr_full **dns, unsigned n) {
int r, c = 0;
unsigned i;
assert(s);
- assert(addresses || n == 0);
+ assert(dns || n == 0);
for (i = 0; i < n; i++) {
- r = ordered_set_put_in_addr_data(s, addresses+i);
+ r = ordered_set_put_dns_server(s, ifindex, dns[i]);
if (r < 0)
return r;
continue;
/* First add the static configured entries */
- r = ordered_set_put_in_addr_datav(dns, link->network->dns, link->network->n_dns);
+ if (link->n_dns != (unsigned) -1)
+ r = ordered_set_put_dns_servers(dns, link->ifindex, link->dns, link->n_dns);
+ else
+ r = ordered_set_put_dns_servers(dns, link->ifindex, link->network->dns, link->network->n_dns);
if (r < 0)
return r;
assert(m);
assert(m->rtnl);
+ if (!m->manage_foreign_routes)
+ return 0;
+
r = sd_rtnl_message_new_route(m->rtnl, &req, RTM_GETROUTE, 0, 0);
if (r < 0)
return r;
unsigned preference;
uint32_t mtu;
usec_t time_now;
- Address *address;
- Iterator i;
int r;
assert(link);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m");
- SET_FOREACH(address, link->addresses, i) {
- if (address->family != AF_INET6)
- continue;
- if (in_addr_equal(AF_INET6, &gateway, &address->in_addr)) {
- if (DEBUG_LOGGING) {
- _cleanup_free_ char *buffer = NULL;
-
- (void) in_addr_to_string(AF_INET6, &address->in_addr, &buffer);
- log_link_debug(link, "No NDisc route added, gateway %s matches local address",
- strnull(buffer));
- }
- return 0;
- }
- }
-
- SET_FOREACH(address, link->addresses_foreign, i) {
- if (address->family != AF_INET6)
- continue;
- if (in_addr_equal(AF_INET6, &gateway, &address->in_addr)) {
- if (DEBUG_LOGGING) {
- _cleanup_free_ char *buffer = NULL;
+ if (address_exists(link, AF_INET6, &gateway)) {
+ if (DEBUG_LOGGING) {
+ _cleanup_free_ char *buffer = NULL;
- (void) in_addr_to_string(AF_INET6, &address->in_addr, &buffer);
- log_link_debug(link, "No NDisc route added, gateway %s matches local address",
- strnull(buffer));
- }
- return 0;
+ (void) in_addr_to_string(AF_INET6, &gateway, &buffer);
+ log_link_debug(link, "No NDisc route added, gateway %s matches local address",
+ strnull(buffer));
}
+ return 0;
}
r = sd_ndisc_router_get_preference(rt, &preference);
break;
case SD_NDISC_EVENT_TIMEOUT:
- log_link_debug(link, "NDISC handler get timeout event");
+ log_link_debug(link, "NDisc handler get timeout event");
link->ndisc_addresses_configured = true;
link->ndisc_routes_configured = true;
link_check_ready(link);
break;
default:
- assert_not_reached("IPv6 Neighbor Discovery unknown event");
+ assert_not_reached("Unknown NDisc event");
}
}
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
- "Failed to parse NDISC deny-listed prefix, ignoring assignment: %s",
+ "Failed to parse NDisc deny-listed prefix, ignoring assignment: %s",
rvalue);
return 0;
}
r = in_addr_from_string(AF_INET6, n, &ip);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
- "NDISC deny-listed prefix is invalid, ignoring assignment: %s", n);
+ "NDisc deny-listed prefix is invalid, ignoring assignment: %s", n);
continue;
}
if (r < 0)
return log_link_error_errno(link, r, "Could not set state: %m");
- r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE);
+ r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_REPLACE);
if (r < 0)
return log_link_error_errno(link, r, "Could not set flags: %m");
sd_ipv4acd_unref(network->dhcp_acd);
strv_free(network->ntp);
+ for (unsigned i = 0; i < network->n_dns; i++)
+ in_addr_full_free(network->dns[i]);
free(network->dns);
ordered_set_free_free(network->search_domains);
ordered_set_free_free(network->route_domains);
assert(rvalue);
if (isempty(rvalue)) {
+ for (unsigned i = 0; i < n->n_dns; i++)
+ in_addr_full_free(n->dns[i]);
n->dns = mfree(n->dns);
n->n_dns = 0;
return 0;
}
for (const char *p = rvalue;;) {
+ _cleanup_(in_addr_full_freep) struct in_addr_full *dns = NULL;
_cleanup_free_ char *w = NULL;
- union in_addr_union a;
- struct in_addr_data *m;
- int family;
+ struct in_addr_full **m;
r = extract_first_word(&p, &w, NULL, 0);
if (r == -ENOMEM)
if (r == 0)
return 0;
- r = in_addr_from_string_auto(w, &family, &a);
+ r = in_addr_full_new_from_string(w, &dns);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse dns server address, ignoring: %s", w);
continue;
}
- m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_data));
+ if (IN_SET(dns->port, 53, 853))
+ dns->port = 0;
+
+ m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_full*));
if (!m)
return log_oom();
- m[n->n_dns++] = (struct in_addr_data) {
- .family = family,
- .address = a,
- };
-
+ m[n->n_dns++] = TAKE_PTR(dns);
n->dns = m;
}
}
#include "networkd-util.h"
#include "ordered-set.h"
#include "resolve-util.h"
+#include "socket-netlink.h"
typedef enum IPv6PrivacyExtensions {
/* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */
OrderedHashmap *sr_iov_by_section;
/* All kinds of DNS configuration */
- struct in_addr_data *dns;
+ struct in_addr_full **dns;
unsigned n_dns;
OrderedSet *search_domains, *route_domains;
for (i = 0; i < network->n_dns; i++) {
union in_addr_union *addr;
- if (network->dns[i].family != AF_INET6)
+ if (network->dns[i]->family != AF_INET6)
continue;
- addr = &network->dns[i].address;
+ addr = &network->dns[i]->address;
if (in_addr_is_null(AF_INET6, addr) ||
in_addr_is_link_local(AF_INET6, addr) ||
if (asprintf((char **)(envp + n_env++), "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0)
return log_oom();
- env_use = strv_env_merge(3, envp, arg_setenv, os_release_pairs);
+ env_use = strv_env_merge(3, envp, os_release_pairs, arg_setenv);
if (!env_use)
return log_oom();
#include "bus-error.h"
#include "bus-locator.h"
#include "bus-map-properties.h"
+#include "bus-message-util.h"
#include "dns-domain.h"
#include "escape.h"
#include "format-table.h"
while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
_cleanup_free_ char *pretty = NULL;
int ifindex, family, k;
- const void *a;
- size_t sz;
+ union in_addr_union a;
assert_cc(sizeof(int) == sizeof(int32_t));
- r = sd_bus_message_read(reply, "ii", &ifindex, &family);
+ r = sd_bus_message_read(reply, "i", &ifindex);
if (r < 0)
return bus_log_parse_error(r);
- r = sd_bus_message_read_array(reply, 'y', &a, &sz);
- if (r < 0)
- return bus_log_parse_error(r);
+ sd_bus_error_free(&error);
+ r = bus_message_read_in_addr_auto(reply, &error, &family, &a);
+ if (r < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS))
+ return log_error_errno(r, "%s: systemd-resolved returned invalid result: %s", name, bus_error_message(&error, r));
r = sd_bus_message_exit_container(reply);
if (r < 0)
return bus_log_parse_error(r);
- if (!IN_SET(family, AF_INET, AF_INET6)) {
- log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
+ if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) {
+ log_debug_errno(r, "%s: systemd-resolved returned invalid result, ignoring: %s", name, bus_error_message(&error, r));
continue;
}
- if (sz != FAMILY_ADDRESS_SIZE(family)) {
- log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
- return -EINVAL;
- }
-
- r = in_addr_ifindex_to_string(family, a, ifindex, &pretty);
+ r = in_addr_ifindex_to_string(family, &a, ifindex, &pretty);
if (r < 0)
return log_error_errno(r, "Failed to print address for %s: %m", name);
while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
_cleanup_free_ char *pretty = NULL;
int ifindex, family, k;
- const void *a;
+ union in_addr_union a;;
assert_cc(sizeof(int) == sizeof(int32_t));
- r = sd_bus_message_read(reply, "ii", &ifindex, &family);
+ r = sd_bus_message_read(reply, "i", &ifindex);
if (r < 0)
return bus_log_parse_error(r);
- r = sd_bus_message_read_array(reply, 'y', &a, &sz);
- if (r < 0)
- return bus_log_parse_error(r);
+ sd_bus_error_free(&error);
+ r = bus_message_read_in_addr_auto(reply, &error, &family, &a);
+ if (r < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS))
+ return log_error_errno(r, "%s: systemd-resolved returned invalid result: %s", name, bus_error_message(&error, r));
r = sd_bus_message_exit_container(reply);
if (r < 0)
return bus_log_parse_error(r);
- if (!IN_SET(family, AF_INET, AF_INET6)) {
- log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
+ if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) {
+ log_debug_errno(r, "%s: systemd-resolved returned invalid result, ignoring: %s", name, bus_error_message(&error, r));
continue;
}
- if (sz != FAMILY_ADDRESS_SIZE(family)) {
- log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
- return -EINVAL;
- }
-
- r = in_addr_ifindex_to_string(family, a, ifindex, &pretty);
+ r = in_addr_ifindex_to_string(family, &a, ifindex, &pretty);
if (r < 0)
return log_error_errno(r, "Failed to print address for %s: %m", name);
return 0;
}
-static int read_dns_server_one(sd_bus_message *m, bool with_ifindex, char **ret) {
+static int read_dns_server_one(sd_bus_message *m, bool with_ifindex, bool extended, char **ret) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *pretty = NULL;
- int ifindex, family, r;
- const void *a;
- size_t sz;
+ int ifindex, family, r, k;
+ union in_addr_union a;
+ const char *name = NULL;
+ uint16_t port = 0;
assert(m);
assert(ret);
- r = sd_bus_message_enter_container(m, 'r', with_ifindex ? "iiay" : "iay");
+ r = sd_bus_message_enter_container(m, 'r', with_ifindex ? (extended ? "iiayqs" : "iiay") : (extended ? "iayqs" : "iay"));
if (r <= 0)
return r;
return r;
}
- r = sd_bus_message_read(m, "i", &family);
- if (r < 0)
- return r;
+ k = bus_message_read_in_addr_auto(m, &error, &family, &a);
+ if (k < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS))
+ return k;
- r = sd_bus_message_read_array(m, 'y', &a, &sz);
- if (r < 0)
- return r;
+ if (extended) {
+ r = sd_bus_message_read(m, "q", &port);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(m, "s", &name);
+ if (r < 0)
+ return r;
+ }
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
- if (with_ifindex && ifindex != 0) {
- /* only show the global ones here */
+ if (k < 0) {
+ log_debug("Invalid DNS server, ignoring: %s", bus_error_message(&error, k));
*ret = NULL;
return 1;
}
- if (!IN_SET(family, AF_INET, AF_INET6)) {
- log_debug("Unexpected family, ignoring: %i", family);
-
- *ret = NULL;
- return 1;
- }
-
- if (sz != FAMILY_ADDRESS_SIZE(family)) {
- log_debug("Address size mismatch, ignoring.");
-
+ if (with_ifindex && ifindex != 0) {
+ /* only show the global ones here */
*ret = NULL;
return 1;
}
- r = in_addr_to_string(family, a, &pretty);
+ r = in_addr_port_ifindex_name_to_string(family, &a, port, ifindex, name, &pretty);
if (r < 0)
return r;
return 1;
}
-static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+static int map_link_dns_servers_internal(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata, bool extended) {
char ***l = userdata;
int r;
assert(m);
assert(l);
- r = sd_bus_message_enter_container(m, 'a', "(iay)");
+ r = sd_bus_message_enter_container(m, 'a', extended ? "(iayqs)" : "(iay)");
if (r < 0)
return r;
for (;;) {
_cleanup_free_ char *pretty = NULL;
- r = read_dns_server_one(m, false, &pretty);
+ r = read_dns_server_one(m, false, extended, &pretty);
if (r < 0)
return r;
if (r == 0)
return 0;
}
+static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ return map_link_dns_servers_internal(bus, member, m, error, userdata, false);
+}
+
+static int map_link_dns_servers_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ return map_link_dns_servers_internal(bus, member, m, error, userdata, true);
+}
+
static int map_link_current_dns_server(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
assert(m);
assert(userdata);
- return read_dns_server_one(m, false, userdata);
+ return read_dns_server_one(m, false, false, userdata);
+}
+
+static int map_link_current_dns_server_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ assert(m);
+ assert(userdata);
+
+ return read_dns_server_one(m, false, true, userdata);
}
static int read_domain_one(sd_bus_message *m, bool with_ifindex, char **ret) {
const char *dns_over_tls;
const char *dnssec;
char *current_dns;
+ char *current_dns_ex;
char **dns;
+ char **dns_ex;
char **domains;
char **ntas;
bool dnssec_supported;
static void link_info_clear(struct link_info *p) {
free(p->current_dns);
+ free(p->current_dns_ex);
strv_free(p->dns);
+ strv_free(p->dns_ex);
strv_free(p->domains);
strv_free(p->ntas);
}
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(struct link_info, scopes_mask) },
- { "DNS", "a(iay)", map_link_dns_servers, offsetof(struct link_info, dns) },
- { "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(struct link_info, current_dns) },
- { "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) },
+ { "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;
(void) pager_open(arg_pager_flags);
if (mode == STATUS_DNS)
- return status_print_strv_ifindex(ifindex, name, link_info.dns);
+ return status_print_strv_ifindex(ifindex, name, link_info.dns_ex ?: link_info.dns);
if (mode == STATUS_DOMAIN)
return status_print_strv_ifindex(ifindex, name, link_info.domains);
if (link_info.current_dns) {
r = table_add_many(table,
TABLE_STRING, "Current DNS Server:",
- TABLE_STRING, link_info.current_dns);
+ TABLE_STRING, link_info.current_dns_ex ?: link_info.current_dns);
if (r < 0)
return table_log_add_error(r);
}
- r = dump_list(table, "DNS Servers:", link_info.dns);
+ r = dump_list(table, "DNS Servers:", link_info.dns_ex ?: link_info.dns);
if (r < 0)
return r;
return 0;
}
-static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+static int map_global_dns_servers_internal(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata, bool extended) {
char ***l = userdata;
int r;
assert(m);
assert(l);
- r = sd_bus_message_enter_container(m, 'a', "(iiay)");
+ r = sd_bus_message_enter_container(m, 'a', extended ? "(iiayqs)" : "(iiay)");
if (r < 0)
return r;
for (;;) {
_cleanup_free_ char *pretty = NULL;
- r = read_dns_server_one(m, true, &pretty);
+ r = read_dns_server_one(m, true, extended, &pretty);
if (r < 0)
return r;
if (r == 0)
return 0;
}
+static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ return map_global_dns_servers_internal(bus, member, m, error, userdata, false);
+}
+
+static int map_global_dns_servers_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ return map_global_dns_servers_internal(bus, member, m, error, userdata, true);
+}
+
static int map_global_current_dns_server(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
assert(m);
assert(userdata);
- return read_dns_server_one(m, true, userdata);
+ return read_dns_server_one(m, true, false, userdata);
+}
+
+static int map_global_current_dns_server_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ assert(m);
+ assert(userdata);
+
+ return read_dns_server_one(m, true, true, userdata);
}
static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
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;
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(struct global_info, dns) },
- { "FallbackDNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, fallback_dns) },
- { "CurrentDNSServer", "(iiay)", map_global_current_dns_server, offsetof(struct global_info, current_dns) },
- { "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) },
+ { "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) },
{}
};
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
(void) pager_open(arg_pager_flags);
if (mode == STATUS_DNS)
- return status_print_strv_global(global_info.dns);
+ return status_print_strv_global(global_info.dns_ex ?: global_info.dns);
if (mode == STATUS_DOMAIN)
return status_print_strv_global(global_info.domains);
if (global_info.current_dns) {
r = table_add_many(table,
TABLE_STRING, "Current DNS Server:",
- TABLE_STRING, global_info.current_dns);
+ TABLE_STRING, global_info.current_dns_ex ?: global_info.current_dns);
if (r < 0)
return table_log_add_error(r);
}
- r = dump_list(table, "DNS Servers:", global_info.dns);
+ r = dump_list(table, "DNS Servers:", global_info.dns_ex ?: global_info.dns);
if (r < 0)
return r;
- r = dump_list(table, "Fallback DNS Servers:", global_info.fallback_dns);
+ r = dump_list(table, "Fallback DNS Servers:", global_info.fallback_dns_ex ?: global_info.fallback_dns);
if (r < 0)
return r;
return r;
}
-static int call_dns(sd_bus *bus, char **dns, const BusLocator *locator, sd_bus_error *error) {
+static int call_dns(sd_bus *bus, char **dns, const BusLocator *locator, sd_bus_error *error, bool extended) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
char **p;
int r;
- r = bus_message_new_method_call(bus, &req, locator, "SetLinkDNS");
+ r = bus_message_new_method_call(bus, &req, locator, extended ? "SetLinkDNSEx" : "SetLinkDNS");
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_open_container(req, 'a', "(iay)");
+ r = sd_bus_message_open_container(req, 'a', extended ? "(iayqs)" : "(iay)");
if (r < 0)
return bus_log_create_error(r);
* empty list, which will clear the list of domains for an interface. */
if (!strv_equal(dns, STRV_MAKE("")))
STRV_FOREACH(p, dns) {
+ _cleanup_free_ char *name = NULL;
struct in_addr_data data;
+ uint16_t port;
+ int ifindex;
- r = in_addr_from_string_auto(*p, &data.family, &data.address);
+ r = in_addr_port_ifindex_name_from_string_auto(*p, &data.family, &data.address, &port, &ifindex, &name);
if (r < 0)
return log_error_errno(r, "Failed to parse DNS server address: %s", *p);
- r = sd_bus_message_open_container(req, 'r', "iay");
+ if (ifindex != 0 && ifindex != arg_ifindex)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid ifindex: %i", ifindex);
+
+ r = sd_bus_message_open_container(req, 'r', extended ? "iayqs" : "iay");
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
+ if (extended) {
+ r = sd_bus_message_append(req, "q", port);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(req, "s", name);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
r = sd_bus_message_close_container(req);
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
- return sd_bus_call(bus, req, 0, error, NULL);
+ r = sd_bus_call(bus, req, 0, error, NULL);
+ if (r < 0 && extended && sd_bus_error_has_name(error, SD_BUS_ERROR_UNKNOWN_METHOD))
+ return call_dns(bus, dns, locator, error, false);
+ return r;
}
static int verb_dns(int argc, char **argv, void *userdata) {
if (argc < 3)
return status_ifindex(bus, arg_ifindex, NULL, STATUS_DNS, NULL);
- r = call_dns(bus, argv + 2, bus_resolve_mgr, &error);
+ r = call_dns(bus, argv + 2, bus_resolve_mgr, &error, true);
if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
sd_bus_error_free(&error);
- r = call_dns(bus, argv + 2, bus_network_mgr, &error);
+ r = call_dns(bus, argv + 2, bus_network_mgr, &error, true);
}
if (r < 0) {
if (arg_ifindex_permissive &&
#include "bus-common-errors.h"
#include "bus-get-properties.h"
#include "bus-log-control-api.h"
+#include "bus-message-util.h"
#include "bus-polkit.h"
#include "dns-domain.h"
#include "memory-util.h"
static int bus_method_resolve_address(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
Manager *m = userdata;
+ union in_addr_union a;
int family, ifindex;
uint64_t flags;
- const void *d;
DnsQuery *q;
- size_t sz;
int r;
assert(message);
assert_cc(sizeof(int) == sizeof(int32_t));
- r = sd_bus_message_read(message, "ii", &ifindex, &family);
+ r = sd_bus_message_read(message, "i", &ifindex);
if (r < 0)
return r;
- if (!IN_SET(family, AF_INET, AF_INET6))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
-
- r = sd_bus_message_read_array(message, 'y', &d, &sz);
+ r = bus_message_read_in_addr_auto(message, error, &family, &a);
if (r < 0)
return r;
- if (sz != FAMILY_ADDRESS_SIZE(family))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
-
r = sd_bus_message_read(message, "t", &flags);
if (r < 0)
return r;
if (r < 0)
return r;
- r = dns_question_new_reverse(&question, family, d);
+ r = dns_question_new_reverse(&question, family, &a);
if (r < 0)
return r;
q->request = sd_bus_message_ref(message);
q->request_family = family;
- memcpy(&q->request_address, d, sz);
+ q->request_address = a;
q->complete = bus_method_resolve_address_complete;
r = dns_query_bus_track(q, message);
return r;
}
-int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex) {
+int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex, bool extended) {
int r;
assert(reply);
if (!s) {
- if (with_ifindex)
- return sd_bus_message_append(reply, "(iiay)", 0, AF_UNSPEC, 0);
- else
- return sd_bus_message_append(reply, "(iay)", AF_UNSPEC, 0);
+ if (with_ifindex) {
+ if (extended)
+ return sd_bus_message_append(reply, "(iiayqs)", 0, AF_UNSPEC, 0, 0, NULL);
+ else
+ return sd_bus_message_append(reply, "(iiay)", 0, AF_UNSPEC, 0);
+ } else {
+ if (extended)
+ return sd_bus_message_append(reply, "(iayqs)", AF_UNSPEC, 0, 0, NULL);
+ else
+ return sd_bus_message_append(reply, "(iay)", AF_UNSPEC, 0);
+ }
}
- r = sd_bus_message_open_container(reply, 'r', with_ifindex ? "iiay" : "iay");
+ r = sd_bus_message_open_container(reply, 'r', with_ifindex ? (extended ? "iiayqs" : "iiay") : (extended ? "iayqs" : "iay"));
if (r < 0)
return r;
if (r < 0)
return r;
+ if (extended) {
+ r = sd_bus_message_append(reply, "q", s->port);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "s", s->server_name);
+ if (r < 0)
+ return r;
+ }
+
return sd_bus_message_close_container(reply);
}
-static int bus_property_get_dns_servers(
+static int bus_property_get_dns_servers_internal(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
- sd_bus_error *error) {
+ sd_bus_error *error,
+ bool extended) {
Manager *m = userdata;
DnsServer *s;
assert(reply);
assert(m);
- r = sd_bus_message_open_container(reply, 'a', "(iiay)");
+ r = sd_bus_message_open_container(reply, 'a', extended ? "(iiayqs)" : "(iiay)");
if (r < 0)
return r;
LIST_FOREACH(servers, s, m->dns_servers) {
- r = bus_dns_server_append(reply, s, true);
+ r = bus_dns_server_append(reply, s, true, extended);
if (r < 0)
return r;
}
HASHMAP_FOREACH(l, m->links, i)
LIST_FOREACH(servers, s, l->dns_servers) {
- r = bus_dns_server_append(reply, s, true);
+ r = bus_dns_server_append(reply, s, true, extended);
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
-static int bus_property_get_fallback_dns_servers(
+static int bus_property_get_dns_servers(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ return bus_property_get_dns_servers_internal(bus, path, interface, property, reply, userdata, error, false);
+}
+
+static int bus_property_get_dns_servers_ex(
sd_bus *bus,
const char *path,
const char *interface,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
+ return bus_property_get_dns_servers_internal(bus, path, interface, property, reply, userdata, error, true);
+}
+
+static int bus_property_get_fallback_dns_servers_internal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error,
+ bool extended) {
DnsServer *s, **f = userdata;
int r;
assert(reply);
assert(f);
- r = sd_bus_message_open_container(reply, 'a', "(iiay)");
+ r = sd_bus_message_open_container(reply, 'a', extended ? "(iiayqs)" : "(iiay)");
if (r < 0)
return r;
LIST_FOREACH(servers, s, *f) {
- r = bus_dns_server_append(reply, s, true);
+ r = bus_dns_server_append(reply, s, true, extended);
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
-static int bus_property_get_current_dns_server(
+static int bus_property_get_fallback_dns_servers(
sd_bus *bus,
const char *path,
const char *interface,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
+ return bus_property_get_fallback_dns_servers_internal(bus, path, interface, property, reply, userdata, error, false);
+}
+
+static int bus_property_get_fallback_dns_servers_ex(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ return bus_property_get_fallback_dns_servers_internal(bus, path, interface, property, reply, userdata, error, true);
+}
+
+static int bus_property_get_current_dns_server_internal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error,
+ bool extended) {
DnsServer *s;
s = *(DnsServer **) userdata;
- return bus_dns_server_append(reply, s, true);
+ return bus_dns_server_append(reply, s, true, extended);
+}
+
+static int bus_property_get_current_dns_server(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ return bus_property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, false);
+}
+
+static int bus_property_get_current_dns_server_ex(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ return bus_property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, true);
}
static int bus_property_get_domains(
assert(m);
assert(ret);
- if (ifindex <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
-
l = hashmap_get(m->links, INT_TO_PTR(ifindex));
if (!l)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex);
assert(message);
assert(handler);
- assert_cc(sizeof(int) == sizeof(int32_t));
- r = sd_bus_message_read(message, "i", &ifindex);
+ r = bus_message_read_ifindex(message, error, &ifindex);
if (r < 0)
return r;
return call_link_method(userdata, message, bus_link_method_set_dns_servers, error);
}
+static int bus_method_set_link_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_dns_servers_ex, error);
+}
+
static int bus_method_set_link_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return call_link_method(userdata, message, bus_link_method_set_domains, error);
}
assert(message);
assert(m);
- assert_cc(sizeof(int) == sizeof(int32_t));
- r = sd_bus_message_read(message, "i", &ifindex);
+ r = bus_message_read_ifindex(message, error, &ifindex);
if (r < 0)
return r;
SD_BUS_PROPERTY("MulticastDNS", "s", bus_property_get_resolve_support, offsetof(Manager, mdns_support), 0),
SD_BUS_PROPERTY("DNSOverTLS", "s", bus_property_get_dns_over_tls_mode, 0, 0),
SD_BUS_PROPERTY("DNS", "a(iiay)", bus_property_get_dns_servers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("DNSEx", "a(iiayqs)", bus_property_get_dns_servers_ex, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("FallbackDNS", "a(iiay)", bus_property_get_fallback_dns_servers, offsetof(Manager, fallback_dns_servers), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("FallbackDNSEx", "a(iiayqs)", bus_property_get_fallback_dns_servers_ex, offsetof(Manager, fallback_dns_servers), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CurrentDNSServer", "(iiay)", bus_property_get_current_dns_server, offsetof(Manager, current_dns_server), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("CurrentDNSServerEx", "(iiayqs)", bus_property_get_current_dns_server_ex, offsetof(Manager, current_dns_server), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Domains", "a(isb)", bus_property_get_domains, 0, 0),
SD_BUS_PROPERTY("TransactionStatistics", "(tt)", bus_property_get_transaction_statistics, 0, 0),
SD_BUS_PROPERTY("CacheStatistics", "(ttt)", bus_property_get_cache_statistics, 0, 0),
SD_BUS_NO_RESULT,
bus_method_set_link_dns_servers,
SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetLinkDNSEx",
+ SD_BUS_ARGS("i", ifindex, "a(iayqs)", addresses),
+ SD_BUS_NO_RESULT,
+ bus_method_set_link_dns_servers_ex,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("SetLinkDomains",
SD_BUS_ARGS("i", ifindex, "a(sb)", domains),
SD_BUS_NO_RESULT,
int manager_connect_bus(Manager *m);
int _manager_send_changed(Manager *manager, const char *property, ...) _sentinel_;
#define manager_send_changed(manager, ...) _manager_send_changed(manager, __VA_ARGS__, NULL)
-int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex);
+int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex, bool extended);
int bus_property_get_resolve_support(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply,
void *userdata, sd_bus_error *error);
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_stub_listener_mode, DnsStubListenerMode, DNS_STUB_LISTENER_YES);
static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) {
+ _cleanup_free_ char *server_name = NULL;
union in_addr_union address;
int family, r, ifindex = 0;
+ uint16_t port;
DnsServer *s;
- _cleanup_free_ char *server_name = NULL;
assert(m);
assert(word);
- r = in_addr_ifindex_name_from_string_auto(word, &family, &address, &ifindex, &server_name);
+ r = in_addr_port_ifindex_name_from_string_auto(word, &family, &address, &port, &ifindex, &server_name);
if (r < 0)
return r;
+ if (IN_SET(port, 53, 853))
+ port = 0;
+
/* Silently filter out 0.0.0.0 and 127.0.0.53 (our own stub DNS listener) */
if (!dns_server_address_valid(family, &address))
return 0;
+ /* By default, the port number is determined with the transaction feature level.
+ * See dns_transaction_port() and dns_server_port(). */
+ if (IN_SET(port, 53, 853))
+ port = 0;
+
/* Filter out duplicates */
- s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, ifindex);
+ s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, port, ifindex, server_name);
if (s) {
/*
* Drop the marker. This is used to find the servers
return 0;
}
- return dns_server_new(m, NULL, type, NULL, family, &address, ifindex, server_name);
+ return dns_server_new(m, NULL, type, NULL, family, &address, port, ifindex, server_name);
}
int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) {
return TAKE_FD(fd);
}
-int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port) {
- return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, server, port, NULL);
+int dns_scope_socket_udp(DnsScope *s, DnsServer *server) {
+ return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, server, dns_server_port(server), NULL);
}
int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port, union sockaddr_union *ret_socket_address) {
int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p);
int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port, union sockaddr_union *ret_socket_address);
-int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port);
+int dns_scope_socket_udp(DnsScope *s, DnsServer *server);
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain);
bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key);
Link *l,
int family,
const union in_addr_union *in_addr,
+ uint16_t port,
int ifindex,
const char *server_name) {
return -E2BIG;
}
- if (server_name) {
+ if (!isempty(server_name)) {
name = strdup(server_name);
if (!name)
return -ENOMEM;
.type = type,
.family = family,
.address = *in_addr,
+ .port = port,
.ifindex = ifindex,
.server_name = TAKE_PTR(name),
};
#endif
free(s->server_string);
+ free(s->server_string_full);
free(s->server_name);
return mfree(s);
}
if (s->verified_feature_level != level) {
log_debug("Verified we get a response at feature level %s from DNS server %s.",
dns_server_feature_level_to_string(level),
- dns_server_string(s));
+ strna(dns_server_string_full(s)));
s->verified_feature_level = level;
}
dns_server_reset_counters(s);
}
- log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", dns_server_string(s));
+ log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", strna(dns_server_string_full(s)));
}
static bool dns_server_grace_period_expired(DnsServer *s) {
log_info("Grace period over, resuming full feature set (%s) for DNS server %s.",
dns_server_feature_level_to_string(s->possible_feature_level),
- dns_server_string(s));
+ strna(dns_server_string_full(s)));
dns_server_flush_cache(s);
log_full(log_level, "Using degraded feature set %s instead of %s for DNS server %s.",
dns_server_feature_level_to_string(s->possible_feature_level),
- dns_server_feature_level_to_string(p), dns_server_string(s));
+ dns_server_feature_level_to_string(p), strna(dns_server_string_full(s)));
}
}
return 0;
}
+uint16_t dns_server_port(const DnsServer *s) {
+ assert(s);
+
+ if (s->port > 0)
+ return s->port;
+
+ return 53;
+}
+
const char *dns_server_string(DnsServer *server) {
assert(server);
if (!server->server_string)
(void) in_addr_ifindex_to_string(server->family, &server->address, dns_server_ifindex(server), &server->server_string);
- return strna(server->server_string);
+ return server->server_string;
+}
+
+const char *dns_server_string_full(DnsServer *server) {
+ assert(server);
+
+ if (!server->server_string_full)
+ (void) in_addr_port_ifindex_name_to_string(
+ server->family,
+ &server->address,
+ server->port,
+ dns_server_ifindex(server),
+ server->server_name,
+ &server->server_string_full);
+
+ return server->server_string_full;
}
bool dns_server_dnssec_supported(DnsServer *server) {
log_struct(LOG_NOTICE,
"MESSAGE_ID=" SD_MESSAGE_DNSSEC_DOWNGRADE_STR,
- LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", dns_server_string(server)),
- "DNS_SERVER=%s", dns_server_string(server),
+ LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", strna(dns_server_string_full(server))),
+ "DNS_SERVER=%s", strna(dns_server_string_full(server)),
"DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(server->possible_feature_level));
server->warned_downgrade = true;
siphash24_compress(&s->family, sizeof(s->family), state);
siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state);
+ siphash24_compress(&s->port, sizeof(s->port), state);
siphash24_compress(&s->ifindex, sizeof(s->ifindex), state);
+ if (s->server_name)
+ siphash24_compress(s->server_name, strlen(s->server_name), state);
}
static int dns_server_compare_func(const DnsServer *x, const DnsServer *y) {
if (r != 0)
return r;
+ r = CMP(x->port, y->port);
+ if (r != 0)
+ return r;
+
r = CMP(x->ifindex, y->ifindex);
if (r != 0)
return r;
- return 0;
+ return streq_ptr(x->server_name, y->server_name);
}
DEFINE_HASH_OPS(dns_server_hash_ops, DnsServer, dns_server_hash_func, dns_server_compare_func);
dns_server_mark_all(first->servers_next);
}
-DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex) {
+DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, uint16_t port, int ifindex, const char *name) {
DnsServer *s;
LIST_FOREACH(servers, s, first)
- if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0 && s->ifindex == ifindex)
+ if (s->family == family &&
+ in_addr_equal(family, &s->address, in_addr) > 0 &&
+ s->port == port &&
+ s->ifindex == ifindex &&
+ streq_ptr(s->server_name, name))
return s;
return NULL;
if (s)
log_debug("Switching to %s DNS server %s.",
dns_server_type_to_string(s->type),
- dns_server_string(s));
+ strna(dns_server_string_full(s)));
dns_server_unref(m->current_dns_server);
m->current_dns_server = dns_server_ref(s);
f = stdout;
fputs("[Server ", f);
- fputs(dns_server_string(s), f);
+ fputs(strna(dns_server_string_full(s)), f);
fputs(" type=", f);
fputs(dns_server_type_to_string(s->type), f);
int family;
union in_addr_union address;
int ifindex; /* for IPv6 link-local DNS servers */
+ uint16_t port;
+ char *server_name;
char *server_string;
-
- char *server_name;
+ char *server_string_full;
/* The long-lived stream towards this server. */
DnsStream *stream;
Link *link,
int family,
const union in_addr_union *address,
+ uint16_t port,
int ifindex,
const char *server_string);
int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level);
const char *dns_server_string(DnsServer *server);
+const char *dns_server_string_full(DnsServer *server);
int dns_server_ifindex(const DnsServer *s);
+uint16_t dns_server_port(const DnsServer *s);
bool dns_server_dnssec_supported(DnsServer *server);
void dns_server_warn_downgrade(DnsServer *server);
-DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex);
+DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, uint16_t port, int ifindex, const char *name);
void dns_server_unlink_all(DnsServer *first);
void dns_server_unlink_marked(DnsServer *first);
"DNS_TRANSACTION=%" PRIu16, t->id,
"DNS_QUESTION=%s", key_str,
"DNSSEC_RESULT=%s", dnssec_result_to_string(t->answer_dnssec_result),
- "DNS_SERVER=%s", dns_server_string(t->server),
+ "DNS_SERVER=%s", strna(dns_server_string_full(t->server)),
"DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(t->server->possible_feature_level));
}
t->n_picked_servers ++;
- log_debug("Using DNS server %s for transaction %u.", dns_server_string(t->server), t->id);
+ log_debug("Using DNS server %s for transaction %u.", strna(dns_server_string_full(t->server)), t->id);
return 1;
}
return 0;
}
-static uint16_t dns_port_for_feature_level(DnsServerFeatureLevel level) {
- return DNS_SERVER_FEATURE_LEVEL_IS_TLS(level) ? 853 : 53;
+static uint16_t dns_transaction_port(DnsTransaction *t) {
+ if (t->server->port > 0)
+ return t->server->port;
+ return DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level) ? 853 : 53;
}
static int dns_transaction_emit_tcp(DnsTransaction *t) {
if (t->server->stream && (DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level) == t->server->stream->encrypted))
s = dns_stream_ref(t->server->stream);
else
- fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, dns_port_for_feature_level(t->current_feature_level), &sa);
+ fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, dns_transaction_port(t), &sa);
type = DNS_STREAM_LOOKUP;
break;
dns_transaction_close_connection(t);
- fd = dns_scope_socket_udp(t->scope, t->server, 53);
+ fd = dns_scope_socket_udp(t->scope, t->server);
if (fd < 0)
return fd;
#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-get-properties.h"
+#include "bus-message-util.h"
#include "bus-polkit.h"
#include "parse-util.h"
#include "resolve-util.h"
#include "resolved-bus.h"
#include "resolved-link-bus.h"
#include "resolved-resolv-conf.h"
+#include "socket-netlink.h"
#include "stdio-util.h"
#include "strv.h"
#include "user-util.h"
return sd_bus_message_append(reply, "s", dns_over_tls_mode_to_string(link_get_dns_over_tls_mode(l)));
}
-static int property_get_dns(
+static int property_get_dns_internal(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
- sd_bus_error *error) {
+ sd_bus_error *error,
+ bool extended) {
Link *l = userdata;
DnsServer *s;
assert(reply);
assert(l);
- r = sd_bus_message_open_container(reply, 'a', "(iay)");
+ r = sd_bus_message_open_container(reply, 'a', extended ? "(iayqs)" : "(iay)");
if (r < 0)
return r;
LIST_FOREACH(servers, s, l->dns_servers) {
- r = bus_dns_server_append(reply, s, false);
+ r = bus_dns_server_append(reply, s, false, extended);
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
-static int property_get_current_dns_server(
+static int property_get_dns(
sd_bus *bus,
const char *path,
const char *interface,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
+ return property_get_dns_internal(bus, path, interface, property, reply, userdata, error, false);
+}
+
+static int property_get_dns_ex(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ return property_get_dns_internal(bus, path, interface, property, reply, userdata, error, true);
+}
+
+static int property_get_current_dns_server_internal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error,
+ bool extended) {
DnsServer *s;
s = *(DnsServer **) userdata;
- return bus_dns_server_append(reply, s, false);
+ return bus_dns_server_append(reply, s, false, extended);
+}
+
+static int property_get_current_dns_server(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ return property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, false);
+}
+
+static int property_get_current_dns_server_ex(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ return property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, true);
}
static int property_get_domains(
return 0;
}
-int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_free_ struct in_addr_data *dns = NULL;
- size_t allocated = 0, n = 0;
+static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, void *userdata, sd_bus_error *error, bool extended) {
+ struct in_addr_full **dns;
Link *l = userdata;
- unsigned i;
+ size_t n;
int r;
assert(message);
if (r < 0)
return r;
- r = sd_bus_message_enter_container(message, 'a', "(iay)");
- if (r < 0)
- return r;
-
- for (;;) {
- int family;
- size_t sz;
- const void *d;
-
- assert_cc(sizeof(int) == sizeof(int32_t));
-
- r = sd_bus_message_enter_container(message, 'r', "iay");
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- r = sd_bus_message_read(message, "i", &family);
- if (r < 0)
- return r;
-
- if (!IN_SET(family, AF_INET, AF_INET6))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
-
- r = sd_bus_message_read_array(message, 'y', &d, &sz);
- if (r < 0)
- return r;
- if (sz != FAMILY_ADDRESS_SIZE(family))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
-
- if (!dns_server_address_valid(family, d))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
-
- r = sd_bus_message_exit_container(message);
- if (r < 0)
- return r;
-
- if (!GREEDY_REALLOC(dns, allocated, n+1))
- return -ENOMEM;
-
- dns[n].family = family;
- memcpy(&dns[n].address, d, sz);
- n++;
- }
-
- r = sd_bus_message_exit_container(message);
+ r = bus_message_read_dns_servers(message, error, extended, &dns, &n);
if (r < 0)
return r;
NULL, true, UID_INVALID,
&l->manager->polkit_registry, error);
if (r < 0)
- return r;
- if (r == 0)
- return 1; /* Polkit will call us back */
+ goto finalize;
+ if (r == 0) {
+ r = 1; /* Polkit will call us back */
+ goto finalize;
+ }
dns_server_mark_all(l->dns_servers);
- for (i = 0; i < n; i++) {
+ for (size_t i = 0; i < n; i++) {
DnsServer *s;
- s = dns_server_find(l->dns_servers, dns[i].family, &dns[i].address, 0);
+ s = dns_server_find(l->dns_servers, dns[i]->family, &dns[i]->address, dns[i]->port, 0, dns[i]->server_name);
if (s)
dns_server_move_back_and_unmark(s);
else {
- r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i].family, &dns[i].address, 0, NULL);
- if (r < 0)
- goto clear;
+ r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i]->family, &dns[i]->address, dns[i]->port, 0, dns[i]->server_name);
+ if (r < 0) {
+ dns_server_unlink_all(l->dns_servers);
+ goto finalize;
+ }
}
}
(void) manager_write_resolv_conf(l->manager);
(void) manager_send_changed(l->manager, "DNS");
- return sd_bus_reply_method_return(message, NULL);
+ r = sd_bus_reply_method_return(message, NULL);
+
+finalize:
+ for (size_t i = 0; i < n; i++)
+ in_addr_full_free(dns[i]);
+ free(dns);
-clear:
- dns_server_unlink_all(l->dns_servers);
return r;
}
+int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return bus_link_method_set_dns_servers_internal(message, userdata, error, false);
+}
+
+int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return bus_link_method_set_dns_servers_internal(message, userdata, error, true);
+}
+
int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Link *l = userdata;
int r;
SD_BUS_PROPERTY("ScopesMask", "t", property_get_scopes_mask, 0, 0),
SD_BUS_PROPERTY("DNS", "a(iay)", property_get_dns, 0, 0),
+ SD_BUS_PROPERTY("DNSEx", "a(iayqs)", property_get_dns_ex, 0, 0),
SD_BUS_PROPERTY("CurrentDNSServer", "(iay)", property_get_current_dns_server, offsetof(Link, current_dns_server), 0),
+ SD_BUS_PROPERTY("CurrentDNSServerEx", "(iayqs)", property_get_current_dns_server_ex, offsetof(Link, current_dns_server), 0),
SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0),
SD_BUS_PROPERTY("DefaultRoute", "b", property_get_default_route, 0, 0),
SD_BUS_PROPERTY("LLMNR", "s", bus_property_get_resolve_support, offsetof(Link, llmnr_support), 0),
SD_BUS_NO_RESULT,
bus_link_method_set_dns_servers,
SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetDNSEx",
+ SD_BUS_ARGS("a(iayqs)", addresses),
+ SD_BUS_NO_RESULT,
+ bus_link_method_set_dns_servers_ex,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("SetDomains",
SD_BUS_ARGS("a(sb)", domains),
SD_BUS_NO_RESULT,
char *link_bus_path(const Link *link);
int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error);
#include "resolved-link.h"
#include "resolved-llmnr.h"
#include "resolved-mdns.h"
+#include "socket-netlink.h"
#include "string-util.h"
#include "strv.h"
#include "tmpfile-util.h"
return 0;
}
-static int link_update_dns_server_one(Link *l, const char *name) {
+static int link_update_dns_server_one(Link *l, const char *str) {
+ _cleanup_free_ char *name = NULL;
+ int family, ifindex, r;
union in_addr_union a;
DnsServer *s;
- int family, r;
+ uint16_t port;
assert(l);
- assert(name);
+ assert(str);
- r = in_addr_from_string_auto(name, &family, &a);
+ r = in_addr_port_ifindex_name_from_string_auto(str, &family, &a, &port, &ifindex, &name);
if (r < 0)
return r;
- s = dns_server_find(l->dns_servers, family, &a, 0);
+ if (ifindex != 0 && ifindex != l->ifindex)
+ return -EINVAL;
+
+ /* By default, the port number is determined with the transaction feature level.
+ * See dns_transaction_port() and dns_server_port(). */
+ if (IN_SET(port, 53, 853))
+ port = 0;
+
+ s = dns_server_find(l->dns_servers, family, &a, port, 0, name);
if (s) {
dns_server_move_back_and_unmark(s);
return 0;
}
- return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, 0, NULL);
+ return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, port, 0, name);
}
static int link_update_dns_servers(Link *l) {
assert(l);
link_read_settings(l);
- link_load_user(l);
+ r = link_load_user(l);
+ if (r < 0)
+ return r;
if (l->llmnr_support != RESOLVE_SUPPORT_NO) {
r = manager_llmnr_start(l->manager);
return s;
if (s)
- log_debug("Switching to DNS server %s for interface %s.", dns_server_string(s), l->ifname);
+ log_debug("Switching to DNS server %s for interface %s.", strna(dns_server_string_full(s)), l->ifname);
dns_server_unref(l->current_dns_server);
l->current_dns_server = dns_server_ref(s);
if (server != l->dns_servers)
fputc(' ', f);
- v = dns_server_string(server);
+ v = dns_server_string_full(server);
if (!v) {
r = -ENOMEM;
goto fail;
else
r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
if (arg_scope)
r = start_transient_scope(bus);
int add_acls_for_user(int fd, uid_t uid) {
_cleanup_(acl_freep) acl_t acl = NULL;
- acl_entry_t entry;
acl_permset_t permset;
+ acl_entry_t entry;
int r;
+ assert(fd >= 0);
+ assert(uid_is_valid(uid));
+
acl = acl_get_fd(fd);
if (!acl)
return -errno;
return -errno;
}
- /* We do not recalculate the mask unconditionally here,
- * so that the fchmod() mask above stays intact. */
+ /* We do not recalculate the mask unconditionally here, so that the fchmod() mask above stays
+ * intact. */
if (acl_get_permset(entry, &permset) < 0 ||
acl_add_perm(permset, ACL_READ) < 0)
return -errno;
if (r < 0)
return r;
- return acl_set_fd(fd, acl);
+ if (acl_set_fd(fd, acl) < 0)
+ return -errno;
+
+ return 0;
}
#include "sd-bus.h"
-#include "sd-bus.h"
-
#include "macro.h"
int bus_property_get_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bus-message-util.h"
+
+#include "resolve-util.h"
+
+int bus_message_read_ifindex(sd_bus_message *message, sd_bus_error *error, int *ret) {
+ int ifindex, r;
+
+ assert(message);
+ assert(ret);
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
+ r = sd_bus_message_read(message, "i", &ifindex);
+ if (r < 0)
+ return r;
+
+ if (ifindex <= 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+
+ *ret = ifindex;
+
+ return 0;
+}
+
+int bus_message_read_family(sd_bus_message *message, sd_bus_error *error, int *ret) {
+ int family, r;
+
+ assert(message);
+ assert(ret);
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
+ r = sd_bus_message_read(message, "i", &family);
+ if (r < 0)
+ return r;
+
+ if (!IN_SET(family, AF_INET, AF_INET6))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
+
+ *ret = family;
+ return 0;
+}
+
+int bus_message_read_in_addr_auto(sd_bus_message *message, sd_bus_error *error, int *ret_family, union in_addr_union *ret_addr) {
+ int family, r;
+ const void *d;
+ size_t sz;
+
+ assert(message);
+
+ r = sd_bus_message_read(message, "i", &family);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_array(message, 'y', &d, &sz);
+ if (r < 0)
+ return r;
+
+ if (!IN_SET(family, AF_INET, AF_INET6))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
+
+ if (sz != FAMILY_ADDRESS_SIZE(family))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
+
+ if (ret_family)
+ *ret_family = family;
+ if (ret_addr)
+ memcpy(ret_addr, d, sz);
+ return 0;
+}
+
+static int bus_message_read_dns_one(
+ sd_bus_message *message,
+ sd_bus_error *error,
+ bool extended,
+ int *ret_family,
+ union in_addr_union *ret_address,
+ uint16_t *ret_port,
+ const char **ret_server_name) {
+ const char *server_name = NULL;
+ union in_addr_union a;
+ uint16_t port = 0;
+ int family, r;
+
+ assert(message);
+ assert(ret_family);
+ assert(ret_address);
+ assert(ret_port);
+ assert(ret_server_name);
+
+ r = sd_bus_message_enter_container(message, 'r', extended ? "iayqs" : "iay");
+ if (r <= 0)
+ return r;
+
+ r = bus_message_read_in_addr_auto(message, error, &family, &a);
+ if (r < 0)
+ return r;
+
+ if (!dns_server_address_valid(family, &a))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
+
+ if (extended) {
+ r = sd_bus_message_read(message, "q", &port);
+ if (r < 0)
+ return r;
+
+ if (IN_SET(port, 53, 853))
+ port = 0;
+
+ r = sd_bus_message_read(message, "s", &server_name);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ *ret_family = family;
+ *ret_address = a;
+ *ret_port = port;
+ *ret_server_name = server_name;
+
+ return 1;
+}
+
+int bus_message_read_dns_servers(
+ sd_bus_message *message,
+ sd_bus_error *error,
+ bool extended,
+ struct in_addr_full ***ret_dns,
+ size_t *ret_n_dns) {
+
+ struct in_addr_full **dns = NULL;
+ size_t n = 0, allocated = 0;
+ int r;
+
+ assert(message);
+ assert(ret_dns);
+ assert(ret_n_dns);
+
+ r = sd_bus_message_enter_container(message, 'a', extended ? "(iayqs)" : "(iay)");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ const char *server_name;
+ union in_addr_union a;
+ uint16_t port;
+ int family;
+
+ r = bus_message_read_dns_one(message, error, extended, &family, &a, &port, &server_name);
+ if (r < 0)
+ goto clear;
+ if (r == 0)
+ break;
+
+ if (!GREEDY_REALLOC(dns, allocated, n+1)) {
+ r = -ENOMEM;
+ goto clear;
+ }
+
+ r = in_addr_full_new(family, &a, port, 0, server_name, dns + n);
+ if (r < 0)
+ goto clear;
+
+ n++;
+ }
+
+ *ret_dns = TAKE_PTR(dns);
+ *ret_n_dns = n;
+ return 0;
+
+clear:
+ for (size_t i = 0; i < n; i++)
+ in_addr_full_free(dns[i]);
+ free(dns);
+
+ return r;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "in-addr-util.h"
+#include "socket-netlink.h"
+
+int bus_message_read_ifindex(sd_bus_message *message, sd_bus_error *error, int *ret);
+int bus_message_read_family(sd_bus_message *message, sd_bus_error *error, int *ret);
+int bus_message_read_in_addr_auto(sd_bus_message *message, sd_bus_error *error, int *ret_family, union in_addr_union *ret_addr);
+
+int bus_message_read_dns_servers(
+ sd_bus_message *message,
+ sd_bus_error *error,
+ bool extended,
+ struct in_addr_full ***ret_dns,
+ size_t *ret_n_dns);
int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus);
int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus);
+#define bus_log_connect_error(r) \
+ log_error_errno(r, "Failed to create bus connection: %m")
+
#define bus_log_parse_error(r) \
log_error_errno(r, "Failed to parse bus message: %m")
r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
r = show_cgroup_get_unit_path_and_warn(bus, unit, &root);
if (r < 0)
bus-log-control-api.h
bus-map-properties.c
bus-map-properties.h
+ bus-message-util.c
+ bus-message-util.h
bus-object.c
bus-object.h
bus-polkit.c
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "fd-util.h"
+#include "fs-util.h"
+#include "offline-passwd.h"
+#include "path-util.h"
+#include "user-util.h"
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(uid_gid_hash_ops, char, string_hash_func, string_compare_func, free);
+
+static int open_passwd_file(const char *root, const char *fname, FILE **ret_file) {
+ _cleanup_free_ char *p = NULL;
+ _cleanup_close_ int fd = -1;
+
+ fd = chase_symlinks_and_open(fname, root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC, &p);
+ if (fd < 0)
+ return fd;
+
+ FILE *f = fdopen(fd, "r");
+ if (!f)
+ return -errno;
+
+ TAKE_FD(fd);
+
+ log_debug("Reading %s entries from %s...", basename(fname), p);
+
+ *ret_file = f;
+ return 0;
+}
+
+static int populate_uid_cache(const char *root, Hashmap **ret) {
+ _cleanup_(hashmap_freep) Hashmap *cache = NULL;
+ int r;
+
+ cache = hashmap_new(&uid_gid_hash_ops);
+ if (!cache)
+ return -ENOMEM;
+
+ /* The directory list is harcoded here: /etc is the standard, and rpm-ostree uses /usr/lib. This
+ * could be made configurable, but I don't see the point right now. */
+
+ const char *fname;
+ FOREACH_STRING(fname, "/etc/passwd", "/usr/lib/passwd") {
+ _cleanup_fclose_ FILE *f = NULL;
+
+ r = open_passwd_file(root, fname, &f);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return r;
+
+ struct passwd *pw;
+ while ((r = fgetpwent_sane(f, &pw)) > 0) {
+ _cleanup_free_ char *n = NULL;
+
+ n = strdup(pw->pw_name);
+ if (!n)
+ return -ENOMEM;
+
+ r = hashmap_put(cache, n, UID_TO_PTR(pw->pw_uid));
+ if (IN_SET(r, 0 -EEXIST))
+ continue;
+ if (r < 0)
+ return r;
+ TAKE_PTR(n);
+ }
+ }
+
+ *ret = TAKE_PTR(cache);
+ return 0;
+}
+
+static int populate_gid_cache(const char *root, Hashmap **ret) {
+ _cleanup_(hashmap_freep) Hashmap *cache = NULL;
+ int r;
+
+ cache = hashmap_new(&uid_gid_hash_ops);
+ if (!cache)
+ return -ENOMEM;
+
+ const char *fname;
+ FOREACH_STRING(fname, "/etc/group", "/usr/lib/group") {
+ _cleanup_fclose_ FILE *f = NULL;
+
+ r = open_passwd_file(root, fname, &f);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return r;
+
+ struct group *gr;
+ while ((r = fgetgrent_sane(f, &gr)) > 0) {
+ _cleanup_free_ char *n = NULL;
+
+ n = strdup(gr->gr_name);
+ if (!n)
+ return -ENOMEM;
+
+ r = hashmap_put(cache, n, GID_TO_PTR(gr->gr_gid));
+ if (IN_SET(r, 0, -EEXIST))
+ continue;
+ if (r < 0)
+ return r;
+ TAKE_PTR(n);
+ }
+ }
+
+ *ret = TAKE_PTR(cache);
+ return 0;
+}
+
+int name_to_uid_offline(
+ const char *root,
+ const char *user,
+ uid_t *ret_uid,
+ Hashmap **cache) {
+
+ void *found;
+ int r;
+
+ assert(user);
+ assert(ret_uid);
+ assert(cache);
+
+ if (!*cache) {
+ r = populate_uid_cache(root, cache);
+ if (r < 0)
+ return r;
+ }
+
+ found = hashmap_get(*cache, user);
+ if (!found)
+ return -ESRCH;
+
+ *ret_uid = PTR_TO_UID(found);
+ return 0;
+}
+
+int name_to_gid_offline(
+ const char *root,
+ const char *group,
+ gid_t *ret_gid,
+ Hashmap **cache) {
+
+ void *found;
+ int r;
+
+ assert(group);
+ assert(ret_gid);
+ assert(cache);
+
+ if (!*cache) {
+ r = populate_gid_cache(root, cache);
+ if (r < 0)
+ return r;
+ }
+
+ found = hashmap_get(*cache, group);
+ if (!found)
+ return -ESRCH;
+
+ *ret_gid = PTR_TO_GID(found);
+ return 0;
+}
return fd;
}
-int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret_addr, int *ret_ifindex) {
- _cleanup_free_ char *buf = NULL;
- const char *suffix;
- int r, ifindex = 0;
+int in_addr_port_ifindex_name_from_string_auto(
+ const char *s,
+ int *ret_family,
+ union in_addr_union *ret_address,
+ uint16_t *ret_port,
+ int *ret_ifindex,
+ char **ret_server_name) {
+
+ _cleanup_free_ char *buf1 = NULL, *buf2 = NULL, *name = NULL;
+ int family, ifindex = 0, r;
+ union in_addr_union a;
+ uint16_t port = 0;
+ const char *m;
assert(s);
- assert(family);
- assert(ret_addr);
- /* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id")
- * if one is found. */
+ /* This accepts the following:
+ * 192.168.0.1:53#example.com
+ * [2001:4860:4860::8888]:53%eth0#example.com */
+
+ /* if ret_port is NULL, then strings with port cannot be specified.
+ * Also, if ret_server_name is NULL, then server_name cannot be specified. */
+
+ m = strchr(s, '#');
+ if (m) {
+ if (!ret_server_name)
+ return -EINVAL;
+
+ if (isempty(m + 1))
+ return -EINVAL;
+
+ name = strdup(m + 1);
+ if (!name)
+ return -ENOMEM;
+
+ s = buf1 = strndup(s, m - s);
+ if (!buf1)
+ return -ENOMEM;
+ }
+
+ m = strchr(s, '%');
+ if (m) {
+ if (isempty(m + 1))
+ return -EINVAL;
- suffix = strchr(s, '%');
- if (suffix) {
if (ret_ifindex) {
/* If we shall return the interface index, try to parse it */
- ifindex = resolve_interface(NULL, suffix + 1);
+ ifindex = resolve_interface(NULL, m + 1);
if (ifindex < 0)
return ifindex;
}
- s = buf = strndup(s, suffix - s);
- if (!buf)
+ s = buf2 = strndup(s, m - s);
+ if (!buf2)
return -ENOMEM;
}
- r = in_addr_from_string_auto(s, family, ret_addr);
- if (r < 0)
- return r;
+ m = strrchr(s, ':');
+ if (m) {
+ if (*s == '[') {
+ _cleanup_free_ char *ip_str = NULL;
+
+ if (!ret_port)
+ return -EINVAL;
+
+ if (*(m - 1) != ']')
+ return -EINVAL;
+
+ family = AF_INET6;
+
+ r = parse_ip_port(m + 1, &port);
+ if (r < 0)
+ return r;
+
+ ip_str = strndup(s + 1, m - s - 2);
+ if (!ip_str)
+ return -ENOMEM;
+
+ r = in_addr_from_string(family, ip_str, &a);
+ if (r < 0)
+ return r;
+ } else {
+ /* First try to parse the string as IPv6 address without port number */
+ r = in_addr_from_string(AF_INET6, s, &a);
+ if (r < 0) {
+ /* Then the input should be IPv4 address with port number */
+ _cleanup_free_ char *ip_str = NULL;
+
+ if (!ret_port)
+ return -EINVAL;
+
+ family = AF_INET;
+
+ ip_str = strndup(s, m - s);
+ if (!ip_str)
+ return -ENOMEM;
+
+ r = in_addr_from_string(family, ip_str, &a);
+ if (r < 0)
+ return r;
+
+ r = parse_ip_port(m + 1, &port);
+ if (r < 0)
+ return r;
+ } else
+ family = AF_INET6;
+ }
+ } else {
+ family = AF_INET;
+ r = in_addr_from_string(family, s, &a);
+ if (r < 0)
+ return r;
+ }
+ if (ret_family)
+ *ret_family = family;
+ if (ret_address)
+ *ret_address = a;
+ if (ret_port)
+ *ret_port = port;
if (ret_ifindex)
*ret_ifindex = ifindex;
+ if (ret_server_name)
+ *ret_server_name = TAKE_PTR(name);
return r;
}
-int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) {
- _cleanup_free_ char *buf = NULL, *name = NULL;
- const char *m;
- int r;
+struct in_addr_full *in_addr_full_free(struct in_addr_full *a) {
+ if (!a)
+ return NULL;
- assert(s);
+ free(a->server_name);
+ free(a->cached_server_string);
+ return mfree(a);
+}
- m = strchr(s, '#');
- if (m) {
- name = strdup(m+1);
+int in_addr_full_new(int family, union in_addr_union *a, uint16_t port, int ifindex, const char *server_name, struct in_addr_full **ret) {
+ _cleanup_free_ char *name = NULL;
+ struct in_addr_full *x;
+
+ assert(ret);
+
+ if (!isempty(server_name)) {
+ name = strdup(server_name);
if (!name)
return -ENOMEM;
+ }
- buf = strndup(s, m - s);
- if (!buf)
- return -ENOMEM;
+ x = new(struct in_addr_full, 1);
+ if (!x)
+ return -ENOMEM;
- s = buf;
- }
+ *x = (struct in_addr_full) {
+ .family = family,
+ .address = *a,
+ .port = port,
+ .ifindex = ifindex,
+ .server_name = TAKE_PTR(name),
+ };
+
+ *ret = x;
+ return 0;
+}
+
+int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret) {
+ _cleanup_free_ char *server_name = NULL;
+ int family, ifindex, r;
+ union in_addr_union a;
+ uint16_t port;
- r = in_addr_ifindex_from_string_auto(s, family, ret, ifindex);
+ assert(s);
+
+ r = in_addr_port_ifindex_name_from_string_auto(s, &family, &a, &port, &ifindex, &server_name);
if (r < 0)
return r;
- if (server_name)
- *server_name = TAKE_PTR(name);
+ return in_addr_full_new(family, &a, port, ifindex, server_name, ret);
+}
- return r;
+const char *in_addr_full_to_string(struct in_addr_full *a) {
+ assert(a);
+
+ if (!a->cached_server_string)
+ (void) in_addr_port_ifindex_name_to_string(
+ a->family,
+ &a->address,
+ a->port,
+ a->ifindex,
+ a->server_name,
+ &a->cached_server_string);
+
+ return a->cached_server_string;
}
bool socket_address_is(const SocketAddress *a, const char *s, int type);
bool socket_address_is_netlink(const SocketAddress *a, const char *s);
-int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex);
-int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name);
+int in_addr_port_ifindex_name_from_string_auto(
+ const char *s,
+ int *ret_family,
+ union in_addr_union *ret_address,
+ uint16_t *ret_port,
+ int *ret_ifindex,
+ char **ret_server_name);
+static inline int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) {
+ return in_addr_port_ifindex_name_from_string_auto(s, family, ret, NULL, ifindex, server_name);
+}
+static inline int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) {
+ return in_addr_ifindex_name_from_string_auto(s, family, ret, ifindex, NULL);
+}
+
+struct in_addr_full {
+ int family;
+ union in_addr_union address;
+ uint16_t port;
+ int ifindex;
+ char *server_name;
+ char *cached_server_string; /* Should not be handled directly, but through in_addr_full_to_string(). */
+};
+
+struct in_addr_full *in_addr_full_free(struct in_addr_full *a);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct in_addr_full*, in_addr_full_free);
+int in_addr_full_new(int family, union in_addr_union *a, uint16_t port, int ifindex, const char *server_name, struct in_addr_full **ret);
+int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret);
+const char *in_addr_full_to_string(struct in_addr_full *a);
int sd_radv_start(sd_radv *ra);
int sd_radv_stop(sd_radv *ra);
+int sd_radv_is_running(sd_radv *ra);
int sd_radv_set_ifindex(sd_radv *ra, int interface_index);
int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr);
[],
[]],
+ [['src/test/test-offline-passwd.c',
+ 'src/shared/offline-passwd.c',
+ 'src/shared/offline-passwd.h'],
+ [],
+ []],
+
[['src/test/test-escape.c'],
[],
[]],
#include "io-util.h"
#include "parse-util.h"
#include "process-util.h"
+#include "rm-rf.h"
+#include "socket-util.h"
#include "string-util.h"
#include "strv.h"
#include "tests.h"
assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 0 && streq_ptr(s, ""));
}
+static void test_read_full_file_socket(void) {
+ _cleanup_(rm_rf_physical_and_freep) char *z = NULL;
+ _cleanup_close_ int listener = -1;
+ _cleanup_free_ char *data = NULL;
+ union sockaddr_union sa;
+ const char *j;
+ size_t size;
+ pid_t pid;
+ int r;
+
+ log_info("/* %s */", __func__);
+
+ listener = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+ assert_se(listener >= 0);
+
+ assert_se(mkdtemp_malloc(NULL, &z) >= 0);
+ j = strjoina(z, "/socket");
+
+ assert_se(sockaddr_un_set_path(&sa.un, j) >= 0);
+
+ assert_se(bind(listener, &sa.sa, SOCKADDR_UN_LEN(sa.un)) >= 0);
+ assert_se(listen(listener, 1) >= 0);
+
+ r = safe_fork("(server)", FORK_DEATHSIG|FORK_LOG, &pid);
+ assert_se(r >= 0);
+ if (r == 0) {
+ _cleanup_close_ int rfd = -1;
+ /* child */
+
+ rfd = accept4(listener, NULL, 0, SOCK_CLOEXEC);
+ assert_se(rfd >= 0);
+
+#define TEST_STR "This is a test\nreally."
+
+ assert_se(write(rfd, TEST_STR, strlen(TEST_STR)) == strlen(TEST_STR));
+ _exit(EXIT_SUCCESS);
+ }
+
+ assert_se(read_full_file_full(AT_FDCWD, j, 0, &data, &size) == -ENXIO);
+ assert_se(read_full_file_full(AT_FDCWD, j, READ_FULL_FILE_CONNECT_SOCKET, &data, &size) >= 0);
+ assert_se(size == strlen(TEST_STR));
+ assert_se(streq(data, TEST_STR));
+
+ assert_se(wait_for_terminate_and_check("(server)", pid, WAIT_LOG) >= 0);
+#undef TEST_STR
+}
+
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
test_read_line3();
test_read_line4();
test_read_nul_string();
+ test_read_full_file_socket();
return 0;
}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <getopt.h>
+
+#include "offline-passwd.h"
+#include "user-util.h"
+#include "format-util.h"
+#include "tests.h"
+
+static char *arg_root = NULL;
+
+static void test_resolve_one(const char *name) {
+ bool relaxed = name || arg_root;
+
+ if (!name)
+ name = "root";
+
+ log_info("/* %s(\"%s\") */", __func__, name);
+
+ _cleanup_(hashmap_freep) Hashmap *uid_cache = NULL, *gid_cache = NULL;
+ uid_t uid = UID_INVALID;
+ gid_t gid = GID_INVALID;
+ int r;
+
+ r = name_to_uid_offline(arg_root, name, &uid, &uid_cache);
+ log_info_errno(r, "name_to_uid_offline: %s → "UID_FMT": %m", name, uid);
+ assert_se(relaxed || r == 0);
+
+ r = name_to_uid_offline(arg_root, name, &uid, &uid_cache);
+ log_info_errno(r, "name_to_uid_offline: %s → "UID_FMT": %m", name, uid);
+ assert_se(relaxed || r == 0);
+
+ r = name_to_gid_offline(arg_root, name, &gid, &gid_cache);
+ log_info_errno(r, "name_to_gid_offline: %s → "GID_FMT": %m", name, gid);
+ assert_se(relaxed || r == 0);
+
+ r = name_to_gid_offline(arg_root, name, &gid, &gid_cache);
+ log_info_errno(r, "name_to_gid_offline: %s → "GID_FMT": %m", name, gid);
+ assert_se(relaxed || r == 0);
+}
+
+static int parse_argv(int argc, char *argv[]) {
+ static const struct option options[] = {
+ { "root", required_argument, NULL, 'r' },
+ {}
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "r:", options, NULL)) >= 0)
+ switch(c) {
+ case 'r':
+ arg_root = optarg;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unhandled option");
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ int r;
+
+ test_setup_logging(LOG_DEBUG);
+
+ r = parse_argv(argc, argv);
+ if (r < 0)
+ return r;
+
+ if (optind >= argc)
+ test_resolve_one(NULL);
+ else
+ while (optind < argc)
+ test_resolve_one(argv[optind++]);
+
+ return 0;
+}
test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19#another.test.com", "another.test.com");
}
+static void test_in_addr_port_ifindex_name_from_string_auto_one(const char *str, int family, uint16_t port, int ifindex, const char *server_name) {
+ _cleanup_free_ char *name = NULL, *x = NULL;
+ union in_addr_union a;
+ uint16_t p;
+ int f, i;
+
+ assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, &p, &i, &name) >= 0);
+ assert_se(family == f);
+ assert_se(port == p);
+ assert_se(ifindex == i);
+ assert_se(streq_ptr(server_name, name));
+ assert_se(in_addr_port_ifindex_name_to_string(f, &a, p, i, name, &x) >= 0);
+ assert_se(streq(str, x));
+}
+
+static void test_in_addr_port_ifindex_name_from_string_auto(void) {
+ log_info("/* %s */", __func__);
+
+ test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1", AF_INET, 0, 0, NULL);
+ test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1#test.com", AF_INET, 0, 0, "test.com");
+ test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1:53", AF_INET, 53, 0, NULL);
+ test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1:53#example.com", AF_INET, 53, 0, "example.com");
+ test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18", AF_INET6, 0, 0, NULL);
+ test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18#hoge.com", AF_INET6, 0, 0, "hoge.com");
+ test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%19", AF_INET6, 0, 19, NULL);
+ test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53", AF_INET6, 53, 0, NULL);
+ test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%19#hoge.com", AF_INET6, 0, 19, "hoge.com");
+ test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53#hoge.com", AF_INET6, 53, 0, "hoge.com");
+ test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19", AF_INET6, 53, 19, NULL);
+ test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19#hoge.com", AF_INET6, 53, 19, "hoge.com");
+}
+
static void test_sockaddr_equal(void) {
union sockaddr_union a = {
.in.sin_family = AF_INET,
test_in_addr_ifindex_to_string();
test_in_addr_ifindex_from_string_auto();
test_in_addr_ifindex_name_from_string_auto();
+ test_in_addr_port_ifindex_name_from_string_auto();
test_sockaddr_equal();
r = bus_connect_transport(arg_transport, arg_host, false, &bus);
if (r < 0)
- return log_error_errno(r, "Failed to create bus connection: %m");
+ return bus_log_connect_error(r);
return timedatectl_main(bus, argc, argv);
}
systemd_tmpfiles_sources = [
'src/tmpfiles/tmpfiles.c',
- 'src/tmpfiles/offline-passwd.c',
- 'src/tmpfiles/offline-passwd.h',
+ 'src/shared/offline-passwd.c',
+ 'src/shared/offline-passwd.h',
]
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1+ */
-
-#include "fd-util.h"
-#include "offline-passwd.h"
-#include "path-util.h"
-#include "user-util.h"
-
-DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(uid_gid_hash_ops, char, string_hash_func, string_compare_func, free);
-
-int name_to_uid_offline(
- const char *root,
- const char *user,
- uid_t *ret_uid,
- Hashmap **cache) {
-
- void *found;
- int r;
-
- assert(user);
- assert(ret_uid);
- assert(cache);
-
- if (!*cache) {
- _cleanup_(hashmap_freep) Hashmap *uid_by_name = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- struct passwd *pw;
- const char *passwd_path;
-
- passwd_path = prefix_roota(root, "/etc/passwd");
- f = fopen(passwd_path, "re");
- if (!f)
- return errno == ENOENT ? -ESRCH : -errno;
-
- uid_by_name = hashmap_new(&uid_gid_hash_ops);
- if (!uid_by_name)
- return -ENOMEM;
-
- while ((r = fgetpwent_sane(f, &pw)) > 0) {
- _cleanup_free_ char *n = NULL;
-
- n = strdup(pw->pw_name);
- if (!n)
- return -ENOMEM;
-
- r = hashmap_put(uid_by_name, n, UID_TO_PTR(pw->pw_uid));
- if (r == -EEXIST) {
- log_warning_errno(r, "Duplicate entry in %s for %s: %m", passwd_path, pw->pw_name);
- continue;
- }
- if (r < 0)
- return r;
-
- TAKE_PTR(n);
- }
-
- *cache = TAKE_PTR(uid_by_name);
- }
-
- found = hashmap_get(*cache, user);
- if (!found)
- return -ESRCH;
-
- *ret_uid = PTR_TO_UID(found);
- return 0;
-}
-
-int name_to_gid_offline(
- const char *root,
- const char *group,
- gid_t *ret_gid,
- Hashmap **cache) {
-
- void *found;
- int r;
-
- assert(group);
- assert(ret_gid);
- assert(cache);
-
- if (!*cache) {
- _cleanup_(hashmap_freep) Hashmap *gid_by_name = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- struct group *gr;
- const char *group_path;
-
- group_path = prefix_roota(root, "/etc/group");
- f = fopen(group_path, "re");
- if (!f)
- return errno == ENOENT ? -ESRCH : -errno;
-
- gid_by_name = hashmap_new(&uid_gid_hash_ops);
- if (!gid_by_name)
- return -ENOMEM;
-
- while ((r = fgetgrent_sane(f, &gr)) > 0) {
- _cleanup_free_ char *n = NULL;
-
- n = strdup(gr->gr_name);
- if (!n)
- return -ENOMEM;
-
- r = hashmap_put(gid_by_name, n, GID_TO_PTR(gr->gr_gid));
- if (r == -EEXIST) {
- log_warning_errno(r, "Duplicate entry in %s for %s: %m", group_path, gr->gr_name);
- continue;
- }
- if (r < 0)
- return r;
-
- TAKE_PTR(n);
- }
-
- *cache = TAKE_PTR(gid_by_name);
- }
-
- found = hashmap_get(*cache, group);
- if (!found)
- return -ESRCH;
-
- *ret_gid = PTR_TO_GID(found);
- return 0;
-}
int link_config_apply(link_config_ctx *ctx, link_config *config,
sd_device *device, const char **name) {
- _cleanup_strv_free_ char **altnames = NULL;
+ _cleanup_strv_free_ char **altnames = NULL, **current_altnames = NULL;
struct ether_addr generated_mac;
struct ether_addr *mac = NULL;
const char *new_name = NULL;
if (new_name)
strv_remove(altnames, new_name);
strv_remove(altnames, old_name);
+
+ r = rtnl_get_link_alternative_names(&ctx->rtnl, ifindex, ¤t_altnames);
+ if (r < 0)
+ log_debug_errno(r, "Failed to get alternative names on %s, ignoring: %m", old_name);
+
+ char **p;
+ STRV_FOREACH(p, current_altnames)
+ strv_remove(altnames, *p);
+
strv_uniq(altnames);
strv_sort(altnames);
-
r = rtnl_set_link_alternative_names(&ctx->rtnl, ifindex, altnames);
if (r == -EOPNOTSUPP)
log_debug_errno(r, "Could not set AlternativeName= or apply AlternativeNamesPolicy= on %s, ignoring: %m", old_name);
}
static const char *modalias_usb(sd_device *dev, char *s, size_t size) {
- const char *v, *p;
+ const char *v, *p, *n = NULL;
uint16_t vn, pn;
if (sd_device_get_sysattr_value(dev, "idVendor", &v) < 0)
return NULL;
if (safe_atoux16(p, &pn) < 0)
return NULL;
- snprintf(s, size, "usb:v%04Xp%04X*", vn, pn);
+ (void) sd_device_get_sysattr_value(dev, "product", &n);
+
+ snprintf(s, size, "usb:v%04Xp%04X:%s", vn, pn, strempty(n));
return s;
}
static int udev_builtin_hwdb_search(sd_device *dev, sd_device *srcdev,
const char *subsystem, const char *prefix,
const char *filter, bool test) {
- sd_device *d;
- char s[16];
+ char s[LINE_MAX];
bool last = false;
int r = 0;
if (!srcdev)
srcdev = dev;
- for (d = srcdev; d; ) {
+ for (sd_device *d = srcdev; d; ) {
const char *dsubsys, *devtype, *modalias = NULL;
if (sd_device_get_subsystem(d, &dsubsys) < 0)
if (!modalias)
goto next;
+ log_device_debug(dev, "hwdb modalias key: \"%s\"", modalias);
+
r = udev_builtin_hwdb_lookup(dev, prefix, modalias, filter, test);
if (r > 0)
break;
_cleanup_free_ struct unipair* unipairs = NULL;
_cleanup_free_ void *fontbuf = NULL;
unsigned i;
+ int log_level;
int r;
unipairs = new(struct unipair, USHRT_MAX);
return;
}
+ log_level = LOG_WARNING;
+
/* get metadata of the current font (width, height, count) */
r = ioctl(src_fd, KDFONTOP, &cfo);
- if (r < 0)
- log_warning_errno(errno, "KD_FONT_OP_GET failed while trying to get the font metadata: %m");
- else {
+ if (r < 0) {
+ /* We might be called to operate on the dummy console (to setup keymap
+ * mainly) when fbcon deferred takeover is used for example. In such case,
+ * setting font is not supported and is expected to fail. */
+ if (errno == ENOSYS)
+ log_level = LOG_DEBUG;
+
+ log_full_errno(log_level, errno,
+ "KD_FONT_OP_GET failed while trying to get the font metadata: %m");
+ } else {
/* verify parameter sanity first */
if (cfo.width > 32 || cfo.height > 32 || cfo.charcount > 512)
log_warning("Invalid font metadata - width: %u (max 32), height: %u (max 32), count: %u (max 512)",
}
if (cfo.op != KD_FONT_OP_SET)
- log_warning("Fonts will not be copied to remaining consoles");
+ log_full(log_level, "Fonts will not be copied to remaining consoles");
for (i = 1; i <= 63; i++) {
char ttyname[sizeof("/dev/tty63")];
if (r < 0)
return log_error_errno(r, "Failed to parse root hash signature '%s': %m", argv[6]);
} else {
- r = read_full_file_full(AT_FDCWD, argv[6], 0, &hash_sig, &hash_sig_size);
+ r = read_full_file_full(AT_FDCWD, argv[6], READ_FULL_FILE_CONNECT_SOCKET, &hash_sig, &hash_sig_size);
if (r < 0)
return log_error_errno(r, "Failed to read root hash signature: %m");
}
command -v mksquashfs >/dev/null 2>&1 || exit 0
command -v veritysetup >/dev/null 2>&1 || exit 0
+command -v sfdisk >/dev/null 2>&1 || exit 0
# Need loop devices for systemd-dissect
test_create_image() {
instmods loop =block
instmods squashfs =squashfs
instmods dm_verity =md
+ install_dmevent
generate_module_dependencies
- inst_binary mksquashfs
- inst_binary veritysetup
+ inst_binary sfdisk
+ inst_binary losetup
+
+ BASICTOOLS=(
+ bash
+ cat
+ )
+ oldinitdir=$initdir
+ export initdir=$TESTDIR/minimal
+ mkdir -p $initdir
+ setup_basic_dirs
+ install_basic_tools
+ inst /usr/lib/os-release
+ ln -s ../usr/lib/os-release $initdir/etc/os-release
+ echo MARKER=1 >> $initdir/usr/lib/os-release
+ mksquashfs $initdir $oldinitdir/usr/share/minimal.raw
+ veritysetup format $oldinitdir/usr/share/minimal.raw $oldinitdir/usr/share/minimal.verity | grep '^Root hash:' | cut -f2 | tr -d '\n' > $oldinitdir/usr/share/minimal.roothash
+ export initdir=$oldinitdir
)
}
[Network]
IPv6AcceptRA=no
-DNS=10.10.10.10 10.10.10.11
+DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com
NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org
Domains=hogehoge ~foofoo
LLMNR=no
self.assertRegex(data, r'REQUIRED_FOR_ONLINE=yes')
self.assertRegex(data, r'REQUIRED_OPER_STATE_FOR_ONLINE=routable')
self.assertRegex(data, r'NETWORK_FILE=/run/systemd/network/state-file-tests.network')
- self.assertRegex(data, r'DNS=10.10.10.10 10.10.10.11')
+ self.assertRegex(data, r'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com \[1111:2222::3333\]:1234#ccc.com')
self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
self.assertRegex(data, r'DOMAINS=hogehoge')
self.assertRegex(data, r'ROUTE_DOMAINS=foofoo')
self.assertRegex(data, r'DNSSEC=no')
self.assertRegex(data, r'ADDRESSES=192.168.(10.10|12.12)/24 192.168.(12.12|10.10)/24')
- check_output(*resolvectl_cmd, 'dns', 'dummy98', '10.10.10.12', '10.10.10.13', env=env)
+ check_output(*resolvectl_cmd, 'dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333', env=env)
check_output(*resolvectl_cmd, 'domain', 'dummy98', 'hogehogehoge', '~foofoofoo', env=env)
check_output(*resolvectl_cmd, 'llmnr', 'dummy98', 'yes', env=env)
check_output(*resolvectl_cmd, 'mdns', 'dummy98', 'no', env=env)
with open(path) as f:
data = f.read()
- self.assertRegex(data, r'DNS=10.10.10.12 10.10.10.13')
+ self.assertRegex(data, r'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333')
self.assertRegex(data, r'NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org')
self.assertRegex(data, r'DOMAINS=hogehogehoge')
self.assertRegex(data, r'ROUTE_DOMAINS=foofoofoo')
with open(path) as f:
data = f.read()
- self.assertRegex(data, r'DNS=10.10.10.12 10.10.10.13')
+ self.assertRegex(data, r'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333')
self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
self.assertRegex(data, r'DOMAINS=hogehogehoge')
self.assertRegex(data, r'ROUTE_DOMAINS=foofoofoo')
with open(path) as f:
data = f.read()
- self.assertRegex(data, r'DNS=10.10.10.10 10.10.10.11')
+ self.assertRegex(data, r'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com \[1111:2222::3333\]:1234#ccc.com')
self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
self.assertRegex(data, r'DOMAINS=hogehoge')
self.assertRegex(data, r'ROUTE_DOMAINS=foofoo')
set -ex
set -o pipefail
+export SYSTEMD_LOG_LEVEL=debug
+
+cleanup()
+{
+ if [ -z "${image_dir}" ]; then
+ return
+ fi
+ rm -rf "${image_dir}"
+}
+
cd /tmp
-image=$(mktemp -d -t -p /tmp tmp.XXXXXX)
-if [ -z "${image}" ] || [ ! -d "${image}" ]; then
- echo "Could not create temporary directory with mktemp under /tmp"
- exit 1
+image_dir="$(mktemp -d -t -p /tmp tmp.XXXXXX)"
+if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then
+ echo "mktemp under /tmp failed"
+ exit 1
fi
-mkdir -p ${image}/usr/lib ${image}/etc
-cp /usr/lib/os-release ${image}/usr/lib/
-cp /etc/machine-id /etc/os-release ${image}/etc/
-mksquashfs ${image} ${image}.raw
-veritysetup format ${image}.raw ${image}.verity | grep '^Root hash:' | cut -f2 | tr -d '\n' > ${image}.roothash
+trap cleanup EXIT
+
+cp /usr/share/minimal.* "${image_dir}/"
+image="${image_dir}/minimal"
+roothash="$(cat ${image}.roothash)"
/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F "Found read-only 'root' partition of type squashfs with verity"
+/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F "MARKER=1"
/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F -f /usr/lib/os-release
mv ${image}.verity ${image}.fooverity
mv ${image}.roothash ${image}.foohash
-/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=`cat ${image}.foohash` --verity-data=${image}.fooverity | grep -q -F "Found read-only 'root' partition of type squashfs with verity"
-/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=`cat ${image}.foohash` --verity-data=${image}.fooverity | grep -q -F -f /usr/lib/os-release
+/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F "Found read-only 'root' partition of type squashfs with verity"
+/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F "MARKER=1"
+/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F -f /usr/lib/os-release
+mv ${image}.fooverity ${image}.verity
+mv ${image}.foohash ${image}.roothash
+
+mkdir -p ${image_dir}/mount
+/usr/lib/systemd/systemd-dissect --mount ${image}.raw ${image_dir}/mount
+cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f /usr/lib/os-release
+cat ${image_dir}/mount/etc/os-release | grep -q -F -f /usr/lib/os-release
+cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
+umount ${image_dir}/mount
+
+systemd-run -t --property RootImage=${image}.raw /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
+mv ${image}.verity ${image}.fooverity
+mv ${image}.roothash ${image}.foohash
+systemd-run -t --property RootImage=${image}.raw --property RootHash=${image}.foohash --property RootVerity=${image}.fooverity /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -t --property RootImage=${image}.raw --property RootHash=${roothash} --property RootVerity=${image}.fooverity /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
+mv ${image}.fooverity ${image}.verity
+mv ${image}.foohash ${image}.roothash
+
+# Make a GPT disk on the fly, with the squashfs as partition 1 and the verity hash tree as partition 2
+machine="$(uname -m)"
+if [ "${machine}" = "x86_64" ]; then
+ root_guid=4f68bce3-e8cd-4db1-96e7-fbcaf984b709
+ verity_guid=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5
+elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then
+ root_guid=44479540-f297-41b2-9af7-d131d5f0458a
+ verity_guid=d13c5d3b-b5d1-422a-b29f-9454fdc89d76
+elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then
+ root_guid=b921b045-1df0-41c3-af44-4c6f280d3fae
+ verity_guid=df3300ce-d69f-4c92-978c-9bfb0f38d820
+elif [ "${machine}" = "arm" ]; then
+ root_guid=69dad710-2ce4-4e3c-b16c-21a1d49abed3
+ verity_guid=7386cdf2-203c-47a9-a498-f2ecce45a2d6
+elif [ "${machine}" = "ia64" ]; then
+ root_guid=993d8d3d-f80e-4225-855a-9daf8ed7ea97
+ verity_guid=86ed10d5-b607-45bb-8957-d350f23d0571
+else
+ echo "Unexpected uname -m: ${machine} in testsuite-50.sh, please fix me"
+ exit 1
+fi
+# du rounds up to block size, which is more helpful for partitioning
+root_size="$(du -k ${image}.raw | cut -f1)"
+verity_size="$(du -k ${image}.verity | cut -f1)"
+# 4MB seems to be the minimum size blkid will accept, below that probing fails
+dd if=/dev/zero of=${image}.gpt bs=512 count=$((8192+${root_size}*2+${verity_size}*2))
+# sfdisk seems unhappy if the size overflows into the next unit, eg: 1580KiB will be interpreted as 1MiB
+# so do some basic rounding up if the minimal image is more than 1 MB
+if [ ${root_size} -ge 1024 ]; then
+ root_size="$((${root_size}/1024 + 1))MiB"
+else
+ root_size="${root_size}KiB"
+fi
+verity_size="${verity_size}KiB"
+uuid="$(head -c 32 ${image}.roothash | cut -c -8)-$(head -c 32 ${image}.roothash | cut -c 9-12)-$(head -c 32 ${image}.roothash | cut -c 13-16)-$(head -c 32 ${image}.roothash | cut -c 17-20)-$(head -c 32 ${image}.roothash | cut -c 21-)"
+echo -e "label: gpt\nsize=${root_size}, type=${root_guid}, uuid=${uuid}" | sfdisk ${image}.gpt
+uuid="$(tail -c 32 ${image}.roothash | cut -c -8)-$(tail -c 32 ${image}.roothash | cut -c 9-12)-$(tail -c 32 ${image}.roothash | cut -c 13-16)-$(tail -c 32 ${image}.roothash | cut -c 17-20)-$(tail -c 32 ${image}.roothash | cut -c 21-)"
+echo -e "size=${verity_size}, type=${verity_guid}, uuid=${uuid}" | sfdisk ${image}.gpt --append
+sfdisk --part-label ${image}.gpt 1 "Root Partition"
+sfdisk --part-label ${image}.gpt 2 "Verity Partition"
+loop="$(losetup --show -P -f ${image}.gpt)"
+dd if=${image}.raw of=${loop}p1
+dd if=${image}.verity of=${loop}p2
+losetup -d ${loop}
+
+/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q "Found read-only 'root' partition (UUID $(head -c 32 ${image}.roothash)) of type squashfs for .* with verity on partition #1"
+/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q "Found read-only 'root-verity' partition (UUID $(tail -c 32 ${image}.roothash)) of type DM_verity_hash for .* on partition #2"
+/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F "MARKER=1"
+/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F -f /usr/lib/os-release
+
+/usr/lib/systemd/systemd-dissect --root-hash ${roothash} --mount ${image}.gpt ${image_dir}/mount
+cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f /usr/lib/os-release
+cat ${image_dir}/mount/etc/os-release | grep -q -F -f /usr/lib/os-release
+cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
+umount ${image_dir}/mount
+
+systemd-run -t --property RootImage=${image}.gpt --property RootHash=${roothash} /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
echo OK > /testok
device = int(device, 16)
print('pci:v{:08X}d{:08X}*'.format(vendor, device))
-print('# usb:v<VEND>p<PROD> (4 uppercase hexadecimal digits twice')
+print('# usb:v<VEND>p<PROD> (4 uppercase hexadecimal digits twice)')
for entry in chromiumos.gen_autosuspend_rules.USB_IDS:
vendor, product = entry.split(':')
vendor = int(vendor, 16)
RUN|RUN_GCC|RUN_CLANG)
if [[ "$phase" = "RUN_CLANG" ]]; then
ENV_VARS="-e CC=clang -e CXX=clang++"
+ MESON_ARGS="--optimization=1"
fi
- docker exec $ENV_VARS -it $CONT_NAME meson --werror -Dtests=unsafe -Dslow-tests=true -Dsplit-usr=true -Dman=true build
+ docker exec $ENV_VARS -it $CONT_NAME meson --werror -Dtests=unsafe -Dslow-tests=true -Dsplit-usr=true -Dman=true $MESON_ARGS build
$DOCKER_EXEC ninja -v -C build
docker exec -e "TRAVIS=$TRAVIS" -it $CONT_NAME ninja -C build test
;;
'sysinit.target.wants/'],
['systemd-importd.service', 'ENABLE_IMPORTD',
'dbus-org.freedesktop.import1.service'],
- ['systemd-initctl.service', ''],
+ ['systemd-initctl.service', 'HAVE_SYSV_COMPAT'],
['systemd-journal-gatewayd.service', 'ENABLE_REMOTE HAVE_MICROHTTPD'],
['systemd-journal-remote.service', 'ENABLE_REMOTE HAVE_MICROHTTPD'],
['systemd-journal-upload.service', 'ENABLE_REMOTE HAVE_LIBCURL'],