summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS21
-rw-r--r--hwdb/20-bluetooth-vendor-product.hwdb51
-rw-r--r--hwdb/70-mouse.hwdb21
-rw-r--r--man/coredumpctl.xml18
-rw-r--r--man/machinectl.xml189
-rw-r--r--man/systemctl.xml12
-rw-r--r--man/systemd-run.xml55
-rw-r--r--man/systemd.exec.xml41
-rw-r--r--man/systemd.netdev.xml14
-rw-r--r--man/systemd.network.xml12
-rw-r--r--man/systemd.special.xml2
-rw-r--r--src/basic/hostname-util.c30
-rw-r--r--src/basic/hostname-util.h5
-rw-r--r--src/basic/time-util.c26
-rw-r--r--src/basic/time-util.h1
-rw-r--r--src/basic/util.c18
-rw-r--r--src/basic/util.h2
-rw-r--r--src/core/dbus-execute.c104
-rw-r--r--src/core/dbus-unit.c32
-rw-r--r--src/core/execute.c15
-rw-r--r--src/core/execute.h12
-rw-r--r--src/core/kmod-setup.c2
-rw-r--r--src/core/load-fragment-gperf.gperf.m41
-rw-r--r--src/core/load-fragment.c2
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/import/export.c1
-rw-r--r--src/import/import-raw.c1
-rw-r--r--src/import/import-tar.c1
-rw-r--r--src/import/import.c1
-rw-r--r--src/import/importd.c1
-rw-r--r--src/import/pull-raw.c1
-rw-r--r--src/import/pull-tar.c3
-rw-r--r--src/import/pull.c1
-rw-r--r--src/journal/coredumpctl.c25
-rw-r--r--src/journal/sd-journal.c1
-rw-r--r--src/libsystemd-network/dhcp6-internal.h7
-rw-r--r--src/libsystemd-network/dhcp6-lease-internal.h20
-rw-r--r--src/libsystemd-network/dhcp6-option.c102
-rw-r--r--src/libsystemd-network/dhcp6-protocol.h8
-rw-r--r--src/libsystemd-network/network-internal.c15
-rw-r--r--src/libsystemd-network/network-internal.h2
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c43
-rw-r--r--src/libsystemd-network/sd-dhcp6-lease.c196
-rw-r--r--src/libsystemd-network/test-dhcp6-client.c65
-rw-r--r--src/libsystemd/sd-bus/bus-control.c2
-rw-r--r--src/libsystemd/sd-bus/bus-objects.c4
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c1
-rw-r--r--src/libsystemd/sd-bus/test-bus-objects.c3
-rw-r--r--src/libsystemd/sd-device/device-enumerator.c2
-rw-r--r--src/libsystemd/sd-login/sd-login.c3
-rw-r--r--src/login/logind-dbus.c45
-rw-r--r--src/login/org.freedesktop.login1.conf4
-rw-r--r--src/login/org.freedesktop.login1.policy.in12
-rw-r--r--src/machine/machine-dbus.c642
-rw-r--r--src/machine/machine-dbus.h1
-rw-r--r--src/machine/machine.c67
-rw-r--r--src/machine/machine.h5
-rw-r--r--src/machine/machinectl.c234
-rw-r--r--src/machine/machined-dbus.c50
-rw-r--r--src/machine/machined.c51
-rw-r--r--src/machine/machined.h2
-rw-r--r--src/machine/org.freedesktop.machine1.conf8
-rw-r--r--src/machine/org.freedesktop.machine1.policy.in53
-rw-r--r--src/network/networkd-link.c64
-rw-r--r--src/network/networkd-network-gperf.gperf2
-rw-r--r--src/network/networkd-network.c30
-rw-r--r--src/network/networkd.h28
-rw-r--r--src/nspawn/nspawn.c16
-rw-r--r--src/nss-myhostname/nss-myhostname.c11
-rw-r--r--src/nss-mymachines/nss-mymachines.c1
-rw-r--r--src/nss-resolve/nss-resolve.c81
-rw-r--r--src/resolve-host/resolve-host.c159
-rw-r--r--src/resolve/resolved-bus.c81
-rw-r--r--src/resolve/resolved-dns-answer.c102
-rw-r--r--src/resolve/resolved-dns-answer.h17
-rw-r--r--src/resolve/resolved-dns-cache.c194
-rw-r--r--src/resolve/resolved-dns-cache.h5
-rw-r--r--src/resolve/resolved-dns-packet.c24
-rw-r--r--src/resolve/resolved-dns-packet.h28
-rw-r--r--src/resolve/resolved-dns-query.c418
-rw-r--r--src/resolve/resolved-dns-query.h1
-rw-r--r--src/resolve/resolved-dns-question.c80
-rw-r--r--src/resolve/resolved-dns-question.h3
-rw-r--r--src/resolve/resolved-dns-rr.c30
-rw-r--r--src/resolve/resolved-dns-rr.h1
-rw-r--r--src/resolve/resolved-dns-scope.c134
-rw-r--r--src/resolve/resolved-dns-scope.h6
-rw-r--r--src/resolve/resolved-dns-transaction.c112
-rw-r--r--src/resolve/resolved-dns-transaction.h10
-rw-r--r--src/resolve/resolved-dns-zone.c57
-rw-r--r--src/resolve/resolved-dns-zone.h3
-rw-r--r--src/resolve/resolved-link.c20
-rw-r--r--src/resolve/resolved-manager.c161
-rw-r--r--src/resolve/resolved-manager.h11
-rw-r--r--src/resolve/resolved.c2
-rw-r--r--src/shared/bus-util.c9
-rw-r--r--src/shared/dns-domain.c40
-rw-r--r--src/shared/dns-domain.h10
-rw-r--r--src/shared/logs-show.c1
-rw-r--r--src/shared/utmp-wtmp.c24
-rw-r--r--src/shared/utmp-wtmp.h4
-rw-r--r--src/systemctl/systemctl.c102
-rw-r--r--src/systemd/sd-dhcp6-lease.h8
-rw-r--r--src/test/test-dns-domain.c35
-rw-r--r--src/test/test-util.c3
-rw-r--r--src/timedate/timedatectl.c2
106 files changed, 3464 insertions, 1036 deletions
diff --git a/NEWS b/NEWS
index fe5eb31672..7f35f66a8d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,26 @@
systemd System and Service Manager
+CHANGES WITH 225:
+
+ * Mostly a bugfix release, with tree-wide cleanups
+
+ * systemd-networkd learned to cope with private-zone DHCP options and
+ allows other programs to query the values.
+
+ Contributions from: Alastair Hughes, Alex Crawford, Daniel Mack,
+ David Herrmann, Dimitri John Ledkov, Eric Kostrowski, Evgeny
+ Vereshchagin, Felipe Sateler, Jan Pokorný, Jan Synacek, Johnny
+ Robeson, Karel Zak, Kay Sievers, Kefeng Wang, Lennart
+ Poettering, Major Hayden, Marcel Holtmann, Markus Elfring,
+ Martin Mikkelsen, Matt Turner, Maxim Mikityanskiy, Michael
+ Biebl, Namhyung Kim, Nicolas Cornu, Owen W. Taylor, Patrik
+ Flykt, Peter Hutterer, reverendhomer, Richard Maw, Ronny
+ Chevalier, Seth Jennings, Stef Walter, Susant Sahani, Thomas
+ Blume, Thomas Hindoe Paaboel Andersen, Thomas Meyer, Tom
+ Gundersen, WaLyong Cho, Zbigniew Jędrzejewski-Szmek
+
+ -- Berlin, 2015-xx-xx
+
CHANGES WITH 224:
* The systemd-efi-boot-generator functionality was merged into
diff --git a/hwdb/20-bluetooth-vendor-product.hwdb b/hwdb/20-bluetooth-vendor-product.hwdb
index 35ea5e9e52..5835d9a103 100644
--- a/hwdb/20-bluetooth-vendor-product.hwdb
+++ b/hwdb/20-bluetooth-vendor-product.hwdb
@@ -1847,3 +1847,54 @@ bluetooth:v0264*
bluetooth:v0265*
ID_VENDOR_FROM_DATABASE=SMK Corporation
+
+bluetooth:v0266*
+ ID_VENDOR_FROM_DATABASE=Schawbel Technologies LLC
+
+bluetooth:v0267*
+ ID_VENDOR_FROM_DATABASE=XMI Systems SA
+
+bluetooth:v0268*
+ ID_VENDOR_FROM_DATABASE=Cerevo
+
+bluetooth:v0269*
+ ID_VENDOR_FROM_DATABASE=Torrox GmbH & Co KG
+
+bluetooth:v026A*
+ ID_VENDOR_FROM_DATABASE=Gemalto
+
+bluetooth:v026B*
+ ID_VENDOR_FROM_DATABASE=DEKA Research & Development Corp.
+
+bluetooth:v026C*
+ ID_VENDOR_FROM_DATABASE=Domster Tadeusz Szydlowski
+
+bluetooth:v026D*
+ ID_VENDOR_FROM_DATABASE=Technogym SPA
+
+bluetooth:v026E*
+ ID_VENDOR_FROM_DATABASE=FLEURBAEY BVBA
+
+bluetooth:v026F*
+ ID_VENDOR_FROM_DATABASE=Aptcode Solutions
+
+bluetooth:v0270*
+ ID_VENDOR_FROM_DATABASE=LSI ADL Technology
+
+bluetooth:v0271*
+ ID_VENDOR_FROM_DATABASE=Animas Corp
+
+bluetooth:v0272*
+ ID_VENDOR_FROM_DATABASE=Alps Electric Co., Ltd.
+
+bluetooth:v0273*
+ ID_VENDOR_FROM_DATABASE=OCEASOFT
+
+bluetooth:v0274*
+ ID_VENDOR_FROM_DATABASE=Motsai Research
+
+bluetooth:v0275*
+ ID_VENDOR_FROM_DATABASE=Geotab
+
+bluetooth:v0276*
+ ID_VENDOR_FROM_DATABASE=E.G.O. Elektro-Gertebau GmbH
diff --git a/hwdb/70-mouse.hwdb b/hwdb/70-mouse.hwdb
index 1451fc02cc..a8bf055ba1 100644
--- a/hwdb/70-mouse.hwdb
+++ b/hwdb/70-mouse.hwdb
@@ -136,6 +136,22 @@ mouse:usb:v0461p4d16:name:USB Optical Mouse:
MOUSE_DPI=500@125
##########################################
+# HandShoe Mouse
+##########################################
+
+# HandShoe Mouse
+mouse:usb:v192fp0916:name:USB Optical Mouse:
+ MOUSE_DPI=1000@128
+
+##########################################
+# HoverStop
+##########################################
+
+# Hoverstop active ergonomic mouse
+mouse:usb:v088dp1234:name:HoverStop NL Hoverstop active ergonomic mouse:
+ MOUSE_DPI=400@129
+
+##########################################
# HP
##########################################
@@ -146,6 +162,7 @@ mouse:usb:v0458p0133:name:Mouse Laser Mouse:
# HP X1000
mouse:usb:v093ap2510:name:PixArt USB Optical Mouse:
+mouse:usb:v093ap2510:name:PIXART USB OPTICAL MOUSE:
MOUSE_DPI=1000@125
##########################################
@@ -160,6 +177,10 @@ mouse:usb:v17efp6019:name:Lenovo Optical USB Mouse:
mouse:usb:v17efp6044:name:ThinkPad USB Laser Mouse:
MOUSE_DPI=1200@125
+# Lenovo Precision USB Mouse
+mouse:usb:v17efp6050:name:Lenovo Precision USB Mouse:
+ MOUSE_DPI=1200@127
+
##########################################
# Logitech
##########################################
diff --git a/man/coredumpctl.xml b/man/coredumpctl.xml
index efbc655a76..0f1afe77c3 100644
--- a/man/coredumpctl.xml
+++ b/man/coredumpctl.xml
@@ -86,8 +86,8 @@
</varlistentry>
<varlistentry>
- <term><option>-F</option></term>
- <term><option>--field=</option></term>
+ <term><option>-F</option> <replaceable>FIELD</replaceable></term>
+ <term><option>--field=</option><replaceable>FIELD</replaceable></term>
<listitem><para>Print all possible data values the specified
field takes in matching coredump entries of the
@@ -95,13 +95,21 @@
</varlistentry>
<varlistentry>
- <term><option>-o</option></term>
- <term><option>--output=FILE</option></term>
+ <term><option>-o</option> <replaceable>FILE</replaceable></term>
+ <term><option>--output=</option><replaceable>FILE</replaceable></term>
<listitem><para>Write the core to <option>FILE</option>.
</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-D</option> <replaceable>DIR</replaceable></term>
+ <term><option>--directory=</option><replaceable>DIR</replaceable></term>
+
+ <listitem><para>Use the journal files in the specified <option>DIR</option>.
+ </para></listitem>
+ </varlistentry>
+
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
<xi:include href="standard-options.xml" xpointer="no-pager" />
@@ -132,7 +140,7 @@
<listitem><para>Extract the last coredump matching specified
characteristics. The coredump will be written on standard
output, unless an output file is specified with
- <option>-o/--output</option>. </para></listitem>
+ <option>--output=</option>. </para></listitem>
</varlistentry>
<varlistentry>
diff --git a/man/machinectl.xml b/man/machinectl.xml
index a5eb3f08e4..6cf405ed29 100644
--- a/man/machinectl.xml
+++ b/man/machinectl.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*-nxml-*-->
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -65,6 +65,43 @@
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
virtual machine and container registration manager
<citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+ <para><command>machinectl</command> may be used to execute
+ operations on machines and images. Machines in this sense are
+ considered running instances of:</para>
+
+ <itemizedlist>
+ <listitem><para>Virtual Machines (VMs) that virtualize hardware
+ to run full operating system (OS) instances (including their kernels)
+ in a virtualized environment on top of the host OS.</para></listitem>
+
+ <listitem><para>Containers that share the hardware and
+ OS kernel with the host OS, in order to run
+ OS userspace instances on top the host OS.</para></listitem>
+
+ <listitem><para>The host system itself</para></listitem>
+ </itemizedlist>
+
+ <para>Machines are identified by names that follow the same rules
+ as UNIX and DNS host names, for details see below. Machines are
+ instantiated from disk or file system images, that frequently but not
+ necessarily carry the same name as machines running from
+ them. Images in this sense are considered:</para>
+
+ <itemizedlist>
+ <listitem><para>Directory trees containing an OS, including its
+ top-level directories <filename>/usr</filename>,
+ <filename>/etc</filename>, and so on.</para></listitem>
+
+ <listitem><para>btrfs subvolumes containing OS trees, similar to
+ normal directory trees.</para></listitem>
+
+ <listitem><para>Binary "raw" disk images containing MBR or GPT
+ partition tables and Linux file system partitions.</para></listitem>
+
+ <listitem><para>The file system tree of the host OS itself.</para></listitem>
+ </itemizedlist>
+
</refsect1>
<refsect1>
@@ -138,6 +175,30 @@
</varlistentry>
<varlistentry>
+ <term><option>--uid=</option></term>
+
+ <listitem><para>When used with the <command>shell</command>
+ command, chooses the user ID to open the interactive shell
+ session as. If this switch is not specified, defaults to
+ <literal>root</literal>. Note that this switch is not
+ supported for the <command>login</command> command (see
+ below).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--setenv=</option></term>
+
+ <listitem><para>When used with the <command>shell</command>
+ command, sets an environment variable to pass to the executed
+ shell. Takes a pair of environment variable name and value,
+ separated by <literal>=</literal> as argument. This switch
+ may be used multiple times to set multiple environment
+ variables. Note that this switch is not supported for the
+ <command>login</command> command (see
+ below).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>--mkdir</option></term>
<listitem><para>When used with <command>bind</command> creates
@@ -145,7 +206,6 @@
mount.</para></listitem>
</varlistentry>
-
<varlistentry>
<term><option>--read-only</option></term>
@@ -247,9 +307,11 @@
<term><command>list</command></term>
<listitem><para>List currently running (online) virtual
- machines and containers. To enumerate container images that
- can be started, use <command>list-images</command> (see
- below).</para></listitem>
+ machines and containers. To enumerate machine images that can
+ be started, use <command>list-images</command> (see
+ below). Note that this command hides the special
+ <literal>.host</literal> machine by default. Use the
+ <option>--all</option> switch to show it.</para></listitem>
</varlistentry>
<varlistentry>
@@ -267,7 +329,7 @@
</varlistentry>
<varlistentry>
- <term><command>show</command> <replaceable>NAME</replaceable>...</term>
+ <term><command>show</command> [<replaceable>NAME</replaceable>...]</term>
<listitem><para>Show properties of one or more registered
virtual machines or containers or the manager itself. If no
@@ -316,21 +378,66 @@
</varlistentry>
<varlistentry>
- <term><command>login</command> <replaceable>NAME</replaceable></term>
-
- <listitem><para>Open an interactive terminal login session to
- a container. This will create a TTY connection to a specific
- container and asks for the execution of a getty on it. Note
- that this is only supported for containers running
+ <term><command>login</command> [<replaceable>NAME</replaceable>]</term>
+
+ <listitem><para>Open an interactive terminal login session in
+ a container or on the local host. If an argument is supplied
+ it refers to the container machine to connect to. If none is
+ specified, or the container name is specified as the empty
+ string, or the special machine name <literal>.host</literal>
+ (see below) is specified, the connection is made to the local
+ host instead. This will create a TTY connection to a specific
+ container or the local host and asks for the execution of a
+ getty on it. Note that this is only supported for containers
+ running
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
as init system.</para>
<para>This command will open a full login prompt on the
- container, which then asks for username and password. Use
+ container or the local host, which then asks for username and
+ password. Use <command>shell</command> (see below) or
+ <citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ with the <option>--machine=</option> switch to directly invoke
+ a single command, either interactively or in the
+ background.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>shell</command> [[<replaceable>NAME</replaceable>@]<replaceable>NAME</replaceable> [<replaceable>PATH</replaceable> [<replaceable>ARGUMENTS</replaceable>...]]] </term>
+
+ <listitem><para>Open an interactive shell session in a
+ container or on the local host. The first argument refers to
+ the container machine to connect to. If none is specified, or
+ the machine name is specified as the empty string, or the
+ special machine name <literal>.host</literal> (see below) is
+ specified, the connection is made to the local host
+ instead. This works similar to <command>login</command> but
+ immediately invokes a user process. This command runs the
+ specified executable with the specified arguments, or
+ <filename>/bin/sh</filename> if none is specified. By default
+ opens a <literal>root</literal> shell, but by using
+ <option>--uid=</option>, or by prefixing the machine name with
+ a username and an <literal>@</literal> character, a different
+ user may be selected. Use <option>--setenv=</option> to set
+ environment variables for the executed process.</para>
+
+ <para>When using the <command>shell</command> command without
+ arguments (thus invoking the executed shell or command on the
+ local host) it is similar in many ways to a <citerefentry
+ project='die-net'><refentrytitle>su</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ session, but unlike <command>su</command> completely isolates
+ the new session from the originating session, so that it
+ shares no process or session properties, and is in a clean and
+ well-defined state. It will be tracked in a new utmp, login,
+ audit and keyring session, and will not inherit an environment
+ variables or resource limits, among other properties.</para>
+
+ <para>Note that the
<citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- with the <option>--machine=</option> switch to invoke a single
- command, either interactively or in the background within a
- local container.</para></listitem>
+ may be used in place of the <command>shell</command> command,
+ and allows more detailed, low-level configuration of the
+ invoked unit. However, it is frequently more privileged than
+ the <command>shell</command> command.</para></listitem>
</varlistentry>
<varlistentry>
@@ -453,7 +560,7 @@
</varlistentry>
<varlistentry>
- <term><command>image-status</command> <replaceable>NAME</replaceable>...</term>
+ <term><command>image-status</command> [<replaceable>NAME</replaceable>...]</term>
<listitem><para>Show terse status information about one or
more container or VM images. This function is intended to
@@ -463,7 +570,7 @@
</varlistentry>
<varlistentry>
- <term><command>show-image</command> <replaceable>NAME</replaceable>...</term>
+ <term><command>show-image</command> [<replaceable>NAME</replaceable>...]</term>
<listitem><para>Show properties of one or more registered
virtual machine or container images, or the manager itself. If
@@ -766,6 +873,41 @@
</refsect1>
<refsect1>
+ <title>Machine and Image Names</title>
+
+ <para>The <command>machinectl</command> tool operates on machines
+ and images, whose names must be chosen following strict
+ rules. Machine names must be suitable for use as host names
+ following a conservative subset of DNS and UNIX/Linux
+ semantics. Specifically, they must consist of one or more
+ non-empty label strings, separated by dots. No leading or trailing
+ dots are allowed. No sequences of multiple dots are allowed. The
+ label strings may only consists of alphanumeric characters as well
+ as the dash and underscore. The maximum length of a machine name
+ is 64 characters.</para>
+
+ <para>A special machine with the name <literal>.host</literal>
+ refers to the running host system itself. This is useful for execution
+ operations or inspecting the host system as well. Not that
+ <command>machinectl list</command> will not show this special
+ machine unless the <option>--all</option> switch is specified.</para>
+
+ <para>Requirements on image names are less strict, however must be
+ valid UTF-8, must be suitable as file names (hence not be the
+ single or double dot, and not include a slash), and may not
+ contain control characters. Since many operations search for an
+ image by the name of a requested machine it is recommended to name
+ images in the same strict fashion as machines.</para>
+
+ <para>A special image with the name <literal>.host</literal>
+ refers to the image of the running host system. It is hence
+ conceptually maps to the special <literal>.host</literal> machine
+ name described above. Note that <command>machinectl
+ list-images</command> won't show this special image either, unless
+ <option>--all</option> is specified.</para>
+ </refsect1>
+
+ <refsect1>
<title>Files and Directories</title>
<para>Machine images are preferably stored in
@@ -872,6 +1014,17 @@
current directory.</para>
</example>
+ <example>
+ <title>Create a new shell session</title>
+
+ <programlisting># machinectl shell --uid=lennart</programlisting>
+
+ <para>This creates a new shell session on the local host, for
+ the user ID <literal>lennart</literal>, in a <citerefentry
+ project='die-net'><refentrytitle>su</refentrytitle><manvolnum>1</manvolnum></citerefentry>-like
+ fashion.</para>
+ </example>
+
</refsect1>
<refsect1>
diff --git a/man/systemctl.xml b/man/systemctl.xml
index 0b5282ba21..20d143741b 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -474,6 +474,18 @@
</varlistentry>
<varlistentry>
+ <term><option>--message=</option></term>
+
+ <listitem>
+ <para>When used with <command>halt</command>,
+ <command>poweroff</command>, <command>reboot</command> or
+ <command>kexec</command>, set a short message explaining the reason
+ for the operation. The message will be logged together with the
+ default shutdown message.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>--now</option></term>
<listitem>
diff --git a/man/systemd-run.xml b/man/systemd-run.xml
index 71b365c8eb..80db148702 100644
--- a/man/systemd-run.xml
+++ b/man/systemd-run.xml
@@ -69,38 +69,41 @@
<title>Description</title>
<para><command>systemd-run</command> may be used to create and
- start a transient <filename>.service</filename> or a transient
- <filename>.timer</filename> or a <filename>.scope</filename> unit
- and run the specified <replaceable>COMMAND</replaceable> in
- it.</para>
+ start a transient <filename>.service</filename> or
+ <filename>.scope</filename> unit and run the specified
+ <replaceable>COMMAND</replaceable> in it. It may also be used to
+ create and start transient <filename>.timer</filename>
+ units.</para>
<para>If a command is run as transient service unit, it will be
started and managed by the service manager like any other service,
- and thus show up in the output of <command>systemctl
+ and thus shows up in the output of <command>systemctl
list-units</command> like any other unit. It will run in a clean
- and detached execution environment. <command>systemd-run</command>
- will start the service asynchronously in the background and
- immediately return.</para>
-
- <para>If a command is run with timer options, transient timer unit
- also be created with transient service unit. But the transient
- timer unit is only started immediately. The transient service unit
- will be started when the transient timer is elapsed. If
- <option>--unit=</option> is specified with timer options, the
- <replaceable>COMMAND</replaceable> can be omitted. In this case,
- <command>systemd-run</command> assumes service unit is already
- loaded and creates transient timer unit only. To successfully
- create timer unit, already loaded service unit should be specified
- with <option>--unit=</option>. This transient timer unit can
- activate the existing service unit like any other timer.</para>
+ and detached execution environment, with the service manager as
+ its parent process. In this mode <command>systemd-run</command>
+ will start the service asynchronously in the background and return
+ after the command has begun execution.</para>
<para>If a command is run as transient scope unit, it will be
- started directly by <command>systemd-run</command> and thus
- inherit the execution environment of the caller. It is however
- managed by the service manager similar to normal services, and
- will also show up in the output of <command>systemctl
- list-units</command>. Execution in this case is synchronous, and
- execution will return only when the command finishes.</para>
+ started by <command>systemd-run</command> itself as parent process
+ and will thus inherit the execution environment of the
+ caller. However, the processes of the command are managed by the
+ service manager similar to normal services, and will show up in
+ the output of <command>systemctl list-units</command>. Execution
+ in this case is synchronous, and will return only when the command
+ finishes. This mode is enabled via the <option>--scope</option>
+ switch (see below). </para>
+
+ <para>If a command is run with timer options such as
+ <option>--on-calendar=</option> (see below), a transient timer
+ unit is created alongside the service unit for the specified
+ command. Only the transient timer unit is started immediately, the
+ transient service unit will be started when the transient timer
+ elapses. If the <option>--unit=</option> is specified, the
+ <replaceable>COMMAND</replaceable> may be omitted. In this case,
+ <command>systemd-run</command> only creates a
+ <filename>.timer</filename> unit that invokes the specified unit
+ when elapsing.</para>
</refsect1>
<refsect1>
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 8fd75d274e..7633948645 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -1,3 +1,4 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -911,10 +912,16 @@
<term><varname>UtmpIdentifier=</varname></term>
<listitem><para>Takes a four character identifier string for
- an utmp/wtmp entry for this service. This should only be set
- for services such as <command>getty</command> implementations
+ an <citerefentry
+ project='man-pages'><refentrytitle>utmp</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ and wtmp entry for this service. This should only be
+ set for services such as <command>getty</command>
+ implementations (such as <citerefentry
+ project='die-net'><refentrytitle>agetty</refentrytitle><manvolnum>8</manvolnum></citerefentry>)
where utmp/wtmp entries must be created and cleared before and
- after execution. If the configured string is longer than four
+ after execution, or for services that shall be executed as if
+ they were run by a <command>getty</command> process (see
+ below). If the configured string is longer than four
characters, it is truncated and the terminal four characters
are used. This setting interprets %I style string
replacements. This setting is unset by default, i.e. no
@@ -923,6 +930,34 @@
</varlistentry>
<varlistentry>
+ <term><varname>UtmpMode=</varname></term>
+
+ <listitem><para>Takes one of <literal>init</literal>,
+ <literal>login</literal> or <literal>user</literal>. If
+ <varname>UtmpIdentifier=</varname> is set, controls which
+ type of <citerefentry
+ project='man-pages'><refentrytitle>utmp</refentrytitle><manvolnum>5</manvolnum></citerefentry>/wtmp
+ entries for this service are generated. This setting has no
+ effect unless <varname>UtmpIdentifier=</varname> is set
+ too. If <literal>init</literal> is set, only an
+ <constant>INIT_PROCESS</constant> entry is generated and the
+ invoked process must implement a
+ <command>getty</command>-compatible utmp/wtmp logic. If
+ <literal>login</literal> is set, first an
+ <constant>INIT_PROCESS</constant> entry, followed by an
+ <constant>LOGIN_PROCESS</constant> entry is generated. In
+ this case the invoked process must implement a <citerefentry
+ project='die-net'><refentrytitle>login</refentrytitle><manvolnum>1</manvolnum></citerefentry>-compatible
+ utmp/wtmp logic. If <literal>user</literal> is set, first an
+ <constant>INIT_PROCESS</constant> entry, then a
+ <constant>LOGIN_PROCESS</constant> entry and finally an
+ <constant>USER_PROCESS</constant> entry is generated. In this
+ case the invoked process may be any process that is suitable
+ to be run as session leader. Defaults to
+ <literal>init</literal>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>SELinuxContext=</varname></term>
<listitem><para>Set the SELinux security context of the
diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml
index a78ceb1252..2680627a78 100644
--- a/man/systemd.netdev.xml
+++ b/man/systemd.netdev.xml
@@ -986,6 +986,20 @@ Name=veth-peer</programlisting>
</example>
<example>
+ <title>/etc/systemd/network/bond.netdev</title>
+ <programlisting>[NetDev]
+Name=bond1
+Kind=bond
+
+[Bond]
+Mode=802.3ad
+TransmitHashPolicy=layer3+4
+MIIMonitorSec=1s
+LACPTransmitRate=fast
+</programlisting>
+ </example>
+
+ <example>
<title>/etc/systemd/network/dummy.netdev</title>
<programlisting>[NetDev]
Name=dummy-test
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index e44491cc2e..e8a164d22d 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -828,6 +828,18 @@ Name=em1
[Network]
Tunnel=vti-tun</programlisting>
</example>
+
+ <example>
+ <title>/etc/systemd/network/bond.network</title>
+
+ <programlisting>[Match]
+Name=bond1
+
+[Network]
+DHCP=yes
+</programlisting>
+ </example>
+
</refsect1>
<refsect1>
diff --git a/man/systemd.special.xml b/man/systemd.special.xml
index c90b0366c1..e4700d950b 100644
--- a/man/systemd.special.xml
+++ b/man/systemd.special.xml
@@ -852,7 +852,7 @@
<varlistentry>
<term><filename>system.slice</filename></term>
<listitem>
- <para>By default, all services services started by
+ <para>By default, all system services started by
<command>systemd</command> are found in this slice.</para>
</listitem>
</varlistentry>
diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c
index d901a5e82b..1b816fb77a 100644
--- a/src/basic/hostname-util.c
+++ b/src/basic/hostname-util.c
@@ -62,16 +62,19 @@ static bool hostname_valid_char(char c) {
}
/**
- * Check if s looks like a valid host name or fqdn. This does not do
+ * Check if s looks like a valid host name or FQDN. This does not do
* full DNS validation, but only checks if the name is composed of
* allowed characters and the length is not above the maximum allowed
* by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if
- * relax is true and at least two components are present in the name.
+ * allow_trailing_dot is true and at least two components are present
+ * in the name. Note that due to the restricted charset and length
+ * this call is substantially more conservative than
+ * dns_domain_is_valid().
*/
-bool hostname_is_valid(const char *s, bool relax) {
+bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
+ unsigned n_dots = 0;
const char *p;
bool dot;
- unsigned dots = 0;
if (isempty(s))
return false;
@@ -87,7 +90,7 @@ bool hostname_is_valid(const char *s, bool relax) {
return false;
dot = true;
- dots ++;
+ n_dots ++;
} else {
if (!hostname_valid_char(*p))
return false;
@@ -96,10 +99,12 @@ bool hostname_is_valid(const char *s, bool relax) {
}
}
- if (dot && (dots < 2 || !relax))
+ if (dot && (n_dots < 2 || !allow_trailing_dot))
return false;
- if (p-s > HOST_NAME_MAX)
+ if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on
+ * Linux, but DNS allows domain names
+ * up to 255 characters */
return false;
return true;
@@ -151,6 +156,17 @@ bool is_localhost(const char *hostname) {
endswith_no_case(hostname, ".localdomain.");
}
+bool is_gateway_hostname(const char *hostname) {
+ assert(hostname);
+
+ /* This tries to identify the valid syntaxes for the our
+ * synthetic "gateway" host. */
+
+ return
+ strcaseeq(hostname, "gateway") ||
+ strcaseeq(hostname, "gateway.");
+}
+
int sethostname_idempotent(const char *s) {
char buf[HOST_NAME_MAX + 1] = {};
diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h
index 6f2b5b66ff..d4f5bfe45e 100644
--- a/src/basic/hostname-util.h
+++ b/src/basic/hostname-util.h
@@ -29,10 +29,13 @@ bool hostname_is_set(void);
char* gethostname_malloc(void);
-bool hostname_is_valid(const char *s, bool relax) _pure_;
+bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_;
char* hostname_cleanup(char *s);
+#define machine_name_is_valid(s) hostname_is_valid(s, false)
+
bool is_localhost(const char *hostname);
+bool is_gateway_hostname(const char *hostname);
int sethostname_idempotent(const char *s);
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index 12f1b193be..e278196c90 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -88,6 +88,32 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
return ts;
}
+dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) {
+ int64_t delta;
+
+ if (u == USEC_INFINITY) {
+ ts->realtime = ts->monotonic = USEC_INFINITY;
+ return ts;
+ }
+ ts->realtime = now(CLOCK_REALTIME);
+ ts->monotonic = now(CLOCK_MONOTONIC);
+
+ delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u;
+
+ if ((int64_t) ts->realtime > delta)
+ ts->realtime -= delta;
+ else
+ ts->realtime = 0;
+
+ if ((int64_t) ts->monotonic > delta)
+ ts->monotonic -= delta;
+ else
+ ts->monotonic = 0;
+
+ return ts;
+}
+
+
usec_t timespec_load(const struct timespec *ts) {
assert(ts);
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
index 7a64d454a0..2aba042217 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -74,6 +74,7 @@ usec_t now(clockid_t clock);
dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
+dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u);
static inline bool dual_timestamp_is_set(dual_timestamp *ts) {
return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
diff --git a/src/basic/util.c b/src/basic/util.c
index 9571f0ab12..f752595ca1 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -3006,21 +3006,6 @@ char* strshorten(char *s, size_t l) {
return s;
}
-bool machine_name_is_valid(const char *s) {
-
- if (!hostname_is_valid(s, false))
- return false;
-
- /* Machine names should be useful hostnames, but also be
- * useful in unit names, hence we enforce a stricter length
- * limitation. */
-
- if (strlen(s) > 64)
- return false;
-
- return true;
-}
-
int pipe_eof(int fd) {
struct pollfd pollfd = {
.fd = fd,
@@ -4928,6 +4913,9 @@ int container_get_leader(const char *machine, pid_t *pid) {
assert(machine);
assert(pid);
+ if (!machine_name_is_valid(machine))
+ return -EINVAL;
+
p = strjoina("/run/systemd/machines/", machine);
r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
if (r == -ENOENT)
diff --git a/src/basic/util.h b/src/basic/util.h
index 8f21d56ed8..1ead7b5419 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -394,8 +394,6 @@ bool nulstr_contains(const char*nulstr, const char *needle);
bool plymouth_running(void);
-bool machine_name_is_valid(const char *s) _pure_;
-
char* strshorten(char *s, size_t l);
int symlink_idempotent(const char *from, const char *to);
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index a9f7971cde..3c98a0b186 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -46,6 +46,8 @@ BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutp
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput);
+static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode);
+
static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_home, protect_home, ProtectHome);
static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_system, protect_system, ProtectSystem);
@@ -653,6 +655,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("ProtectSystem", "s", bus_property_get_protect_system, offsetof(ExecContext, protect_system), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SameProcessGroup", "b", bus_property_get_bool, offsetof(ExecContext, same_pgrp), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("UtmpIdentifier", "s", NULL, offsetof(ExecContext, utmp_id), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("UtmpMode", "s", property_get_exec_utmp_mode, offsetof(ExecContext, utmp_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SELinuxContext", "(bs)", property_get_selinux_context, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("AppArmorProfile", "(bs)", property_get_apparmor_profile, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SmackProcessLabel", "(bs)", property_get_smack_process_label, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -932,6 +935,107 @@ int bus_exec_context_set_transient_property(
return 1;
+ } else if (streq(name, "IgnoreSIGPIPE")) {
+ int b;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ c->ignore_sigpipe = b;
+
+ unit_write_drop_in_private_format(u, mode, name, "IgnoreSIGPIPE=%s\n", yes_no(b));
+ }
+
+ return 1;
+
+ } else if (streq(name, "TTYVHangup")) {
+ int b;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ c->tty_vhangup = b;
+
+ unit_write_drop_in_private_format(u, mode, name, "TTYVHangup=%s\n", yes_no(b));
+ }
+
+ return 1;
+
+ } else if (streq(name, "TTYReset")) {
+ int b;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ c->tty_reset = b;
+
+ unit_write_drop_in_private_format(u, mode, name, "TTYReset=%s\n", yes_no(b));
+ }
+
+ return 1;
+
+ } else if (streq(name, "UtmpIdentifier")) {
+ const char *id;
+
+ r = sd_bus_message_read(message, "s", &id);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ if (isempty(id))
+ c->utmp_id = mfree(c->utmp_id);
+ else if (free_and_strdup(&c->utmp_id, id) < 0)
+ return -ENOMEM;
+
+ unit_write_drop_in_private_format(u, mode, name, "UtmpIdentifier=%s\n", strempty(id));
+ }
+
+ return 1;
+
+ } else if (streq(name, "UtmpMode")) {
+ const char *s;
+ ExecUtmpMode m;
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+
+ m = exec_utmp_mode_from_string(s);
+ if (m < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid utmp mode");
+
+ if (mode != UNIT_CHECK) {
+ c->utmp_mode = m;
+
+ unit_write_drop_in_private_format(u, mode, name, "UtmpMode=%s\n", exec_utmp_mode_to_string(m));
+ }
+
+ return 1;
+
+ } else if (streq(name, "PAMName")) {
+ const char *n;
+
+ r = sd_bus_message_read(message, "s", &n);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ if (isempty(n))
+ c->pam_name = mfree(c->pam_name);
+ else if (free_and_strdup(&c->pam_name, n) < 0)
+ return -ENOMEM;
+
+ unit_write_drop_in_private_format(u, mode, name, "PAMName=%s\n", strempty(n));
+ }
+
+ return 1;
+
} else if (streq(name, "Environment")) {
_cleanup_strv_free_ char **l = NULL;
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 1892725f91..0a9effda71 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -697,10 +697,40 @@ static int property_get_cpu_usage(
return sd_bus_message_append(reply, "t", ns);
}
+static int property_get_cgroup(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Unit *u = userdata;
+ const char *t;
+
+ assert(bus);
+ assert(reply);
+ assert(u);
+
+ /* Three cases: a) u->cgroup_path is NULL, in which case the
+ * unit has no control group, which we report as the empty
+ * string. b) u->cgroup_path is the empty string, which
+ * indicates the root cgroup, which we report as "/". c) all
+ * other cases we report as-is. */
+
+ if (u->cgroup_path)
+ t = isempty(u->cgroup_path) ? "/" : u->cgroup_path;
+ else
+ t = "";
+
+ return sd_bus_message_append(reply, "s", t);
+}
+
const sd_bus_vtable bus_unit_cgroup_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0),
- SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Unit, cgroup_path), 0),
+ SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0),
SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0),
SD_BUS_VTABLE_END
diff --git a/src/core/execute.c b/src/core/execute.c
index 3820165241..28eeeaad18 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -31,6 +31,7 @@
#include <grp.h>
#include <poll.h>
#include <glob.h>
+#include <utmpx.h>
#include <sys/personality.h>
#ifdef HAVE_PAM
@@ -1504,7 +1505,11 @@ static int exec_child(
}
if (context->utmp_id)
- utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path);
+ utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path,
+ context->utmp_mode == EXEC_UTMP_INIT ? INIT_PROCESS :
+ context->utmp_mode == EXEC_UTMP_LOGIN ? LOGIN_PROCESS :
+ USER_PROCESS,
+ username ? "root" : context->user);
if (context->user && is_terminal_input(context->std_input)) {
r = chown_terminal(STDIN_FILENO, uid);
@@ -2968,3 +2973,11 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
+
+static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
+ [EXEC_UTMP_INIT] = "init",
+ [EXEC_UTMP_LOGIN] = "login",
+ [EXEC_UTMP_USER] = "user",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
diff --git a/src/core/execute.h b/src/core/execute.h
index f5d5c1dee7..8d14fe23d0 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -38,6 +38,14 @@ typedef struct ExecParameters ExecParameters;
#include "namespace.h"
#include "bus-endpoint.h"
+typedef enum ExecUtmpMode {
+ EXEC_UTMP_INIT,
+ EXEC_UTMP_LOGIN,
+ EXEC_UTMP_USER,
+ _EXEC_UTMP_MODE_MAX,
+ _EXEC_UTMP_MODE_INVALID = -1
+} ExecUtmpMode;
+
typedef enum ExecInput {
EXEC_INPUT_NULL,
EXEC_INPUT_TTY,
@@ -131,6 +139,7 @@ struct ExecContext {
char *pam_name;
char *utmp_id;
+ ExecUtmpMode utmp_mode;
bool selinux_context_ignore;
char *selinux_context;
@@ -265,3 +274,6 @@ ExecOutput exec_output_from_string(const char *s) _pure_;
const char* exec_input_to_string(ExecInput i) _const_;
ExecInput exec_input_from_string(const char *s) _pure_;
+
+const char* exec_utmp_mode_to_string(ExecUtmpMode i) _const_;
+ExecUtmpMode exec_utmp_mode_from_string(const char *s) _pure_;
diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c
index fc6d2f4acb..2068ffd69b 100644
--- a/src/core/kmod-setup.c
+++ b/src/core/kmod-setup.c
@@ -112,7 +112,7 @@ int kmod_setup(void) {
r = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL);
if (r == 0)
- log_info("Inserted module '%s'", kmod_module_get_name(mod));
+ log_debug("Inserted module '%s'", kmod_module_get_name(mod));
else if (r == KMOD_PROBE_APPLY_BLACKLIST)
log_info("Module '%s' is blacklisted", kmod_module_get_name(mod));
else {
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index aae81c80cb..60b97722db 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -91,6 +91,7 @@ m4_ifdef(`HAVE_PAM',
`$1.PAMName, config_parse_warn_compat, DISABLED_CONFIGURATION, 0')
$1.IgnoreSIGPIPE, config_parse_bool, 0, offsetof($1, exec_context.ignore_sigpipe)
$1.UtmpIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.utmp_id)
+$1.UtmpMode, config_parse_exec_utmp_mode, 0, offsetof($1, exec_context.utmp_mode)
m4_ifdef(`HAVE_SELINUX',
`$1.SELinuxContext, config_parse_exec_selinux_context, 0, offsetof($1, exec_context)',
`$1.SELinuxContext, config_parse_warn_compat, DISABLED_CONFIGURATION, 0')
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index e1e3a5ffb7..b3bf8bdb40 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -1142,6 +1142,8 @@ int config_parse_sysv_priority(const char *unit,
}
#endif
+DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
+
DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
int config_parse_kill_signal(const char *unit,
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index ce10d03c3f..fcca2b0221 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -104,6 +104,7 @@ int config_parse_cpu_quota(const char *unit, const char *filename, unsigned line
int config_parse_protect_home(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_protect_system(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_bus_name(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_utmp_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
diff --git a/src/import/export.c b/src/import/export.c
index ec7dbe210a..b88d71fec6 100644
--- a/src/import/export.c
+++ b/src/import/export.c
@@ -24,6 +24,7 @@
#include "sd-event.h"
#include "event-util.h"
#include "signal-util.h"
+#include "hostname-util.h"
#include "verbs.h"
#include "build.h"
#include "machine-image.h"
diff --git a/src/import/import-raw.c b/src/import/import-raw.c
index 43cd413042..a27e81bbd4 100644
--- a/src/import/import-raw.c
+++ b/src/import/import-raw.c
@@ -26,6 +26,7 @@
#include "util.h"
#include "path-util.h"
#include "btrfs-util.h"
+#include "hostname-util.h"
#include "copy.h"
#include "mkdir.h"
#include "rm-rf.h"
diff --git a/src/import/import-tar.c b/src/import/import-tar.c
index 2bf0b0680c..7ffe83cc33 100644
--- a/src/import/import-tar.c
+++ b/src/import/import-tar.c
@@ -26,6 +26,7 @@
#include "util.h"
#include "path-util.h"
#include "btrfs-util.h"
+#include "hostname-util.h"
#include "copy.h"
#include "mkdir.h"
#include "rm-rf.h"
diff --git a/src/import/import.c b/src/import/import.c
index b7772390e9..929a840298 100644
--- a/src/import/import.c
+++ b/src/import/import.c
@@ -26,6 +26,7 @@
#include "verbs.h"
#include "build.h"
#include "signal-util.h"
+#include "hostname-util.h"
#include "machine-image.h"
#include "import-util.h"
#include "import-tar.h"
diff --git a/src/import/importd.c b/src/import/importd.c
index dd314f5b00..8b508eaeec 100644
--- a/src/import/importd.c
+++ b/src/import/importd.c
@@ -35,6 +35,7 @@
#include "import-util.h"
#include "process-util.h"
#include "signal-util.h"
+#include "hostname-util.h"
typedef struct Transfer Transfer;
typedef struct Manager Manager;
diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c
index 5bfaf012c0..d0e0faa261 100644
--- a/src/import/pull-raw.c
+++ b/src/import/pull-raw.c
@@ -33,6 +33,7 @@
#include "mkdir.h"
#include "rm-rf.h"
#include "path-util.h"
+#include "hostname-util.h"
#include "import-util.h"
#include "import-common.h"
#include "curl-util.h"
diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c
index 71b8908a58..d38a2158c4 100644
--- a/src/import/pull-tar.c
+++ b/src/import/pull-tar.c
@@ -32,13 +32,14 @@
#include "mkdir.h"
#include "rm-rf.h"
#include "path-util.h"
+#include "process-util.h"
+#include "hostname-util.h"
#include "import-util.h"
#include "import-common.h"
#include "curl-util.h"
#include "pull-job.h"
#include "pull-common.h"
#include "pull-tar.h"
-#include "process-util.h"
typedef enum TarProgress {
TAR_DOWNLOADING,
diff --git a/src/import/pull.c b/src/import/pull.c
index ca7be6be85..e13cd6af97 100644
--- a/src/import/pull.c
+++ b/src/import/pull.c
@@ -26,6 +26,7 @@
#include "verbs.h"
#include "build.h"
#include "signal-util.h"
+#include "hostname-util.h"
#include "machine-image.h"
#include "import-util.h"
#include "pull-tar.h"
diff --git a/src/journal/coredumpctl.c b/src/journal/coredumpctl.c
index 098f62af50..644ba91b0d 100644
--- a/src/journal/coredumpctl.c
+++ b/src/journal/coredumpctl.c
@@ -49,6 +49,7 @@ static enum {
ACTION_GDB,
} arg_action = ACTION_LIST;
static const char* arg_field = NULL;
+static const char *arg_directory = NULL;
static int arg_no_pager = false;
static int arg_no_legend = false;
static int arg_one = false;
@@ -131,6 +132,7 @@ static void help(void) {
" -1 Show information about most recent entry only\n"
" -F --field=FIELD List all values a certain field takes\n"
" -o --output=FILE Write output to FILE\n\n"
+ " -D --directory=DIR Use journal files from directory\n\n"
"Commands:\n"
" list [MATCHES...] List available coredumps (default)\n"
@@ -156,13 +158,14 @@ static int parse_argv(int argc, char *argv[], Set *matches) {
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "output", required_argument, NULL, 'o' },
{ "field", required_argument, NULL, 'F' },
+ { "directory", required_argument, NULL, 'D' },
{}
};
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "ho:F:1", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "ho:F:1D:", options, NULL)) >= 0)
switch(c) {
case 'h':
@@ -208,6 +211,10 @@ static int parse_argv(int argc, char *argv[], Set *matches) {
arg_one = true;
break;
+ case 'D':
+ arg_directory = optarg;
+ break;
+
case '?':
return -EINVAL;
@@ -808,10 +815,18 @@ int main(int argc, char *argv[]) {
sigbus_install();
- r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
- if (r < 0) {
- log_error_errno(r, "Failed to open journal: %m");
- goto end;
+ if (arg_directory) {
+ r = sd_journal_open_directory(&j, arg_directory, 0);
+ if (r < 0) {
+ log_error_errno(r, "Failed to open journals in directory: %s: %m", arg_directory);
+ goto end;
+ }
+ } else {
+ r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
+ if (r < 0) {
+ log_error_errno(r, "Failed to open journal: %m");
+ goto end;
+ }
}
/* We want full data, nothing truncated. */
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index bfd1901e16..13fa9b52fc 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -43,6 +43,7 @@
#include "replace-var.h"
#include "fileio.h"
#include "formats-util.h"
+#include "hostname-util.h"
#define JOURNAL_FILES_MAX 7168
diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h
index 4f54ad89a6..83e8192f58 100644
--- a/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libsystemd-network/dhcp6-internal.h
@@ -5,7 +5,7 @@
/***
This file is part of systemd.
- Copyright (C) 2014 Intel Corporation. All rights reserved.
+ Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -68,6 +68,11 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
size_t *optlen, uint8_t **optvalue);
int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
DHCP6IA *ia);
+int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
+ struct in6_addr **addrs, size_t count,
+ size_t *allocated);
+int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen,
+ char ***str_arr);
int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address);
int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h
index 109e0f4f21..037f580eb6 100644
--- a/src/libsystemd-network/dhcp6-lease-internal.h
+++ b/src/libsystemd-network/dhcp6-lease-internal.h
@@ -6,7 +6,7 @@
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
- Copyright (C) 2014 Intel Corporation. All rights reserved.
+ Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -40,6 +40,17 @@ struct sd_dhcp6_lease {
DHCP6IA ia;
DHCP6Address *addr_iter;
+
+ struct in6_addr *dns;
+ size_t dns_count;
+ size_t dns_allocated;
+ char **domains;
+ size_t domains_count;
+ struct in6_addr *ntp;
+ size_t ntp_count;
+ size_t ntp_allocated;
+ char **ntp_fqdn;
+ size_t ntp_fqdn_count;
};
int dhcp6_lease_clear_timers(DHCP6IA *ia);
@@ -56,6 +67,13 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit);
int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
+int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen);
+int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
+ size_t optlen);
+int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen);
+int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval,
+ size_t optlen) ;
+
int dhcp6_lease_new(sd_dhcp6_lease **ret);
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_lease*, sd_dhcp6_lease_unref);
diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c
index ea863f45e4..6da7ea7e27 100644
--- a/src/libsystemd-network/dhcp6-option.c
+++ b/src/libsystemd-network/dhcp6-option.c
@@ -3,7 +3,7 @@
/***
This file is part of systemd.
- Copyright (C) 2014 Intel Corporation. All rights reserved.
+ Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -26,9 +26,11 @@
#include "sparse-endian.h"
#include "unaligned.h"
#include "util.h"
+#include "strv.h"
#include "dhcp6-internal.h"
#include "dhcp6-protocol.h"
+#include "dns-domain.h"
#define DHCP6_OPTION_IA_NA_LEN 12
#define DHCP6_OPTION_IA_TA_LEN 4
@@ -317,3 +319,101 @@ error:
return r;
}
+
+int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
+ struct in6_addr **addrs, size_t count,
+ size_t *allocated) {
+
+ if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0)
+ return -EINVAL;
+
+ if (!GREEDY_REALLOC(*addrs, *allocated,
+ count * sizeof(struct in6_addr) + optlen))
+ return -ENOMEM;
+
+ memcpy(*addrs + count, optval, optlen);
+
+ count += optlen / sizeof(struct in6_addr);
+
+ return count;
+}
+
+int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen,
+ char ***str_arr)
+{
+ size_t pos = 0, idx = 0;
+ _cleanup_free_ char **names = NULL;
+ int r;
+
+ assert_return(optlen > 1, -ENODATA);
+ assert_return(optval[optlen] == '\0', -EINVAL);
+
+ while (pos < optlen) {
+ _cleanup_free_ char *ret = NULL;
+ size_t n = 0, allocated = 0;
+ bool first = true;
+
+ for (;;) {
+ uint8_t c;
+
+ c = optval[pos++];
+
+ if (c == 0)
+ /* End of name */
+ break;
+ else if (c <= 63) {
+ _cleanup_free_ char *t = NULL;
+ const char *label;
+
+ /* Literal label */
+ label = (const char *)&optval[pos];
+ pos += c;
+ if (pos > optlen)
+ return -EMSGSIZE;
+
+ r = dns_label_escape(label, c, &t);
+ if (r < 0)
+ goto fail;
+
+ if (!GREEDY_REALLOC0(ret, allocated, n + !first + strlen(t) + 1)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (!first)
+ ret[n++] = '.';
+ else
+ first = false;
+
+ memcpy(ret + n, t, r);
+ n += r;
+ continue;
+ } else {
+ r = -EBADMSG;
+ goto fail;
+ }
+ }
+
+ if (!GREEDY_REALLOC(ret, allocated, n + 1)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ ret[n] = 0;
+
+ r = strv_extend(&names, ret);
+ if (r < 0)
+ goto fail;
+
+ ret = NULL;
+ idx++;
+ }
+
+ *str_arr = names;
+ names = NULL;
+
+ return idx;
+
+fail:
+ return r;
+}
diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h
index 3e0f339237..b3a28f88b4 100644
--- a/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/libsystemd-network/dhcp6-protocol.h
@@ -123,7 +123,7 @@ enum {
DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */
DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */
- DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075 */
+ DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */
/* option code 35 is unassigned */
@@ -134,6 +134,12 @@ enum {
};
enum {
+ DHCP6_NTP_SUBOPTION_SRV_ADDR = 1,
+ DHCP6_NTP_SUBOPTION_MC_ADDR = 2,
+ DHCP6_NTP_SUBOPTION_SRV_FQDN = 3,
+};
+
+enum {
DHCP6_STATUS_SUCCESS = 0,
DHCP6_STATUS_UNSPEC_FAIL = 1,
DHCP6_STATUS_NO_ADDRS_AVAIL = 2,
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index 3d78bf8b35..d8357c687e 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -32,6 +32,7 @@
#include "conf-parser.h"
#include "condition.h"
#include "network-internal.h"
+#include "sd-icmp6-nd.h"
const char *net_get_name(struct udev_device *device) {
const char *name, *field;
@@ -384,6 +385,20 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) {
return size;
}
+void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses,
+ size_t size) {
+ unsigned i;
+
+ assert(f);
+ assert(addresses);
+ assert(size);
+
+ for (i = 0; i < size; i++)
+ fprintf(f, SD_ICMP6_ADDRESS_FORMAT_STR"%s",
+ SD_ICMP6_ADDRESS_FORMAT_VAL(addresses[i]),
+ (i < (size - 1)) ? " ": "");
+}
+
int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
_cleanup_free_ struct in6_addr *addresses = NULL;
int size = 0;
diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h
index 7aaecbb10d..dca82646ce 100644
--- a/src/libsystemd-network/network-internal.h
+++ b/src/libsystemd-network/network-internal.h
@@ -67,6 +67,8 @@ const char *net_get_name(struct udev_device *device);
void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size);
int deserialize_in_addrs(struct in_addr **addresses, const char *string);
+void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses,
+ size_t size);
int deserialize_in6_addrs(struct in6_addr **addresses, const char *string);
/* don't include "dhcp-lease-internal.h" as it causes conflicts between netinet/ip.h and linux/ip.h */
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index e2f5862851..bc17c6adc5 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -3,7 +3,7 @@
/***
This file is part of systemd.
- Copyright (C) 2014 Intel Corporation. All rights reserved.
+ Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -73,6 +73,7 @@ static const uint16_t default_req_opts[] = {
DHCP6_OPTION_DNS_SERVERS,
DHCP6_OPTION_DOMAIN_LIST,
DHCP6_OPTION_NTP_SERVER,
+ DHCP6_OPTION_SNTP_SERVERS,
};
const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
@@ -272,6 +273,11 @@ static void client_notify(sd_dhcp6_client *client, int event) {
static int client_reset(sd_dhcp6_client *client) {
assert_return(client, -EINVAL);
+ if (client->lease) {
+ dhcp6_lease_clear_timers(&client->lease->ia);
+ client->lease = sd_dhcp6_lease_unref(client->lease);
+ }
+
client->receive_message =
sd_event_source_unref(client->receive_message);
@@ -748,7 +754,36 @@ static int client_parse_message(sd_dhcp6_client *client,
return r;
break;
+
+ case DHCP6_OPTION_DNS_SERVERS:
+ r = dhcp6_lease_set_dns(lease, optval, optlen);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case DHCP6_OPTION_DOMAIN_LIST:
+ r = dhcp6_lease_set_domains(lease, optval, optlen);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case DHCP6_OPTION_NTP_SERVER:
+ r = dhcp6_lease_set_ntp(lease, optval, optlen);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case DHCP6_OPTION_SNTP_SERVERS:
+ r = dhcp6_lease_set_sntp(lease, optval, optlen);
+ if (r < 0)
+ return r;
+
+ break;
}
+
}
if (r == -ENOMSG)
@@ -802,10 +837,8 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
client->lease = sd_dhcp6_lease_unref(client->lease);
}
- if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
- client->lease = lease;
- lease = NULL;
- }
+ client->lease = lease;
+ lease = NULL;
return DHCP6_STATE_BOUND;
}
diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c
index 2442269a3f..f0494b3c91 100644
--- a/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libsystemd-network/sd-dhcp6-lease.c
@@ -1,8 +1,10 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/***
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
- Copyright (C) 2014 Intel Corporation. All rights reserved.
+ Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -20,9 +22,11 @@
#include <errno.h>
+#include "strv.h"
#include "util.h"
#include "dhcp6-lease-internal.h"
+#include "dhcp6-protocol.h"
int dhcp6_lease_clear_timers(DHCP6IA *ia) {
assert_return(ia, -EINVAL);
@@ -173,6 +177,189 @@ void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
lease->addr_iter = lease->ia.addresses;
}
+int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
+ int r;
+
+ assert_return(lease, -EINVAL);
+ assert_return(optval, -EINVAL);
+
+ if (!optlen)
+ return 0;
+
+ r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns,
+ lease->dns_count,
+ &lease->dns_allocated);
+ if (r < 0) {
+ log_dhcp6_client(client, "Invalid DNS server option: %s",
+ strerror(-r));
+
+ return r;
+ }
+
+ lease->dns_count = r;
+
+ return 0;
+}
+
+int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs) {
+ assert_return(lease, -EINVAL);
+ assert_return(addrs, -EINVAL);
+
+ if (lease->dns_count) {
+ *addrs = lease->dns;
+ return lease->dns_count;
+ }
+
+ return -ENOENT;
+}
+
+int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
+ size_t optlen) {
+ int r;
+ char **domains;
+
+ assert_return(lease, -EINVAL);
+ assert_return(optval, -EINVAL);
+
+ if (!optlen)
+ return 0;
+
+ r = dhcp6_option_parse_domainname(optval, optlen, &domains);
+ if (r < 0)
+ return 0;
+
+ free(lease->domains);
+ lease->domains = domains;
+ lease->domains_count = r;
+
+ return r;
+}
+
+int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) {
+ assert_return(lease, -EINVAL);
+ assert_return(domains, -EINVAL);
+
+ if (lease->domains_count) {
+ *domains = lease->domains;
+ return lease->domains_count;
+ }
+
+ return -ENOENT;
+}
+
+int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen)
+{
+ int r;
+ uint16_t subopt;
+ size_t sublen;
+ uint8_t *subval;
+
+ assert_return(lease, -EINVAL);
+ assert_return(optval, -EINVAL);
+
+ free(lease->ntp);
+ lease->ntp_count = 0;
+ lease->ntp_allocated = 0;
+
+ while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen,
+ &subval)) >= 0) {
+ int s;
+ char **servers;
+
+ switch(subopt) {
+ case DHCP6_NTP_SUBOPTION_SRV_ADDR:
+ case DHCP6_NTP_SUBOPTION_MC_ADDR:
+ if (sublen != 16)
+ return 0;
+
+ s = dhcp6_option_parse_ip6addrs(subval, sublen,
+ &lease->ntp,
+ lease->ntp_count,
+ &lease->ntp_allocated);
+ if (s < 0)
+ return s;
+
+ lease->ntp_count = s;
+
+ break;
+
+ case DHCP6_NTP_SUBOPTION_SRV_FQDN:
+ r = dhcp6_option_parse_domainname(subval, sublen,
+ &servers);
+ if (r < 0)
+ return 0;
+
+ lease->ntp_fqdn = strv_free(lease->ntp_fqdn);
+ lease->ntp_fqdn = servers;
+ lease->ntp_fqdn_count = r;
+
+ break;
+ }
+ }
+
+ if (r != -ENOMSG)
+ return r;
+
+ return 0;
+}
+
+int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
+ int r;
+
+ assert_return(lease, -EINVAL);
+ assert_return(optval, -EINVAL);
+
+ if (!optlen)
+ return 0;
+
+ if (lease->ntp || lease->ntp_fqdn) {
+ log_dhcp6_client(client, "NTP information already provided");
+
+ return 0;
+ }
+
+ log_dhcp6_client(client, "Using deprecated SNTP information");
+
+ r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp,
+ lease->ntp_count,
+ &lease->ntp_allocated);
+ if (r < 0) {
+ log_dhcp6_client(client, "Invalid SNTP server option: %s",
+ strerror(-r));
+
+ return r;
+ }
+
+ lease->ntp_count = r;
+
+ return 0;
+}
+
+int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease,
+ struct in6_addr **addrs) {
+ assert_return(lease, -EINVAL);
+ assert_return(addrs, -EINVAL);
+
+ if (lease->ntp_count) {
+ *addrs = lease->ntp;
+ return lease->ntp_count;
+ }
+
+ return -ENOENT;
+}
+
+int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) {
+ assert_return(lease, -EINVAL);
+ assert_return(ntp_fqdn, -EINVAL);
+
+ if (lease->ntp_fqdn_count) {
+ *ntp_fqdn = lease->ntp_fqdn;
+ return lease->ntp_fqdn_count;
+ }
+
+ return -ENOENT;
+}
+
sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) {
if (lease)
assert_se(REFCNT_INC(lease->n_ref) >= 2);
@@ -185,6 +372,13 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) {
free(lease->serverid);
dhcp6_lease_free_ia(&lease->ia);
+ free(lease->dns);
+
+ lease->domains = strv_free(lease->domains);
+
+ free(lease->ntp);
+
+ lease->ntp_fqdn = strv_free(lease->ntp_fqdn);
free(lease);
}
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index 761854714b..6e62262443 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -73,7 +73,7 @@ static int test_client_basic(sd_event *e) {
assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_CLIENTID) == -EINVAL);
assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EEXIST);
assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_NTP_SERVER) == -EEXIST);
- assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_SNTP_SERVERS) == 0);
+ assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_SNTP_SERVERS) == -EEXIST);
assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DOMAIN_LIST) == -EEXIST);
assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL);
@@ -216,6 +216,8 @@ static int test_advertise_option(sd_event *e) {
uint32_t lt_pref, lt_valid;
int r;
bool opt_clientid = false;
+ struct in6_addr *addrs;
+ char **domains;
if (verbose)
printf("* %s\n", __FUNCTION__);
@@ -276,6 +278,24 @@ static int test_advertise_option(sd_event *e) {
break;
+ case DHCP6_OPTION_DNS_SERVERS:
+ assert_se(optlen == 16);
+ assert_se(dhcp6_lease_set_dns(lease, optval,
+ optlen) >= 0);
+ break;
+
+ case DHCP6_OPTION_DOMAIN_LIST:
+ assert_se(optlen == 11);
+ assert_se(dhcp6_lease_set_domains(lease, optval,
+ optlen) >= 0);
+ break;
+
+ case DHCP6_OPTION_SNTP_SERVERS:
+ assert_se(optlen == 16);
+ assert_se(dhcp6_lease_set_sntp(lease, optval,
+ optlen) >= 0);
+ break;
+
default:
break;
}
@@ -315,6 +335,19 @@ static int test_advertise_option(sd_event *e) {
assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0);
assert_se(preference == 0);
+ r = sd_dhcp6_lease_get_dns(lease, &addrs);
+ assert_se(r == 1);
+ assert_se(!memcmp(addrs, &msg_advertise[124], r * 16));
+
+ r = sd_dhcp6_lease_get_domains(lease, &domains);
+ assert_se(r == 1);
+ assert_se(!strcmp("lab.intra", domains[0]));
+ assert_se(domains[1] == NULL);
+
+ r = sd_dhcp6_lease_get_ntp_addrs(lease, &addrs);
+ assert_se(r == 1);
+ assert_se(!memcmp(addrs, &msg_advertise[159], r * 16));
+
return 0;
}
@@ -339,10 +372,25 @@ int detect_virtualization(const char **id) {
static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
void *userdata) {
sd_event *e = userdata;
+ sd_dhcp6_lease *lease;
+ struct in6_addr *addrs;
+ char **domains;
assert_se(e);
assert_se(event == DHCP6_EVENT_IP_ACQUIRE);
+ assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0);
+
+ assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1);
+ assert_se(!strcmp("lab.intra", domains[0]));
+ assert_se(domains[1] == NULL);
+
+ assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1);
+ assert_se(!memcmp(addrs, &msg_advertise[124], 16));
+
+ assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1);
+ assert_se(!memcmp(addrs, &msg_advertise[159], 16));
+
assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EBUSY);
if (verbose)
@@ -524,10 +572,25 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
static void test_client_information_cb(sd_dhcp6_client *client, int event,
void *userdata) {
sd_event *e = userdata;
+ sd_dhcp6_lease *lease;
+ struct in6_addr *addrs;
+ char **domains;
assert_se(e);
assert_se(event == DHCP6_EVENT_INFORMATION_REQUEST);
+ assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0);
+
+ assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1);
+ assert_se(!strcmp("lab.intra", domains[0]));
+ assert_se(domains[1] == NULL);
+
+ assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1);
+ assert_se(!memcmp(addrs, &msg_advertise[124], 16));
+
+ assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1);
+ assert_se(!memcmp(addrs, &msg_advertise[159], 16));
+
if (verbose)
printf(" got DHCPv6 event %d\n", event);
diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c
index 92b12f2ae8..95c7d4ebe4 100644
--- a/src/libsystemd/sd-bus/bus-control.c
+++ b/src/libsystemd/sd-bus/bus-control.c
@@ -257,7 +257,7 @@ static int kernel_get_list(sd_bus *bus, uint64_t flags, char ***x) {
KDBUS_FOREACH(name, name_list, cmd.list_size) {
struct kdbus_item *item;
- if ((flags & KDBUS_LIST_UNIQUE) && name->id != previous_id) {
+ if ((flags & KDBUS_LIST_UNIQUE) && name->id != previous_id && !(name->flags & KDBUS_HELLO_ACTIVATOR)) {
char *n;
if (asprintf(&n, ":1.%llu", (unsigned long long) name->id) < 0) {
diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c
index 17ce297510..b2e617ada4 100644
--- a/src/libsystemd/sd-bus/bus-objects.c
+++ b/src/libsystemd/sd-bus/bus-objects.c
@@ -1175,10 +1175,6 @@ static int process_get_managed_objects(
if (bus->nodes_modified)
return 0;
- r = set_put_strdup(s, m->path);
- if (r < 0)
- return r;
-
r = sd_bus_message_new_method_return(m, &reply);
if (r < 0)
return r;
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index db4f21e9ff..31cdcb4e5b 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -33,6 +33,7 @@
#include "missing.h"
#include "def.h"
#include "cgroup-util.h"
+#include "hostname-util.h"
#include "bus-label.h"
#include "sd-bus.h"
diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c
index 1457759d0e..6b3bfad5b6 100644
--- a/src/libsystemd/sd-bus/test-bus-objects.c
+++ b/src/libsystemd/sd-bus/test-bus-objects.c
@@ -114,13 +114,14 @@ static int set_handler(sd_bus *bus, const char *path, const char *interface, con
static int value_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *s = NULL;
+ const char *x;
int r;
assert_se(asprintf(&s, "object %p, path %s", userdata, path) >= 0);
r = sd_bus_message_append(reply, "s", s);
assert_se(r >= 0);
- assert_se(startswith(path, "/value/") != NULL || strcmp(path, "/value") == 0);
+ assert_se(x = startswith(path, "/value/"));
assert_se(PTR_TO_UINT(userdata) == 30);
diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c
index 7fd77e9480..5eb37e16cb 100644
--- a/src/libsystemd/sd-device/device-enumerator.c
+++ b/src/libsystemd/sd-device/device-enumerator.c
@@ -719,6 +719,8 @@ static int parent_add_child(sd_device_enumerator *enumerator, const char *path)
return r;
r = sd_device_get_subsystem(device, &subsystem);
+ if (r == -ENOENT)
+ return 0;
if (r < 0)
return r;
diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c
index 9bbf8974dd..0eadc8c747 100644
--- a/src/libsystemd/sd-login/sd-login.c
+++ b/src/libsystemd/sd-login/sd-login.c
@@ -32,6 +32,7 @@
#include "fileio.h"
#include "login-util.h"
#include "formats-util.h"
+#include "hostname-util.h"
#include "sd-login.h"
_public_ int sd_pid_get_session(pid_t pid, char **session) {
@@ -790,7 +791,7 @@ _public_ int sd_get_machine_names(char ***machines) {
/* Filter out the unit: symlinks */
for (a = l, b = l; *a; a++) {
- if (startswith(*a, "unit:"))
+ if (startswith(*a, "unit:") || !machine_name_is_valid(*a))
free(*a);
else {
*b = *a;
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 992a9f5b4a..5b2b36b9c0 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -1339,7 +1339,8 @@ static int bus_manager_log_shutdown(
InhibitWhat w,
const char *unit_name) {
- const char *p, *q;
+ const char *p;
+ const char *q;
assert(m);
assert(unit_name);
@@ -1364,6 +1365,9 @@ static int bus_manager_log_shutdown(
q = NULL;
}
+ if (m->wall_message)
+ p = strjoina(p, " (", m->wall_message, ")", NULL);
+
return log_struct(LOG_NOTICE,
LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN),
p,
@@ -2282,6 +2286,44 @@ static int method_can_reboot_to_firmware_setup(
return sd_bus_reply_method_return(message, "s", result);
}
+static int method_set_wall_message(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ int r;
+ Manager *m = userdata;
+ char *wall_message;
+ bool enable_wall_messages;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "sb", &wall_message, &enable_wall_messages);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.login1.set-wall-message",
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = free_and_strdup(&m->wall_message, wall_message);
+ if (r < 0)
+ return log_oom();
+ m->enable_wall_messages = enable_wall_messages;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
const char *who, *why, *what, *mode;
@@ -2463,6 +2505,7 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetWallMessage", "sb", NULL, method_set_wall_message, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL("SessionNew", "so", 0),
SD_BUS_SIGNAL("SessionRemoved", "so", 0),
diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf
index d8deb7bc8b..1662d4c428 100644
--- a/src/login/org.freedesktop.login1.conf
+++ b/src/login/org.freedesktop.login1.conf
@@ -182,6 +182,10 @@
<allow send_destination="org.freedesktop.login1"
send_interface="org.freedesktop.login1.Manager"
+ send_member="SetWallMessage"/>
+
+ <allow send_destination="org.freedesktop.login1"
+ send_interface="org.freedesktop.login1.Manager"
send_member="AttachDevice"/>
<allow send_destination="org.freedesktop.login1"
diff --git a/src/login/org.freedesktop.login1.policy.in b/src/login/org.freedesktop.login1.policy.in
index 83e7183323..23326bb79f 100644
--- a/src/login/org.freedesktop.login1.policy.in
+++ b/src/login/org.freedesktop.login1.policy.in
@@ -150,6 +150,7 @@
<allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>yes</allow_active>
</defaults>
+ <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.set-wall-message</annotate>
</action>
<action id="org.freedesktop.login1.power-off-multiple-sessions">
@@ -182,6 +183,7 @@
<allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>yes</allow_active>
</defaults>
+ <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.set-wall-message</annotate>
</action>
<action id="org.freedesktop.login1.reboot-multiple-sessions">
@@ -300,4 +302,14 @@
</defaults>
</action>
+ <action id="org.freedesktop.login1.set-wall-message">
+ <_description>Set a wall message</_description>
+ <_message>Authentication is required to set a wall message</_message>
+ <defaults>
+ <allow_any>auth_admin_keep</allow_any>
+ <allow_inactive>auth_admin_keep</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
</policyconfig>
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index b62a9bd813..af2b8eff06 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -44,6 +44,7 @@
#include "machine-dbus.h"
#include "formats-util.h"
#include "process-util.h"
+#include "env-util.h"
static int property_get_id(
sd_bus *bus,
@@ -185,141 +186,179 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_close_pair_ int pair[2] = { -1, -1 };
- _cleanup_free_ char *us = NULL, *them = NULL;
- _cleanup_close_ int netns_fd = -1;
Machine *m = userdata;
- const char *p;
- siginfo_t si;
- pid_t child;
int r;
assert(message);
assert(m);
- if (m->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines.");
-
- r = readlink_malloc("/proc/self/ns/net", &us);
- if (r < 0)
- return r;
-
- p = procfs_file_alloca(m->leader, "ns/net");
- r = readlink_malloc(p, &them);
+ r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
return r;
- if (streq(us, them))
- return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
-
- r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL);
+ r = sd_bus_message_open_container(reply, 'a', "(iay)");
if (r < 0)
return r;
- if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
- return -errno;
-
- child = fork();
- if (child < 0)
- return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+ switch (m->class) {
- if (child == 0) {
+ case MACHINE_HOST: {
_cleanup_free_ struct local_address *addresses = NULL;
struct local_address *a;
- int i, n;
-
- pair[0] = safe_close(pair[0]);
-
- r = namespace_enter(-1, -1, netns_fd, -1, -1);
- if (r < 0)
- _exit(EXIT_FAILURE);
+ int n, i;
n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
if (n < 0)
- _exit(EXIT_FAILURE);
+ return n;
for (a = addresses, i = 0; i < n; a++, i++) {
- struct iovec iov[2] = {
- { .iov_base = &a->family, .iov_len = sizeof(a->family) },
- { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) },
- };
- r = writev(pair[1], iov, 2);
+ r = sd_bus_message_open_container(reply, 'r', "iay");
if (r < 0)
- _exit(EXIT_FAILURE);
- }
-
- pair[1] = safe_close(pair[1]);
+ return r;
- _exit(EXIT_SUCCESS);
- }
-
- pair[1] = safe_close(pair[1]);
+ r = sd_bus_message_append(reply, "i", addresses[i].family);
+ if (r < 0)
+ return r;
- r = sd_bus_message_new_method_return(message, &reply);
- if (r < 0)
- return r;
+ r = sd_bus_message_append_array(reply, 'y', &addresses[i].address, FAMILY_ADDRESS_SIZE(addresses[i].family));
+ if (r < 0)
+ return r;
- r = sd_bus_message_open_container(reply, 'a', "(iay)");
- if (r < 0)
- return r;
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+ }
- for (;;) {
- int family;
- ssize_t n;
- union in_addr_union in_addr;
- struct iovec iov[2];
- struct msghdr mh = {
- .msg_iov = iov,
- .msg_iovlen = 2,
- };
+ break;
+ }
- iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
- iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
+ case MACHINE_CONTAINER: {
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ _cleanup_free_ char *us = NULL, *them = NULL;
+ _cleanup_close_ int netns_fd = -1;
+ const char *p;
+ siginfo_t si;
+ pid_t child;
- n = recvmsg(pair[0], &mh, 0);
- if (n < 0)
- return -errno;
- if ((size_t) n < sizeof(family))
- break;
+ r = readlink_malloc("/proc/self/ns/net", &us);
+ if (r < 0)
+ return r;
- r = sd_bus_message_open_container(reply, 'r', "iay");
+ p = procfs_file_alloca(m->leader, "ns/net");
+ r = readlink_malloc(p, &them);
if (r < 0)
return r;
- r = sd_bus_message_append(reply, "i", family);
+ if (streq(us, them))
+ return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
+
+ r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL);
if (r < 0)
return r;
- switch (family) {
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
+ return -errno;
+
+ child = fork();
+ if (child < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+
+ if (child == 0) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ struct local_address *a;
+ int i, n;
+
+ pair[0] = safe_close(pair[0]);
- case AF_INET:
- if (n != sizeof(struct in_addr) + sizeof(family))
- return -EIO;
+ r = namespace_enter(-1, -1, netns_fd, -1, -1);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
+ if (n < 0)
+ _exit(EXIT_FAILURE);
+
+ for (a = addresses, i = 0; i < n; a++, i++) {
+ struct iovec iov[2] = {
+ { .iov_base = &a->family, .iov_len = sizeof(a->family) },
+ { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) },
+ };
- r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
- break;
+ r = writev(pair[1], iov, 2);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+ }
- case AF_INET6:
- if (n != sizeof(struct in6_addr) + sizeof(family))
- return -EIO;
+ pair[1] = safe_close(pair[1]);
- r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
- break;
+ _exit(EXIT_SUCCESS);
}
- if (r < 0)
- return r;
- r = sd_bus_message_close_container(reply);
+ pair[1] = safe_close(pair[1]);
+
+ for (;;) {
+ int family;
+ ssize_t n;
+ union in_addr_union in_addr;
+ struct iovec iov[2];
+ struct msghdr mh = {
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ };
+
+ iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
+ iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
+
+ n = recvmsg(pair[0], &mh, 0);
+ if (n < 0)
+ return -errno;
+ if ((size_t) n < sizeof(family))
+ break;
+
+ r = sd_bus_message_open_container(reply, 'r', "iay");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "i", family);
+ if (r < 0)
+ return r;
+
+ switch (family) {
+
+ case AF_INET:
+ if (n != sizeof(struct in_addr) + sizeof(family))
+ return -EIO;
+
+ r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
+ break;
+
+ case AF_INET6:
+ if (n != sizeof(struct in6_addr) + sizeof(family))
+ return -EIO;
+
+ r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
+ break;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+ }
+
+ r = wait_for_terminate(child, &si);
if (r < 0)
- return r;
+ return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
+ if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
+ break;
}
- r = wait_for_terminate(child, &si);
- if (r < 0)
- return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
- if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
+ default:
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines.");
+ }
r = sd_bus_message_close_container(reply);
if (r < 0)
@@ -330,73 +369,88 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_close_ int mntns_fd = -1, root_fd = -1;
- _cleanup_close_pair_ int pair[2] = { -1, -1 };
_cleanup_strv_free_ char **l = NULL;
- _cleanup_fclose_ FILE *f = NULL;
Machine *m = userdata;
char **k, **v;
- siginfo_t si;
- pid_t child;
int r;
assert(message);
assert(m);
- if (m->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
+ switch (m->class) {
- r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd);
- if (r < 0)
- return r;
+ case MACHINE_HOST:
+ r = load_env_file_pairs(NULL, "/etc/os-release", NULL, &l);
+ if (r < 0)
+ return r;
- if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
- return -errno;
+ break;
- child = fork();
- if (child < 0)
- return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+ case MACHINE_CONTAINER: {
+ _cleanup_close_ int mntns_fd = -1, root_fd = -1;
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ _cleanup_fclose_ FILE *f = NULL;
+ siginfo_t si;
+ pid_t child;
- if (child == 0) {
- _cleanup_close_ int fd = -1;
+ r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd);
+ if (r < 0)
+ return r;
- pair[0] = safe_close(pair[0]);
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
+ return -errno;
- r = namespace_enter(-1, mntns_fd, -1, -1, root_fd);
- if (r < 0)
- _exit(EXIT_FAILURE);
+ child = fork();
+ if (child < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+
+ if (child == 0) {
+ _cleanup_close_ int fd = -1;
- fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
- if (fd < 0) {
- fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
- if (fd < 0)
+ pair[0] = safe_close(pair[0]);
+
+ r = namespace_enter(-1, mntns_fd, -1, -1, root_fd);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ _exit(EXIT_FAILURE);
+ }
+
+ r = copy_bytes(fd, pair[1], (off_t) -1, false);
+ if (r < 0)
_exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
}
- r = copy_bytes(fd, pair[1], (off_t) -1, false);
- if (r < 0)
- _exit(EXIT_FAILURE);
+ pair[1] = safe_close(pair[1]);
- _exit(EXIT_SUCCESS);
- }
+ f = fdopen(pair[0], "re");
+ if (!f)
+ return -errno;
- pair[1] = safe_close(pair[1]);
+ pair[0] = -1;
- f = fdopen(pair[0], "re");
- if (!f)
- return -errno;
+ r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
+ if (r < 0)
+ return r;
- pair[0] = -1;
+ r = wait_for_terminate(child, &si);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
+ if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
- r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
- if (r < 0)
- return r;
+ break;
+ }
- r = wait_for_terminate(child, &si);
- if (r < 0)
- return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
- if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
+ default:
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
+ }
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
@@ -429,10 +483,20 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_
assert(message);
assert(m);
- if (m->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening pseudo TTYs is only supported on container machines.");
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
+ false,
+ UID_INVALID,
+ &m->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
- master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC);
if (master < 0)
return master;
@@ -451,26 +515,67 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_
return sd_bus_send(NULL, reply, NULL);
}
+static int container_bus_new(Machine *m, sd_bus **ret) {
+ int r;
+
+ assert(m);
+ assert(ret);
+
+ switch (m->class) {
+
+ case MACHINE_HOST:
+ *ret = NULL;
+ break;
+
+ case MACHINE_CONTAINER: {
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+ char *address;
+
+ r = sd_bus_new(&bus);
+ if (r < 0)
+ return r;
+
+ if (asprintf(&address, "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI, m->leader) < 0)
+ return -ENOMEM;
+
+ bus->address = address;
+ bus->bus_client = true;
+ bus->trusted = false;
+ bus->is_system = true;
+
+ r = sd_bus_start(bus);
+ if (r < 0)
+ return r;
+
+ *ret = bus;
+ bus = NULL;
+ break;
+ }
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_free_ char *pty_name = NULL, *getty = NULL;
- _cleanup_bus_unref_ sd_bus *container_bus = NULL;
+ _cleanup_free_ char *pty_name = NULL;
+ _cleanup_bus_flush_close_unref_ sd_bus *allocated_bus = NULL;
_cleanup_close_ int master = -1;
+ sd_bus *container_bus = NULL;
Machine *m = userdata;
- const char *p;
- char *address;
+ const char *p, *getty;
int r;
assert(message);
assert(m);
- if (m->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening logins is only supported on container machines.");
-
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
- "org.freedesktop.machine1.login",
+ m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
false,
UID_INVALID,
&m->manager->polkit_registry,
@@ -480,7 +585,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
if (r == 0)
return 1; /* Will call us back */
- master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC);
if (master < 0)
return master;
@@ -495,26 +600,13 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
if (unlockpt(master) < 0)
return -errno;
- r = sd_bus_new(&container_bus);
+ r = container_bus_new(m, &allocated_bus);
if (r < 0)
return r;
-# define ADDRESS_FMT "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI
- if (asprintf(&address, ADDRESS_FMT, m->leader) < 0)
- return log_oom();
-
- container_bus->address = address;
- container_bus->bus_client = true;
- container_bus->trusted = false;
- container_bus->is_system = true;
+ container_bus = allocated_bus ?: m->manager->bus;
- r = sd_bus_start(container_bus);
- if (r < 0)
- return r;
-
- getty = strjoin("container-getty@", p, ".service", NULL);
- if (!getty)
- return log_oom();
+ getty = strjoina("container-getty@", p, ".service");
r = sd_bus_call_method(
container_bus,
@@ -527,7 +619,228 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
if (r < 0)
return r;
- container_bus = sd_bus_unref(container_bus);
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "hs", master, pty_name);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
+int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *tm = NULL;
+ _cleanup_free_ char *pty_name = NULL;
+ _cleanup_bus_flush_close_unref_ sd_bus *allocated_bus = NULL;
+ sd_bus *container_bus = NULL;
+ _cleanup_close_ int master = -1;
+ _cleanup_strv_free_ char **env = NULL, **args = NULL;
+ Machine *m = userdata;
+ const char *p, *unit, *user, *path, *description, *utmp_id;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "ss", &user, &path);
+ if (r < 0)
+ return r;
+ if (isempty(user))
+ user = NULL;
+ if (isempty(path))
+ path = "/bin/sh";
+ if (!path_is_absolute(path))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path);
+
+ r = sd_bus_message_read_strv(message, &args);
+ if (r < 0)
+ return r;
+ if (strv_isempty(args)) {
+ args = strv_free(args);
+
+ args = strv_new(path, NULL);
+ if (!args)
+ return -ENOMEM;
+
+ args[0][0] = '-'; /* Tell /bin/sh that this shall be a login shell */
+ }
+
+ r = sd_bus_message_read_strv(message, &env);
+ if (r < 0)
+ return r;
+ if (!strv_env_is_valid(env))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
+ false,
+ UID_INVALID,
+ &m->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (master < 0)
+ return master;
+
+ r = ptsname_malloc(master, &pty_name);
+ if (r < 0)
+ return r;
+
+ p = path_startswith(pty_name, "/dev/pts/");
+ if (!p)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name);
+
+ utmp_id = path_startswith(pty_name, "/dev/");
+ assert(utmp_id);
+
+ if (unlockpt(master) < 0)
+ return -errno;
+
+ r = container_bus_new(m, &allocated_bus);
+ if (r < 0)
+ return r;
+
+ container_bus = allocated_bus ?: m->manager->bus;
+
+ r = sd_bus_message_new_method_call(
+ container_bus,
+ &tm,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "StartTransientUnit");
+ if (r < 0)
+ return r;
+
+ /* Name and mode */
+ unit = strjoina("container-shell@", p, ".service", NULL);
+ r = sd_bus_message_append(tm, "ss", unit, "fail");
+ if (r < 0)
+ return r;
+
+ /* Properties */
+ r = sd_bus_message_open_container(tm, 'a', "(sv)");
+ if (r < 0)
+ return r;
+
+ description = strjoina("Shell for User ", isempty(user) ? "root" : user);
+ r = sd_bus_message_append(tm,
+ "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
+ "Description", "s", description,
+ "StandardInput", "s", "tty",
+ "StandardOutput", "s", "tty",
+ "StandardError", "s", "tty",
+ "TTYPath", "s", pty_name,
+ "SendSIGHUP", "b", true,
+ "IgnoreSIGPIPE", "b", false,
+ "KillMode", "s", "mixed",
+ "TTYVHangup", "b", true,
+ "TTYReset", "b", true,
+ "UtmpIdentifier", "s", utmp_id,
+ "UtmpMode", "s", "user",
+ "PAMName", "s", "login");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(tm, "(sv)", "User", "s", isempty(user) ? "root" : user);
+ if (r < 0)
+ return r;
+
+ if (!strv_isempty(env)) {
+ r = sd_bus_message_open_container(tm, 'r', "sv");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(tm, "s", "Environment");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(tm, 'v', "as");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_strv(tm, env);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(tm);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(tm);
+ if (r < 0)
+ return r;
+ }
+
+ /* Exec container */
+ r = sd_bus_message_open_container(tm, 'r', "sv");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(tm, "s", "ExecStart");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(tm, 'v', "a(sasb)");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(tm, 'a', "(sasb)");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(tm, 'r', "sasb");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(tm, "s", path);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_strv(tm, args);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(tm, "b", true);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(tm);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(tm);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(tm);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(tm);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(tm);
+ if (r < 0)
+ return r;
+
+ /* Auxiliary units */
+ r = sd_bus_message_append(tm, "a(sa(sv))", 0);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_call(container_bus, tm, 0, error, NULL);
+ if (r < 0)
+ return r;
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
@@ -966,8 +1279,9 @@ const sd_bus_vtable machine_vtable[] = {
SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0),
+ SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h
index d309131860..38b46ad936 100644
--- a/src/machine/machine-dbus.h
+++ b/src/machine/machine-dbus.h
@@ -35,6 +35,7 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/machine/machine.c b/src/machine/machine.c
index f045159d41..0d1b119dc1 100644
--- a/src/machine/machine.c
+++ b/src/machine/machine.c
@@ -37,12 +37,17 @@
#include "machine-dbus.h"
#include "formats-util.h"
-Machine* machine_new(Manager *manager, const char *name) {
+Machine* machine_new(Manager *manager, MachineClass class, const char *name) {
Machine *m;
assert(manager);
+ assert(class < _MACHINE_CLASS_MAX);
assert(name);
+ /* Passing class == _MACHINE_CLASS_INVALID here is fine. It
+ * means as much as "we don't know yet", and that we'll figure
+ * it out later when loading the state file. */
+
m = new0(Machine, 1);
if (!m)
return NULL;
@@ -51,14 +56,17 @@ Machine* machine_new(Manager *manager, const char *name) {
if (!m->name)
goto fail;
- m->state_file = strappend("/run/systemd/machines/", m->name);
- if (!m->state_file)
- goto fail;
+ if (class != MACHINE_HOST) {
+ m->state_file = strappend("/run/systemd/machines/", m->name);
+ if (!m->state_file)
+ goto fail;
+ }
+
+ m->class = class;
if (hashmap_put(manager->machines, m->name, m) < 0)
goto fail;
- m->class = _MACHINE_CLASS_INVALID;
m->manager = manager;
return m;
@@ -86,6 +94,9 @@ void machine_free(Machine *m) {
(void) hashmap_remove(m->manager->machines, m->name);
+ if (m->manager->host_machine == m)
+ m->manager->host_machine = NULL;
+
if (m->leader > 0)
(void) hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
@@ -105,7 +116,9 @@ int machine_save(Machine *m) {
int r;
assert(m);
- assert(m->state_file);
+
+ if (!m->state_file)
+ return 0;
if (!m->started)
return 0;
@@ -244,6 +257,9 @@ int machine_load(Machine *m) {
assert(m);
+ if (!m->state_file)
+ return 0;
+
r = parse_env_file(m->state_file, NEWLINE,
"SCOPE", &m->unit,
"SCOPE_JOB", &m->scope_job,
@@ -325,6 +341,7 @@ static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_er
int r = 0;
assert(m);
+ assert(m->class != MACHINE_HOST);
if (!m->unit) {
_cleanup_free_ char *escaped = NULL;
@@ -364,6 +381,9 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
assert(m);
+ if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
+ return -EOPNOTSUPP;
+
if (m->started)
return 0;
@@ -402,6 +422,7 @@ static int machine_stop_scope(Machine *m) {
int r;
assert(m);
+ assert(m->class != MACHINE_HOST);
if (!m->unit)
return 0;
@@ -422,6 +443,9 @@ int machine_stop(Machine *m) {
int r;
assert(m);
+ if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
+ return -EOPNOTSUPP;
+
r = machine_stop_scope(m);
m->stopping = true;
@@ -456,6 +480,9 @@ int machine_finalize(Machine *m) {
bool machine_check_gc(Machine *m, bool drop_not_started) {
assert(m);
+ if (m->class == MACHINE_HOST)
+ return true;
+
if (drop_not_started && !m->started)
return false;
@@ -481,6 +508,9 @@ void machine_add_to_gc_queue(Machine *m) {
MachineState machine_get_state(Machine *s) {
assert(s);
+ if (s->class == MACHINE_HOST)
+ return MACHINE_RUNNING;
+
if (s->stopping)
return MACHINE_CLOSING;
@@ -493,6 +523,9 @@ MachineState machine_get_state(Machine *s) {
int machine_kill(Machine *m, KillWho who, int signo) {
assert(m);
+ if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER))
+ return -EOPNOTSUPP;
+
if (!m->unit)
return -ESRCH;
@@ -509,6 +542,25 @@ int machine_kill(Machine *m, KillWho who, int signo) {
return manager_kill_unit(m->manager, m->unit, signo, NULL);
}
+int machine_openpt(Machine *m, int flags) {
+ assert(m);
+
+ switch (m->class) {
+
+ case MACHINE_HOST:
+ return posix_openpt(flags);
+
+ case MACHINE_CONTAINER:
+ if (m->leader <= 0)
+ return -EINVAL;
+
+ return openpt_in_namespace(m->leader, flags);
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
MachineOperation *machine_operation_unref(MachineOperation *o) {
if (!o)
return NULL;
@@ -544,7 +596,8 @@ void machine_release_unit(Machine *m) {
static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
[MACHINE_CONTAINER] = "container",
- [MACHINE_VM] = "vm"
+ [MACHINE_VM] = "vm",
+ [MACHINE_HOST] = "host",
};
DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
diff --git a/src/machine/machine.h b/src/machine/machine.h
index 0132b65a97..5f978289f2 100644
--- a/src/machine/machine.h
+++ b/src/machine/machine.h
@@ -39,6 +39,7 @@ typedef enum MachineState {
typedef enum MachineClass {
MACHINE_CONTAINER,
MACHINE_VM,
+ MACHINE_HOST,
_MACHINE_CLASS_MAX,
_MACHINE_CLASS_INVALID = -1
} MachineClass;
@@ -95,7 +96,7 @@ struct Machine {
unsigned n_operations;
};
-Machine* machine_new(Manager *manager, const char *name);
+Machine* machine_new(Manager *manager, MachineClass class, const char *name);
void machine_free(Machine *m);
bool machine_check_gc(Machine *m, bool drop_not_started);
void machine_add_to_gc_queue(Machine *m);
@@ -120,3 +121,5 @@ MachineState machine_state_from_string(const char *s) _pure_;
const char *kill_who_to_string(KillWho k) _const_;
KillWho kill_who_from_string(const char *s) _pure_;
+
+int machine_openpt(Machine *m, int flags);
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index 66ed41087c..f9f49ee892 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -55,6 +55,8 @@
#include "process-util.h"
#include "terminal-util.h"
#include "signal-util.h"
+#include "env-util.h"
+#include "hostname-util.h"
static char **arg_property = NULL;
static bool arg_all = false;
@@ -75,6 +77,8 @@ static bool arg_force = false;
static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
static const char* arg_dkr_index_url = NULL;
static const char* arg_format = NULL;
+static const char *arg_uid = NULL;
+static char **arg_setenv = NULL;
static void pager_open_if_enabled(void) {
@@ -154,6 +158,9 @@ static int list_machines(int argc, char *argv[], void *userdata) {
while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
size_t l;
+ if (name[0] == '.' && !arg_all)
+ continue;
+
if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
return log_oom();
@@ -354,7 +361,8 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
bus,
"org.freedesktop.systemd1",
path,
- endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
+ endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" :
+ endswith(unit, ".slice") ? "org.freedesktop.systemd1.Slice" : "org.freedesktop.systemd1.Service",
"ControlGroup",
&error,
&reply,
@@ -368,9 +376,6 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
if (r < 0)
return bus_log_parse_error(r);
- if (isempty(cgroup))
- return 0;
-
if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
return 0;
@@ -1170,20 +1175,72 @@ static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *r
return 0;
}
+static int process_forward(sd_event *event, PTYForward **forward, int master, bool ignore_vhangup, const char *name) {
+ char last_char = 0;
+ bool machine_died;
+ int ret = 0, r;
+
+ assert(event);
+ assert(master >= 0);
+ assert(name);
+
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
+
+ if (streq(name, ".host"))
+ log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
+ else
+ log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name);
+
+ sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
+ sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+
+ r = pty_forward_new(event, master, ignore_vhangup, false, forward);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create PTY forwarder: %m");
+
+ r = sd_event_loop(event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
+
+ pty_forward_get_last_char(*forward, &last_char);
+
+ machine_died =
+ ignore_vhangup &&
+ pty_forward_get_ignore_vhangup(*forward) == 0;
+
+ *forward = pty_forward_free(*forward);
+
+ if (last_char != '\n')
+ fputc('\n', stdout);
+
+ if (machine_died)
+ log_info("Machine %s terminated.", name);
+ else if (streq(name, ".host"))
+ log_info("Connection to the local host terminated.");
+ else
+ log_info("Connection to machine %s terminated.", name);
+
+ sd_event_get_exit_code(event, &ret);
+ return ret;
+}
+
static int login_machine(int argc, char *argv[], void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
+ _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
_cleanup_event_unref_ sd_event *event = NULL;
- int master = -1, r, ret = 0;
+ int master = -1, r;
sd_bus *bus = userdata;
- const char *pty, *match;
- char last_char = 0;
- bool machine_died;
+ const char *pty, *match, *machine;
assert(bus);
+ if (!strv_isempty(arg_setenv) || arg_uid) {
+ log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
+ return -EINVAL;
+ }
+
if (arg_transport != BUS_TRANSPORT_LOCAL &&
arg_transport != BUS_TRANSPORT_MACHINE) {
log_error("Login only supported on local machines.");
@@ -1200,14 +1257,14 @@ static int login_machine(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
+ machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1];
+
match = strjoina("type='signal',"
- "sender='org.freedesktop.machine1',"
- "path='/org/freedesktop/machine1',",
- "interface='org.freedesktop.machine1.Manager',"
- "member='MachineRemoved',"
- "arg0='",
- argv[1],
- "'");
+ "sender='org.freedesktop.machine1',"
+ "path='/org/freedesktop/machine1',",
+ "interface='org.freedesktop.machine1.Manager',"
+ "member='MachineRemoved',"
+ "arg0='", machine, "'");
r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
if (r < 0)
@@ -1221,9 +1278,9 @@ static int login_machine(int argc, char *argv[], void *userdata) {
"OpenMachineLogin",
&error,
&reply,
- "s", argv[1]);
+ "s", machine);
if (r < 0) {
- log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
+ log_error("Failed to get login PTY: %s", bus_error_message(&error, -r));
return r;
}
@@ -1231,36 +1288,100 @@ static int login_machine(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_parse_error(r);
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
+ return process_forward(event, &forward, master, true, machine);
+}
- log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
+static int shell_machine(int argc, char *argv[], void *userdata) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
+ _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
+ _cleanup_event_unref_ sd_event *event = NULL;
+ int master = -1, r;
+ sd_bus *bus = userdata;
+ const char *pty, *match, *machine, *path, *uid = NULL;
- sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
- sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+ assert(bus);
+
+ if (arg_transport != BUS_TRANSPORT_LOCAL &&
+ arg_transport != BUS_TRANSPORT_MACHINE) {
+ log_error("Shell only supported on local machines.");
+ return -EOPNOTSUPP;
+ }
+
+ polkit_agent_open_if_enabled();
- r = pty_forward_new(event, master, true, false, &forward);
+ r = sd_event_default(&event);
if (r < 0)
- return log_error_errno(r, "Failed to create PTY forwarder: %m");
+ return log_error_errno(r, "Failed to get event loop: %m");
- r = sd_event_loop(event);
+ r = sd_bus_attach_event(bus, event, 0);
if (r < 0)
- return log_error_errno(r, "Failed to run event loop: %m");
+ return log_error_errno(r, "Failed to attach bus to event loop: %m");
- pty_forward_get_last_char(forward, &last_char);
- machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
+ machine = argc < 2 || isempty(argv[1]) ? NULL : argv[1];
- forward = pty_forward_free(forward);
+ if (arg_uid)
+ uid = arg_uid;
+ else if (machine) {
+ const char *at;
- if (last_char != '\n')
- fputc('\n', stdout);
+ at = strchr(machine, '@');
+ if (at) {
+ uid = strndupa(machine, at - machine);
+ machine = at + 1;
+ }
+ }
- if (machine_died)
- log_info("Machine %s terminated.", argv[1]);
- else
- log_info("Connection to machine %s terminated.", argv[1]);
+ if (isempty(machine))
+ machine = ".host";
- sd_event_get_exit_code(event, &ret);
- return ret;
+ match = strjoina("type='signal',"
+ "sender='org.freedesktop.machine1',"
+ "path='/org/freedesktop/machine1',",
+ "interface='org.freedesktop.machine1.Manager',"
+ "member='MachineRemoved',"
+ "arg0='", machine, "'");
+
+ r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add machine removal match: %m");
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "OpenMachineShell");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ path = argc < 3 || isempty(argv[2]) ? NULL : argv[2];
+
+ r = sd_bus_message_append(m, "sss", machine, uid, path);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(m, arg_setenv);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ if (r < 0) {
+ log_error("Failed to get shell PTY: %s", bus_error_message(&error, -r));
+ return r;
+ }
+
+ r = sd_bus_message_read(reply, "hs", &master, &pty);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return process_forward(event, &forward, master, false, machine);
}
static int remove_image(int argc, char *argv[], void *userdata) {
@@ -2314,6 +2435,8 @@ static int help(int argc, char *argv[], void *userdata) {
" -l --full Do not ellipsize output\n"
" --kill-who=WHO Who to send signal to\n"
" -s --signal=SIGNAL Which signal to send\n"
+ " --uid=USER Specify user ID to invoke shell as\n"
+ " --setenv=VAR=VALUE Add an environment variable for shell\n"
" --read-only Create read-only bind mount\n"
" --mkdir Create directory before bind mounting, if missing\n"
" -n --lines=INTEGER Number of journal entries to show\n"
@@ -2328,9 +2451,13 @@ static int help(int argc, char *argv[], void *userdata) {
"Machine Commands:\n"
" list List running VMs and containers\n"
" status NAME... Show VM/container details\n"
- " show NAME... Show properties of one or more VMs/containers\n"
+ " show [NAME...] Show properties of one or more VMs/containers\n"
" start NAME... Start container as a service\n"
- " login NAME Get a login prompt on a container\n"
+ " login [NAME] Get a login prompt in a container or on the\n"
+ " local host\n"
+ " shell [[USER@]NAME [COMMAND...]]\n"
+ " Invoke a shell (or other command) in a container\n"
+ " or on the local host\n"
" enable NAME... Enable automatic container start at boot\n"
" disable NAME... Disable automatic container start at boot\n"
" poweroff NAME... Power off one or more containers\n"
@@ -2342,8 +2469,8 @@ static int help(int argc, char *argv[], void *userdata) {
" bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
"Image Commands:\n"
" list-images Show available container and VM images\n"
- " image-status NAME... Show image details\n"
- " show-image NAME... Show properties of image\n"
+ " image-status [NAME...] Show image details\n"
+ " show-image [NAME...] Show properties of image\n"
" clone NAME NAME Clone an image\n"
" rename NAME NAME Rename an image\n"
" read-only NAME [BOOL] Mark or unmark image read-only\n"
@@ -2378,6 +2505,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_FORCE,
ARG_DKR_INDEX_URL,
ARG_FORMAT,
+ ARG_UID,
+ ARG_SETENV,
};
static const struct option options[] = {
@@ -2402,6 +2531,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "force", no_argument, NULL, ARG_FORCE },
{ "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
{ "format", required_argument, NULL, ARG_FORMAT },
+ { "uid", required_argument, NULL, ARG_UID },
+ { "setenv", required_argument, NULL, ARG_SETENV },
{}
};
@@ -2532,6 +2663,21 @@ static int parse_argv(int argc, char *argv[]) {
arg_format = optarg;
break;
+ case ARG_UID:
+ arg_uid = optarg;
+ break;
+
+ case ARG_SETENV:
+ if (!env_assignment_is_valid(optarg)) {
+ log_error("Environment assignment invalid: %s", optarg);
+ return -EINVAL;
+ }
+
+ r = strv_extend(&arg_setenv, optarg);
+ if (r < 0)
+ return log_oom();
+ break;
+
case '?':
return -EINVAL;
@@ -2556,7 +2702,8 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
{ "reboot", 2, VERB_ANY, 0, reboot_machine },
{ "poweroff", 2, VERB_ANY, 0, poweroff_machine },
{ "kill", 2, VERB_ANY, 0, kill_machine },
- { "login", 2, 2, 0, login_machine },
+ { "login", VERB_ANY, 2, 0, login_machine },
+ { "shell", VERB_ANY, VERB_ANY, 0, shell_machine },
{ "bind", 3, 4, 0, bind_mount },
{ "copy-to", 3, 4, 0, copy_files },
{ "copy-from", 3, 4, 0, copy_files },
@@ -2610,6 +2757,7 @@ finish:
polkit_agent_close();
strv_free(arg_property);
+ strv_free(arg_setenv);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 08a7f58ef5..0d52c693e4 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -33,6 +33,7 @@
#include "btrfs-util.h"
#include "formats-util.h"
#include "process-util.h"
+#include "hostname-util.h"
#include "machine-image.h"
#include "machine-pool.h"
#include "image-dbus.h"
@@ -637,6 +638,27 @@ static int method_open_machine_login(sd_bus_message *message, void *userdata, sd
return bus_machine_method_open_login(message, machine, error);
}
+static int method_open_machine_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ Machine *machine;
+ const char *name;
+
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ machine = hashmap_get(m->machines, name);
+ if (!machine)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
+
+ return bus_machine_method_open_shell(message, machine, error);
+}
+
static int method_bind_mount_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
Machine *machine;
@@ -860,6 +882,9 @@ static int method_map_from_machine_user(sd_bus_message *message, void *userdata,
if (!machine)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
+ if (machine->class != MACHINE_CONTAINER)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines.");
+
p = procfs_file_alloca(machine->leader, "uid_map");
f = fopen(p, "re");
if (!f)
@@ -912,6 +937,9 @@ static int method_map_to_machine_user(sd_bus_message *message, void *userdata, s
_cleanup_fclose_ FILE *f = NULL;
char p[strlen("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
+ if (machine->class != MACHINE_CONTAINER)
+ continue;
+
xsprintf(p, "/proc/" UID_FMT "/uid_map", machine->leader);
f = fopen(p, "re");
if (!f) {
@@ -972,6 +1000,9 @@ static int method_map_from_machine_group(sd_bus_message *message, void *groupdat
if (!machine)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
+ if (machine->class != MACHINE_CONTAINER)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines.");
+
p = procfs_file_alloca(machine->leader, "gid_map");
f = fopen(p, "re");
if (!f)
@@ -1024,6 +1055,9 @@ static int method_map_to_machine_group(sd_bus_message *message, void *groupdata,
_cleanup_fclose_ FILE *f = NULL;
char p[strlen("/proc//gid_map") + DECIMAL_STR_MAX(pid_t) + 1];
+ if (machine->class != MACHINE_CONTAINER)
+ continue;
+
xsprintf(p, "/proc/" GID_FMT "/gid_map", machine->leader);
f = fopen(p, "re");
if (!f) {
@@ -1085,6 +1119,7 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("OpenMachinePTY", "s", "hs", method_open_machine_pty, 0),
SD_BUS_METHOD("OpenMachineLogin", "s", "hs", method_open_machine_login, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("OpenMachineShell", "sssasas", "hs", method_open_machine_shell, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -1455,7 +1490,6 @@ int manager_job_is_active(Manager *manager, const char *path) {
}
int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
- _cleanup_free_ char *unit = NULL;
Machine *mm;
int r;
@@ -1463,12 +1497,14 @@ int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
assert(pid >= 1);
assert(machine);
- r = cg_pid_get_unit(pid, &unit);
- if (r < 0)
- mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid));
- else
- mm = hashmap_get(m->machine_units, unit);
+ mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid));
+ if (!mm) {
+ _cleanup_free_ char *unit = NULL;
+ r = cg_pid_get_unit(pid, &unit);
+ if (r >= 0)
+ mm = hashmap_get(m->machine_units, unit);
+ }
if (!mm)
return 0;
@@ -1484,7 +1520,7 @@ int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
machine = hashmap_get(m->machines, name);
if (!machine) {
- machine = machine_new(m, name);
+ machine = machine_new(m, _MACHINE_CLASS_INVALID, name);
if (!machine)
return -ENOMEM;
}
diff --git a/src/machine/machined.c b/src/machine/machined.c
index c8ad157326..df3cc9972a 100644
--- a/src/machine/machined.c
+++ b/src/machine/machined.c
@@ -30,6 +30,7 @@
#include "label.h"
#include "formats-util.h"
#include "signal-util.h"
+#include "hostname-util.h"
#include "machine-image.h"
#include "machined.h"
@@ -89,6 +90,45 @@ void manager_free(Manager *m) {
free(m);
}
+static int manager_add_host_machine(Manager *m) {
+ _cleanup_free_ char *rd = NULL, *unit = NULL;
+ sd_id128_t mid;
+ Machine *t;
+ int r;
+
+ if (m->host_machine)
+ return 0;
+
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get machine ID: %m");
+
+ rd = strdup("/");
+ if (!rd)
+ return log_oom();
+
+ unit = strdup("-.slice");
+ if (!unit)
+ return log_oom();
+
+ t = machine_new(m, MACHINE_HOST, ".host");
+ if (!t)
+ return log_oom();
+
+ t->leader = 1;
+ t->id = mid;
+
+ t->root_directory = rd;
+ t->unit = unit;
+ rd = unit = NULL;
+
+ dual_timestamp_from_boottime_or_monotonic(&t->timestamp, 0);
+
+ m->host_machine = t;
+
+ return 0;
+}
+
int manager_enumerate_machines(Manager *m) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
@@ -96,6 +136,10 @@ int manager_enumerate_machines(Manager *m) {
assert(m);
+ r = manager_add_host_machine(m);
+ if (r < 0)
+ return r;
+
/* Read in machine data stored on disk */
d = opendir("/run/systemd/machines");
if (!d) {
@@ -117,11 +161,12 @@ int manager_enumerate_machines(Manager *m) {
if (startswith(de->d_name, "unit:"))
continue;
+ if (!machine_name_is_valid(de->d_name))
+ continue;
+
k = manager_add_machine(m, de->d_name, &machine);
if (k < 0) {
- log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name);
-
- r = k;
+ r = log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name);
continue;
}
diff --git a/src/machine/machined.h b/src/machine/machined.h
index 61dbefb5f1..b3e59bf998 100644
--- a/src/machine/machined.h
+++ b/src/machine/machined.h
@@ -48,6 +48,8 @@ struct Manager {
sd_event_source *image_cache_defer_event;
LIST_HEAD(Machine, machine_gc_queue);
+
+ Machine *host_machine;
};
Manager *manager_new(void);
diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf
index d58f01507b..9d40b90151 100644
--- a/src/machine/org.freedesktop.machine1.conf
+++ b/src/machine/org.freedesktop.machine1.conf
@@ -70,6 +70,10 @@
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Manager"
+ send_member="OpenMachineShell"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Manager"
send_member="TerminateMachine"/>
<allow send_destination="org.freedesktop.machine1"
@@ -142,6 +146,10 @@
<allow send_destination="org.freedesktop.machine1"
send_interface="org.freedesktop.machine1.Machine"
+ send_member="OpenShell"/>
+
+ <allow send_destination="org.freedesktop.machine1"
+ send_interface="org.freedesktop.machine1.Machine"
send_member="Terminate"/>
<allow send_destination="org.freedesktop.machine1"
diff --git a/src/machine/org.freedesktop.machine1.policy.in b/src/machine/org.freedesktop.machine1.policy.in
index 02714e83ae..69f78a5c25 100644
--- a/src/machine/org.freedesktop.machine1.policy.in
+++ b/src/machine/org.freedesktop.machine1.policy.in
@@ -26,6 +26,58 @@
</defaults>
</action>
+ <action id="org.freedesktop.machine1.host-login">
+ <_description>Log into the local host</_description>
+ <_message>Authentication is required to log into the local host.</_message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.machine1.shell">
+ <_description>Acquire a shell in a local container</_description>
+ <_message>Authentication is required to acquire a shell in a local container.</_message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.login</annotate>
+ </action>
+
+ <action id="org.freedesktop.machine1.host-shell">
+ <_description>Acquire a shell on the local host</_description>
+ <_message>Authentication is required to acquire a shell on the local host.</_message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.host-login</annotate>
+ </action>
+
+ <action id="org.freedesktop.machine1.open-pty">
+ <_description>Acquire a pseudo TTY in a local container</_description>
+ <_message>Authentication is required to acquire a pseudo TTY in a local container.</_message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.machine1.host-open-pty">
+ <_description>Acquire a pseudo TTY on the local host</_description>
+ <_message>Authentication is required to acquire a pseudo TTY on the local host.</_message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
<action id="org.freedesktop.machine1.manage-machines">
<_description>Manage local virtual machines and containers</_description>
<_message>Authentication is required to manage local virtual machines and containers.</_message>
@@ -34,6 +86,7 @@
<allow_inactive>auth_admin</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
+ <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.shell org.freedesktop.login1.login</annotate>
</action>
<action id="org.freedesktop.machine1.manage-images">
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 78e96c4e5b..cc9dc393c6 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -2240,6 +2240,14 @@ int link_save(Link *link) {
if (link->network) {
char **address, **domain;
bool space;
+ sd_dhcp6_lease *dhcp6_lease = NULL;
+
+ if (link->dhcp6_client) {
+ r = sd_dhcp6_client_get_lease(link->dhcp6_client,
+ &dhcp6_lease);
+ if (r < 0)
+ log_link_debug(link, "No DHCPv6 lease");
+ }
fprintf(f, "NETWORK_FILE=%s\n", link->network->filename);
@@ -2261,6 +2269,19 @@ int link_save(Link *link) {
if (space)
fputc(' ', f);
serialize_in_addrs(f, addresses, r);
+ space = true;
+ }
+ }
+
+ if (link->network->dhcp_dns && dhcp6_lease) {
+ struct in6_addr *in6_addrs;
+
+ r = sd_dhcp6_lease_get_dns(dhcp6_lease, &in6_addrs);
+ if (r > 0) {
+ if (space)
+ fputc(' ', f);
+ serialize_in6_addrs(f, in6_addrs, r);
+ space = true;
}
}
@@ -2284,6 +2305,32 @@ int link_save(Link *link) {
if (space)
fputc(' ', f);
serialize_in_addrs(f, addresses, r);
+ space = true;
+ }
+ }
+
+ if (link->network->dhcp_ntp && dhcp6_lease) {
+ struct in6_addr *in6_addrs;
+ char **hosts;
+ char **hostname;
+
+ r = sd_dhcp6_lease_get_ntp_addrs(dhcp6_lease,
+ &in6_addrs);
+ if (r > 0) {
+ if (space)
+ fputc(' ', f);
+ serialize_in6_addrs(f, in6_addrs, r);
+ space = true;
+ }
+
+ r = sd_dhcp6_lease_get_ntp_fqdn(dhcp6_lease, &hosts);
+ if (r > 0) {
+ STRV_FOREACH(hostname, hosts) {
+ if (space)
+ fputc(' ', f);
+ fputs(*hostname, f);
+ space = true;
+ }
}
}
@@ -2307,6 +2354,21 @@ int link_save(Link *link) {
if (space)
fputc(' ', f);
fputs(domainname, f);
+ space = true;
+ }
+ }
+
+ if (link->network->dhcp_domains && dhcp6_lease) {
+ char **domains;
+
+ r = sd_dhcp6_lease_get_domains(dhcp6_lease, &domains);
+ if (r >= 0) {
+ STRV_FOREACH(domain, domains) {
+ if (space)
+ fputc(' ', f);
+ fputs(*domain, f);
+ space = true;
+ }
}
}
@@ -2316,7 +2378,7 @@ int link_save(Link *link) {
yes_no(link->network->wildcard_domain));
fprintf(f, "LLMNR=%s\n",
- llmnr_support_to_string(link->network->llmnr));
+ resolve_support_to_string(link->network->llmnr));
}
if (!hashmap_isempty(link->bound_to_links)) {
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 8735b39581..7ac7ef1ea3 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -45,7 +45,7 @@ Network.Address, config_parse_address, 0
Network.Gateway, config_parse_gateway, 0, 0
Network.Domains, config_parse_domains, 0, offsetof(Network, domains)
Network.DNS, config_parse_strv, 0, offsetof(Network, dns)
-Network.LLMNR, config_parse_llmnr, 0, offsetof(Network, llmnr)
+Network.LLMNR, config_parse_resolve, 0, offsetof(Network, llmnr)
Network.NTP, config_parse_strv, 0, offsetof(Network, ntp)
Network.IPForward, config_parse_address_family_boolean_with_kernel,0, offsetof(Network, ip_forward)
Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 3f7e77da3e..6587ea994c 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -111,7 +111,7 @@ static int network_load_one(Manager *manager, const char *filename) {
network->allow_port_to_be_root = true;
network->unicast_flood = true;
- network->llmnr = LLMNR_SUPPORT_YES;
+ network->llmnr = RESOLVE_SUPPORT_YES;
network->link_local = ADDRESS_FAMILY_IPV6;
@@ -632,15 +632,15 @@ static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DCHPClientIdentifier);
DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DCHPClientIdentifier, "Failed to parse client identifier type");
-static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
- [LLMNR_SUPPORT_NO] = "no",
- [LLMNR_SUPPORT_YES] = "yes",
- [LLMNR_SUPPORT_RESOLVE] = "resolve",
+static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = {
+ [RESOLVE_SUPPORT_NO] = "no",
+ [RESOLVE_SUPPORT_YES] = "yes",
+ [RESOLVE_SUPPORT_RESOLVE] = "resolve",
};
-DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
+DEFINE_STRING_TABLE_LOOKUP(resolve_support, ResolveSupport);
-int config_parse_llmnr(
+int config_parse_resolve(
const char* unit,
const char *filename,
unsigned line,
@@ -652,32 +652,32 @@ int config_parse_llmnr(
void *data,
void *userdata) {
- LLMNRSupport *llmnr = data;
+ ResolveSupport *resolve = data;
int k;
assert(filename);
assert(lvalue);
assert(rvalue);
- assert(llmnr);
+ assert(resolve);
/* Our enum shall be a superset of booleans, hence first try
* to parse as boolean, and then as enum */
k = parse_boolean(rvalue);
if (k > 0)
- *llmnr = LLMNR_SUPPORT_YES;
+ *resolve = RESOLVE_SUPPORT_YES;
else if (k == 0)
- *llmnr = LLMNR_SUPPORT_NO;
+ *resolve = RESOLVE_SUPPORT_NO;
else {
- LLMNRSupport s;
+ ResolveSupport s;
- s = llmnr_support_from_string(rvalue);
+ s = resolve_support_from_string(rvalue);
if (s < 0){
- log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse %s option, ignoring: %s", lvalue, rvalue);
return 0;
}
- *llmnr = s;
+ *resolve = s;
}
return 0;
diff --git a/src/network/networkd.h b/src/network/networkd.h
index a285a4b08f..5340922bf1 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -64,13 +64,13 @@ typedef enum AddressFamilyBoolean {
_ADDRESS_FAMILY_BOOLEAN_INVALID = -1,
} AddressFamilyBoolean;
-typedef enum LLMNRSupport {
- LLMNR_SUPPORT_NO,
- LLMNR_SUPPORT_YES,
- LLMNR_SUPPORT_RESOLVE,
- _LLMNR_SUPPORT_MAX,
- _LLMNR_SUPPORT_INVALID = -1,
-} LLMNRSupport;
+typedef enum ResolveSupport {
+ RESOLVE_SUPPORT_NO,
+ RESOLVE_SUPPORT_YES,
+ RESOLVE_SUPPORT_RESOLVE,
+ _RESOLVE_SUPPORT_MAX,
+ _RESOLVE_SUPPORT_INVALID = -1,
+} ResolveSupport;
typedef enum LinkOperationalState {
LINK_OPERSTATE_OFF,
@@ -178,7 +178,7 @@ struct Network {
bool wildcard_domain;
char **domains, **dns, **ntp, **bind_carrier;
- LLMNRSupport llmnr;
+ ResolveSupport llmnr;
LIST_FIELDS(Network, networks);
};
@@ -421,14 +421,14 @@ int config_parse_ipv6token(const char *unit, const char *filename, unsigned line
const char *section, unsigned section_line, const char *lvalue,
int ltype, const char *rvalue, void *data, void *userdata);
-/* LLMNR support */
+/* Resolve protocols support */
-const char* llmnr_support_to_string(LLMNRSupport i) _const_;
-LLMNRSupport llmnr_support_from_string(const char *s) _pure_;
+const char* resolve_support_to_string(ResolveSupport i) _const_;
+ResolveSupport resolve_support_from_string(const char *s) _pure_;
-int config_parse_llmnr(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_resolve(const char *unit, const char *filename, unsigned line,
+ const char *section, unsigned section_line, const char *lvalue,
+ int ltype, const char *rvalue, void *data, void *userdata);
/* Address Pool */
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 347260013a..837947ee28 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -559,9 +559,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'M':
- if (isempty(optarg)) {
+ if (isempty(optarg))
arg_machine = mfree(arg_machine);
- } else {
+ else {
if (!machine_name_is_valid(optarg)) {
log_error("Invalid machine name: %s", optarg);
return -EINVAL;
@@ -658,7 +658,6 @@ static int parse_argv(int argc, char *argv[]) {
const char *current = optarg;
_cleanup_free_ char *source = NULL, *destination = NULL;
CustomMount *m;
- _cleanup_strv_free_ char **strv = NULL;
r = extract_many_words(&current, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL);
switch (r) {
@@ -4003,6 +4002,17 @@ static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo
static int determine_names(void) {
int r;
+ if (arg_template && !arg_directory && arg_machine) {
+
+ /* If --template= was specified then we should not
+ * search for a machine, but instead create a new one
+ * in /var/lib/machine. */
+
+ arg_directory = strjoin("/var/lib/machines/", arg_machine, NULL);
+ if (!arg_directory)
+ return log_oom();
+ }
+
if (!arg_image && !arg_directory) {
if (arg_machine) {
_cleanup_(image_unrefp) Image *i = NULL;
diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c
index 69069cc75d..0dca891447 100644
--- a/src/nss-myhostname/nss-myhostname.c
+++ b/src/nss-myhostname/nss-myhostname.c
@@ -43,13 +43,6 @@
NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
-static bool is_gateway(const char *hostname) {
- assert(hostname);
-
- return streq(hostname, "gateway") ||
- streq(hostname, "gateway.");
-}
-
enum nss_status _nss_myhostname_gethostbyname4_r(
const char *name,
struct gaih_addrtuple **pat,
@@ -81,7 +74,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
canonical = "localhost";
local_address_ipv4 = htonl(INADDR_LOOPBACK);
- } else if (is_gateway(name)) {
+ } else if (is_gateway_hostname(name)) {
n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
if (n_addresses <= 0) {
@@ -351,7 +344,7 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
canonical = "localhost";
local_address_ipv4 = htonl(INADDR_LOOPBACK);
- } else if (is_gateway(name)) {
+ } else if (is_gateway_hostname(name)) {
n_addresses = local_gateways(NULL, 0, af, &addresses);
if (n_addresses <= 0) {
diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c
index cdec83d074..5758ea1569 100644
--- a/src/nss-mymachines/nss-mymachines.c
+++ b/src/nss-mymachines/nss-mymachines.c
@@ -30,6 +30,7 @@
#include "bus-util.h"
#include "bus-common-errors.h"
#include "in-addr-util.h"
+#include "hostname-util.h"
NSS_GETHOSTBYNAME_PROTOTYPES(mymachines);
NSS_GETPW_PROTOTYPES(mymachines);
diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c
index da22f98eba..ef5eb7b4cf 100644
--- a/src/nss-resolve/nss-resolve.c
+++ b/src/nss-resolve/nss-resolve.c
@@ -61,23 +61,21 @@ static bool bus_error_shall_fallback(sd_bus_error *e) {
}
static int count_addresses(sd_bus_message *m, int af, const char **canonical) {
- int c = 0, r, ifindex;
+ int c = 0, r;
assert(m);
assert(canonical);
- r = sd_bus_message_read(m, "i", &ifindex);
+ r = sd_bus_message_enter_container(m, 'a', "(iiay)");
if (r < 0)
return r;
- r = sd_bus_message_enter_container(m, 'a', "(iay)");
- if (r < 0)
- return r;
+ while ((r = sd_bus_message_enter_container(m, 'r', "iiay")) > 0) {
+ int family, ifindex;
- while ((r = sd_bus_message_enter_container(m, 'r', "iay")) > 0) {
- int family;
+ assert_cc(sizeof(int32_t) == sizeof(int));
- r = sd_bus_message_read(m, "i", &family);
+ r = sd_bus_message_read(m, "ii", &ifindex, &family);
if (r < 0)
return r;
@@ -126,7 +124,7 @@ enum nss_status _nss_resolve_gethostbyname4_r(
const char *canonical = NULL;
size_t l, ms, idx;
char *r_name;
- int c, r, i = 0, ifindex;
+ int c, r, i = 0;
assert(name);
assert(pat);
@@ -218,28 +216,26 @@ enum nss_status _nss_resolve_gethostbyname4_r(
/* Second, append addresses */
r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
- r = sd_bus_message_read(reply, "i", &ifindex);
- if (r < 0)
- goto fail;
-
- if (ifindex < 0) {
- r = -EINVAL;
- goto fail;
- }
-
- r = sd_bus_message_enter_container(reply, 'a', "(iay)");
+ r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
if (r < 0)
goto fail;
- while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
- int family;
+ while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
+ int family, ifindex;
const void *a;
size_t sz;
- r = sd_bus_message_read(reply, "i", &family);
+ assert_cc(sizeof(int32_t) == sizeof(int));
+
+ r = sd_bus_message_read(reply, "ii", &ifindex, &family);
if (r < 0)
goto fail;
+ if (ifindex < 0) {
+ r = -EINVAL;
+ goto fail;
+ }
+
r = sd_bus_message_read_array(reply, 'y', &a, &sz);
if (r < 0)
goto fail;
@@ -308,7 +304,7 @@ enum nss_status _nss_resolve_gethostbyname3_r(
_cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
size_t l, idx, ms, alen;
const char *canonical;
- int c, r, i = 0, ifindex;
+ int c, r, i = 0;
assert(name);
assert(result);
@@ -420,28 +416,24 @@ enum nss_status _nss_resolve_gethostbyname3_r(
/* Third, append addresses */
r_addr = buffer + idx;
- r = sd_bus_message_read(reply, "i", &ifindex);
- if (r < 0)
- goto fail;
-
- if (ifindex < 0) {
- r = -EINVAL;
- goto fail;
- }
-
- r = sd_bus_message_enter_container(reply, 'a', "(iay)");
+ r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
if (r < 0)
goto fail;
- while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
- int family;
+ while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
+ int ifindex, family;
const void *a;
size_t sz;
- r = sd_bus_message_read(reply, "i", &family);
+ r = sd_bus_message_read(reply, "ii", &ifindex, &family);
if (r < 0)
goto fail;
+ if (ifindex < 0) {
+ r = -EINVAL;
+ goto fail;
+ }
+
r = sd_bus_message_read_array(reply, 'y', &a, &sz);
if (r < 0)
goto fail;
@@ -603,20 +595,17 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
return NSS_STATUS_UNAVAIL;
}
- r = sd_bus_message_read(reply, "i", &ifindex);
+ r = sd_bus_message_enter_container(reply, 'a', "(is)");
if (r < 0)
goto fail;
- if (ifindex < 0) {
- r = -EINVAL;
- goto fail;
- }
+ while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) {
- r = sd_bus_message_enter_container(reply, 'a', "s");
- if (r < 0)
- goto fail;
+ if (ifindex < 0) {
+ r = -EINVAL;
+ goto fail;
+ }
- while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
c++;
ms += ALIGN(strlen(n) + 1);
}
@@ -661,7 +650,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
/* Fourth, place aliases */
i = 0;
r_name = buffer + idx;
- while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
+ while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) {
char *p;
size_t l;
diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c
index 7525d7c32f..9847effb53 100644
--- a/src/resolve-host/resolve-host.c
+++ b/src/resolve-host/resolve-host.c
@@ -41,13 +41,13 @@ static uint16_t arg_class = 0;
static bool arg_legend = true;
static uint64_t arg_flags = 0;
-static void print_source(int ifindex, uint64_t flags, usec_t rtt) {
+static void print_source(uint64_t flags, usec_t rtt) {
char rtt_str[FORMAT_TIMESTAMP_MAX];
if (!arg_legend)
return;
- if (ifindex <= 0 && flags == 0)
+ if (flags == 0)
return;
fputs("\n-- Information acquired via", stdout);
@@ -58,11 +58,6 @@ static void print_source(int ifindex, uint64_t flags, usec_t rtt) {
flags & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "",
flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "");
- if (ifindex > 0) {
- char ifname[IF_NAMESIZE] = "";
- printf(" interface %s", strna(if_indextoname(ifindex, ifname)));
- }
-
assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100));
printf(" in %s", rtt_str);
@@ -76,14 +71,18 @@ static int resolve_host(sd_bus *bus, const char *name) {
_cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
const char *canonical = NULL;
+ char ifname[IF_NAMESIZE] = "";
unsigned c = 0;
- int r, ifindex;
+ int r;
uint64_t flags;
usec_t ts;
assert(name);
- log_debug("Resolving %s (family %s, ifindex %i).", name, af_to_name(arg_family) ?: "*", arg_ifindex);
+ if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
+ return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
+
+ log_debug("Resolving %s (family %s, interface %s).", name, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
r = sd_bus_message_new_method_call(
bus,
@@ -109,22 +108,19 @@ static int resolve_host(sd_bus *bus, const char *name) {
ts = now(CLOCK_MONOTONIC) - ts;
- r = sd_bus_message_read(reply, "i", &ifindex);
+ r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
if (r < 0)
return bus_log_parse_error(r);
- r = sd_bus_message_enter_container(reply, 'a', "(iay)");
- if (r < 0)
- return bus_log_parse_error(r);
-
- while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
+ while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
const void *a;
- int family;
size_t sz;
_cleanup_free_ char *pretty = NULL;
- char ifname[IF_NAMESIZE] = "";
+ int ifindex, family;
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
- r = sd_bus_message_read(reply, "i", &family);
+ r = sd_bus_message_read(reply, "ii", &ifindex, &family);
if (r < 0)
return bus_log_parse_error(r);
@@ -142,26 +138,17 @@ static int resolve_host(sd_bus *bus, const char *name) {
}
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");
+ log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
continue;
}
- if (ifindex > 0) {
- char *t;
-
- t = if_indextoname(ifindex, ifname);
- if (!t) {
- log_error("Failed to resolve interface name for index %i", ifindex);
- continue;
- }
- }
+ ifname[0] = 0;
+ if (ifindex > 0 && !if_indextoname(ifindex, ifname))
+ log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
r = in_addr_to_string(family, a, &pretty);
- if (r < 0) {
- log_error_errno(r, "%s: failed to print address: %m", name);
- continue;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to print address for %s: %m", name);
printf("%*s%s %s%s%s\n",
(int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
@@ -192,7 +179,7 @@ static int resolve_host(sd_bus *bus, const char *name) {
return -ESRCH;
}
- print_source(ifindex, flags, ts);
+ print_source(flags, ts);
return 0;
}
@@ -204,7 +191,6 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
char ifname[IF_NAMESIZE] = "";
uint64_t flags;
unsigned c = 0;
- const char *n;
usec_t ts;
int r;
@@ -212,19 +198,15 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
assert(IN_SET(family, AF_INET, AF_INET6));
assert(address);
+ if (ifindex <= 0)
+ ifindex = arg_ifindex;
+
r = in_addr_to_string(family, address, &pretty);
if (r < 0)
return log_oom();
- if (ifindex > 0) {
- char *t;
-
- t = if_indextoname(ifindex, ifname);
- if (!t) {
- log_error("Failed to resolve interface name for index %i", ifindex);
- return -errno;
- }
- }
+ if (ifindex > 0 && !if_indextoname(ifindex, ifname))
+ return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname);
@@ -238,10 +220,6 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_set_auto_start(req, false);
- if (r < 0)
- return bus_log_create_error(r);
-
r = sd_bus_message_append(req, "ii", ifindex, family);
if (r < 0)
return bus_log_create_error(r);
@@ -264,19 +242,31 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
ts = now(CLOCK_MONOTONIC) - ts;
- r = sd_bus_message_read(reply, "i", &ifindex);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_enter_container(reply, 'a', "s");
+ r = sd_bus_message_enter_container(reply, 'a', "(is)");
if (r < 0)
return bus_log_create_error(r);
- while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
+ while ((r = sd_bus_message_enter_container(reply, 'r', "is")) > 0) {
+ const char *n;
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
- printf("%*s%s%s%s %s\n",
+ r = sd_bus_message_read(reply, "is", &ifindex, &n);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return r;
+
+ ifname[0] = 0;
+ if (ifindex > 0 && !if_indextoname(ifindex, ifname))
+ log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
+
+ printf("%*s%*s%*s%s %s\n",
(int) strlen(pretty), c == 0 ? pretty : "",
- isempty(ifname) ? "" : "%", ifname,
+ isempty(ifname) ? 0 : 1, c > 0 || isempty(ifname) ? "" : "%",
+ (int) strlen(ifname), c == 0 ? ifname : "",
c == 0 ? ":" : " ",
n);
@@ -298,7 +288,7 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
return -ESRCH;
}
- print_source(ifindex, flags, ts);
+ print_source(flags, ts);
return 0;
}
@@ -333,14 +323,18 @@ static int resolve_record(sd_bus *bus, const char *name) {
_cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ char ifname[IF_NAMESIZE] = "";
unsigned n = 0;
uint64_t flags;
- int r, ifindex;
+ int r;
usec_t ts;
assert(name);
- log_debug("Resolving %s %s %s.", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type));
+ if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
+ return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
+
+ log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type), isempty(ifname) ? "*" : ifname);
r = sd_bus_message_new_method_call(
bus,
@@ -367,23 +361,22 @@ static int resolve_record(sd_bus *bus, const char *name) {
ts = now(CLOCK_MONOTONIC) - ts;
- r = sd_bus_message_read(reply, "i", &ifindex);
+ r = sd_bus_message_enter_container(reply, 'a', "(iqqay)");
if (r < 0)
return bus_log_parse_error(r);
- r = sd_bus_message_enter_container(reply, 'a', "(qqay)");
- if (r < 0)
- return bus_log_parse_error(r);
-
- while ((r = sd_bus_message_enter_container(reply, 'r', "qqay")) > 0) {
+ while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
_cleanup_free_ char *s = NULL;
uint16_t c, t;
+ int ifindex;
const void *d;
size_t l;
- r = sd_bus_message_read(reply, "qq", &c, &t);
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
+ r = sd_bus_message_read(reply, "iqq", &ifindex, &c, &t);
if (r < 0)
return bus_log_parse_error(r);
@@ -399,6 +392,8 @@ static int resolve_record(sd_bus *bus, const char *name) {
if (r < 0)
return log_oom();
+ p->refuse_compression = true;
+
r = dns_packet_append_blob(p, d, l, NULL);
if (r < 0)
return log_oom();
@@ -415,7 +410,11 @@ static int resolve_record(sd_bus *bus, const char *name) {
return r;
}
- printf("%s\n", s);
+ ifname[0] = 0;
+ if (ifindex > 0 && !if_indextoname(ifindex, ifname))
+ log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
+
+ printf("%s%s%s\n", s, isempty(ifname) ? "" : " # interface ", ifname);
n++;
}
if (r < 0)
@@ -434,7 +433,7 @@ static int resolve_record(sd_bus *bus, const char *name) {
return -ESRCH;
}
- print_source(ifindex, flags, ts);
+ print_source(flags, ts);
return 0;
}
@@ -444,7 +443,7 @@ static void help_dns_types(void) {
const char *t;
if (arg_legend)
- puts("Known dns types:");
+ puts("Known DNS RR types:");
for (i = 0; i < _DNS_TYPE_MAX; i++) {
t = dns_type_to_string(i);
if (t)
@@ -457,7 +456,7 @@ static void help_dns_classes(void) {
const char *t;
if (arg_legend)
- puts("Known dns classes:");
+ puts("Known DNS RR classes:");
for (i = 0; i < _DNS_CLASS_MAX; i++) {
t = dns_class_to_string(i);
if (t)
@@ -491,7 +490,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "version", no_argument, NULL, ARG_VERSION },
{ "type", required_argument, NULL, 't' },
{ "class", required_argument, NULL, 'c' },
- { "legend", optional_argument, NULL, ARG_LEGEND },
+ { "legend", optional_argument, NULL, ARG_LEGEND },
{ "protocol", required_argument, NULL, 'p' },
{}
};
@@ -521,11 +520,21 @@ static int parse_argv(int argc, char *argv[]) {
arg_family = AF_INET6;
break;
- case 'i':
- arg_ifindex = if_nametoindex(optarg);
- if (arg_ifindex <= 0)
- return log_error_errno(errno, "Unknown interfaces %s: %m", optarg);
+ case 'i': {
+ int ifi;
+
+ if (safe_atoi(optarg, &ifi) >= 0 && ifi > 0)
+ arg_ifindex = ifi;
+ else {
+ ifi = if_nametoindex(optarg);
+ if (ifi <= 0)
+ return log_error_errno(errno, "Unknown interface %s: %m", optarg);
+
+ arg_ifindex = ifi;
+ }
+
break;
+ }
case 't':
if (streq(optarg, "help")) {
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 6db12511f9..12c17003e9 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -91,13 +91,17 @@ static int reply_query_state(DnsQuery *q) {
}
}
-static int append_address(sd_bus_message *reply, DnsResourceRecord *rr) {
+static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifindex) {
int r;
assert(reply);
assert(rr);
- r = sd_bus_message_open_container(reply, 'r', "iay");
+ r = sd_bus_message_open_container(reply, 'r', "iiay");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "i", ifindex);
if (r < 0)
return r;
@@ -145,11 +149,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- r = sd_bus_message_append(reply, "i", q->answer_ifindex);
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_open_container(reply, 'a', "(iay)");
+ r = sd_bus_message_open_container(reply, 'a', "(iiay)");
if (r < 0)
goto finish;
@@ -157,27 +157,27 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
answer = dns_answer_ref(q->answer);
for (i = 0; i < answer->n_rrs; i++) {
- r = dns_question_matches_rr(q->question, answer->rrs[i]);
+ r = dns_question_matches_rr(q->question, answer->items[i].rr);
if (r < 0)
goto finish;
if (r == 0) {
/* Hmm, if this is not an address record,
maybe it's a cname? If so, remember this */
- r = dns_question_matches_cname(q->question, answer->rrs[i]);
+ r = dns_question_matches_cname(q->question, answer->items[i].rr);
if (r < 0)
goto finish;
if (r > 0)
- cname = dns_resource_record_ref(answer->rrs[i]);
+ cname = dns_resource_record_ref(answer->items[i].rr);
continue;
}
- r = append_address(reply, answer->rrs[i]);
+ r = append_address(reply, answer->items[i].rr, answer->items[i].ifindex);
if (r < 0)
goto finish;
if (!canonical)
- canonical = dns_resource_record_ref(answer->rrs[i]);
+ canonical = dns_resource_record_ref(answer->items[i].rr);
added ++;
}
@@ -204,18 +204,18 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
/* Before we restart the query, let's see if any of
* the RRs we already got already answers our query */
for (i = 0; i < answer->n_rrs; i++) {
- r = dns_question_matches_rr(q->question, answer->rrs[i]);
+ r = dns_question_matches_rr(q->question, answer->items[i].rr);
if (r < 0)
goto finish;
if (r == 0)
continue;
- r = append_address(reply, answer->rrs[i]);
+ r = append_address(reply, answer->items[i].rr, answer->items[i].ifindex);
if (r < 0)
goto finish;
if (!canonical)
- canonical = dns_resource_record_ref(answer->rrs[i]);
+ canonical = dns_resource_record_ref(answer->items[i].rr);
added++;
}
@@ -226,10 +226,6 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
* query, this time with the cname */
if (added <= 0) {
r = dns_query_go(q);
- if (r == -ESRCH) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
- goto finish;
- }
if (r < 0) {
r = sd_bus_reply_method_errno(q->request, -r, NULL);
goto finish;
@@ -346,10 +342,6 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
r = dns_query_go(q);
if (r < 0) {
dns_query_free(q);
-
- if (r == -ESRCH)
- sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
-
return r;
}
@@ -373,11 +365,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- r = sd_bus_message_append(reply, "i", q->answer_ifindex);
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_open_container(reply, 'a', "s");
+ r = sd_bus_message_open_container(reply, 'a', "(is)");
if (r < 0)
goto finish;
@@ -385,13 +373,13 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
answer = dns_answer_ref(q->answer);
for (i = 0; i < answer->n_rrs; i++) {
- r = dns_question_matches_rr(q->question, answer->rrs[i]);
+ r = dns_question_matches_rr(q->question, answer->items[i].rr);
if (r < 0)
goto finish;
if (r == 0)
continue;
- r = sd_bus_message_append(reply, "s", answer->rrs[i]->ptr.name);
+ r = sd_bus_message_append(reply, "(is)", answer->items[i].ifindex, answer->items[i].rr->ptr.name);
if (r < 0)
goto finish;
@@ -498,10 +486,6 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
r = dns_query_go(q);
if (r < 0) {
dns_query_free(q);
-
- if (r == -ESRCH)
- sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
-
return r;
}
@@ -525,11 +509,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- r = sd_bus_message_append(reply, "i", q->answer_ifindex);
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_open_container(reply, 'a', "(qqay)");
+ r = sd_bus_message_open_container(reply, 'a', "(iqqay)");
if (r < 0)
goto finish;
@@ -540,7 +520,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
size_t start;
- r = dns_question_matches_rr(q->question, answer->rrs[i]);
+ r = dns_question_matches_rr(q->question, answer->items[i].rr);
if (r < 0)
goto finish;
if (r == 0)
@@ -550,15 +530,20 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- r = dns_packet_append_rr(p, answer->rrs[i], &start);
+ p->refuse_compression = true;
+
+ r = dns_packet_append_rr(p, answer->items[i].rr, &start);
if (r < 0)
goto finish;
- r = sd_bus_message_open_container(reply, 'r', "qqay");
+ r = sd_bus_message_open_container(reply, 'r', "iqqay");
if (r < 0)
goto finish;
- r = sd_bus_message_append(reply, "qq", answer->rrs[i]->key->class, answer->rrs[i]->key->type);
+ r = sd_bus_message_append(reply, "iqq",
+ answer->items[i].ifindex,
+ answer->items[i].rr->key->class,
+ answer->items[i].rr->key->type);
if (r < 0)
goto finish;
@@ -650,10 +635,6 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
r = dns_query_go(q);
if (r < 0) {
dns_query_free(q);
-
- if (r == -ESRCH)
- sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
-
return r;
}
@@ -662,9 +643,9 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_VTABLE_START(0),
- SD_BUS_METHOD("ResolveHostname", "isit", "ia(iay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("ResolveAddress", "iiayt", "iast", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("ResolveRecord", "isqqt", "ia(qqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ResolveHostname", "isit", "a(iiay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ResolveRecord", "isqqt", "a(iqqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END,
};
diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c
index f77b98e505..13ad4ca6bd 100644
--- a/src/resolve/resolved-dns-answer.c
+++ b/src/resolve/resolved-dns-answer.c
@@ -25,7 +25,7 @@
DnsAnswer *dns_answer_new(unsigned n) {
DnsAnswer *a;
- a = malloc0(offsetof(DnsAnswer, rrs) + sizeof(DnsResourceRecord*) * n);
+ a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n);
if (!a)
return NULL;
@@ -54,7 +54,7 @@ DnsAnswer *dns_answer_unref(DnsAnswer *a) {
unsigned i;
for (i = 0; i < a->n_rrs; i++)
- dns_resource_record_unref(a->rrs[i]);
+ dns_resource_record_unref(a->items[i].rr);
free(a);
} else
@@ -63,25 +63,30 @@ DnsAnswer *dns_answer_unref(DnsAnswer *a) {
return NULL;
}
-int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr) {
+int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex) {
unsigned i;
int r;
- assert(a);
assert(rr);
+ if (!a)
+ return -ENOSPC;
+
for (i = 0; i < a->n_rrs; i++) {
- r = dns_resource_record_equal(a->rrs[i], rr);
+ if (a->items[i].ifindex != ifindex)
+ continue;
+
+ r = dns_resource_record_equal(a->items[i].rr, rr);
if (r < 0)
return r;
if (r > 0) {
/* Entry already exists, keep the entry with
* the higher RR, or the one with TTL 0 */
- if (rr->ttl == 0 || (rr->ttl > a->rrs[i]->ttl && a->rrs[i]->ttl != 0)) {
+ if (rr->ttl == 0 || (rr->ttl > a->items[i].rr->ttl && a->items[i].rr->ttl != 0)) {
dns_resource_record_ref(rr);
- dns_resource_record_unref(a->rrs[i]);
- a->rrs[i] = rr;
+ dns_resource_record_unref(a->items[i].rr);
+ a->items[i].rr = rr;
}
return 0;
@@ -91,7 +96,10 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr) {
if (a->n_rrs >= a->n_allocated)
return -ENOSPC;
- a->rrs[a->n_rrs++] = dns_resource_record_ref(rr);
+ a->items[a->n_rrs].rr = dns_resource_record_ref(rr);
+ a->items[a->n_rrs].ifindex = ifindex;
+ a->n_rrs++;
+
return 1;
}
@@ -118,18 +126,20 @@ int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) {
soa->soa.expire = 1;
soa->soa.minimum = ttl;
- return dns_answer_add(a, soa);
+ return dns_answer_add(a, soa, 0);
}
int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) {
unsigned i;
int r;
- assert(a);
assert(key);
+ if (!a)
+ return 0;
+
for (i = 0; i < a->n_rrs; i++) {
- r = dns_resource_key_match_rr(key, a->rrs[i]);
+ r = dns_resource_key_match_rr(key, a->items[i].rr);
if (r < 0)
return r;
if (r > 0)
@@ -142,24 +152,26 @@ int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) {
int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret) {
unsigned i;
- assert(a);
assert(key);
assert(ret);
+ if (!a)
+ return 0;
+
/* For a SOA record we can never find a matching SOA record */
if (key->type == DNS_TYPE_SOA)
return 0;
for (i = 0; i < a->n_rrs; i++) {
- if (a->rrs[i]->key->class != DNS_CLASS_IN)
+ if (a->items[i].rr->key->class != DNS_CLASS_IN)
continue;
- if (a->rrs[i]->key->type != DNS_TYPE_SOA)
+ if (a->items[i].rr->key->type != DNS_TYPE_SOA)
continue;
- if (dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(a->rrs[i]->key))) {
- *ret = a->rrs[i];
+ if (dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(a->items[i].rr->key))) {
+ *ret = a->items[i].rr;
return 1;
}
}
@@ -184,7 +196,7 @@ DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) {
if (a) {
for (i = 0; i < a->n_rrs; i++) {
- r = dns_answer_add(ret, a->rrs[i]);
+ r = dns_answer_add(ret, a->items[i].rr, a->items[i].ifindex);
if (r < 0)
return NULL;
}
@@ -192,7 +204,7 @@ DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) {
if (b) {
for (i = 0; i < b->n_rrs; i++) {
- r = dns_answer_add(ret, b->rrs[i]);
+ r = dns_answer_add(ret, b->items[i].rr, b->items[i].ifindex);
if (r < 0)
return NULL;
}
@@ -205,9 +217,11 @@ DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) {
}
void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
- DnsResourceRecord **rrs;
+ DnsAnswerItem *items;
unsigned i, start, end;
- assert(a);
+
+ if (!a)
+ return;
if (a->n_rrs <= 1)
return;
@@ -218,19 +232,51 @@ void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
/* RFC 4795, Section 2.6 suggests we should order entries
* depending on whether the sender is a link-local address. */
- rrs = newa(DnsResourceRecord*, a->n_rrs);
+ items = newa(DnsAnswerItem, a->n_rrs);
for (i = 0; i < a->n_rrs; i++) {
- if (a->rrs[i]->key->class == DNS_CLASS_IN &&
- ((a->rrs[i]->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->rrs[i]->a.in_addr) != prefer_link_local) ||
- (a->rrs[i]->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->rrs[i]->aaaa.in6_addr) != prefer_link_local)))
+ if (a->items[i].rr->key->class == DNS_CLASS_IN &&
+ ((a->items[i].rr->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->items[i].rr->a.in_addr) != prefer_link_local) ||
+ (a->items[i].rr->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->items[i].rr->aaaa.in6_addr) != prefer_link_local)))
/* Order address records that are are not preferred to the end of the array */
- rrs[end--] = a->rrs[i];
+ items[end--] = a->items[i];
else
/* Order all other records to the beginning of the array */
- rrs[start++] = a->rrs[i];
+ items[start++] = a->items[i];
}
assert(start == end+1);
- memcpy(a->rrs, rrs, sizeof(DnsResourceRecord*) * a->n_rrs);
+ memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
+}
+
+int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
+ DnsAnswer *n;
+
+ if (n_free <= 0)
+ return 0;
+
+ if (*a) {
+ unsigned ns;
+
+ if ((*a)->n_ref > 1)
+ return -EBUSY;
+
+ ns = (*a)->n_rrs + n_free;
+
+ if ((*a)->n_allocated >= ns)
+ return 0;
+
+ n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns);
+ if (!n)
+ return -ENOMEM;
+
+ n->n_allocated = ns;
+ } else {
+ n = dns_answer_new(n_free);
+ if (!n)
+ return -ENOMEM;
+ }
+
+ *a = n;
+ return 0;
}
diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h
index af3e462ed5..0757dd60d0 100644
--- a/src/resolve/resolved-dns-answer.h
+++ b/src/resolve/resolved-dns-answer.h
@@ -22,22 +22,31 @@
***/
typedef struct DnsAnswer DnsAnswer;
+typedef struct DnsAnswerItem DnsAnswerItem;
#include "resolved-dns-rr.h"
-/* A simple array of resource records */
+/* A simple array of resource records. We keep track of the
+ * originating ifindex for each RR where that makes sense, so that we
+ * can qualify A and AAAA RRs referring to a local link with the
+ * right ifindex. */
+
+struct DnsAnswerItem {
+ DnsResourceRecord *rr;
+ int ifindex;
+};
struct DnsAnswer {
unsigned n_ref;
unsigned n_rrs, n_allocated;
- DnsResourceRecord* rrs[0];
+ DnsAnswerItem items[0];
};
DnsAnswer *dns_answer_new(unsigned n);
DnsAnswer *dns_answer_ref(DnsAnswer *a);
DnsAnswer *dns_answer_unref(DnsAnswer *a);
-int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr);
+int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex);
int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl);
int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key);
int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret);
@@ -45,4 +54,6 @@ int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **r
DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b);
void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local);
+int dns_answer_reserve(DnsAnswer **a, unsigned n_free);
+
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref);
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index 81ea1cafcb..ef6b69c768 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -411,22 +411,23 @@ int dns_cache_put(
int owner_family,
const union in_addr_union *owner_address) {
- unsigned i;
+ unsigned cache_keys, i;
int r;
assert(c);
- assert(q);
- /* First, delete all matching old RRs, so that we only keep
- * complete by_key in place. */
- for (i = 0; i < q->n_keys; i++)
- dns_cache_remove(c, q->keys[i]);
+ if (q) {
+ /* First, if we were passed a question, delete all matching old RRs,
+ * so that we only keep complete by_key in place. */
+ for (i = 0; i < q->n_keys; i++)
+ dns_cache_remove(c, q->keys[i]);
+ }
if (!answer)
return 0;
for (i = 0; i < answer->n_rrs; i++)
- dns_cache_remove(c, answer->rrs[i]->key);
+ dns_cache_remove(c, answer->items[i].rr->key);
/* We only care for positive replies and NXDOMAINs, on all
* other replies we will simply flush the respective entries,
@@ -435,19 +436,27 @@ int dns_cache_put(
if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
return 0;
+ cache_keys = answer->n_rrs;
+
+ if (q)
+ cache_keys += q->n_keys;
+
/* Make some space for our new entries */
- dns_cache_make_space(c, answer->n_rrs + q->n_keys);
+ dns_cache_make_space(c, cache_keys);
if (timestamp <= 0)
timestamp = now(clock_boottime_or_monotonic());
/* Second, add in positive entries for all contained RRs */
for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) {
- r = dns_cache_put_positive(c, answer->rrs[i], timestamp, owner_family, owner_address);
+ r = dns_cache_put_positive(c, answer->items[i].rr, timestamp, owner_family, owner_address);
if (r < 0)
goto fail;
}
+ if (!q)
+ return 0;
+
/* Third, add in negative entries for all keys with no RR */
for (i = 0; i < q->n_keys; i++) {
DnsResourceRecord *soa = NULL;
@@ -458,6 +467,10 @@ int dns_cache_put(
if (r > 0)
continue;
+ /* See https://tools.ietf.org/html/rfc2308, which
+ * say that a matching SOA record in the packet
+ * is used to to enable negative caching. */
+
r = dns_answer_find_soa(answer, q->keys[i], &soa);
if (r < 0)
goto fail;
@@ -475,80 +488,76 @@ fail:
/* Adding all RRs failed. Let's clean up what we already
* added, just in case */
- for (i = 0; i < q->n_keys; i++)
- dns_cache_remove(c, q->keys[i]);
+ if (q) {
+ for (i = 0; i < q->n_keys; i++)
+ dns_cache_remove(c, q->keys[i]);
+ }
+
for (i = 0; i < answer->n_rrs; i++)
- dns_cache_remove(c, answer->rrs[i]->key);
+ dns_cache_remove(c, answer->items[i].rr->key);
return r;
}
-int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) {
+int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- unsigned i, n = 0;
+ unsigned n = 0;
int r;
bool nxdomain = false;
+ _cleanup_free_ char *key_str = NULL;
+ DnsCacheItem *j, *first;
assert(c);
- assert(q);
+ assert(key);
assert(rcode);
assert(ret);
- if (q->n_keys <= 0) {
- *ret = NULL;
- *rcode = 0;
- return 0;
- }
+ if (key->type == DNS_TYPE_ANY ||
+ key->class == DNS_CLASS_ANY) {
- for (i = 0; i < q->n_keys; i++) {
- _cleanup_free_ char *key_str = NULL;
- DnsCacheItem *j;
+ /* If we have ANY lookups we simply refresh */
- if (q->keys[i]->type == DNS_TYPE_ANY ||
- q->keys[i]->class == DNS_CLASS_ANY) {
- /* If we have ANY lookups we simply refresh */
+ r = dns_resource_key_to_string(key, &key_str);
+ if (r < 0)
+ return r;
- r = dns_resource_key_to_string(q->keys[i], &key_str);
- if (r < 0)
- return r;
+ log_debug("Ignoring cache for ANY lookup: %s", key_str);
- log_debug("Ignoring cache for ANY lookup: %s", key_str);
+ *ret = NULL;
+ *rcode = DNS_RCODE_SUCCESS;
+ return 0;
+ }
- *ret = NULL;
- *rcode = 0;
- return 0;
- }
+ first = hashmap_get(c->by_key, key);
+ if (!first) {
+ /* If one question cannot be answered we need to refresh */
- j = hashmap_get(c->by_key, q->keys[i]);
- if (!j) {
- /* If one question cannot be answered we need to refresh */
+ r = dns_resource_key_to_string(key, &key_str);
+ if (r < 0)
+ return r;
- r = dns_resource_key_to_string(q->keys[i], &key_str);
- if (r < 0)
- return r;
+ log_debug("Cache miss for %s", key_str);
- log_debug("Cache miss for %s", key_str);
+ *ret = NULL;
+ *rcode = DNS_RCODE_SUCCESS;
+ return 0;
+ }
- *ret = NULL;
- *rcode = 0;
- return 0;
- } else {
- r = dns_resource_key_to_string(j->key, &key_str);
- if (r < 0)
- return r;
+ LIST_FOREACH(by_key, j, first) {
+ if (j->rr)
+ n++;
+ else if (j->type == DNS_CACHE_NXDOMAIN)
+ nxdomain = true;
+ }
- log_debug("%s cache hit for %s",
- j->type == DNS_CACHE_POSITIVE ? "Positive" :
- (j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN"), key_str);
- }
+ r = dns_resource_key_to_string(key, &key_str);
+ if (r < 0)
+ return r;
- LIST_FOREACH(by_key, j, j) {
- if (j->rr)
- n++;
- else if (j->type == DNS_CACHE_NXDOMAIN)
- nxdomain = true;
- }
- }
+ log_debug("%s cache hit for %s",
+ nxdomain ? "NXDOMAIN" :
+ n > 0 ? "Positive" : "NODATA",
+ key_str);
if (n <= 0) {
*ret = NULL;
@@ -560,17 +569,13 @@ int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) {
if (!answer)
return -ENOMEM;
- for (i = 0; i < q->n_keys; i++) {
- DnsCacheItem *j;
+ LIST_FOREACH(by_key, j, first) {
+ if (!j->rr)
+ continue;
- j = hashmap_get(c->by_key, q->keys[i]);
- LIST_FOREACH(by_key, j, j) {
- if (j->rr) {
- r = dns_answer_add(answer, j->rr);
- if (r < 0)
- return r;
- }
- }
+ r = dns_answer_add(answer, j->rr, 0);
+ if (r < 0)
+ return r;
}
*ret = answer;
@@ -615,3 +620,54 @@ int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_
/* There's a conflict */
return 1;
}
+
+void dns_cache_dump(DnsCache *cache, FILE *f) {
+ Iterator iterator;
+ DnsCacheItem *i;
+ int r;
+
+ if (!cache)
+ return;
+
+ if (!f)
+ f = stdout;
+
+ HASHMAP_FOREACH(i, cache->by_key, iterator) {
+ DnsCacheItem *j;
+
+ LIST_FOREACH(by_key, j, i) {
+ _cleanup_free_ char *t = NULL;
+
+ fputc('\t', f);
+
+ if (j->rr) {
+ r = dns_resource_record_to_string(j->rr, &t);
+ if (r < 0) {
+ log_oom();
+ continue;
+ }
+
+ fputs(t, f);
+ fputc('\n', f);
+ } else {
+ r = dns_resource_key_to_string(j->key, &t);
+ if (r < 0) {
+ log_oom();
+ continue;
+ }
+
+ fputs(t, f);
+ fputs(" -- ", f);
+ fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f);
+ fputc('\n', f);
+ }
+ }
+ }
+}
+
+bool dns_cache_is_empty(DnsCache *cache) {
+ if (!cache)
+ return true;
+
+ return hashmap_isempty(cache->by_key);
+}
diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h
index 8a9b3d459d..1225e58de4 100644
--- a/src/resolve/resolved-dns-cache.h
+++ b/src/resolve/resolved-dns-cache.h
@@ -40,6 +40,9 @@ void dns_cache_flush(DnsCache *c);
void dns_cache_prune(DnsCache *c);
int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
-int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **answer);
+int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **answer);
int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address);
+
+void dns_cache_dump(DnsCache *cache, FILE *f);
+bool dns_cache_is_empty(DnsCache *cache);
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index 35ad899544..bebd1ee4a6 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -388,14 +388,21 @@ int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start
return 0;
}
-int dns_packet_append_name(DnsPacket *p, const char *name,
- bool allow_compression, size_t *start) {
+int dns_packet_append_name(
+ DnsPacket *p,
+ const char *name,
+ bool allow_compression,
+ size_t *start) {
+
size_t saved_size;
int r;
assert(p);
assert(name);
+ if (p->refuse_compression)
+ allow_compression = false;
+
saved_size = p->size;
while (*name) {
@@ -1053,8 +1060,12 @@ fail:
return r;
}
-int dns_packet_read_name(DnsPacket *p, char **_ret,
- bool allow_compression, size_t *start) {
+int dns_packet_read_name(
+ DnsPacket *p,
+ char **_ret,
+ bool allow_compression,
+ size_t *start) {
+
size_t saved_rindex, after_rindex = 0, jump_barrier;
_cleanup_free_ char *ret = NULL;
size_t n = 0, allocated = 0;
@@ -1064,6 +1075,9 @@ int dns_packet_read_name(DnsPacket *p, char **_ret,
assert(p);
assert(_ret);
+ if (p->refuse_compression)
+ allow_compression = false;
+
saved_rindex = p->rindex;
jump_barrier = p->rindex;
@@ -1795,7 +1809,7 @@ int dns_packet_extract(DnsPacket *p) {
if (r < 0)
goto finish;
- r = dns_answer_add(answer, rr);
+ r = dns_answer_add(answer, rr, p->ifindex);
if (r < 0)
goto finish;
}
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index 58559c85df..5628e579ad 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -86,6 +86,7 @@ struct DnsPacket {
uint32_t ttl;
bool extracted;
+ bool refuse_compression;
};
static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) {
@@ -120,15 +121,15 @@ static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) {
#define DNS_PACKET_ARCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->arcount)
#define DNS_PACKET_MAKE_FLAGS(qr, opcode, aa, tc, rd, ra, ad, cd, rcode) \
- (((uint16_t) !!qr << 15) | \
- ((uint16_t) (opcode & 15) << 11) | \
- ((uint16_t) !!aa << 10) | \
- ((uint16_t) !!tc << 9) | \
- ((uint16_t) !!rd << 8) | \
- ((uint16_t) !!ra << 7) | \
- ((uint16_t) !!ad << 5) | \
- ((uint16_t) !!cd << 4) | \
- ((uint16_t) (rcode & 15)))
+ (((uint16_t) !!(qr) << 15) | \
+ ((uint16_t) ((opcode) & 15) << 11) | \
+ ((uint16_t) !!(aa) << 10) | /* on LLMNR: c */ \
+ ((uint16_t) !!(tc) << 9) | \
+ ((uint16_t) !!(rd) << 8) | /* on LLMNR: t */ \
+ ((uint16_t) !!(ra) << 7) | \
+ ((uint16_t) !!(ad) << 5) | \
+ ((uint16_t) !!(cd) << 4) | \
+ ((uint16_t) ((rcode) & 15)))
static inline unsigned DNS_PACKET_RRCOUNT(DnsPacket *p) {
return
@@ -238,11 +239,16 @@ static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family)
/* Converts a protocol + family into a flags field as used in queries */
- if (protocol == DNS_PROTOCOL_DNS)
+ switch (protocol) {
+ case DNS_PROTOCOL_DNS:
return SD_RESOLVED_DNS;
- if (protocol == DNS_PROTOCOL_LLMNR)
+ case DNS_PROTOCOL_LLMNR:
return family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4;
+ default:
+ break;
+ }
+
return 0;
}
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index 3ada442006..c0b4c8ba81 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -21,6 +21,7 @@
#include "hostname-util.h"
#include "dns-domain.h"
+#include "local-addresses.h"
#include "resolved-dns-query.h"
@@ -137,31 +138,20 @@ static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
}
static int dns_query_add_transaction(DnsQuery *q, DnsScope *s, DnsResourceKey *key) {
- _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
DnsTransaction *t;
int r;
assert(q);
assert(s);
+ assert(key);
r = set_ensure_allocated(&q->transactions, NULL);
if (r < 0)
return r;
- if (key) {
- question = dns_question_new(1);
- if (!question)
- return -ENOMEM;
-
- r = dns_question_add(question, key);
- if (r < 0)
- return r;
- } else
- question = dns_question_ref(q->question);
-
- t = dns_scope_find_transaction(s, question, true);
+ t = dns_scope_find_transaction(s, key, true);
if (!t) {
- r = dns_transaction_new(&t, s, question);
+ r = dns_transaction_new(&t, s, key);
if (r < 0)
return r;
}
@@ -188,27 +178,18 @@ gc:
}
static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) {
+ unsigned i;
int r;
assert(q);
assert(s);
- if (s->protocol == DNS_PROTOCOL_MDNS) {
- r = dns_query_add_transaction(q, s, NULL);
+ /* Create one transaction per question key */
+
+ for (i = 0; i < q->question->n_keys; i++) {
+ r = dns_query_add_transaction(q, s, q->question->keys[i]);
if (r < 0)
return r;
- } else {
- unsigned i;
-
- /* On DNS and LLMNR we can only send a single
- * question per datagram, hence issue multiple
- * transactions. */
-
- for (i = 0; i < q->question->n_keys; i++) {
- r = dns_query_add_transaction(q, s, q->question->keys[i]);
- if (r < 0)
- return r;
- }
}
return 0;
@@ -216,9 +197,10 @@ static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) {
static int SYNTHESIZE_IFINDEX(int ifindex) {
- /* When the caller asked for resolving on a specific interface,
- * we synthesize the answer for that interface. However, if
- * nothing specific was claimed, we synthesize the answer for
+ /* When the caller asked for resolving on a specific
+ * interface, we synthesize the answer for that
+ * interface. However, if nothing specific was claimed and we
+ * only return localhost RRs, we synthesize the answer for
* localhost. */
if (ifindex > 0)
@@ -261,7 +243,289 @@ static DnsProtocol SYNTHESIZE_PROTOCOL(uint64_t flags) {
return DNS_PROTOCOL_DNS;
}
-static void dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
+static int dns_type_to_af(uint16_t t) {
+ switch (t) {
+
+ case DNS_TYPE_A:
+ return AF_INET;
+
+ case DNS_TYPE_AAAA:
+ return AF_INET6;
+
+ case DNS_TYPE_ANY:
+ return AF_UNSPEC;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int synthesize_localhost_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
+ int r;
+
+ assert(q);
+ assert(key);
+ assert(answer);
+
+ r = dns_answer_reserve(answer, 2);
+ if (r < 0)
+ return r;
+
+ if (IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_ANY)) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, DNS_RESOURCE_KEY_NAME(key));
+ if (!rr)
+ return -ENOMEM;
+
+ rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK);
+
+ r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex));
+ if (r < 0)
+ return r;
+ }
+
+ if (IN_SET(key->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, DNS_RESOURCE_KEY_NAME(key));
+ if (!rr)
+ return -ENOMEM;
+
+ rr->aaaa.in6_addr = in6addr_loopback;
+
+ r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex));
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, from);
+ if (!rr)
+ return -ENOMEM;
+
+ rr->ptr.name = strdup(to);
+ if (!rr->ptr.name)
+ return -ENOMEM;
+
+ return dns_answer_add(*answer, rr, ifindex);
+}
+
+static int synthesize_localhost_ptr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
+ int r;
+
+ assert(q);
+ assert(key);
+ assert(answer);
+
+ r = dns_answer_reserve(answer, 1);
+ if (r < 0)
+ return r;
+
+ if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) {
+ r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", SYNTHESIZE_IFINDEX(q->ifindex));
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int answer_add_addresses_rr(
+ DnsAnswer **answer,
+ const char *name,
+ struct local_address *addresses,
+ unsigned n_addresses) {
+
+ unsigned j;
+ int r;
+
+ assert(answer);
+ assert(name);
+
+ r = dns_answer_reserve(answer, n_addresses);
+ if (r < 0)
+ return r;
+
+ for (j = 0; j < n_addresses; j++) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ r = dns_resource_record_new_address(&rr, addresses[j].family, &addresses[j].address, name);
+ if (r < 0)
+ return r;
+
+ r = dns_answer_add(*answer, rr, addresses[j].ifindex);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int answer_add_addresses_ptr(
+ DnsAnswer **answer,
+ const char *name,
+ struct local_address *addresses,
+ unsigned n_addresses,
+ int af, const union in_addr_union *match) {
+
+ unsigned j;
+ int r;
+
+ assert(answer);
+ assert(name);
+
+ for (j = 0; j < n_addresses; j++) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ if (af != AF_UNSPEC) {
+
+ if (addresses[j].family != af)
+ continue;
+
+ if (match && !in_addr_equal(af, match, &addresses[j].address))
+ continue;
+ }
+
+ r = dns_answer_reserve(answer, 1);
+ if (r < 0)
+ return r;
+
+ r = dns_resource_record_new_reverse(&rr, addresses[j].family, &addresses[j].address, name);
+ if (r < 0)
+ return r;
+
+ r = dns_answer_add(*answer, rr, addresses[j].ifindex);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int synthesize_system_hostname_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ int n = 0, af;
+
+ assert(q);
+ assert(key);
+ assert(answer);
+
+ af = dns_type_to_af(key->type);
+ if (af >= 0) {
+ n = local_addresses(q->manager->rtnl, q->ifindex, af, &addresses);
+ if (n < 0)
+ return n;
+
+ if (n == 0) {
+ struct local_address buffer[2];
+
+ /* If we have no local addresses then use ::1
+ * and 127.0.0.2 as local ones. */
+
+ if (af == AF_INET || af == AF_UNSPEC)
+ buffer[n++] = (struct local_address) {
+ .family = AF_INET,
+ .ifindex = SYNTHESIZE_IFINDEX(q->ifindex),
+ .address.in.s_addr = htobe32(0x7F000002),
+ };
+
+ if (af == AF_INET6 || af == AF_UNSPEC)
+ buffer[n++] = (struct local_address) {
+ .family = AF_INET6,
+ .ifindex = SYNTHESIZE_IFINDEX(q->ifindex),
+ .address.in6 = in6addr_loopback,
+ };
+
+ return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), buffer, n);
+ }
+ }
+
+ return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n);
+}
+
+static int synthesize_system_hostname_ptr(DnsQuery *q, int af, const union in_addr_union *address, DnsAnswer **answer) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ int n, r;
+
+ assert(q);
+ assert(address);
+ assert(answer);
+
+ if (af == AF_INET && address->in.s_addr == htobe32(0x7F000002)) {
+
+ /* Always map the IPv4 address 127.0.0.2 to the local
+ * hostname, in addition to "localhost": */
+
+ r = dns_answer_reserve(answer, 3);
+ if (r < 0)
+ return r;
+
+ r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->llmnr_hostname, SYNTHESIZE_IFINDEX(q->ifindex));
+ if (r < 0)
+ return r;
+
+ r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->mdns_hostname, SYNTHESIZE_IFINDEX(q->ifindex));
+ if (r < 0)
+ return r;
+
+ r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", SYNTHESIZE_IFINDEX(q->ifindex));
+ if (r < 0)
+ return r;
+
+ return 0;
+ }
+
+ n = local_addresses(q->manager->rtnl, q->ifindex, af, &addresses);
+ if (n < 0)
+ return n;
+
+ r = answer_add_addresses_ptr(answer, q->manager->llmnr_hostname, addresses, n, af, address);
+ if (r < 0)
+ return r;
+
+ return answer_add_addresses_ptr(answer, q->manager->mdns_hostname, addresses, n, af, address);
+}
+
+static int synthesize_gateway_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ int n = 0, af;
+
+ assert(q);
+ assert(key);
+ assert(answer);
+
+ af = dns_type_to_af(key->type);
+ if (af >= 0) {
+ n = local_gateways(q->manager->rtnl, q->ifindex, af, &addresses);
+ if (n < 0)
+ return n;
+ }
+
+ return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n);
+}
+
+static int synthesize_gateway_ptr(DnsQuery *q, int af, const union in_addr_union *address, DnsAnswer **answer) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ int n;
+
+ assert(q);
+ assert(address);
+ assert(answer);
+
+ n = local_gateways(q->manager->rtnl, q->ifindex, af, &addresses);
+ if (n < 0)
+ return n;
+
+ return answer_add_addresses_ptr(answer, "gateway", addresses, n, af, address);
+}
+
+static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
unsigned i;
int r;
@@ -276,11 +540,12 @@ static void dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state)
DNS_TRANSACTION_NO_SERVERS,
DNS_TRANSACTION_TIMEOUT,
DNS_TRANSACTION_ATTEMPTS_MAX_REACHED))
- return;
+ return 0;
for (i = 0; i < q->question->n_keys; i++) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ union in_addr_union address;
const char *name;
+ int af;
if (q->question->keys[i]->class != DNS_CLASS_IN &&
q->question->keys[i]->class != DNS_CLASS_ANY)
@@ -290,78 +555,55 @@ static void dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state)
if (is_localhost(name)) {
- switch (q->question->keys[i]->type) {
-
- case DNS_TYPE_A:
- case DNS_TYPE_ANY:
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, name);
- if (!rr) {
- log_oom();
- return;
- }
+ r = synthesize_localhost_rr(q, q->question->keys[i], &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize localhost RRs: %m");
- rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK);
- break;
+ } else if (manager_is_own_hostname(q->manager, name)) {
- case DNS_TYPE_AAAA:
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, name);
- if (!rr) {
- log_oom();
- return;
- }
+ r = synthesize_system_hostname_rr(q, q->question->keys[i], &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize system hostname RRs: %m");
- rr->aaaa.in6_addr = in6addr_loopback;
- break;
- }
+ } else if (is_gateway_hostname(name)) {
- } else if (IN_SET(q->question->keys[i]->type, DNS_TYPE_PTR, DNS_TYPE_ANY) &&
- (dns_name_endswith(name, "127.in-addr.arpa") > 0 ||
- dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)) {
+ r = synthesize_gateway_rr(q, q->question->keys[i], &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize gateway RRs: %m");
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, name);
- if (!rr) {
- log_oom();
- return;
- }
+ } else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 && dns_name_equal(name, "2.0.0.127.in-addr.arpa") == 0) ||
+ dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) {
- rr->ptr.name = strdup("localhost");
- if (!rr->ptr.name) {
- log_oom();
- return;
- }
- }
+ r = synthesize_localhost_ptr(q, q->question->keys[i], &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m");
- if (!rr)
- continue;
+ } else if (dns_name_address(name, &af, &address) > 0) {
- if (!answer) {
- answer = dns_answer_new(q->question->n_keys);
- if (!answer) {
- log_oom();
- return;
- }
- }
+ r = synthesize_system_hostname_ptr(q, af, &address, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize system hostname PTR RR: %m");
- r = dns_answer_add(answer, rr);
- if (r < 0) {
- log_error_errno(r, "Failed to add synthetic RR to answer: %m");
- return;
+ r = synthesize_gateway_ptr(q, af, &address, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m");
}
}
if (!answer)
- return;
+ return 0;
dns_answer_unref(q->answer);
q->answer = answer;
answer = NULL;
- q->answer_ifindex = SYNTHESIZE_IFINDEX(q->ifindex);
q->answer_family = SYNTHESIZE_FAMILY(q->flags);
q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags);
q->answer_rcode = DNS_RCODE_SUCCESS;
*state = DNS_TRANSACTION_SUCCESS;
+
+ return 1;
}
int dns_query_go(DnsQuery *q) {
@@ -409,12 +651,8 @@ int dns_query_go(DnsQuery *q) {
DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
dns_query_synthesize_reply(q, &state);
- if (state != DNS_TRANSACTION_NO_SERVERS) {
- dns_query_complete(q, state);
- return 1;
- }
-
- return -ESRCH;
+ dns_query_complete(q, state);
+ return 1;
}
r = dns_query_add_transaction_split(q, first);
@@ -437,7 +675,6 @@ int dns_query_go(DnsQuery *q) {
}
q->answer = dns_answer_unref(q->answer);
- q->answer_ifindex = 0;
q->answer_rcode = 0;
q->answer_family = AF_UNSPEC;
q->answer_protocol = _DNS_PROTOCOL_INVALID;
@@ -584,7 +821,6 @@ void dns_query_ready(DnsQuery *q) {
if (IN_SET(state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE)) {
q->answer = dns_answer_ref(answer);
q->answer_rcode = rcode;
- q->answer_ifindex = (scope && scope->link) ? scope->link->ifindex : 0;
q->answer_protocol = scope ? scope->protocol : _DNS_PROTOCOL_INVALID;
q->answer_family = scope ? scope->family : AF_UNSPEC;
}
diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h
index 5a319f0a62..93d49301fa 100644
--- a/src/resolve/resolved-dns-query.h
+++ b/src/resolve/resolved-dns-query.h
@@ -45,7 +45,6 @@ struct DnsQuery {
/* Discovered data */
DnsAnswer *answer;
- int answer_ifindex;
int answer_family;
DnsProtocol answer_protocol;
int answer_rcode;
diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c
index 0efe740d1a..c94928d725 100644
--- a/src/resolve/resolved-dns-question.c
+++ b/src/resolve/resolved-dns-question.c
@@ -68,9 +68,11 @@ int dns_question_add(DnsQuestion *q, DnsResourceKey *key) {
unsigned i;
int r;
- assert(q);
assert(key);
+ if (!q)
+ return -ENOSPC;
+
for (i = 0; i < q->n_keys; i++) {
r = dns_resource_key_equal(q->keys[i], key);
if (r < 0)
@@ -90,9 +92,11 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) {
unsigned i;
int r;
- assert(q);
assert(rr);
+ if (!q)
+ return 0;
+
for (i = 0; i < q->n_keys; i++) {
r = dns_resource_key_match_rr(q->keys[i], rr);
if (r != 0)
@@ -106,9 +110,11 @@ int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) {
unsigned i;
int r;
- assert(q);
assert(rr);
+ if (!q)
+ return 0;
+
for (i = 0; i < q->n_keys; i++) {
r = dns_resource_key_match_cname(q->keys[i], rr);
if (r != 0)
@@ -123,7 +129,8 @@ int dns_question_is_valid(DnsQuestion *q) {
unsigned i;
int r;
- assert(q);
+ if (!q)
+ return 0;
if (q->n_keys <= 0)
return 0;
@@ -151,16 +158,19 @@ int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other) {
unsigned j;
int r;
- assert(q);
- assert(other);
-
/* Checks if all keys in "other" are also contained in "q" */
+ if (!other)
+ return 1;
+
for (j = 0; j < other->n_keys; j++) {
DnsResourceKey *b = other->keys[j];
bool found = false;
unsigned i;
+ if (!q)
+ return 0;
+
for (i = 0; i < q->n_keys; i++) {
DnsResourceKey *a = q->keys[i];
@@ -192,9 +202,11 @@ int dns_question_contains(DnsQuestion *a, DnsResourceKey *k) {
unsigned j;
int r;
- assert(a);
assert(k);
+ if (!a)
+ return 0;
+
for (j = 0; j < a->n_keys; j++) {
r = dns_resource_key_equal(a->keys[j], k);
if (r != 0)
@@ -208,8 +220,10 @@ int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
unsigned j;
int r;
- assert(a);
- assert(b);
+ if (!a)
+ return !b || b->n_keys == 0;
+ if (!b)
+ return a->n_keys == 0;
/* Checks if all keys in a are also contained b, and vice versa */
@@ -234,10 +248,19 @@ int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **
unsigned i;
int r;
- assert(q);
assert(name);
assert(ret);
+ if (!q) {
+ n = dns_question_new(0);
+ if (!n)
+ return -ENOMEM;
+
+ *ret = n;
+ n = 0;
+ return 0;
+ }
+
for (i = 0; i < q->n_keys; i++) {
r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name);
if (r < 0)
@@ -277,38 +300,3 @@ int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **
return 1;
}
-
-int dns_question_endswith(DnsQuestion *q, const char *suffix) {
- unsigned i;
-
- assert(q);
- assert(suffix);
-
- for (i = 0; i < q->n_keys; i++) {
- int k;
-
- k = dns_name_endswith(DNS_RESOURCE_KEY_NAME(q->keys[i]), suffix);
- if (k <= 0)
- return k;
- }
-
- return 1;
-}
-
-int dns_question_extract_reverse_address(DnsQuestion *q, int *family, union in_addr_union *address) {
- unsigned i;
-
- assert(q);
- assert(family);
- assert(address);
-
- for (i = 0; i < q->n_keys; i++) {
- int k;
-
- k = dns_name_address(DNS_RESOURCE_KEY_NAME(q->keys[i]), family, address);
- if (k != 0)
- return k;
- }
-
- return 0;
-}
diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h
index fc98677798..77de0c7a2c 100644
--- a/src/resolve/resolved-dns-question.h
+++ b/src/resolve/resolved-dns-question.h
@@ -48,7 +48,4 @@ int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b);
int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **ret);
-int dns_question_endswith(DnsQuestion *q, const char *suffix);
-int dns_question_extract_reverse_address(DnsQuestion *q, int *family, union in_addr_union *address);
-
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref);
diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c
index ad7ca26cfe..f31644eebc 100644
--- a/src/resolve/resolved-dns-rr.c
+++ b/src/resolve/resolved-dns-rr.c
@@ -350,6 +350,36 @@ int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const u
return 0;
}
+int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name) {
+ DnsResourceRecord *rr;
+
+ assert(ret);
+ assert(address);
+ assert(family);
+
+ if (family == AF_INET) {
+
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, name);
+ if (!rr)
+ return -ENOMEM;
+
+ rr->a.in_addr = address->in;
+
+ } else if (family == AF_INET6) {
+
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, name);
+ if (!rr)
+ return -ENOMEM;
+
+ rr->aaaa.in6_addr = address->in6;
+ } else
+ return -EAFNOSUPPORT;
+
+ *ret = rr;
+
+ return 0;
+}
+
int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
int r;
diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h
index 0f40f3ceef..8986a298af 100644
--- a/src/resolve/resolved-dns-rr.h
+++ b/src/resolve/resolved-dns-rr.h
@@ -191,6 +191,7 @@ DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, c
DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr);
DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr);
int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name);
+int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name);
int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b);
int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref);
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index 57d9071dfc..9e6f595a1b 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -78,8 +78,7 @@ DnsScope* dns_scope_free(DnsScope *s) {
dns_scope_llmnr_membership(s, false);
- while ((t = s->transactions)) {
-
+ while ((t = hashmap_steal_first(s->transactions))) {
/* Abort the transaction, but make sure it is not
* freed while we still look at it */
@@ -90,6 +89,8 @@ DnsScope* dns_scope_free(DnsScope *s) {
dns_transaction_free(t);
}
+ hashmap_free(s->transactions);
+
while ((rr = ordered_hashmap_steal_first(s->conflict_queue)))
dns_resource_record_unref(rr);
@@ -165,7 +166,8 @@ int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) {
} else
mtu = manager_find_mtu(s->manager);
- if (s->protocol == DNS_PROTOCOL_DNS) {
+ switch (s->protocol) {
+ case DNS_PROTOCOL_DNS:
if (DNS_PACKET_QDCOUNT(p) > 1)
return -EOPNOTSUPP;
@@ -179,8 +181,9 @@ int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) {
if (r < 0)
return r;
- } else if (s->protocol == DNS_PROTOCOL_LLMNR) {
+ break;
+ case DNS_PROTOCOL_LLMNR:
if (DNS_PACKET_QDCOUNT(p) > 1)
return -EOPNOTSUPP;
@@ -204,8 +207,12 @@ int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) {
r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
if (r < 0)
return r;
- } else
+
+ break;
+
+ default:
return -EAFNOSUPPORT;
+ }
return 1;
}
@@ -328,11 +335,11 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
if (dns_name_root(domain) != 0)
return DNS_SCOPE_NO;
- if (is_localhost(domain))
- return DNS_SCOPE_NO;
-
- /* Never resolve any loopback IP address via DNS, LLMNR or mDNS */
- if (dns_name_endswith(domain, "127.in-addr.arpa") > 0 ||
+ /* Never resolve any loopback hostname or IP address via DNS,
+ * LLMNR or mDNS. Instead, always rely on synthesized RRs for
+ * these. */
+ if (is_localhost(domain) ||
+ dns_name_endswith(domain, "127.in-addr.arpa") > 0 ||
dns_name_equal(domain, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)
return DNS_SCOPE_NO;
@@ -340,35 +347,38 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
if (dns_name_endswith(domain, *i) > 0)
return DNS_SCOPE_YES;
- if (s->protocol == DNS_PROTOCOL_DNS) {
+ switch (s->protocol) {
+ case DNS_PROTOCOL_DNS:
if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 &&
dns_name_single_label(domain) == 0)
return DNS_SCOPE_MAYBE;
return DNS_SCOPE_NO;
- }
- if (s->protocol == DNS_PROTOCOL_MDNS) {
- if (dns_name_endswith(domain, "254.169.in-addr.arpa") > 0 ||
- dns_name_endswith(domain, "0.8.e.f.ip6.arpa") > 0 ||
- (dns_name_endswith(domain, "local") > 0 && dns_name_equal(domain, "local") == 0))
+ case DNS_PROTOCOL_MDNS:
+ if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
+ (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) ||
+ (dns_name_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */
+ dns_name_equal(domain, "local") == 0 && /* but not the single-label "local" name itself */
+ manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via mDNS */
return DNS_SCOPE_MAYBE;
return DNS_SCOPE_NO;
- }
- if (s->protocol == DNS_PROTOCOL_LLMNR) {
- if (dns_name_endswith(domain, "in-addr.arpa") > 0 ||
- dns_name_endswith(domain, "ip6.arpa") > 0 ||
- (dns_name_single_label(domain) > 0 &&
- dns_name_equal(domain, "gateway") <= 0)) /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */
+ case DNS_PROTOCOL_LLMNR:
+ if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
+ (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) ||
+ (dns_name_single_label(domain) > 0 && /* only resolve single label names via LLMNR */
+ !is_gateway_hostname(domain) && /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */
+ manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */
return DNS_SCOPE_MAYBE;
return DNS_SCOPE_NO;
- }
- assert_not_reached("Unknown scope protocol");
+ default:
+ assert_not_reached("Unknown scope protocol");
+ }
}
int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
@@ -490,7 +500,7 @@ static int dns_scope_make_reply_packet(
if (answer) {
for (i = 0; i < answer->n_rrs; i++) {
- r = dns_packet_append_rr(p, answer->rrs[i], NULL);
+ r = dns_packet_append_rr(p, answer->items[i].rr, NULL);
if (r < 0)
return r;
}
@@ -500,7 +510,7 @@ static int dns_scope_make_reply_packet(
if (soa) {
for (i = 0; i < soa->n_rrs; i++) {
- r = dns_packet_append_rr(p, soa->rrs[i], NULL);
+ r = dns_packet_append_rr(p, soa->items[i].rr, NULL);
if (r < 0)
return r;
}
@@ -525,7 +535,7 @@ static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) {
dns_zone_verify_conflicts(&s->zone, p->question->keys[n]);
if (p->answer)
for (n = 0; n < p->answer->n_rrs; n++)
- dns_zone_verify_conflicts(&s->zone, p->answer->rrs[n]->key);
+ dns_zone_verify_conflicts(&s->zone, p->answer->items[n].rr->key);
}
void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
@@ -614,30 +624,26 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
}
}
-DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsQuestion *question, bool cache_ok) {
+DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, bool cache_ok) {
DnsTransaction *t;
assert(scope);
- assert(question);
-
- /* Try to find an ongoing transaction that is a equal or a
- * superset of the specified question */
-
- LIST_FOREACH(transactions_by_scope, t, scope->transactions) {
+ assert(key);
- /* Refuse reusing transactions that completed based on
- * cached data instead of a real packet, if that's
- * requested. */
- if (!cache_ok &&
- IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE) &&
- !t->received)
- continue;
+ /* Try to find an ongoing transaction that is a equal to the
+ * specified question */
+ t = hashmap_get(scope->transactions, key);
+ if (!t)
+ return NULL;
- if (dns_question_is_superset(t->question, question) > 0)
- return t;
- }
+ /* Refuse reusing transactions that completed based on cached
+ * data instead of a real packet, if that's requested. */
+ if (!cache_ok &&
+ IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE) &&
+ !t->received)
+ return NULL;
- return NULL;
+ return t;
}
static int dns_scope_make_conflict_packet(
@@ -793,16 +799,48 @@ void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) {
/* Check for conflicts against the local zone. If we
* found one, we won't check any further */
- r = dns_zone_check_conflicts(&scope->zone, p->answer->rrs[i]);
+ r = dns_zone_check_conflicts(&scope->zone, p->answer->items[i].rr);
if (r != 0)
continue;
/* Check for conflicts against the local cache. If so,
* send out an advisory query, to inform everybody */
- r = dns_cache_check_conflicts(&scope->cache, p->answer->rrs[i], p->family, &p->sender);
+ r = dns_cache_check_conflicts(&scope->cache, p->answer->items[i].rr, p->family, &p->sender);
if (r <= 0)
continue;
- dns_scope_notify_conflict(scope, p->answer->rrs[i]);
+ dns_scope_notify_conflict(scope, p->answer->items[i].rr);
+ }
+}
+
+void dns_scope_dump(DnsScope *s, FILE *f) {
+ assert(s);
+
+ if (!f)
+ f = stdout;
+
+ fputs("[Scope protocol=", f);
+ fputs(dns_protocol_to_string(s->protocol), f);
+
+ if (s->link) {
+ fputs(" interface=", f);
+ fputs(s->link->name, f);
+ }
+
+ if (s->family != AF_UNSPEC) {
+ fputs(" family=", f);
+ fputs(af_to_name(s->family), f);
+ }
+
+ fputs("]\n", f);
+
+ if (!dns_zone_is_empty(&s->zone)) {
+ fputs("ZONE:\n", f);
+ dns_zone_dump(&s->zone, f);
+ }
+
+ if (!dns_cache_is_empty(&s->cache)) {
+ fputs("CACHE:\n", f);
+ dns_cache_dump(&s->cache, f);
}
}
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index b2dac86b44..b75f212897 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -60,7 +60,7 @@ struct DnsScope {
usec_t resend_timeout;
usec_t max_rtt;
- LIST_HEAD(DnsTransaction, transactions);
+ Hashmap *transactions;
LIST_FIELDS(DnsScope, scopes);
};
@@ -85,7 +85,9 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b);
void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p);
-DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsQuestion *question, bool cache_ok);
+DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, bool cache_ok);
int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr);
void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p);
+
+void dns_scope_dump(DnsScope *s, FILE *f);
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 2d9d1a47ee..8092bb514d 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -24,6 +24,7 @@
#include "resolved-llmnr.h"
#include "resolved-dns-transaction.h"
#include "random-util.h"
+#include "dns-domain.h"
DnsTransaction* dns_transaction_free(DnsTransaction *t) {
DnsQuery *q;
@@ -34,24 +35,25 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
sd_event_source_unref(t->timeout_event_source);
- dns_question_unref(t->question);
dns_packet_unref(t->sent);
dns_packet_unref(t->received);
dns_answer_unref(t->cached);
- sd_event_source_unref(t->dns_event_source);
- safe_close(t->dns_fd);
+ sd_event_source_unref(t->dns_udp_event_source);
+ safe_close(t->dns_udp_fd);
dns_server_unref(t->server);
dns_stream_free(t->stream);
if (t->scope) {
- LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
+ hashmap_remove(t->scope->transactions, t->key);
if (t->id != 0)
hashmap_remove(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id));
}
+ dns_resource_key_unref(t->key);
+
while ((q = set_steal_first(t->queries)))
set_remove(q->transactions, t);
set_free(t->queries);
@@ -76,26 +78,30 @@ void dns_transaction_gc(DnsTransaction *t) {
dns_transaction_free(t);
}
-int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q) {
+int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) {
_cleanup_(dns_transaction_freep) DnsTransaction *t = NULL;
int r;
assert(ret);
assert(s);
- assert(q);
+ assert(key);
r = hashmap_ensure_allocated(&s->manager->dns_transactions, NULL);
if (r < 0)
return r;
+ r = hashmap_ensure_allocated(&s->transactions, &dns_resource_key_hash_ops);
+ if (r < 0)
+ return r;
+
t = new0(DnsTransaction, 1);
if (!t)
return -ENOMEM;
- t->dns_fd = -1;
-
- t->question = dns_question_ref(q);
+ t->dns_udp_fd = -1;
+ t->key = dns_resource_key_ref(key);
+ /* Find a fresh, unused transaction id */
do
random_bytes(&t->id, sizeof(t->id));
while (t->id == 0 ||
@@ -107,7 +113,12 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q) {
return r;
}
- LIST_PREPEND(transactions_by_scope, s->transactions, t);
+ r = hashmap_put(s->transactions, t->key, t);
+ if (r < 0) {
+ hashmap_remove(s->manager->dns_transactions, UINT_TO_PTR(t->id));
+ return r;
+ }
+
t->scope = s;
if (ret)
@@ -175,9 +186,6 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
assert(t);
assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
- if (!IN_SET(t->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING))
- return;
-
/* Note that this call might invalidate the query. Callers
* should hence not attempt to access the query or transaction
* after calling this function. */
@@ -252,11 +260,13 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
if (t->stream)
return 0;
- if (t->scope->protocol == DNS_PROTOCOL_DNS)
+ switch (t->scope->protocol) {
+ case DNS_PROTOCOL_DNS:
fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53, &server);
- else if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
+ break;
- /* When we already received a query to this (but it was truncated), send to its sender address */
+ case DNS_PROTOCOL_LLMNR:
+ /* When we already received a reply to this (but it was truncated), send to its sender address */
if (t->received)
fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port, NULL);
else {
@@ -266,16 +276,23 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
/* Otherwise, try to talk to the owner of a
* the IP address, in case this is a reverse
* PTR lookup */
- r = dns_question_extract_reverse_address(t->question, &family, &address);
+
+ r = dns_name_address(DNS_RESOURCE_KEY_NAME(t->key), &family, &address);
if (r < 0)
return r;
if (r == 0)
return -EINVAL;
+ if (family != t->scope->family)
+ return -ESRCH;
fd = dns_scope_tcp_socket(t->scope, family, &address, LLMNR_PORT, NULL);
}
- } else
+
+ break;
+
+ default:
return -EAFNOSUPPORT;
+ }
if (fd < 0)
return fd;
@@ -292,7 +309,6 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
return r;
}
-
dns_server_unref(t->server);
t->server = dns_server_ref(server);
t->received = dns_packet_unref(t->received);
@@ -312,8 +328,8 @@ static void dns_transaction_next_dns_server(DnsTransaction *t) {
assert(t);
t->server = dns_server_unref(t->server);
- t->dns_event_source = sd_event_source_unref(t->dns_event_source);
- t->dns_fd = safe_close(t->dns_fd);
+ t->dns_udp_event_source = sd_event_source_unref(t->dns_udp_event_source);
+ t->dns_udp_fd = safe_close(t->dns_udp_fd);
dns_scope_next_dns_server(t->scope);
}
@@ -332,7 +348,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
* should hence not attempt to access the query or transaction
* after calling this function. */
- if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
+ switch (t->scope->protocol) {
+ case DNS_PROTOCOL_LLMNR:
assert(t->scope->link);
/* For LLMNR we will not accept any packets from other
@@ -351,6 +368,14 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
dns_transaction_tentative(t, p);
return;
}
+
+ break;
+
+ case DNS_PROTOCOL_DNS:
+ break;
+
+ default:
+ assert_not_reached("Invalid DNS protocol.");
}
if (t->received != p) {
@@ -387,7 +412,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
break;
default:
- assert_not_reached("Invalid DNS protocol.");
+ break;
}
if (DNS_PACKET_TC(p)) {
@@ -427,8 +452,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
}
/* Only consider responses with equivalent query section to the request */
- r = dns_question_is_equal(p->question, t->question);
- if (r <= 0) {
+ if (p->question->n_keys != 1 || dns_resource_key_equal(p->question->keys[0], t->key) <= 0) {
dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
return;
}
@@ -476,16 +500,16 @@ static int dns_transaction_emit(DnsTransaction *t) {
if (fd < 0)
return fd;
- r = sd_event_add_io(t->scope->manager->event, &t->dns_event_source, fd, EPOLLIN, on_dns_packet, t);
+ r = sd_event_add_io(t->scope->manager->event, &t->dns_udp_event_source, fd, EPOLLIN, on_dns_packet, t);
if (r < 0)
return r;
- t->dns_fd = fd;
+ t->dns_udp_fd = fd;
fd = -1;
t->server = dns_server_ref(server);
}
- r = dns_scope_emit(t->scope, t->dns_fd, t->sent);
+ r = dns_scope_emit(t->scope, t->dns_udp_fd, t->sent);
if (r < 0)
return r;
@@ -517,7 +541,6 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat
static int dns_transaction_make_packet(DnsTransaction *t) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- unsigned n, added = 0;
int r;
assert(t);
@@ -529,24 +552,17 @@ static int dns_transaction_make_packet(DnsTransaction *t) {
if (r < 0)
return r;
- for (n = 0; n < t->question->n_keys; n++) {
- r = dns_scope_good_key(t->scope, t->question->keys[n]);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- r = dns_packet_append_key(p, t->question->keys[n], NULL);
- if (r < 0)
- return r;
-
- added++;
- }
-
- if (added <= 0)
+ r = dns_scope_good_key(t->scope, t->key);
+ if (r < 0)
+ return r;
+ if (r == 0)
return -EDOM;
- DNS_PACKET_HEADER(p)->qdcount = htobe16(added);
+ r = dns_packet_append_key(p, t->key, NULL);
+ if (r < 0)
+ return r;
+
+ DNS_PACKET_HEADER(p)->qdcount = htobe16(1);
DNS_PACKET_HEADER(p)->id = t->id;
t->sent = p;
@@ -620,7 +636,7 @@ int dns_transaction_go(DnsTransaction *t) {
/* Let's then prune all outdated entries */
dns_cache_prune(&t->scope->cache);
- r = dns_cache_lookup(&t->scope->cache, t->question, &t->cached_rcode, &t->cached);
+ r = dns_cache_lookup(&t->scope->cache, t->key, &t->cached_rcode, &t->cached);
if (r < 0)
return r;
if (r > 0) {
@@ -673,8 +689,8 @@ int dns_transaction_go(DnsTransaction *t) {
return r;
if (t->scope->protocol == DNS_PROTOCOL_LLMNR &&
- (dns_question_endswith(t->question, "in-addr.arpa") > 0 ||
- dns_question_endswith(t->question, "ip6.arpa") > 0)) {
+ (dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), "in-addr.arpa") > 0 ||
+ dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), "ip6.arpa") > 0)) {
/* RFC 4795, Section 2.4. says reverse lookups shall
* always be made via TCP on LLMNR */
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index d8a5647609..acf6a6f651 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -47,7 +47,7 @@ enum DnsTransactionState {
struct DnsTransaction {
DnsScope *scope;
- DnsQuestion *question;
+ DnsResourceKey *key;
DnsTransactionState state;
uint16_t id;
@@ -62,10 +62,10 @@ struct DnsTransaction {
sd_event_source *timeout_event_source;
unsigned n_attempts;
- int dns_fd;
- sd_event_source *dns_event_source;
+ int dns_udp_fd;
+ sd_event_source *dns_udp_event_source;
- /* the active server */
+ /* The active server */
DnsServer *server;
/* TCP connection logic, if we need it */
@@ -84,7 +84,7 @@ struct DnsTransaction {
LIST_FIELDS(DnsTransaction, transactions_by_scope);
};
-int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q);
+int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key);
DnsTransaction* dns_transaction_free(DnsTransaction *t);
void dns_transaction_gc(DnsTransaction *t);
diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c
index 32d771a954..674bb6af28 100644
--- a/src/resolve/resolved-dns-zone.c
+++ b/src/resolve/resolved-dns-zone.c
@@ -166,7 +166,6 @@ static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) {
static int dns_zone_item_probe_start(DnsZoneItem *i) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
- _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
DnsTransaction *t;
int r;
@@ -179,17 +178,9 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) {
if (!key)
return -ENOMEM;
- question = dns_question_new(1);
- if (!question)
- return -ENOMEM;
-
- r = dns_question_add(question, key);
- if (r < 0)
- return r;
-
- t = dns_scope_find_transaction(i->scope, question, false);
+ t = dns_scope_find_transaction(i->scope, key, false);
if (!t) {
- r = dns_transaction_new(&t, i->scope, question);
+ r = dns_transaction_new(&t, i->scope, key);
if (r < 0)
return r;
}
@@ -217,7 +208,6 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) {
}
dns_zone_item_ready(i);
-
return 0;
gc:
@@ -422,7 +412,7 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswe
if (k < 0)
return k;
if (k > 0) {
- r = dns_answer_add(answer, j->rr);
+ r = dns_answer_add(answer, j->rr, 0);
if (r < 0)
return r;
@@ -448,7 +438,7 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswe
if (j->state != DNS_ZONE_ITEM_PROBING)
tentative = false;
- r = dns_answer_add(answer, j->rr);
+ r = dns_answer_add(answer, j->rr, 0);
if (r < 0)
return r;
}
@@ -505,7 +495,7 @@ void dns_zone_item_conflict(DnsZoneItem *i) {
i->state = DNS_ZONE_ITEM_WITHDRAWN;
/* Maybe change the hostname */
- if (dns_name_equal(i->scope->manager->hostname, DNS_RESOURCE_KEY_NAME(i->rr->key)) > 0)
+ if (manager_is_own_hostname(i->scope->manager, DNS_RESOURCE_KEY_NAME(i->rr->key)) > 0)
manager_next_hostname(i->scope->manager);
}
@@ -646,3 +636,40 @@ void dns_zone_verify_all(DnsZone *zone) {
dns_zone_item_verify(j);
}
}
+
+void dns_zone_dump(DnsZone *zone, FILE *f) {
+ Iterator iterator;
+ DnsZoneItem *i;
+ int r;
+
+ if (!zone)
+ return;
+
+ if (!f)
+ f = stdout;
+
+ HASHMAP_FOREACH(i, zone->by_key, iterator) {
+ DnsZoneItem *j;
+
+ LIST_FOREACH(by_key, j, i) {
+ _cleanup_free_ char *t = NULL;
+
+ r = dns_resource_record_to_string(j->rr, &t);
+ if (r < 0) {
+ log_oom();
+ continue;
+ }
+
+ fputc('\t', f);
+ fputs(t, f);
+ fputc('\n', f);
+ }
+ }
+}
+
+bool dns_zone_is_empty(DnsZone *zone) {
+ if (!zone)
+ return true;
+
+ return hashmap_isempty(zone->by_key);
+}
diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h
index 71851265c6..495d17cdb1 100644
--- a/src/resolve/resolved-dns-zone.h
+++ b/src/resolve/resolved-dns-zone.h
@@ -78,3 +78,6 @@ int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key);
void dns_zone_verify_all(DnsZone *zone);
void dns_zone_item_probe_stop(DnsZoneItem *i);
+
+void dns_zone_dump(DnsZone *zone, FILE *f);
+bool dns_zone_is_empty(DnsZone *zone);
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index d66b3a88fc..47f461a37d 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -419,16 +419,16 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
a->link->llmnr_support == SUPPORT_YES &&
a->link->manager->llmnr_support == SUPPORT_YES) {
- if (!a->link->manager->host_ipv4_key) {
- a->link->manager->host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->hostname);
- if (!a->link->manager->host_ipv4_key) {
+ if (!a->link->manager->llmnr_host_ipv4_key) {
+ a->link->manager->llmnr_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->llmnr_hostname);
+ if (!a->link->manager->llmnr_host_ipv4_key) {
r = -ENOMEM;
goto fail;
}
}
if (!a->llmnr_address_rr) {
- a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv4_key);
+ a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv4_key);
if (!a->llmnr_address_rr) {
r = -ENOMEM;
goto fail;
@@ -439,7 +439,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
}
if (!a->llmnr_ptr_rr) {
- r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
+ r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
if (r < 0)
goto fail;
@@ -476,16 +476,16 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
a->link->llmnr_support == SUPPORT_YES &&
a->link->manager->llmnr_support == SUPPORT_YES) {
- if (!a->link->manager->host_ipv6_key) {
- a->link->manager->host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->hostname);
- if (!a->link->manager->host_ipv6_key) {
+ if (!a->link->manager->llmnr_host_ipv6_key) {
+ a->link->manager->llmnr_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->llmnr_hostname);
+ if (!a->link->manager->llmnr_host_ipv6_key) {
r = -ENOMEM;
goto fail;
}
}
if (!a->llmnr_address_rr) {
- a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv6_key);
+ a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv6_key);
if (!a->llmnr_address_rr) {
r = -ENOMEM;
goto fail;
@@ -496,7 +496,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
}
if (!a->llmnr_ptr_rr) {
- r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname);
+ r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
if (r < 0)
goto fail;
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 645f2a824c..13852192c4 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -310,51 +310,84 @@ static int manager_network_monitor_listen(Manager *m) {
return 0;
}
-static int determine_hostname(char **ret) {
+static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) {
_cleanup_free_ char *h = NULL, *n = NULL;
- int r;
+ char label[DNS_LABEL_MAX];
+ const char *p;
+ int r, k;
- assert(ret);
+ assert(llmnr_hostname);
+ assert(mdns_hostname);
+
+ /* Extract and normalize the first label of the locally
+ * configured hostname, and check it's not "localhost". */
h = gethostname_malloc();
if (!h)
return log_oom();
- if (!utf8_is_valid(h)) {
+ p = h;
+ r = dns_label_unescape(&p, label, sizeof(label));
+ if (r < 0)
+ return log_error_errno(r, "Failed to unescape host name: %m");
+ if (r == 0) {
+ log_error("Couldn't find a single label in hosntame.");
+ return -EINVAL;
+ }
+
+ k = dns_label_undo_idna(label, r, label, sizeof(label));
+ if (k < 0)
+ return log_error_errno(k, "Failed to undo IDNA: %m");
+ if (k > 0)
+ r = k;
+
+ if (!utf8_is_valid(label)) {
log_error("System hostname is not UTF-8 clean.");
return -EINVAL;
}
- r = dns_name_normalize(h, &n);
- if (r < 0) {
- log_error("System hostname '%s' cannot be normalized.", h);
- return r;
+ r = dns_label_escape(label, r, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to escape host name: %m");
+
+ if (is_localhost(n)) {
+ log_debug("System hostname is 'localhost', ignoring.");
+ return -EINVAL;
}
- *ret = n;
+ r = dns_name_concat(n, "local", mdns_hostname);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine mDNS hostname: %m");
+
+ *llmnr_hostname = n;
n = NULL;
return 0;
}
static int on_hostname_change(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
- _cleanup_free_ char *h = NULL;
+ _cleanup_free_ char *llmnr_hostname = NULL, *mdns_hostname = NULL;
Manager *m = userdata;
int r;
assert(m);
- r = determine_hostname(&h);
+ r = determine_hostname(&llmnr_hostname, &mdns_hostname);
if (r < 0)
return 0; /* ignore invalid hostnames */
- if (streq(h, m->hostname))
+ if (streq(llmnr_hostname, m->llmnr_hostname) && streq(mdns_hostname, m->mdns_hostname))
return 0;
- log_info("System hostname changed to '%s'.", h);
- free(m->hostname);
- m->hostname = h;
- h = NULL;
+ log_info("System hostname changed to '%s'.", llmnr_hostname);
+
+ free(m->llmnr_hostname);
+ free(m->mdns_hostname);
+
+ m->llmnr_hostname = llmnr_hostname;
+ m->mdns_hostname = mdns_hostname;
+
+ llmnr_hostname = mdns_hostname = NULL;
manager_refresh_rrs(m);
@@ -381,15 +414,44 @@ static int manager_watch_hostname(Manager *m) {
return log_error_errno(r, "Failed to add hostname event source: %m");
}
- r = determine_hostname(&m->hostname);
+ r = determine_hostname(&m->llmnr_hostname, &m->mdns_hostname);
if (r < 0) {
log_info("Defaulting to hostname 'linux'.");
- m->hostname = strdup("linux");
- if (!m->hostname)
+ m->llmnr_hostname = strdup("linux");
+ if (!m->llmnr_hostname)
+ return log_oom();
+
+ m->mdns_hostname = strdup("linux.local");
+ if (!m->mdns_hostname)
return log_oom();
} else
- log_info("Using system hostname '%s'.", m->hostname);
+ log_info("Using system hostname '%s'.", m->llmnr_hostname);
+
+ return 0;
+}
+
+static int manager_sigusr1(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ _cleanup_free_ char *buffer = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ Manager *m = userdata;
+ size_t size = 0;
+ DnsScope *scope;
+
+ assert(s);
+ assert(si);
+ assert(m);
+
+ f = open_memstream(&buffer, &size);
+ if (!f)
+ return log_oom();
+
+ LIST_FOREACH(scopes, scope, m->dns_scopes)
+ dns_scope_dump(scope, f);
+
+ if (fflush_and_check(f) < 0)
+ return log_oom();
+ log_dump(LOG_INFO, buffer);
return 0;
}
@@ -443,6 +505,8 @@ int manager_new(Manager **ret) {
if (r < 0)
return r;
+ (void) sd_event_add_signal(m->event, &m->sigusr1_event_source, SIGUSR1, manager_sigusr1, m);
+
*ret = m;
m = NULL;
@@ -490,14 +554,17 @@ Manager *manager_free(Manager *m) {
sd_event_source_unref(m->bus_retry_event_source);
sd_bus_unref(m->bus);
+ sd_event_source_unref(m->sigusr1_event_source);
+
sd_event_unref(m->event);
- dns_resource_key_unref(m->host_ipv4_key);
- dns_resource_key_unref(m->host_ipv6_key);
+ dns_resource_key_unref(m->llmnr_host_ipv4_key);
+ dns_resource_key_unref(m->llmnr_host_ipv6_key);
- safe_close(m->hostname_fd);
sd_event_source_unref(m->hostname_event_source);
- free(m->hostname);
+ safe_close(m->hostname_fd);
+ free(m->llmnr_hostname);
+ free(m->mdns_hostname);
free(m);
@@ -1229,8 +1296,8 @@ void manager_refresh_rrs(Manager *m) {
assert(m);
- m->host_ipv4_key = dns_resource_key_unref(m->host_ipv4_key);
- m->host_ipv6_key = dns_resource_key_unref(m->host_ipv6_key);
+ m->llmnr_host_ipv4_key = dns_resource_key_unref(m->llmnr_host_ipv4_key);
+ m->llmnr_host_ipv6_key = dns_resource_key_unref(m->llmnr_host_ipv6_key);
HASHMAP_FOREACH(l, m->links, i) {
link_add_rrs(l, true);
@@ -1241,14 +1308,15 @@ void manager_refresh_rrs(Manager *m) {
int manager_next_hostname(Manager *m) {
const char *p;
uint64_t u, a;
- char *h;
+ char *h, *k;
+ int r;
assert(m);
- p = strchr(m->hostname, 0);
+ p = strchr(m->llmnr_hostname, 0);
assert(p);
- while (p > m->hostname) {
+ while (p > m->llmnr_hostname) {
if (!strchr("0123456789", p[-1]))
break;
@@ -1268,13 +1336,22 @@ int manager_next_hostname(Manager *m) {
random_bytes(&a, sizeof(a));
u += 1 + a % 10;
- if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->hostname), m->hostname, u) < 0)
+ if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->llmnr_hostname), m->llmnr_hostname, u) < 0)
return -ENOMEM;
- log_info("Hostname conflict, changing published hostname from '%s' to '%s'.", m->hostname, h);
+ r = dns_name_concat(h, "local", &k);
+ if (r < 0) {
+ free(h);
+ return r;
+ }
+
+ log_info("Hostname conflict, changing published hostname from '%s' to '%s'.", m->llmnr_hostname, h);
- free(m->hostname);
- m->hostname = h;
+ free(m->llmnr_hostname);
+ m->llmnr_hostname = h;
+
+ free(m->mdns_hostname);
+ m->mdns_hostname = k;
manager_refresh_rrs(m);
@@ -1356,6 +1433,24 @@ void manager_flush_dns_servers(Manager *m, DnsServerType t) {
}
}
+int manager_is_own_hostname(Manager *m, const char *name) {
+ int r;
+
+ assert(m);
+ assert(name);
+
+ if (m->llmnr_hostname) {
+ r = dns_name_equal(name, m->llmnr_hostname);
+ if (r != 0)
+ return r;
+ }
+
+ if (m->mdns_hostname)
+ return dns_name_equal(name, m->mdns_hostname);
+
+ return 0;
+}
+
static const char* const support_table[_SUPPORT_MAX] = {
[SUPPORT_NO] = "no",
[SUPPORT_YES] = "yes",
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index 53b5acb33c..fe7fe99505 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -91,9 +91,10 @@ struct Manager {
sd_event_source *bus_retry_event_source;
/* The hostname we publish on LLMNR and mDNS */
- char *hostname;
- DnsResourceKey *host_ipv4_key;
- DnsResourceKey *host_ipv6_key;
+ char *llmnr_hostname;
+ char *mdns_hostname;
+ DnsResourceKey *llmnr_host_ipv4_key;
+ DnsResourceKey *llmnr_host_ipv6_key;
/* Watch the system hostname */
int hostname_fd;
@@ -101,6 +102,8 @@ struct Manager {
/* Watch for system suspends */
sd_bus_slot *prepare_for_sleep_slot;
+
+ sd_event_source *sigusr1_event_source;
};
/* Manager */
@@ -140,5 +143,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
#define EXTRA_CMSG_SPACE 1024
+int manager_is_own_hostname(Manager *m, const char *name);
+
const char* support_to_string(Support p) _const_;
int support_from_string(const char *s) _pure_;
diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c
index 0af5545f8e..32e61af925 100644
--- a/src/resolve/resolved.c
+++ b/src/resolve/resolved.c
@@ -71,7 +71,7 @@ int main(int argc, char *argv[]) {
if (r < 0)
goto finish;
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGUSR1, -1) >= 0);
r = manager_new(&m);
if (r < 0) {
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 0df0594ea3..1bcb8903f3 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -1382,7 +1382,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
if (STR_IN_SET(field,
"CPUAccounting", "MemoryAccounting", "BlockIOAccounting",
- "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies")) {
+ "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
+ "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit")) {
r = parse_boolean(eq);
if (r < 0) {
@@ -1414,7 +1415,11 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
r = sd_bus_message_append(m, "v", "t", u);
- } else if (STR_IN_SET(field, "User", "Group", "DevicePolicy", "KillMode"))
+ } else if (STR_IN_SET(field,
+ "User", "Group", "DevicePolicy", "KillMode",
+ "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
+ "StandardInput", "StandardOutput", "StandardError",
+ "Description", "Slice", "Type"))
r = sd_bus_message_append(m, "v", "s", eq);
else if (streq(field, "DeviceAllow")) {
diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c
index 8a0dec1540..6dc04d51e4 100644
--- a/src/shared/dns-domain.c
+++ b/src/shared/dns-domain.c
@@ -308,14 +308,14 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded,
#endif
}
-int dns_name_normalize(const char *s, char **_ret) {
+int dns_name_concat(const char *a, const char *b, char **_ret) {
_cleanup_free_ char *ret = NULL;
size_t n = 0, allocated = 0;
- const char *p = s;
+ const char *p = a;
bool first = true;
int r;
- assert(s);
+ assert(a);
for (;;) {
_cleanup_free_ char *t = NULL;
@@ -328,6 +328,14 @@ int dns_name_normalize(const char *s, char **_ret) {
if (r == 0) {
if (*p != 0)
return -EINVAL;
+
+ if (b) {
+ /* Now continue with the second string, if there is one */
+ p = b;
+ b = NULL;
+ continue;
+ }
+
break;
}
@@ -341,27 +349,29 @@ int dns_name_normalize(const char *s, char **_ret) {
if (r < 0)
return r;
- if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1))
- return -ENOMEM;
+ if (_ret) {
+ if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1))
+ return -ENOMEM;
- if (!first)
- ret[n++] = '.';
- else
- first = false;
+ if (!first)
+ ret[n++] = '.';
+ else
+ first = false;
+
+ memcpy(ret + n, t, r);
+ }
- memcpy(ret + n, t, r);
n += r;
}
if (n > DNS_NAME_MAX)
return -EINVAL;
- if (!GREEDY_REALLOC(ret, allocated, n + 1))
- return -ENOMEM;
-
- ret[n] = 0;
-
if (_ret) {
+ if (!GREEDY_REALLOC(ret, allocated, n + 1))
+ return -ENOMEM;
+
+ ret[n] = 0;
*_ret = ret;
ret = NULL;
}
diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h
index bd50ad3e6d..8e73d9c20f 100644
--- a/src/shared/dns-domain.h
+++ b/src/shared/dns-domain.h
@@ -35,9 +35,17 @@ int dns_label_escape(const char *p, size_t l, char **ret);
int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
-int dns_name_normalize(const char *s, char **_ret);
+int dns_name_concat(const char *a, const char *b, char **ret);
+
+static inline int dns_name_normalize(const char *s, char **ret) {
+ /* dns_name_concat() normalizes as a side-effect */
+ return dns_name_concat(s, NULL, ret);
+}
+
static inline int dns_name_is_valid(const char *s) {
int r;
+
+ /* dns_name_normalize() verifies as a side effect */
r = dns_name_normalize(s, NULL);
if (r == -EINVAL)
return 0;
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index 60144ab8a6..9a5ffb7a6c 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -34,6 +34,7 @@
#include "formats-util.h"
#include "process-util.h"
#include "terminal-util.h"
+#include "hostname-util.h"
/* up to three lines (each up to 100 characters),
or 300 characters, whichever is less */
diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c
index 8f66df7718..63f1e4ca6f 100644
--- a/src/shared/utmp-wtmp.c
+++ b/src/shared/utmp-wtmp.c
@@ -204,12 +204,13 @@ _pure_ static const char *sanitize_id(const char *id) {
return id + l - sizeof(((struct utmpx*) NULL)->ut_id);
}
-int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) {
+int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) {
struct utmpx store = {
.ut_type = INIT_PROCESS,
.ut_pid = pid,
.ut_session = sid,
};
+ int r;
assert(id);
@@ -221,7 +222,26 @@ int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line
if (line)
strncpy(store.ut_line, basename(line), sizeof(store.ut_line));
- return write_entry_both(&store);
+ r = write_entry_both(&store);
+ if (r < 0)
+ return r;
+
+ if (ut_type == LOGIN_PROCESS || ut_type == USER_PROCESS) {
+ store.ut_type = LOGIN_PROCESS;
+ r = write_entry_both(&store);
+ if (r < 0)
+ return r;
+ }
+
+ if (ut_type == USER_PROCESS) {
+ store.ut_type = USER_PROCESS;
+ strncpy(store.ut_user, user, sizeof(store.ut_user)-1);
+ r = write_entry_both(&store);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
}
int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h
index 5d26ba6fb1..e0ceb873ac 100644
--- a/src/shared/utmp-wtmp.h
+++ b/src/shared/utmp-wtmp.h
@@ -31,7 +31,7 @@ int utmp_put_reboot(usec_t timestamp);
int utmp_put_runlevel(int runlevel, int previous);
int utmp_put_dead_process(const char *id, pid_t pid, int code, int status);
-int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line);
+int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user);
int utmp_wall(
const char *message,
@@ -57,7 +57,7 @@ static inline int utmp_put_runlevel(int runlevel, int previous) {
static inline int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
return 0;
}
-static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) {
+static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) {
return 0;
}
static inline int utmp_wall(
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 1a9dbadbe1..3cb5f61868 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -2794,6 +2794,33 @@ static int reboot_with_logind(sd_bus *bus, enum action a) {
return -EINVAL;
}
+ if (!strv_isempty(arg_wall)) {
+ _cleanup_free_ char *m;
+
+ m = strv_join(arg_wall, " ");
+ if (!m)
+ return log_oom();
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "SetWallMessage",
+ &error,
+ NULL,
+ "sb",
+ m,
+ !arg_no_wall);
+
+ if (r < 0) {
+ log_warning_errno(r, "Failed to set wall message, ignoring: %s",
+ bus_error_message(&error, r));
+ sd_bus_error_free(&error);
+ }
+ }
+
+
r = sd_bus_call_method(
bus,
"org.freedesktop.login1",
@@ -6260,6 +6287,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
ARG_PRESET_MODE,
ARG_FIRMWARE_SETUP,
ARG_NOW,
+ ARG_MESSAGE,
};
static const struct option options[] = {
@@ -6304,6 +6332,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "preset-mode", required_argument, NULL, ARG_PRESET_MODE },
{ "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP },
{ "now", no_argument, NULL, ARG_NOW },
+ { "message", required_argument, NULL, ARG_MESSAGE },
{}
};
@@ -6588,6 +6617,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_now = true;
break;
+ case ARG_MESSAGE:
+ if (strv_extend(&arg_wall, optarg) < 0)
+ return log_oom();
+ break;
+
case '?':
return -EINVAL;
@@ -7356,30 +7390,20 @@ static int halt_main(sd_bus *bus) {
if (!m)
return log_oom();
- r = sd_bus_set_property(
- b,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "WallMessage",
- &error,
- "s", m);
- if (r < 0) {
- log_warning_errno(r, "Failed to set WallMessage property in logind: %s",
- bus_error_message(&error, r));
- sd_bus_error_free(&error);
- }
+ r = sd_bus_call_method(
+ b,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "SetWallMessage",
+ &error,
+ NULL,
+ "sb",
+ m,
+ !arg_no_wall);
- r = sd_bus_set_property(
- b,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "EnableWallMessages",
- &error,
- "b", !arg_no_wall);
if (r < 0) {
- log_warning_errno(r, "Failed to set EnableWallMessages property in logind: %s",
+ log_warning_errno(r, "Failed to set wall message, ignoring: %s",
bus_error_message(&error, r));
sd_bus_error_free(&error);
}
@@ -7537,30 +7561,20 @@ int main(int argc, char*argv[]) {
}
}
- r = sd_bus_set_property(
- b,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "WallMessage",
- &error,
- "s", arg_wall);
- if (r < 0) {
- log_warning_errno(r, "Failed to set WallMessage property in logind: %s",
- bus_error_message(&error, r));
- sd_bus_error_free(&error);
- }
+ r = sd_bus_call_method(
+ b,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "SetWallMessage",
+ &error,
+ NULL,
+ "sb",
+ m,
+ !arg_no_wall);
- r = sd_bus_set_property(
- b,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "EnableWallMessages",
- &error,
- "b", !arg_no_wall);
if (r < 0) {
- log_warning_errno(r, "Failed to set EnableWallMessages property in logind: %s",
+ log_warning_errno(r, "Failed to set wall message, ignoring: %s",
bus_error_message(&error, r));
sd_bus_error_free(&error);
}
diff --git a/src/systemd/sd-dhcp6-lease.h b/src/systemd/sd-dhcp6-lease.h
index 716d7678f1..dc3df3bbf7 100644
--- a/src/systemd/sd-dhcp6-lease.h
+++ b/src/systemd/sd-dhcp6-lease.h
@@ -7,7 +7,7 @@
This file is part of systemd.
Copyright (C) 2014 Tom Gundersen
- Copyright (C) 2014 Intel Corporation. All rights reserved.
+ Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -33,6 +33,12 @@ int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid);
+int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs);
+int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains);
+int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease,
+ struct in6_addr **addrs);
+int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn);
+
sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);
diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c
index 0042722c99..2193eb6f7d 100644
--- a/src/test/test-dns-domain.c
+++ b/src/test/test-dns-domain.c
@@ -251,6 +251,39 @@ static void test_dns_name_reverse(void) {
test_dns_name_reverse_one("::1", "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa");
}
+static void test_dns_name_concat_one(const char *a, const char *b, int r, const char *result) {
+ _cleanup_free_ char *p = NULL;
+
+ assert_se(dns_name_concat(a, b, &p) == r);
+ assert_se(streq_ptr(p, result));
+}
+
+static void test_dns_name_concat(void) {
+ test_dns_name_concat_one("foo", "bar", 0, "foo.bar");
+ test_dns_name_concat_one("foo.foo", "bar.bar", 0, "foo.foo.bar.bar");
+ test_dns_name_concat_one("foo", NULL, 0, "foo");
+ test_dns_name_concat_one("foo.", "bar.", 0, "foo.bar");
+}
+
+static void test_dns_name_is_valid_one(const char *s, int ret) {
+ assert_se(dns_name_is_valid(s) == ret);
+}
+
+static void test_dns_name_is_valid(void) {
+ test_dns_name_is_valid_one("foo", 1);
+ test_dns_name_is_valid_one("foo.", 1);
+ test_dns_name_is_valid_one("Foo", 1);
+ test_dns_name_is_valid_one("foo.bar", 1);
+ test_dns_name_is_valid_one("foo.bar.baz", 1);
+ test_dns_name_is_valid_one("", 1);
+ test_dns_name_is_valid_one("foo..bar", 0);
+ test_dns_name_is_valid_one(".foo.bar", 0);
+ test_dns_name_is_valid_one("foo.bar.", 1);
+ test_dns_name_is_valid_one("\\zbar", 0);
+ test_dns_name_is_valid_one("ä", 1);
+ test_dns_name_is_valid_one("\n", 0);
+}
+
int main(int argc, char *argv[]) {
test_dns_label_unescape();
@@ -263,6 +296,8 @@ int main(int argc, char *argv[]) {
test_dns_name_root();
test_dns_name_single_label();
test_dns_name_reverse();
+ test_dns_name_concat();
+ test_dns_name_is_valid();
return 0;
}
diff --git a/src/test/test-util.c b/src/test/test-util.c
index 3c1bac4606..dff38ab6f6 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -1539,6 +1539,7 @@ static void test_extract_first_word(void) {
p = original = "\"";
assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
assert_se(streq(t, "\""));
+ free(t);
assert_se(isempty(p));
p = original = "\"";
@@ -1548,6 +1549,7 @@ static void test_extract_first_word(void) {
p = original = "\'";
assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
assert_se(streq(t, "\'"));
+ free(t);
assert_se(isempty(p));
p = original = "\'";
@@ -1557,6 +1559,7 @@ static void test_extract_first_word(void) {
p = original = "\'fooo";
assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
assert_se(streq(t, "\'fooo"));
+ free(t);
assert_se(isempty(p));
p = original = "\'fooo";
diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c
index 8ec6adb493..1c3f03c803 100644
--- a/src/timedate/timedatectl.c
+++ b/src/timedate/timedatectl.c
@@ -96,7 +96,7 @@ static void print_status_info(const StatusInfo *i) {
old_tz = strdupa(tz);
/* Set the new $TZ */
- if (i->timezone && setenv("TZ", i->timezone, true) < 0)
+ if (setenv("TZ", isempty(i->timezone) ? "UTC" : i->timezone, true) < 0)
log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m");
else
tzset();