Compare commits

...

48 Commits

Author SHA1 Message Date
Lennart Poettering 1008f5b069
Merge pull request #17351 from poettering/exec-rt-typo-fix
fix one character typo in execute.c
2020-10-14 19:41:27 +02:00
Lennart Poettering 21ad331873
Merge pull request #17350 from poettering/bus-read-array
sd-bus: initialize return values on success in sd_bus_message_read_ar…
2020-10-14 19:41:01 +02:00
Lennart Poettering 64a7fcc5cd bootctl: separate boot loader specific commands in man and --help
bootctl implements three types of operation: those that work with an EFI
boot loader, those which work with any EFI boot loader that implements
the boot loader spec + interface, and finally those specific to sd-boot.
Previously the --help text and the man page mixed them all up. Let's put
them clearly in three separate sections however, to communicate clearly
what is supposed to work everywhere, and what is specific to
systemd-boot or boot loaders implementing the two specs.

This adjusts wording here and there, but is mostly just about
re-ordering existing docs, and putting them under new sections.
2020-10-14 19:40:39 +02:00
Lennart Poettering 84fc961082
Merge pull request #17270 from keszybz/less-secure-mode
Use less in "secure" mode when under sudo
2020-10-14 18:33:10 +02:00
Lennart Poettering b0eb40cda4
Merge pull request #17188 from keszybz/envvars-posix
Follow (mostly) POSIX rules for environment variables
2020-10-14 18:32:22 +02:00
Lennart Poettering fc8bc57f6b
Merge pull request #16968 from yuwata/remove-old-device-on-move-event
core, udev: remove old device on move event
2020-10-14 17:49:37 +02:00
Lennart Poettering 4840807c6d man: update sd_bus_message_read_array() docs to clarify return value 0 vs. 1 2020-10-14 17:36:06 +02:00
Lennart Poettering 6293d958a4 sd-bus: initialize return values on success in sd_bus_message_read_array()
Fixes: #17346
2020-10-14 17:35:00 +02:00
Lennart Poettering 7848cb8c57
Merge pull request #17338 from poettering/close-range
make use of new kernel 5.9 close_range() syscall in close_all_fds()
2020-10-14 17:22:15 +02:00
Lennart Poettering 670eed4c8c core: debug log about received fds 2020-10-14 16:41:37 +02:00
Lennart Poettering 74aaf59b1a execute: make sure some more functions follow coding style
Initialize all return values on success, as our usual coding style
suggests.
2020-10-14 16:41:37 +02:00
Lennart Poettering f5fa352f1e execute: fix single character typo
Corrects: c413bb28df

Fixes: #17313
2020-10-14 16:41:37 +02:00
Yu Watanabe fd8f865c9f
Merge pull request #17342 from yuwata/network-dhcp-ipv4-acd-fixes
network: fixes several issues in IPv4 DAD for DHCP4
2020-10-14 23:12:41 +09:00
Zbigniew Jędrzejewski-Szmek 547f724f7a
Merge pull request #17267 from yuwata/hashmap_put_strdup
hashmap: make hashmap_put_strdup() take hash_ops
2020-10-14 15:05:15 +02:00
Zbigniew Jędrzejewski-Szmek 540e0bad3e
Merge pull request #17316 from yuwata/network-address-ipv4-peer-issue-17304
network: directly compare with in_addr element for IPv4 case
2020-10-14 15:02:14 +02:00
Lennart Poettering eaa2751685 update TODO 2020-10-14 10:40:36 +02:00
Lennart Poettering 59c4bbfb93 test-fd-util: add test case for close_all_fd() 2020-10-14 10:40:32 +02:00
Lennart Poettering b8cfa2da7c fd-util: port close_all_fds() to close_range() 2020-10-14 10:40:29 +02:00
Lennart Poettering 441e0fdb90 missing: add close_range() wrapper
The syscall was added in 5.9 and is not yet exposed in glibc, hence
define our own wrapper.
2020-10-14 10:40:10 +02:00
Lennart Poettering 6ea0d25c57 seccomp: allowlist close_range() by default in @basic-io 2020-10-14 10:40:06 +02:00
Lennart Poettering 562b01e996 alloc-util: avoid allocating zero size memory blocks
It's not clear what libc's make of this. We clamp to 1 byte allocations
in most cases already, let's add this for a few where this was missing.
2020-10-14 10:39:48 +02:00
Zbigniew Jędrzejewski-Szmek 0a42426d79 pager: make pager secure when under euid is changed or explicitly requested
The variable is renamed to SYSTEMD_PAGERSECURE (because it's not just about
less now), and we automatically enable secure mode in certain cases, but not
otherwise.

This approach is more nuanced, but should provide a better experience for
users:

- Previusly we would set LESSSECURE=1 and trust the pager to make use of
  it. But this has an effect only on less. We need to not start pagers which
  are insecure when in secure mode. In particular more is like that and is a
  very popular pager.

- We don't enable secure mode always, which means that those other pagers can
  reasonably used.

- We do the right thing by default, but the user has ultimate control by
  setting SYSTEMD_PAGERSECURE.

Fixes #5666.

v2:
- also check $PKEXEC_UID

v3:
- use 'sd_pid_get_owner_uid() != geteuid()' as the condition
2020-10-14 10:04:12 +02:00
Yu Watanabe 5431227400 network: update MAC address in IPv4ACD client for DHCP4 2020-10-14 15:38:29 +09:00
Yu Watanabe a7df5cae54 network: also stop IPv4ACD client in link_stop_clients() 2020-10-14 15:38:29 +09:00
Yu Watanabe 66f507e1ba network: stop IPv4ACD client for DHCPv4 when lease is exprired 2020-10-14 15:38:29 +09:00
Yu Watanabe 10fa21c0dc network: move IPv4ACD client for DHCPv4 from Network to Link object
A .network file may matches multiple interfaces.
2020-10-14 15:38:29 +09:00
Yu Watanabe 8ff85383b4 network: voidify link_stop_clients() in link_enter_failed() 2020-10-14 15:38:29 +09:00
Yu Watanabe a0887abbd8 sd-device: use trivial_hash_ops_free_free for managing match sysattrs or properties
This fixes an issue caused by eb1c1dc029.

Before the commit, multiple values can be specified for the same
sysattr or property.

Fixes #17259.
2020-10-13 22:45:15 +09:00
Yu Watanabe 5e71868ced util: introduce two trivial hash_ops
Will be used in a later commit.
2020-10-13 22:40:22 +09:00
Yu Watanabe 11e9fec259 hashmap: introduce {hashmap,set}_put_strdup_full()
They can take hash_ops.
2020-10-13 22:39:06 +09:00
Zbigniew Jędrzejewski-Szmek 1b5b507cd2 test-login: always test sd_pid_get_owner_uid(), modernize
A long time some function only worked when in a session, and the test
didn't execute them when sd_pid_get_session() failed. Let's always call
them to increase coverage.

While at it, let's test for ==0 not >=0 where we don't expect the function
to return anything except 0 or error.
2020-10-13 14:43:08 +02:00
Yu Watanabe 21266e60e9 test-network: add test for issue #17304 2020-10-13 20:32:17 +09:00
Yu Watanabe b1476b5210 network: constify arguments 2020-10-13 20:30:38 +09:00
Yu Watanabe fe841414ef network: read peer address, label, broadcast from rtnl message
Then, Address objects in Network and Link can be easily compared by
address_equal().
2020-10-13 20:30:35 +09:00
Yu Watanabe 9b9c5fff16 network: directly compare with in_addr element for IPv4 case
When peer address is set, address_compare_func() (or address_equal())
does not work in link_is_static_address_configured(), as an Address object
stored in a Link does not contain peer addresses. So, we need to also
compare with in_addr element for IPv4 case.

Fixes #17304.
2020-10-13 20:30:16 +09:00
Zbigniew Jędrzejewski-Szmek a4ccce22d9 systemctl: ignore invalid variables in import-environment
When doing import-environment, we shouldn't fail if some assignment is invalid.
OTOH, if the invalid assignment is specified as a positional argument, we should
keep failing.

This would also fix https://bugzilla.redhat.com/show_bug.cgi?id=1754395, by
ignoring certain variables which are not important in that scenario. It seems
like the right thing to do in general.
2020-10-12 18:27:20 +02:00
Zbigniew Jędrzejewski-Szmek b45c068dd8 basic/env-util: (mostly) follow POSIX for what variable names are allowed
There was some confusion about what POSIX says about variable names:

   names shall not contain the character '='. For values to be portable
   across systems conforming to POSIX.1-2008, the value shall be composed
   of characters from the portable character set (except NUL and as
   indicated below).

i.e. it allows almost all ASCII in variable names (without NUL and DEL and
'='). OTOH, it says that *utilities* use a smaller set of characters:

   Environment variable names used by the utilities in the Shell and
   Utilities volume of POSIX.1-2008 consist solely of uppercase letters,
   digits, and the <underscore> ( '_' ) from the characters defined in
   Portable Character Set and do not begin with a digit.

When enforcing variable names in environment blocks, we need to use this
first definition, so that we can propagate all valid variables.
I think having non-printable characters in variable names is too much, so
I took out the whitespace stuff from the first definition.

OTOH, when we use *shell syntax*, for example doing variable expansion,
it seems enough to support expansion of variables that the shell would allow.

Fixes #14878,
https://bugzilla.redhat.com/show_bug.cgi?id=1754395,
https://bugzilla.redhat.com/show_bug.cgi?id=1879216.
2020-10-12 18:24:28 +02:00
Zbigniew Jędrzejewski-Szmek 0b3456428b test-env-util: print function headers 2020-10-12 18:23:52 +02:00
Lennart Poettering 612ebf6c91 pager: set $LESSSECURE whenver we invoke a pager
Some extra safety when invoked via "sudo". With this we address a
genuine design flaw of sudo, and we shouldn't need to deal with this.
But it's still a good idea to disable this surface given how exotic it
is.

Prompted by #5666
2020-10-07 09:23:07 +02:00
Yu Watanabe efdaeb88f0 test: add test for device renaming issue #16967 2020-09-15 11:29:38 +09:00
Yu Watanabe 6d4bcea4ca udev: merge rules for bluetooth device 2020-09-15 11:29:38 +09:00
Yu Watanabe 7f67b01e3f udev: do not update return value on failure 2020-09-15 11:29:38 +09:00
Yu Watanabe f576730256 udev: allow to match OriginalName= with renamed interface name 2020-09-15 11:29:38 +09:00
Yu Watanabe e0e789c1e9 udev: re-assign ID_NET_DRIVER=, ID_NET_LINK_FILE=, ID_NET_NAME= properties on non-'add' uevent
Previous commit makes drop ID_NET_DRIVER=, ID_NET_LINK_FILE=, and
ID_NET_NAME= properties for network interfaces on 'move' uevent.
ID_NET_DRIVER= and ID_NET_LINK_FILE= properties are used by networkctl.
ID_NET_NAME= may be used by end-user rules or programs. So, let's
re-assign them on 'move' uevent. (Note that strictly speaking, this
makes them re-assigned on all but 'remove' uevent.)
2020-09-15 11:29:35 +09:00
Yu Watanabe 51d9aec0ff Revert "udev: import the full db on MOVE events for devices without dev_t"
This reverts commit b081b27e14.

If a network interface get a 'move' event, then previously SYSTEMD_ALIAS=
property still contains an old alias, and the old alias .device unit
will not be removed.

This makes all properties cleared on 'move' event, and then old alias
.device unit will be removed by pid1.

Fixes #16967.
2020-09-15 11:06:25 +09:00
Yu Watanabe 2e17fed5f3 udev: split link_config_apply() into small pieces 2020-09-15 11:06:25 +09:00
Yu Watanabe cadc7ed2e2 ethtool: constify arguments for ethtool_set_xxx() 2020-09-15 11:06:25 +09:00
Yu Watanabe 87bc687a8c core/device: remove .device unit corresponding to DEVPATH_OLD
Partially fixes #16967.
2020-09-15 09:40:08 +09:00
49 changed files with 1201 additions and 532 deletions

2
TODO
View File

@ -24,8 +24,6 @@ Features:
* in fd_get_path() if we see (deleted) then do stat and check for st_nlink * in fd_get_path() if we see (deleted) then do stat and check for st_nlink
* add support for close_range() added in kernel 5.9
* Add service setting to run a service within the specified VRF. i.e. do the * Add service setting to run a service within the specified VRF. i.e. do the
equivalent of "ip vrf exec". equivalent of "ip vrf exec".

View File

@ -17,7 +17,7 @@
<refnamediv> <refnamediv>
<refname>bootctl</refname> <refname>bootctl</refname>
<refpurpose>Control the firmware and boot manager settings</refpurpose> <refpurpose>Control EFI firmware boot settings and manage boot loader</refpurpose>
</refnamediv> </refnamediv>
<refsynopsisdiv> <refsynopsisdiv>
@ -31,16 +31,18 @@
<refsect1> <refsect1>
<title>Description</title> <title>Description</title>
<para><command>bootctl</command> can check the EFI boot loader status, list available boot loaders and boot loader <para><command>bootctl</command> can check the EFI firmware and boot loader status, list and manage
entries, and install, update, or remove the available boot loaders and boot loader entries, and install, update, or remove the
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> boot loader on the <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> boot
current system.</para> loader on the current system.</para>
</refsect1> </refsect1>
<refsect1> <refsect1>
<title>Commands</title> <title>Generic EFI Firmware/Boot Loader Commands</title>
<variablelist>
<para>These commands are available on any EFI system, regardless of the boot loader used.</para>
<variablelist>
<varlistentry> <varlistentry>
<term><option>status</option></term> <term><option>status</option></term>
@ -49,7 +51,69 @@
loaders and the current default boot loader entry. If no command is specified, this is the implied loaders and the current default boot loader entry. If no command is specified, this is the implied
default.</para></listitem> default.</para></listitem>
</varlistentry> </varlistentry>
</variablelist>
<varlistentry>
<term><option>reboot-to-firmware</option> <optional><replaceable>BOOL</replaceable></optional></term>
<listitem><para>Query or set the "Reboot-Into-Firmware-Setup" flag of the EFI firmware. Takes a
boolean argument which controls whether to show the firmware setup on next system reboot. If the
argument is omitted shows the current status of the flag, or whether the flag is supported. This
controls the same flag as <command>systemctl reboot --firmware-setup</command>, but is more
low-level and allows setting the flag independently from actually requesting a
reboot.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>systemd-efi-options</option> <optional><replaceable>STRING</replaceable></optional></term>
<listitem><para>When called without the optional argument, prints the current value of the
<literal>SystemdOptions</literal> EFI variable. When called with an argument, sets the
variable to that value. See
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for the meaning of that variable.</para></listitem>
</varlistentry>
</refsect1>
<refsect1>
<title>Boot Loader Specification Commands</title>
<para>These commands are available for all boot loaders that implement the <ulink
url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink> and/or the <ulink
url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>, such as
<command>systemd-boot</command>.</para>
<variablelist>
<varlistentry>
<term><option>list</option></term>
<listitem><para>Shows all available boot loader entries implementing the <ulink
url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>, as well as any
other entries discovered or automatically generated by a boot loader implementing the <ulink
url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader
Interface</ulink>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>set-default</option> <replaceable>ID</replaceable></term>
<term><option>set-oneshot</option> <replaceable>ID</replaceable></term>
<listitem><para>Sets the default boot loader entry. Takes a single boot loader entry ID string as
argument. The <option>set-oneshot</option> command will set the default entry only for the next boot,
the <option>set-default</option> will set it persistently for all future boots.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title><command>systemd-boot</command> Commands</title>
<para>These commands manage the <command>systemd-boot</command> EFI boot loader, and do not work in
conjunction with other boot loaders.</para>
<variablelist>
<varlistentry> <varlistentry>
<term><option>install</option></term> <term><option>install</option></term>
@ -101,45 +165,6 @@
information.</para></listitem> information.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>systemd-efi-options</option> <optional><replaceable>STRING</replaceable></optional></term>
<listitem><para>When called without the optional argument, prints the current value of the
<literal>SystemdOptions</literal> EFI variable. When called with an argument, sets the
variable to that value. See
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for the meaning of that variable.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>reboot-to-firmware</option> <optional><replaceable>BOOL</replaceable></optional></term>
<listitem><para>Query or set the "Reboot-Into-Firmware-Setup" flag of the EFI firmware. Takes a
boolean argument which controls whether to show the firmware setup on next system reboot. If the
argument is omitted shows the current status of the flag, or whether the flag is supported. This
controls the same flag as <command>systemctl reboot --firmware-setup</command>, but is more
low-level and allows setting the flag independently from actually requesting a
reboot.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>list</option></term>
<listitem><para>Shows all available boot loader entries implementing the <ulink
url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader
Specification</ulink>, as well as any other entries discovered or automatically generated by the boot
loader.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>set-default</option> <replaceable>ID</replaceable></term>
<term><option>set-oneshot</option> <replaceable>ID</replaceable></term>
<listitem><para>Sets the default boot loader entry. Takes a single boot loader entry ID string as argument. The
<option>set-oneshot</option> command will set the default entry only for the next boot, the
<option>set-default</option> will set it persistently for all future boots.</para></listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>

View File

@ -64,6 +64,33 @@
the invoking terminal is determined to be UTF-8 compatible).</para></listitem> the invoking terminal is determined to be UTF-8 compatible).</para></listitem>
</varlistentry> </varlistentry>
<varlistentry id='lesssecure'>
<term><varname>$SYSTEMD_PAGERSECURE</varname></term>
<listitem><para>Takes a boolean argument. When true, the "secure" mode of the pager is enabled; if
false, disabled. If <varname>$SYSTEMD_PAGERSECURE</varname> is not set at all, secure mode is enabled
if the effective UID is not the same as the owner of the login session, see <citerefentry
project='man-pages'><refentrytitle>geteuid</refentrytitle><manvolnum>2</manvolnum></citerefentry> and
<citerefentry><refentrytitle>sd_pid_get_owner_uid</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
In secure mode, <option>LESSSECURE=1</option> will be set when invoking the pager, and the pager shall
disable commands that open or create new files or start new subprocesses. When
<varname>$SYSTEMD_PAGERSECURE</varname> is not set at all, pagers which are not known to implement
secure mode will not be used. (Currently only
<citerefentry><refentrytitle>less</refentrytitle><manvolnum>1</manvolnum></citerefentry> implements
secure mode.)</para>
<para>Note: when commands are invoked with elevated privileges, for example under <citerefentry
project='man-pages'><refentrytitle>sudo</refentrytitle><manvolnum>8</manvolnum></citerefentry> or
<citerefentry
project='die-net'><refentrytitle>pkexec</refentrytitle><manvolnum>1</manvolnum></citerefentry>, care
must be taken to ensure that unintended interactive features are not enabled. "Secure" mode for the
pager may be enabled automatically as describe above. Setting <varname>SYSTEMD_PAGERSECURE=0</varname>
or not removing it from the inherited environment allows the user to invoke arbitrary commands. Note
that if the <varname>$SYSTEMD_PAGER</varname> or <varname>$PAGER</varname> variables are to be
honoured, <varname>$SYSTEMD_PAGERSECURE</varname> must be set too. It might be reasonable to completly
disable the pager using <option>--no-pager</option> instead.</para></listitem>
</varlistentry>
<varlistentry id='colors'> <varlistentry id='colors'>
<term><varname>$SYSTEMD_COLORS</varname></term> <term><varname>$SYSTEMD_COLORS</varname></term>

View File

@ -38,16 +38,16 @@
<refsect1> <refsect1>
<title>Description</title> <title>Description</title>
<para><function>sd_bus_message_read_array()</function> gives access to an element array in <para><function>sd_bus_message_read_array()</function> provides access to an array elements in the
message <parameter>m</parameter>. The "read pointer" in the message must be right before an bus message <parameter>m</parameter>. The "read pointer" in the message must be right before an array of type
array of type <parameter>type</parameter>. As a special case, <parameter>type</parameter> may be <parameter>type</parameter>. As a special case, <parameter>type</parameter> may be
<constant>NUL</constant>, in which case any type is acceptable. A pointer to the array data is <constant>NUL</constant>, in which case any trivial type is acceptable. A pointer to the array data is returned
returned in the parameter <parameter>ptr</parameter> and the size of array data (in bytes) is in the parameter <parameter>ptr</parameter> and the size of array data (in bytes) is returned in the
returned in the parameter <parameter>size</parameter>. If <parameter>size</parameter> is 0, a parameter <parameter>size</parameter>. If the returned <parameter>size</parameter> parameter is 0, a
valid non-null pointer will be returned, but it may not be dereferenced. The data is aligned as valid non-null pointer will be returned as <parameter>ptr</parameter>, but it may not be
appropriate for the data type. The data is part of the message — it may not be modified and is dereferenced. The data is aligned as appropriate for the data type. The data is part of the message — it
valid only as long as the message is referenced. After this function returns, the "read pointer" may not be modified and is valid only as long as the message is referenced. After this function returns,
points at the next element after the array.</para> the "read pointer" points at the next element after the array.</para>
<para>Note that this function only supports arrays of trivial types, i.e. arrays of booleans, the various <para>Note that this function only supports arrays of trivial types, i.e. arrays of booleans, the various
integer types, as well as floating point numbers. In particular it may not be used for arrays of strings, integer types, as well as floating point numbers. In particular it may not be used for arrays of strings,
@ -58,9 +58,12 @@
<title>Return Value</title> <title>Return Value</title>
<para> <para>
On success, <function>sd_bus_message_read_array()</function> returns 0 or On success and when an array was read, <function>sd_bus_message_read_array()</function> returns an
a positive integer. On failure, it returns a negative errno-style error integer greater than zero. If invoked while inside a container element (such as an array, e.g. when
code. operating on an array of arrays) and the final element of the outer container has been read already and
the read pointer is thus behind the last element of the outer container this call returns 0 (and the
returned pointer will be <constant>NULL</constant> and the size will be 0). On failure, it returns a
negative errno-style error code.
</para> </para>
<refsect2> <refsect2>

View File

@ -2303,6 +2303,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<xi:include href="less-variables.xml" xpointer="pager"/> <xi:include href="less-variables.xml" xpointer="pager"/>
<xi:include href="less-variables.xml" xpointer="less"/> <xi:include href="less-variables.xml" xpointer="less"/>
<xi:include href="less-variables.xml" xpointer="lesscharset"/> <xi:include href="less-variables.xml" xpointer="lesscharset"/>
<xi:include href="less-variables.xml" xpointer="lesssecure"/>
<xi:include href="less-variables.xml" xpointer="colors"/> <xi:include href="less-variables.xml" xpointer="colors"/>
<xi:include href="less-variables.xml" xpointer="urlify"/> <xi:include href="less-variables.xml" xpointer="urlify"/>
</refsect1> </refsect1>

View File

@ -691,6 +691,7 @@
<xi:include href="less-variables.xml" xpointer="pager"/> <xi:include href="less-variables.xml" xpointer="pager"/>
<xi:include href="less-variables.xml" xpointer="less"/> <xi:include href="less-variables.xml" xpointer="less"/>
<xi:include href="less-variables.xml" xpointer="lesscharset"/> <xi:include href="less-variables.xml" xpointer="lesscharset"/>
<xi:include href="less-variables.xml" xpointer="lesssecure"/>
<xi:include href="less-variables.xml" xpointer="colors"/> <xi:include href="less-variables.xml" xpointer="colors"/>
<xi:include href="less-variables.xml" xpointer="urlify"/> <xi:include href="less-variables.xml" xpointer="urlify"/>

View File

@ -533,6 +533,7 @@ foreach ident : [
#include <signal.h> #include <signal.h>
#include <sys/wait.h>'''], #include <sys/wait.h>'''],
['mallinfo', '''#include <malloc.h>'''], ['mallinfo', '''#include <malloc.h>'''],
['close_range', '''#include <unistd.h>'''],
] ]
have = cc.has_function(ident[0], prefix : ident[1], args : '-D_GNU_SOURCE') have = cc.has_function(ident[0], prefix : ident[1], args : '-D_GNU_SOURCE')

View File

@ -4,7 +4,7 @@ SUBSYSTEM!="net", GOTO="net_setup_link_end"
IMPORT{builtin}="path_id" IMPORT{builtin}="path_id"
ACTION!="add", GOTO="net_setup_link_end" ACTION=="remove", GOTO="net_setup_link_end"
IMPORT{builtin}="net_setup_link" IMPORT{builtin}="net_setup_link"

View File

@ -46,9 +46,9 @@ SUBSYSTEM=="block", KERNEL=="nbd*", ENV{DEVTYPE}=="disk", TEST!="pid", ENV{SYSTE
# http://cgit.freedesktop.org/systemd/systemd/tree/src/libudev/libudev-enumerate.c#n955 # http://cgit.freedesktop.org/systemd/systemd/tree/src/libudev/libudev-enumerate.c#n955
SUBSYSTEM=="net", KERNEL!="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/net/devices/$name" SUBSYSTEM=="net", KERNEL!="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/net/devices/$name"
SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/bluetooth/devices/%k" SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/bluetooth/devices/%k", \
ENV{SYSTEMD_WANTS}+="bluetooth.target", ENV{SYSTEMD_USER_WANTS}+="bluetooth.target"
SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_WANTS}+="bluetooth.target", ENV{SYSTEMD_USER_WANTS}+="bluetooth.target"
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0b????:*", ENV{ID_SMARTCARD_READER}="1" SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0b????:*", ENV{ID_SMARTCARD_READER}="1"
ENV{ID_SMARTCARD_READER}=="?*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="smartcard.target", ENV{SYSTEMD_USER_WANTS}+="smartcard.target" ENV{ID_SMARTCARD_READER}=="?*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="smartcard.target", ENV{SYSTEMD_USER_WANTS}+="smartcard.target"
SUBSYSTEM=="sound", KERNEL=="card*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sound.target", ENV{SYSTEMD_USER_WANTS}+="sound.target" SUBSYSTEM=="sound", KERNEL=="card*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sound.target", ENV{SYSTEMD_USER_WANTS}+="sound.target"

View File

@ -27,7 +27,7 @@ typedef void (*free_func_t)(void *p);
size_t _n_ = n; \ size_t _n_ = n; \
assert(!size_multiply_overflow(sizeof(t), _n_)); \ assert(!size_multiply_overflow(sizeof(t), _n_)); \
assert(sizeof(t)*_n_ <= ALLOCA_MAX); \ assert(sizeof(t)*_n_ <= ALLOCA_MAX); \
(t*) alloca(sizeof(t)*_n_); \ (t*) alloca((sizeof(t)*_n_) ?: 1); \
}) })
#define newa0(t, n) \ #define newa0(t, n) \
@ -35,14 +35,14 @@ typedef void (*free_func_t)(void *p);
size_t _n_ = n; \ size_t _n_ = n; \
assert(!size_multiply_overflow(sizeof(t), _n_)); \ assert(!size_multiply_overflow(sizeof(t), _n_)); \
assert(sizeof(t)*_n_ <= ALLOCA_MAX); \ assert(sizeof(t)*_n_ <= ALLOCA_MAX); \
(t*) alloca0(sizeof(t)*_n_); \ (t*) alloca0((sizeof(t)*_n_) ?: 1); \
}) })
#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) #define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n))) #define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n)))
#define malloc0(n) (calloc(1, (n))) #define malloc0(n) (calloc(1, (n) ?: 1))
static inline void *mfree(void *memory) { static inline void *mfree(void *memory) {
free(memory); free(memory);
@ -65,7 +65,7 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s
void *_q_; \ void *_q_; \
size_t _l_ = l; \ size_t _l_ = l; \
assert(_l_ <= ALLOCA_MAX); \ assert(_l_ <= ALLOCA_MAX); \
_q_ = alloca(_l_); \ _q_ = alloca(_l_ ?: 1); \
memcpy(_q_, p, _l_); \ memcpy(_q_, p, _l_); \
}) })
@ -135,7 +135,7 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
char *_new_; \ char *_new_; \
size_t _len_ = n; \ size_t _len_ = n; \
assert(_len_ <= ALLOCA_MAX); \ assert(_len_ <= ALLOCA_MAX); \
_new_ = alloca(_len_); \ _new_ = alloca(_len_ ?: 1); \
(void *) memset(_new_, 0, _len_); \ (void *) memset(_new_, 0, _len_); \
}) })
@ -146,7 +146,7 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
size_t _mask_ = (align) - 1; \ size_t _mask_ = (align) - 1; \
size_t _size_ = size; \ size_t _size_ = size; \
assert(_size_ <= ALLOCA_MAX); \ assert(_size_ <= ALLOCA_MAX); \
_ptr_ = alloca(_size_ + _mask_); \ _ptr_ = alloca((_size_ + _mask_) ?: 1); \
(void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \ (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \
}) })

View File

@ -16,22 +16,26 @@
#include "strv.h" #include "strv.h"
#include "utf8.h" #include "utf8.h"
#define VALID_CHARS_ENV_NAME \ /* We follow bash for the character set. Different shells have different rules. */
#define VALID_BASH_ENV_NAME_CHARS \
DIGITS LETTERS \ DIGITS LETTERS \
"_" "_"
static bool env_name_is_valid_n(const char *e, size_t n) { static bool printable_portable_character(char c) {
const char *p; /* POSIX.1-2008 specifies almost all ASCII characters as "portable". (Only DEL is excluded, and
* additionally NUL and = are not allowed in variable names). We are stricter, and additionally
* reject BEL, BS, HT, CR, LF, VT, FF and SPACE, i.e. all whitespace. */
return c >= '!' && c <= '~';
}
static bool env_name_is_valid_n(const char *e, size_t n) {
if (!e) if (!e)
return false; return false;
if (n <= 0) if (n <= 0)
return false; return false;
if (e[0] >= '0' && e[0] <= '9')
return false;
/* POSIX says the overall size of the environment block cannot /* POSIX says the overall size of the environment block cannot
* be > ARG_MAX, an individual assignment hence cannot be * be > ARG_MAX, an individual assignment hence cannot be
* either. Discounting the equal sign and trailing NUL this * either. Discounting the equal sign and trailing NUL this
@ -40,8 +44,8 @@ static bool env_name_is_valid_n(const char *e, size_t n) {
if (n > (size_t) sysconf(_SC_ARG_MAX) - 2) if (n > (size_t) sysconf(_SC_ARG_MAX) - 2)
return false; return false;
for (p = e; p < e + n; p++) for (const char *p = e; p < e + n; p++)
if (!strchr(VALID_CHARS_ENV_NAME, *p)) if (!printable_portable_character(*p) || *p == '=')
return false; return false;
return true; return true;
@ -546,7 +550,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
word = e+1; word = e+1;
state = WORD; state = WORD;
} else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) { } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
k = strnappend(r, word, e-word-1); k = strnappend(r, word, e-word-1);
if (!k) if (!k)
return NULL; return NULL;
@ -636,7 +640,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
case VARIABLE_RAW: case VARIABLE_RAW:
assert(flags & REPLACE_ENV_ALLOW_BRACELESS); assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
if (!strchr(VALID_CHARS_ENV_NAME, *e)) { if (!strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
const char *t; const char *t;
t = strv_env_get_n(env, word+1, e-word-1, flags); t = strv_env_get_n(env, word+1, e-word-1, flags);

View File

@ -21,6 +21,7 @@
#include "path-util.h" #include "path-util.h"
#include "process-util.h" #include "process-util.h"
#include "socket-util.h" #include "socket-util.h"
#include "sort-util.h"
#include "stat-util.h" #include "stat-util.h"
#include "stdio-util.h" #include "stdio-util.h"
#include "tmpfile-util.h" #include "tmpfile-util.h"
@ -210,13 +211,102 @@ static int get_max_fd(void) {
return (int) (m - 1); return (int) (m - 1);
} }
static int cmp_int(const int *a, const int *b) {
return CMP(*a, *b);
}
int close_all_fds(const int except[], size_t n_except) { int close_all_fds(const int except[], size_t n_except) {
static bool have_close_range = true; /* Assume we live in the future */
_cleanup_closedir_ DIR *d = NULL; _cleanup_closedir_ DIR *d = NULL;
struct dirent *de; struct dirent *de;
int r = 0; int r = 0;
assert(n_except == 0 || except); assert(n_except == 0 || except);
if (have_close_range) {
/* In the best case we have close_range() to close all fds between a start and an end fd,
* which we can use on the "inverted" exception array, i.e. all intervals between all
* adjacent pairs from the sorted exception array. This changes loop complexity from O(n)
* where n is number of open fds to O(mlog(m)) where m is the number of fds to keep
* open. Given that we assume n m that's preferable to us. */
if (n_except == 0) {
/* Close everything. Yay! */
if (close_range(3, -1, 0) >= 0)
return 1;
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
have_close_range = false;
} else {
_cleanup_free_ int *sorted_malloc = NULL;
size_t n_sorted;
int *sorted;
assert(n_except < SIZE_MAX);
n_sorted = n_except + 1;
if (n_sorted > 64) /* Use heap for large numbers of fds, stack otherwise */
sorted = sorted_malloc = new(int, n_sorted);
else
sorted = newa(int, n_sorted);
if (sorted) {
int c = 0;
memcpy(sorted, except, n_except * sizeof(int));
/* Let's add fd 2 to the list of fds, to simplify the loop below, as this
* allows us to cover the head of the array the same way as the body */
sorted[n_sorted-1] = 2;
typesafe_qsort(sorted, n_sorted, cmp_int);
for (size_t i = 0; i < n_sorted-1; i++) {
int start, end;
start = MAX(sorted[i], 2); /* The first three fds shall always remain open */
end = MAX(sorted[i+1], 2);
assert(end >= start);
if (end - start <= 1)
continue;
/* Close everything between the start and end fds (both of which shall stay open) */
if (close_range(start + 1, end - 1, 0) < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
have_close_range = false;
break;
}
c += end - start - 1;
}
if (have_close_range) {
/* The loop succeeded. Let's now close everything beyond the end */
if (sorted[n_sorted-1] >= INT_MAX) /* Dont let the addition below overflow */
return c;
if (close_range(sorted[n_sorted-1] + 1, -1, 0) >= 0)
return c + 1;
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
have_close_range = false;
}
}
}
/* Fallback on OOM or if close_range() is not supported */
}
d = opendir("/proc/self/fd"); d = opendir("/proc/self/fd");
if (!d) { if (!d) {
int fd, max_fd; int fd, max_fd;

View File

@ -71,6 +71,19 @@ const struct hash_ops trivial_hash_ops = {
.compare = trivial_compare_func, .compare = trivial_compare_func,
}; };
const struct hash_ops trivial_hash_ops_free = {
.hash = trivial_hash_func,
.compare = trivial_compare_func,
.free_key = free,
};
const struct hash_ops trivial_hash_ops_free_free = {
.hash = trivial_hash_func,
.compare = trivial_compare_func,
.free_key = free,
.free_value = free,
};
void uint64_hash_func(const uint64_t *p, struct siphash *state) { void uint64_hash_func(const uint64_t *p, struct siphash *state) {
siphash24_compress(p, sizeof(uint64_t), state); siphash24_compress(p, sizeof(uint64_t), state);
} }

View File

@ -88,6 +88,8 @@ extern const struct hash_ops path_hash_ops_free;
void trivial_hash_func(const void *p, struct siphash *state); void trivial_hash_func(const void *p, struct siphash *state);
int trivial_compare_func(const void *a, const void *b) _const_; int trivial_compare_func(const void *a, const void *b) _const_;
extern const struct hash_ops trivial_hash_ops; extern const struct hash_ops trivial_hash_ops;
extern const struct hash_ops trivial_hash_ops_free;
extern const struct hash_ops trivial_hash_ops_free_free;
/* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit /* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit
* values indirectly, since they don't fit in a pointer. */ * values indirectly, since they don't fit in a pointer. */

View File

@ -1794,10 +1794,10 @@ int set_consume(Set *s, void *value) {
return r; return r;
} }
int _hashmap_put_strdup(Hashmap **h, const char *k, const char *v HASHMAP_DEBUG_PARAMS) { int _hashmap_put_strdup_full(Hashmap **h, const struct hash_ops *hash_ops, const char *k, const char *v HASHMAP_DEBUG_PARAMS) {
int r; int r;
r = _hashmap_ensure_allocated(h, &string_hash_ops_free_free HASHMAP_DEBUG_PASS_ARGS); r = _hashmap_ensure_allocated(h, hash_ops HASHMAP_DEBUG_PASS_ARGS);
if (r < 0) if (r < 0)
return r; return r;
@ -1828,14 +1828,14 @@ int _hashmap_put_strdup(Hashmap **h, const char *k, const char *v HASHMAP_DEBUG
return r; return r;
} }
int _set_put_strdup(Set **s, const char *p HASHMAP_DEBUG_PARAMS) { int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p HASHMAP_DEBUG_PARAMS) {
char *c; char *c;
int r; int r;
assert(s); assert(s);
assert(p); assert(p);
r = _set_ensure_allocated(s, &string_hash_ops_free HASHMAP_DEBUG_PASS_ARGS); r = _set_ensure_allocated(s, hash_ops HASHMAP_DEBUG_PASS_ARGS);
if (r < 0) if (r < 0)
return r; return r;
@ -1849,14 +1849,14 @@ int _set_put_strdup(Set **s, const char *p HASHMAP_DEBUG_PARAMS) {
return set_consume(*s, c); return set_consume(*s, c);
} }
int _set_put_strdupv(Set **s, char **l HASHMAP_DEBUG_PARAMS) { int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l HASHMAP_DEBUG_PARAMS) {
int n = 0, r; int n = 0, r;
char **i; char **i;
assert(s); assert(s);
STRV_FOREACH(i, l) { STRV_FOREACH(i, l) {
r = _set_put_strdup(s, *i HASHMAP_DEBUG_PASS_ARGS); r = _set_put_strdup_full(s, hash_ops, *i HASHMAP_DEBUG_PASS_ARGS);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -153,8 +153,9 @@ static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *
return hashmap_put(PLAIN_HASHMAP(h), key, value); return hashmap_put(PLAIN_HASHMAP(h), key, value);
} }
int _hashmap_put_strdup(Hashmap **h, const char *k, const char *v HASHMAP_DEBUG_PARAMS); int _hashmap_put_strdup_full(Hashmap **h, const struct hash_ops *hash_ops, const char *k, const char *v HASHMAP_DEBUG_PARAMS);
#define hashmap_put_strdup(h, k, v) _hashmap_put_strdup(h, k, v HASHMAP_DEBUG_SRC_ARGS) #define hashmap_put_strdup_full(h, hash_ops, k, v) _hashmap_put_strdup_full(h, hash_ops, k, v HASHMAP_DEBUG_SRC_ARGS)
#define hashmap_put_strdup(h, k, v) hashmap_put_strdup_full(h, &string_hash_ops_free_free, k, v)
int hashmap_update(Hashmap *h, const void *key, void *value); int hashmap_update(Hashmap *h, const void *key, void *value);
static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) { static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) {

View File

@ -734,3 +734,49 @@ static inline int missing_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info)
# define rt_sigqueueinfo missing_rt_sigqueueinfo # define rt_sigqueueinfo missing_rt_sigqueueinfo
#endif #endif
/* ======================================================================= */
#define systemd_NR_close_range systemd_SC_arch_bias(436)
/* may be (invalid) negative number due to libseccomp, see PR 13319 */
#if defined __NR_close_range && __NR_close_range >= 0
# if defined systemd_NR_close_range
assert_cc(__NR_close_range == systemd_NR_close_range);
# endif
#else
# if defined __NR_close_range
# undef __NR_close_range
# endif
# if defined systemd_NR_close_range
# define __NR_close_range systemd_NR_close_range
# endif
#endif
#if !HAVE_CLOSE_RANGE
static inline int missing_close_range(int first_fd, int end_fd, unsigned flags) {
# ifdef __NR_close_range
/* Kernel-side the syscall expects fds as unsigned integers (just like close() actually), while
* userspace exclusively uses signed integers for fds. We don't know just yet how glibc is going to
* wrap this syscall, but let's assume it's going to be similar to what they do for close(),
* i.e. make the same unsigned signed type change from the raw kernel syscall compared to the
* userspace wrapper. There's only one caveat for this: unlike for close() there's the special
* UINT_MAX fd value for the 'end_fd' argument. Let's safely map that to -1 here. And let's refuse
* any other negative values. */
if ((first_fd < 0) || (end_fd < 0 && end_fd != -1)) {
errno = -EBADF;
return -1;
}
return syscall(__NR_close_range,
(unsigned) first_fd,
end_fd == -1 ? UINT_MAX : (unsigned) end_fd, /* Of course, the compiler should figure out that this is the identity mapping IRL */
flags);
# else
errno = ENOSYS;
return -1;
# endif
}
# define close_range missing_close_range
#endif

View File

@ -128,10 +128,12 @@ int _set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key HAS
int set_consume(Set *s, void *value); int set_consume(Set *s, void *value);
int _set_put_strdup(Set **s, const char *p HASHMAP_DEBUG_PARAMS); int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p HASHMAP_DEBUG_PARAMS);
#define set_put_strdup(s, p) _set_put_strdup(s, p HASHMAP_DEBUG_SRC_ARGS) #define set_put_strdup_full(s, hash_ops, p) _set_put_strdup_full(s, hash_ops, p HASHMAP_DEBUG_SRC_ARGS)
int _set_put_strdupv(Set **s, char **l HASHMAP_DEBUG_PARAMS); #define set_put_strdup(s, p) set_put_strdup_full(s, &string_hash_ops_free, p)
#define set_put_strdupv(s, l) _set_put_strdupv(s, l HASHMAP_DEBUG_SRC_ARGS) int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l HASHMAP_DEBUG_PARAMS);
#define set_put_strdupv_full(s, hash_ops, l) _set_put_strdupv_full(s, hash_ops, l HASHMAP_DEBUG_SRC_ARGS)
#define set_put_strdupv(s, l) set_put_strdupv_full(s, &string_hash_ops_free, l)
int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags); int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags);

View File

@ -1014,24 +1014,25 @@ static int help(int argc, char *argv[], void *userdata) {
if (r < 0) if (r < 0)
return log_oom(); return log_oom();
printf("%s [OPTIONS...] COMMAND ...\n" printf("%1$s [OPTIONS...] COMMAND ...\n"
"\n%sInstall/update/remove the systemd-boot EFI boot manager and list/select entries.%s\n" "\n%5$sControl EFI firmware boot settings and manage boot loader.%6$s\n"
"\nBoot Loader Commands:\n" "\n%3$sGeneric EFI Firmware/Boot Loader Commands:%4$s\n"
" status Show status of installed systemd-boot and EFI variables\n" " status Show status of installed boot loader and EFI variables\n"
" reboot-to-firmware [BOOL]\n"
" Query or set reboot-to-firmware EFI flag\n"
" systemd-efi-options [STRING]\n"
" Query or set system options string in EFI variable\n"
"\n%3$sBoot Loader Specification Commands:%4$s\n"
" list List boot loader entries\n"
" set-default ID Set default boot loader entry\n"
" set-oneshot ID Set default boot loader entry, for next boot only\n"
"\n%3$ssystemd-boot Commands:%4$s\n"
" install Install systemd-boot to the ESP and EFI variables\n" " install Install systemd-boot to the ESP and EFI variables\n"
" update Update systemd-boot in the ESP and EFI variables\n" " update Update systemd-boot in the ESP and EFI variables\n"
" remove Remove systemd-boot from the ESP and EFI variables\n" " remove Remove systemd-boot from the ESP and EFI variables\n"
" is-installed Test whether systemd-boot is installed in the ESP\n" " is-installed Test whether systemd-boot is installed in the ESP\n"
" random-seed Initialize random seed in ESP and EFI variables\n" " random-seed Initialize random seed in ESP and EFI variables\n"
" systemd-efi-options [STRING]\n" "\n%3$sOptions:%4$s\n"
" Query or set system options string in EFI variable\n"
" reboot-to-firmware [BOOL]\n"
" Query or set reboot-to-firmware EFI flag\n"
"\nBoot Loader Entries Commands:\n"
" list List boot loader entries\n"
" set-default ID Set default boot loader entry\n"
" set-oneshot ID Set default boot loader entry, for next boot only\n"
"\nOptions:\n"
" -h --help Show this help\n" " -h --help Show this help\n"
" --version Print version\n" " --version Print version\n"
" --esp-path=PATH Path to the EFI System Partition (ESP)\n" " --esp-path=PATH Path to the EFI System Partition (ESP)\n"
@ -1042,11 +1043,12 @@ static int help(int argc, char *argv[], void *userdata) {
" --no-pager Do not pipe output into a pager\n" " --no-pager Do not pipe output into a pager\n"
" --graceful Don't fail when the ESP cannot be found or EFI\n" " --graceful Don't fail when the ESP cannot be found or EFI\n"
" variables cannot be written\n" " variables cannot be written\n"
"\nSee the %s for details.\n" "\nSee the %2$s for details.\n"
, program_invocation_short_name , program_invocation_short_name
, ansi_highlight() , link
, ansi_normal() , ansi_underline(), ansi_normal()
, link); , ansi_highlight(), ansi_normal()
);
return 0; return 0;
} }

View File

@ -894,6 +894,29 @@ static void device_propagate_reload_by_sysfs(Manager *m, const char *sysfs) {
} }
} }
static int device_remove_old(Manager *m, sd_device *dev) {
_cleanup_free_ char *syspath_old = NULL, *e = NULL;
const char *devpath_old;
int r;
r = sd_device_get_property_value(dev, "DEVPATH_OLD", &devpath_old);
if (r < 0) {
log_device_debug_errno(dev, r, "Failed to get DEVPATH_OLD= property on 'move' uevent, ignoring: %m");
return 0;
}
syspath_old = path_join("/sys", devpath_old);
if (!syspath_old)
return log_oom();
r = unit_name_from_path(syspath_old, ".device", &e);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to generate unit name from old device path: %m");
device_update_found_by_sysfs(m, syspath_old, 0, DEVICE_FOUND_UDEV|DEVICE_FOUND_MOUNT|DEVICE_FOUND_SWAP);
return 0;
}
static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *userdata) { static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
Manager *m = userdata; Manager *m = userdata;
DeviceAction action; DeviceAction action;
@ -918,6 +941,9 @@ static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *
if (!IN_SET(action, DEVICE_ACTION_ADD, DEVICE_ACTION_REMOVE, DEVICE_ACTION_MOVE)) if (!IN_SET(action, DEVICE_ACTION_ADD, DEVICE_ACTION_REMOVE, DEVICE_ACTION_MOVE))
device_propagate_reload_by_sysfs(m, sysfs); device_propagate_reload_by_sysfs(m, sysfs);
if (action == DEVICE_ACTION_MOVE)
(void) device_remove_old(m, dev);
/* A change event can signal that a device is becoming ready, in particular if the device is using /* A change event can signal that a device is becoming ready, in particular if the device is using
* the SYSTEMD_READY logic in udev so we need to reach the else block of the following if, even for * the SYSTEMD_READY logic in udev so we need to reach the else block of the following if, even for
* change events */ * change events */

View File

@ -6068,7 +6068,12 @@ static int exec_runtime_add(
return 0; return 0;
} }
static int exec_runtime_make(Manager *m, const ExecContext *c, const char *id, ExecRuntime **ret) { static int exec_runtime_make(
Manager *m,
const ExecContext *c,
const char *id,
ExecRuntime **ret) {
_cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL; _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
_cleanup_close_pair_ int netns_storage_socket[2] = { -1, -1 }; _cleanup_close_pair_ int netns_storage_socket[2] = { -1, -1 };
int r; int r;
@ -6078,8 +6083,10 @@ static int exec_runtime_make(Manager *m, const ExecContext *c, const char *id, E
assert(id); assert(id);
/* It is not necessary to create ExecRuntime object. */ /* It is not necessary to create ExecRuntime object. */
if (!c->private_network && !c->private_tmp && !c->network_namespace_path) if (!c->private_network && !c->private_tmp && !c->network_namespace_path) {
*ret = NULL;
return 0; return 0;
}
if (c->private_tmp && if (c->private_tmp &&
!(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") && !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
@ -6115,14 +6122,20 @@ int exec_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool
/* We already have a ExecRuntime object, let's increase the ref count and reuse it */ /* We already have a ExecRuntime object, let's increase the ref count and reuse it */
goto ref; goto ref;
if (!create) if (!create) {
*ret = NULL;
return 0; return 0;
}
/* If not found, then create a new object. */ /* If not found, then create a new object. */
r = exec_runtime_make(m, c, id, &rt); r = exec_runtime_make(m, c, id, &rt);
if (r <= 0) if (r < 0)
/* When r == 0, it is not necessary to create ExecRuntime object. */
return r; return r;
if (r == 0) {
/* When r == 0, it is not necessary to create ExecRuntime object. */
*ret = NULL;
return 0;
}
ref: ref:
/* increment reference counter. */ /* increment reference counter. */
@ -6348,7 +6361,7 @@ int exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
r = safe_atoi(buf, &fdpair[1]); r = safe_atoi(buf, &fdpair[1]);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Unable to parse exec-runtime specification netns-socket-1=%s: %m", buf); return log_debug_errno(r, "Unable to parse exec-runtime specification netns-socket-1=%s: %m", buf);
if (!fdset_contains(fds, fdpair[0])) if (!fdset_contains(fds, fdpair[1]))
return log_debug_errno(SYNTHETIC_ERRNO(EBADF), return log_debug_errno(SYNTHETIC_ERRNO(EBADF),
"exec-runtime specification netns-socket-1= refers to unknown fd %d: %m", fdpair[1]); "exec-runtime specification netns-socket-1= refers to unknown fd %d: %m", fdpair[1]);
fdpair[1] = fdset_remove(fds, fdpair[1]); fdpair[1] = fdset_remove(fds, fdpair[1]);

View File

@ -3492,6 +3492,24 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
assert(m); assert(m);
assert(f); assert(f);
if (DEBUG_LOGGING) {
if (fdset_isempty(fds))
log_debug("No file descriptors passed");
else {
int fd;
FDSET_FOREACH(fd, fds) {
_cleanup_free_ char *fn = NULL;
r = fd_get_path(fd, &fn);
if (r < 0)
log_debug_errno(r, "Received serialized fd %i → %m", fd);
else
log_debug("Received serialized fd %i → %s", fd, strna(fn));
}
}
}
log_debug("Deserializing state..."); log_debug("Deserializing state...");
/* If we are not in reload mode yet, enter it now. Not that this is recursive, a caller might already have /* If we are not in reload mode yet, enter it now. Not that this is recursive, a caller might already have

View File

@ -4795,8 +4795,13 @@ _public_ int sd_bus_message_read_array(
assert_return(!BUS_MESSAGE_NEED_BSWAP(m), -EOPNOTSUPP); assert_return(!BUS_MESSAGE_NEED_BSWAP(m), -EOPNOTSUPP);
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type)); r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type));
if (r <= 0) if (r < 0)
return r; return r;
if (r == 0) {
*ptr = NULL;
*size = 0;
return 0;
}
c = message_get_last_container(m); c = message_get_last_container(m);

View File

@ -118,7 +118,7 @@ _public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumer
else else
hashmap = &enumerator->nomatch_sysattr; hashmap = &enumerator->nomatch_sysattr;
r = hashmap_put_strdup(hashmap, sysattr, value); r = hashmap_put_strdup_full(hashmap, &trivial_hash_ops_free_free, sysattr, value);
if (r <= 0) if (r <= 0)
return r; return r;
@ -133,7 +133,7 @@ _public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enume
assert_return(enumerator, -EINVAL); assert_return(enumerator, -EINVAL);
assert_return(property, -EINVAL); assert_return(property, -EINVAL);
r = hashmap_put_strdup(&enumerator->match_property, property, value); r = hashmap_put_strdup_full(&enumerator->match_property, &trivial_hash_ops_free_free, property, value);
if (r <= 0) if (r <= 0)
return r; return r;

View File

@ -5,21 +5,22 @@
#include "sd-login.h" #include "sd-login.h"
#include "alloc-util.h" #include "alloc-util.h"
#include "errno-list.h"
#include "fd-util.h" #include "fd-util.h"
#include "format-util.h" #include "format-util.h"
#include "log.h" #include "log.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
#include "time-util.h" #include "time-util.h"
#include "util.h" #include "user-util.h"
static char* format_uids(char **buf, uid_t* uids, int count) { static char* format_uids(char **buf, uid_t* uids, int count) {
int pos = 0, k, inc; int pos = 0, inc;
size_t size = (DECIMAL_STR_MAX(uid_t) + 1) * count + 1; size_t size = (DECIMAL_STR_MAX(uid_t) + 1) * count + 1;
assert_se(*buf = malloc(size)); assert_se(*buf = malloc(size));
for (k = 0; k < count; k++) { for (int k = 0; k < count; k++) {
sprintf(*buf + pos, "%s"UID_FMT"%n", k > 0 ? " " : "", uids[k], &inc); sprintf(*buf + pos, "%s"UID_FMT"%n", k > 0 ? " " : "", uids[k], &inc);
pos += inc; pos += inc;
} }
@ -30,6 +31,10 @@ static char* format_uids(char **buf, uid_t* uids, int count) {
return *buf; return *buf;
} }
static const char *e(int r) {
return r == 0 ? "OK" : errno_to_name(r);
}
static void test_login(void) { static void test_login(void) {
_cleanup_close_pair_ int pair[2] = { -1, -1 }; _cleanup_close_pair_ int pair[2] = { -1, -1 };
_cleanup_free_ char *pp = NULL, *qq = NULL, _cleanup_free_ char *pp = NULL, *qq = NULL,
@ -39,65 +44,71 @@ static void test_login(void) {
*seat = NULL, *session = NULL, *seat = NULL, *session = NULL,
*unit = NULL, *user_unit = NULL, *slice = NULL; *unit = NULL, *user_unit = NULL, *slice = NULL;
int r; int r;
uid_t u, u2; uid_t u, u2 = UID_INVALID;
char *t, **seats, **sessions; char *t, **seats = NULL, **sessions = NULL;
r = sd_pid_get_unit(0, &unit); r = sd_pid_get_unit(0, &unit);
assert_se(r >= 0 || r == -ENODATA); log_info("sd_pid_get_unit(0, …) → %s / \"%s\"", e(r), strnull(unit));
log_info("sd_pid_get_unit(0, …) → \"%s\"", strna(unit)); assert_se(IN_SET(r, 0, -ENODATA));
r = sd_pid_get_user_unit(0, &user_unit); r = sd_pid_get_user_unit(0, &user_unit);
assert_se(r >= 0 || r == -ENODATA); log_info("sd_pid_get_user_unit(0, …) → %s / \"%s\"", e(r), strnull(user_unit));
log_info("sd_pid_get_user_unit(0, …) → \"%s\"", strna(user_unit)); assert_se(IN_SET(r, 0, -ENODATA));
r = sd_pid_get_slice(0, &slice); r = sd_pid_get_slice(0, &slice);
assert_se(r >= 0 || r == -ENODATA); log_info("sd_pid_get_slice(0, …) → %s / \"%s\"", e(r), strnull(slice));
log_info("sd_pid_get_slice(0, …) → \"%s\"", strna(slice)); assert_se(IN_SET(r, 0, -ENODATA));
r = sd_pid_get_owner_uid(0, &u2);
log_info("sd_pid_get_owner_uid(0, …) → %s / "UID_FMT, e(r), u2);
assert_se(IN_SET(r, 0, -ENODATA));
r = sd_pid_get_session(0, &session); r = sd_pid_get_session(0, &session);
if (r < 0) { log_info("sd_pid_get_session(0, …) → %s / \"%s\"", e(r), strnull(session));
log_warning_errno(r, "sd_pid_get_session(0, …): %m");
if (r == -ENODATA)
log_info("Seems we are not running in a session, skipping some tests.");
} else {
log_info("sd_pid_get_session(0, …) → \"%s\"", session);
assert_se(sd_pid_get_owner_uid(0, &u2) == 0); r = sd_pid_get_cgroup(0, &cgroup);
log_info("sd_pid_get_owner_uid(0, …) → "UID_FMT, u2); log_info("sd_pid_get_cgroup(0, …) → %s / \"%s\"", e(r), strnull(cgroup));
assert_se(r == 0);
assert_se(sd_pid_get_cgroup(0, &cgroup) == 0); r = sd_uid_get_display(u2, &display_session);
log_info("sd_pid_get_cgroup(0, …) → \"%s\"", cgroup); log_info("sd_uid_get_display("UID_FMT", …) → %s / \"%s\"", u2, e(r), strnull(display_session));
if (u2 == UID_INVALID)
assert_se(r == -EINVAL);
else
assert_se(IN_SET(r, 0, -ENODATA));
r = sd_uid_get_display(u2, &display_session); assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == 0);
assert_se(r >= 0 || r == -ENODATA); sd_peer_get_session(pair[0], &pp);
log_info("sd_uid_get_display("UID_FMT", …) → \"%s\"", sd_peer_get_session(pair[1], &qq);
u2, strnull(display_session)); assert_se(streq_ptr(pp, qq));
assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == 0); r = sd_uid_get_sessions(u2, false, &sessions);
sd_peer_get_session(pair[0], &pp); assert_se(t = strv_join(sessions, " "));
sd_peer_get_session(pair[1], &qq); log_info("sd_uid_get_sessions("UID_FMT", …) → %s \"%s\"", u2, e(r), t);
assert_se(streq_ptr(pp, qq)); if (u2 == UID_INVALID)
assert_se(r == -EINVAL);
r = sd_uid_get_sessions(u2, false, &sessions); else {
assert_se(r >= 0); assert_se(r >= 0);
assert_se(r == (int) strv_length(sessions)); assert_se(r == (int) strv_length(sessions));
assert_se(t = strv_join(sessions, " ")); }
strv_free(sessions); sessions = strv_free(sessions);
log_info("sd_uid_get_sessions("UID_FMT", …) → [%i] \"%s\"", u2, r, t); free(t);
free(t);
assert_se(r == sd_uid_get_sessions(u2, false, NULL)); assert_se(r == sd_uid_get_sessions(u2, false, NULL));
r = sd_uid_get_seats(u2, false, &seats); r = sd_uid_get_seats(u2, false, &seats);
assert_se(t = strv_join(seats, " "));
log_info("sd_uid_get_seats("UID_FMT", …) → %s \"%s\"", u2, e(r), t);
if (u2 == UID_INVALID)
assert_se(r == -EINVAL);
else {
assert_se(r >= 0); assert_se(r >= 0);
assert_se(r == (int) strv_length(seats)); assert_se(r == (int) strv_length(seats));
assert_se(t = strv_join(seats, " "));
strv_free(seats);
log_info("sd_uid_get_seats("UID_FMT", …) → [%i] \"%s\"", u2, r, t);
free(t);
assert_se(r == sd_uid_get_seats(u2, false, NULL));
} }
seats = strv_free(seats);
free(t);
assert_se(r == sd_uid_get_seats(u2, false, NULL));
if (session) { if (session) {
r = sd_session_is_active(session); r = sd_session_is_active(session);
@ -109,7 +120,7 @@ static void test_login(void) {
log_info("sd_session_is_remote(\"%s\") → %s", session, yes_no(r)); log_info("sd_session_is_remote(\"%s\") → %s", session, yes_no(r));
r = sd_session_get_state(session, &state); r = sd_session_get_state(session, &state);
assert_se(r >= 0); assert_se(r == 0);
log_info("sd_session_get_state(\"%s\") → \"%s\"", session, state); log_info("sd_session_get_state(\"%s\") → \"%s\"", session, state);
assert_se(sd_session_get_uid(session, &u) >= 0); assert_se(sd_session_get_uid(session, &u) >= 0);
@ -123,16 +134,16 @@ static void test_login(void) {
log_info("sd_session_get_class(\"%s\") → \"%s\"", session, class); log_info("sd_session_get_class(\"%s\") → \"%s\"", session, class);
r = sd_session_get_display(session, &display); r = sd_session_get_display(session, &display);
assert_se(r >= 0 || r == -ENODATA); assert_se(IN_SET(r, 0, -ENODATA));
log_info("sd_session_get_display(\"%s\") → \"%s\"", session, strna(display)); log_info("sd_session_get_display(\"%s\") → \"%s\"", session, strna(display));
r = sd_session_get_remote_user(session, &remote_user); r = sd_session_get_remote_user(session, &remote_user);
assert_se(r >= 0 || r == -ENODATA); assert_se(IN_SET(r, 0, -ENODATA));
log_info("sd_session_get_remote_user(\"%s\") → \"%s\"", log_info("sd_session_get_remote_user(\"%s\") → \"%s\"",
session, strna(remote_user)); session, strna(remote_user));
r = sd_session_get_remote_host(session, &remote_host); r = sd_session_get_remote_host(session, &remote_host);
assert_se(r >= 0 || r == -ENODATA); assert_se(IN_SET(r, 0, -ENODATA));
log_info("sd_session_get_remote_host(\"%s\") → \"%s\"", log_info("sd_session_get_remote_host(\"%s\") → \"%s\"",
session, strna(remote_host)); session, strna(remote_host));
@ -161,7 +172,7 @@ static void test_login(void) {
assert_se(r == -ENODATA); assert_se(r == -ENODATA);
} }
assert_se(sd_uid_get_state(u, &state2) >= 0); assert_se(sd_uid_get_state(u, &state2) == 0);
log_info("sd_uid_get_state("UID_FMT", …) → %s", u, state2); log_info("sd_uid_get_state("UID_FMT", …) → %s", u, state2);
} }
@ -173,11 +184,11 @@ static void test_login(void) {
assert_se(sd_uid_is_on_seat(u, 0, seat) > 0); assert_se(sd_uid_is_on_seat(u, 0, seat) > 0);
r = sd_seat_get_active(seat, &session2, &u2); r = sd_seat_get_active(seat, &session2, &u2);
assert_se(r >= 0); assert_se(r == 0);
log_info("sd_seat_get_active(\"%s\", …) → \"%s\", "UID_FMT, seat, session2, u2); log_info("sd_seat_get_active(\"%s\", …) → \"%s\", "UID_FMT, seat, session2, u2);
r = sd_uid_is_on_seat(u, 1, seat); r = sd_uid_is_on_seat(u, 1, seat);
assert_se(r >= 0); assert_se(IN_SET(r, 0, 1));
assert_se(!!r == streq(session, session2)); assert_se(!!r == streq(session, session2));
r = sd_seat_get_sessions(seat, &sessions, &uids, &n); r = sd_seat_get_sessions(seat, &sessions, &uids, &n);
@ -185,8 +196,8 @@ static void test_login(void) {
assert_se(r == (int) strv_length(sessions)); assert_se(r == (int) strv_length(sessions));
assert_se(t = strv_join(sessions, " ")); assert_se(t = strv_join(sessions, " "));
strv_free(sessions); strv_free(sessions);
log_info("sd_seat_get_sessions(\"%s\", …) → %i, \"%s\", [%i] {%s}", log_info("sd_seat_get_sessions(\"%s\", …) → %s, \"%s\", [%u] {%s}",
seat, r, t, n, format_uids(&buf, uids, n)); seat, e(r), t, n, format_uids(&buf, uids, n));
free(t); free(t);
assert_se(sd_seat_get_sessions(seat, NULL, NULL, NULL) == r); assert_se(sd_seat_get_sessions(seat, NULL, NULL, NULL) == r);
@ -204,7 +215,7 @@ static void test_login(void) {
r = sd_seat_get_active(NULL, &t, NULL); r = sd_seat_get_active(NULL, &t, NULL);
assert_se(IN_SET(r, 0, -ENODATA)); assert_se(IN_SET(r, 0, -ENODATA));
log_info("sd_seat_get_active(NULL, …) (active session on current seat) → %s", strnull(t)); log_info("sd_seat_get_active(NULL, …) (active session on current seat) → %s / \"%s\"", e(r), strnull(t));
free(t); free(t);
r = sd_get_sessions(&sessions); r = sd_get_sessions(&sessions);
@ -244,13 +255,11 @@ static void test_login(void) {
static void test_monitor(void) { static void test_monitor(void) {
sd_login_monitor *m = NULL; sd_login_monitor *m = NULL;
unsigned n;
int r; int r;
r = sd_login_monitor_new("session", &m); assert_se(sd_login_monitor_new("session", &m) == 0);
assert_se(r >= 0);
for (n = 0; n < 5; n++) { for (unsigned n = 0; n < 5; n++) {
struct pollfd pollfd = {}; struct pollfd pollfd = {};
usec_t timeout, nw; usec_t timeout, nw;

View File

@ -17,7 +17,7 @@
#define ADDRESSES_PER_LINK_MAX 2048U #define ADDRESSES_PER_LINK_MAX 2048U
#define STATIC_ADDRESSES_PER_NETWORK_MAX 1024U #define STATIC_ADDRESSES_PER_NETWORK_MAX 1024U
int generate_ipv6_eui_64_address(Link *link, struct in6_addr *ret) { int generate_ipv6_eui_64_address(const Link *link, struct in6_addr *ret) {
assert(link); assert(link);
assert(ret); assert(ret);
@ -135,20 +135,6 @@ Address *address_free(Address *address) {
return mfree(address); return mfree(address);
} }
static uint32_t address_prefix(const Address *a) {
assert(a);
/* make sure we don't try to shift by 32.
* See ISO/IEC 9899:TC3 § 6.5.7.3. */
if (a->prefixlen == 0)
return 0;
if (a->in_addr_peer.in.s_addr != 0)
return be32toh(a->in_addr_peer.in.s_addr) >> (32 - a->prefixlen);
else
return be32toh(a->in_addr.in.s_addr) >> (32 - a->prefixlen);
}
void address_hash_func(const Address *a, struct siphash *state) { void address_hash_func(const Address *a, struct siphash *state) {
assert(a); assert(a);
@ -156,16 +142,16 @@ void address_hash_func(const Address *a, struct siphash *state) {
switch (a->family) { switch (a->family) {
case AF_INET: case AF_INET:
siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state); siphash24_compress(&a->broadcast, sizeof(a->broadcast), state);
siphash24_compress_string(a->label, state);
/* peer prefix */
uint32_t prefix = address_prefix(a);
siphash24_compress(&prefix, sizeof(prefix), state);
_fallthrough_; _fallthrough_;
case AF_INET6: case AF_INET6:
siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
/* local address */ /* local address */
siphash24_compress(&a->in_addr, FAMILY_ADDRESS_SIZE(a->family), state); siphash24_compress(&a->in_addr, FAMILY_ADDRESS_SIZE(a->family), state);
/* peer address */
siphash24_compress(&a->in_addr_peer, FAMILY_ADDRESS_SIZE(a->family), state);
break; break;
default: default:
@ -184,19 +170,25 @@ int address_compare_func(const Address *a1, const Address *a2) {
switch (a1->family) { switch (a1->family) {
/* use the same notion of equality as the kernel does */ /* use the same notion of equality as the kernel does */
case AF_INET: case AF_INET:
r = CMP(a1->prefixlen, a2->prefixlen); r = CMP(a1->broadcast.s_addr, a2->broadcast.s_addr);
if (r != 0) if (r != 0)
return r; return r;
uint32_t prefix1 = address_prefix(a1); r = strcmp_ptr(a1->label, a2->label);
uint32_t prefix2 = address_prefix(a2);
r = CMP(prefix1, prefix2);
if (r != 0) if (r != 0)
return r; return r;
_fallthrough_; _fallthrough_;
case AF_INET6: case AF_INET6:
return memcmp(&a1->in_addr, &a2->in_addr, FAMILY_ADDRESS_SIZE(a1->family)); r = CMP(a1->prefixlen, a2->prefixlen);
if (r != 0)
return r;
r = memcmp(&a1->in_addr, &a2->in_addr, FAMILY_ADDRESS_SIZE(a1->family));
if (r != 0)
return r;
return memcmp(&a1->in_addr_peer, &a2->in_addr_peer, FAMILY_ADDRESS_SIZE(a1->family));
default: default:
/* treat any other address family as AF_UNSPEC */ /* treat any other address family as AF_UNSPEC */
return 0; return 0;
@ -205,7 +197,7 @@ int address_compare_func(const Address *a1, const Address *a2) {
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(address_hash_ops, Address, address_hash_func, address_compare_func, address_free); DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(address_hash_ops, Address, address_hash_func, address_compare_func, address_free);
bool address_equal(Address *a1, Address *a2) { bool address_equal(const Address *a1, const Address *a2) {
if (a1 == a2) if (a1 == a2)
return true; return true;
@ -274,25 +266,22 @@ static int address_set_masquerade(Address *address, bool add) {
return 0; return 0;
} }
static int address_add_internal(Link *link, Set **addresses, static int address_add_internal(Link *link, Set **addresses, const Address *in, Address **ret) {
int family,
const union in_addr_union *in_addr,
unsigned char prefixlen,
Address **ret) {
_cleanup_(address_freep) Address *address = NULL; _cleanup_(address_freep) Address *address = NULL;
int r; int r;
assert(link); assert(link);
assert(addresses); assert(addresses);
assert(in_addr); assert(in);
r = address_new(&address); r = address_new(&address);
if (r < 0) if (r < 0)
return r; return r;
address->family = family; r = address_copy(address, in);
address->in_addr = *in_addr; if (r < 0)
address->prefixlen = prefixlen; return r;
/* Consider address tentative until we get the real flags from the kernel */ /* Consider address tentative until we get the real flags from the kernel */
address->flags = IFA_F_TENTATIVE; address->flags = IFA_F_TENTATIVE;
@ -310,18 +299,21 @@ static int address_add_internal(Link *link, Set **addresses,
return 0; return 0;
} }
static int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) { static int address_add_foreign(Link *link, const Address *in, Address **ret) {
return address_add_internal(link, &link->addresses_foreign, family, in_addr, prefixlen, ret); return address_add_internal(link, &link->addresses_foreign, in, ret);
} }
static int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) { static int address_add(Link *link, const Address *in, Address **ret) {
Address *address; Address *address;
int r; int r;
r = address_get(link, family, in_addr, prefixlen, &address); assert(link);
assert(in);
r = address_get(link, in, &address);
if (r == -ENOENT) { if (r == -ENOENT) {
/* Address does not exist, create a new one */ /* Address does not exist, create a new one */
r = address_add_internal(link, &link->addresses, family, in_addr, prefixlen, &address); r = address_add_internal(link, &link->addresses, in, &address);
if (r < 0) if (r < 0)
return r; return r;
} else if (r == 0) { } else if (r == 0) {
@ -343,24 +335,19 @@ static int address_add(Link *link, int family, const union in_addr_union *in_add
return 0; return 0;
} }
static int address_update( static int address_update(Address *address, const Address *src) {
Address *address,
unsigned char flags,
unsigned char scope,
const struct ifa_cacheinfo *cinfo) {
bool ready; bool ready;
int r; int r;
assert(address); assert(address);
assert(address->link); assert(address->link);
assert(cinfo); assert(src);
ready = address_is_ready(address); ready = address_is_ready(address);
address->flags = flags; address->flags = src->flags;
address->scope = scope; address->scope = src->scope;
address->cinfo = *cinfo; address->cinfo = src->cinfo;
if (IN_SET(address->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) if (IN_SET(address->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 0; return 0;
@ -412,31 +399,20 @@ static int address_drop(Address *address) {
return 0; return 0;
} }
int address_get(Link *link, int address_get(Link *link, const Address *in, Address **ret) {
int family, Address *existing;
const union in_addr_union *in_addr,
unsigned char prefixlen,
Address **ret) {
Address address, *existing;
assert(link); assert(link);
assert(in_addr); assert(in);
address = (Address) { existing = set_get(link->addresses, in);
.family = family,
.in_addr = *in_addr,
.prefixlen = prefixlen,
};
existing = set_get(link->addresses, &address);
if (existing) { if (existing) {
if (ret) if (ret)
*ret = existing; *ret = existing;
return 1; return 1;
} }
existing = set_get(link->addresses_foreign, &address); existing = set_get(link->addresses_foreign, in);
if (existing) { if (existing) {
if (ret) if (ret)
*ret = existing; *ret = existing;
@ -491,7 +467,7 @@ static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
} }
int address_remove( int address_remove(
Address *address, const Address *address,
Link *link, Link *link,
link_netlink_message_handler_t callback) { link_netlink_message_handler_t callback) {
@ -536,7 +512,7 @@ int address_remove(
return 0; return 0;
} }
static bool link_is_static_address_configured(Link *link, Address *address) { static bool link_is_static_address_configured(const Link *link, const Address *address) {
Address *net_address; Address *net_address;
assert(link); assert(link);
@ -548,14 +524,11 @@ static bool link_is_static_address_configured(Link *link, Address *address) {
ORDERED_HASHMAP_FOREACH(net_address, link->network->addresses_by_section) ORDERED_HASHMAP_FOREACH(net_address, link->network->addresses_by_section)
if (address_equal(net_address, address)) if (address_equal(net_address, address))
return true; return true;
else if (address->family == AF_INET6 && net_address->family == AF_INET6 &&
in_addr_equal(AF_INET6, &address->in_addr, &net_address->in_addr_peer) > 0)
return true;
return false; return false;
} }
static bool link_address_is_dynamic(Link *link, Address *address) { static bool link_address_is_dynamic(const Link *link, const Address *address) {
Route *route; Route *route;
assert(link); assert(link);
@ -649,7 +622,7 @@ int link_drop_foreign_addresses(Link *link) {
continue; continue;
if (link_is_static_address_configured(link, address)) { if (link_is_static_address_configured(link, address)) {
k = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL); k = address_add(link, address, NULL);
if (k < 0) { if (k < 0) {
log_link_error_errno(link, k, "Failed to add address: %m"); log_link_error_errno(link, k, "Failed to add address: %m");
if (r >= 0) if (r >= 0)
@ -784,7 +757,7 @@ static int address_acquire(Link *link, const Address *original, Address **ret) {
static int ipv4_dad_configure(Address *address); static int ipv4_dad_configure(Address *address);
int address_configure( int address_configure(
Address *address, const Address *address,
Link *link, Link *link,
link_netlink_message_handler_t callback, link_netlink_message_handler_t callback,
bool update, bool update,
@ -804,7 +777,7 @@ int address_configure(
assert(callback); assert(callback);
/* If this is a new address, then refuse adding more than the limit */ /* If this is a new address, then refuse adding more than the limit */
if (address_get(link, address->family, &address->in_addr, address->prefixlen, NULL) <= 0 && if (address_get(link, address, NULL) <= 0 &&
set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX) set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX)
return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG), return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG),
"Too many addresses are configured, refusing: %m"); "Too many addresses are configured, refusing: %m");
@ -874,14 +847,10 @@ int address_configure(
if (r < 0) if (r < 0)
return log_link_error_errno(link, r, "Could not append IFA_CACHEINFO attribute: %m"); return log_link_error_errno(link, r, "Could not append IFA_CACHEINFO attribute: %m");
if (address->family == AF_INET6 && !in_addr_is_null(address->family, &address->in_addr_peer)) r = address_add(link, address, &a);
r = address_add(link, address->family, &address->in_addr_peer, address->prefixlen, &a);
else
r = address_add(link, address->family, &address->in_addr, address->prefixlen, &a);
if (r < 0) if (r < 0)
return log_link_error_errno(link, r, "Could not add address: %m"); return log_link_error_errno(link, r, "Could not add address: %m");
a->scope = address->scope;
r = address_set_masquerade(a, true); r = address_set_masquerade(a, true);
if (r < 0) if (r < 0)
log_link_warning_errno(link, r, "Could not enable IP masquerading, ignoring: %m"); log_link_warning_errno(link, r, "Could not enable IP masquerading, ignoring: %m");
@ -987,7 +956,7 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
return 1; return 1;
} }
static int static_address_configure(Address *address, Link *link, bool update) { static int static_address_configure(const Address *address, Link *link, bool update) {
Address *ret; Address *ret;
int r; int r;
@ -1025,11 +994,7 @@ int link_set_addresses(Link *link) {
ORDERED_HASHMAP_FOREACH(ad, link->network->addresses_by_section) { ORDERED_HASHMAP_FOREACH(ad, link->network->addresses_by_section) {
bool update; bool update;
if (ad->family == AF_INET6 && !in_addr_is_null(ad->family, &ad->in_addr_peer)) update = address_get(link, ad, NULL) > 0;
update = address_get(link, ad->family, &ad->in_addr_peer, ad->prefixlen, NULL) > 0;
else
update = address_get(link, ad->family, &ad->in_addr, ad->prefixlen, NULL) > 0;
r = static_address_configure(ad, link, update); r = static_address_configure(ad, link, update);
if (r < 0) if (r < 0)
return r; return r;
@ -1078,16 +1043,16 @@ int link_set_addresses(Link *link) {
} }
int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
_cleanup_free_ char *buf = NULL; _cleanup_(address_freep) Address *tmp = NULL;
_cleanup_free_ char *buf = NULL, *buf_peer = NULL;
Link *link = NULL; Link *link = NULL;
uint16_t type; uint16_t type;
unsigned char flags, prefixlen, scope; unsigned char flags;
union in_addr_union in_addr = IN_ADDR_NULL;
struct ifa_cacheinfo cinfo;
Address *address = NULL; Address *address = NULL;
char valid_buf[FORMAT_TIMESPAN_MAX]; char valid_buf[FORMAT_TIMESPAN_MAX];
const char *valid_str = NULL; const char *valid_str = NULL;
int ifindex, family, r; int ifindex, r;
bool has_peer = false;
assert(rtnl); assert(rtnl);
assert(message); assert(message);
@ -1128,22 +1093,26 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
return 0; return 0;
} }
r = sd_rtnl_message_addr_get_family(message, &family); r = address_new(&tmp);
if (r < 0)
return log_oom();
r = sd_rtnl_message_addr_get_family(message, &tmp->family);
if (r < 0) { if (r < 0) {
log_link_warning(link, "rtnl: received address message without family, ignoring."); log_link_warning(link, "rtnl: received address message without family, ignoring.");
return 0; return 0;
} else if (!IN_SET(family, AF_INET, AF_INET6)) { } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
log_link_debug(link, "rtnl: received address message with invalid family '%i', ignoring.", family); log_link_debug(link, "rtnl: received address message with invalid family '%i', ignoring.", tmp->family);
return 0; return 0;
} }
r = sd_rtnl_message_addr_get_prefixlen(message, &prefixlen); r = sd_rtnl_message_addr_get_prefixlen(message, &tmp->prefixlen);
if (r < 0) { if (r < 0) {
log_link_warning_errno(link, r, "rtnl: received address message without prefixlen, ignoring: %m"); log_link_warning_errno(link, r, "rtnl: received address message without prefixlen, ignoring: %m");
return 0; return 0;
} }
r = sd_rtnl_message_addr_get_scope(message, &scope); r = sd_rtnl_message_addr_get_scope(message, &tmp->scope);
if (r < 0) { if (r < 0) {
log_link_warning_errno(link, r, "rtnl: received address message without scope, ignoring: %m"); log_link_warning_errno(link, r, "rtnl: received address message without scope, ignoring: %m");
return 0; return 0;
@ -1154,21 +1123,61 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
log_link_warning_errno(link, r, "rtnl: received address message without flags, ignoring: %m"); log_link_warning_errno(link, r, "rtnl: received address message without flags, ignoring: %m");
return 0; return 0;
} }
tmp->flags = flags;
switch (family) { switch (tmp->family) {
case AF_INET: case AF_INET:
r = sd_netlink_message_read_in_addr(message, IFA_LOCAL, &in_addr.in); r = sd_netlink_message_read_in_addr(message, IFA_LOCAL, &tmp->in_addr.in);
if (r < 0) { if (r < 0) {
log_link_warning_errno(link, r, "rtnl: received address message without valid address, ignoring: %m"); log_link_warning_errno(link, r, "rtnl: received address message without valid address, ignoring: %m");
return 0; return 0;
} }
r = sd_netlink_message_read_in_addr(message, IFA_ADDRESS, &tmp->in_addr_peer.in);
if (r < 0 && r != -ENODATA) {
log_link_warning_errno(link, r, "rtnl: could not get peer address from address message, ignoring: %m");
return 0;
} else if (r >= 0) {
if (in4_addr_equal(&tmp->in_addr.in, &tmp->in_addr_peer.in))
tmp->in_addr_peer = IN_ADDR_NULL;
else
has_peer = true;
}
r = sd_netlink_message_read_in_addr(message, IFA_BROADCAST, &tmp->broadcast);
if (r < 0 && r != -ENODATA) {
log_link_warning_errno(link, r, "rtnl: could not get broadcast from address message, ignoring: %m");
return 0;
}
r = sd_netlink_message_read_string_strdup(message, IFA_LABEL, &tmp->label);
if (r < 0 && r != -ENODATA) {
log_link_warning_errno(link, r, "rtnl: could not get label from address message, ignoring: %m");
return 0;
} else if (r >= 0 && streq_ptr(tmp->label, link->ifname))
tmp->label = mfree(tmp->label);
break; break;
case AF_INET6: case AF_INET6:
r = sd_netlink_message_read_in6_addr(message, IFA_ADDRESS, &in_addr.in6); r = sd_netlink_message_read_in6_addr(message, IFA_LOCAL, &tmp->in_addr.in6);
if (r < 0) { if (r >= 0) {
log_link_warning_errno(link, r, "rtnl: received address message without valid address, ignoring: %m"); /* Have peer address. */
r = sd_netlink_message_read_in6_addr(message, IFA_ADDRESS, &tmp->in_addr_peer.in6);
if (r < 0) {
log_link_warning_errno(link, r, "rtnl: could not get peer address from address message, ignoring: %m");
return 0;
}
has_peer = true;
} else if (r == -ENODATA) {
/* Does not have peer address. */
r = sd_netlink_message_read_in6_addr(message, IFA_ADDRESS, &tmp->in_addr.in6);
if (r < 0) {
log_link_warning_errno(link, r, "rtnl: received address message without valid address, ignoring: %m");
return 0;
}
} else {
log_link_warning_errno(link, r, "rtnl: could not get local address from address message, ignoring: %m");
return 0; return 0;
} }
@ -1178,40 +1187,43 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
assert_not_reached("Received unsupported address family"); assert_not_reached("Received unsupported address family");
} }
(void) in_addr_to_string(family, &in_addr, &buf); (void) in_addr_to_string(tmp->family, &tmp->in_addr, &buf);
(void) in_addr_to_string(tmp->family, &tmp->in_addr_peer, &buf_peer);
r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo); r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &tmp->cinfo);
if (r < 0 && r != -ENODATA) { if (r < 0 && r != -ENODATA) {
log_link_warning_errno(link, r, "rtnl: cannot get IFA_CACHEINFO attribute, ignoring: %m"); log_link_warning_errno(link, r, "rtnl: cannot get IFA_CACHEINFO attribute, ignoring: %m");
return 0; return 0;
} else if (r >= 0 && cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME) } else if (r >= 0 && tmp->cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME)
valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX, valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
cinfo.ifa_valid * USEC_PER_SEC, tmp->cinfo.ifa_valid * USEC_PER_SEC,
USEC_PER_SEC); USEC_PER_SEC);
(void) address_get(link, family, &in_addr, prefixlen, &address); (void) address_get(link, tmp, &address);
switch (type) { switch (type) {
case RTM_NEWADDR: case RTM_NEWADDR:
if (address) if (address)
log_link_debug(link, "Remembering updated address: %s/%u (valid %s%s)", log_link_debug(link, "Remembering updated address: %s%s%s/%u (valid %s%s)",
strnull(buf), prefixlen, strnull(buf), has_peer ? " peer " : "",
has_peer ? strnull(buf_peer) : "", tmp->prefixlen,
valid_str ? "for " : "forever", strempty(valid_str)); valid_str ? "for " : "forever", strempty(valid_str));
else { else {
/* An address appeared that we did not request */ /* An address appeared that we did not request */
r = address_add_foreign(link, family, &in_addr, prefixlen, &address); r = address_add_foreign(link, tmp, &address);
if (r < 0) { if (r < 0) {
log_link_warning_errno(link, r, "Failed to remember foreign address %s/%u, ignoring: %m", log_link_warning_errno(link, r, "Failed to remember foreign address %s/%u, ignoring: %m",
strnull(buf), prefixlen); strnull(buf), tmp->prefixlen);
return 0; return 0;
} else } else
log_link_debug(link, "Remembering foreign address: %s/%u (valid %s%s)", log_link_debug(link, "Remembering foreign address: %s%s%s/%u (valid %s%s)",
strnull(buf), prefixlen, strnull(buf), has_peer ? " peer " : "",
has_peer ? strnull(buf_peer) : "", tmp->prefixlen,
valid_str ? "for " : "forever", strempty(valid_str)); valid_str ? "for " : "forever", strempty(valid_str));
} }
/* address_update() logs internally, so we don't need to here. */ /* address_update() logs internally, so we don't need to here. */
r = address_update(address, flags, scope, &cinfo); r = address_update(address, tmp);
if (r < 0) if (r < 0)
link_enter_failed(link); link_enter_failed(link);
@ -1219,13 +1231,15 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
case RTM_DELADDR: case RTM_DELADDR:
if (address) { if (address) {
log_link_debug(link, "Forgetting address: %s/%u (valid %s%s)", log_link_debug(link, "Forgetting address: %s%s%s/%u (valid %s%s)",
strnull(buf), prefixlen, strnull(buf), has_peer ? " peer " : "",
has_peer ? strnull(buf_peer) : "", tmp->prefixlen,
valid_str ? "for " : "forever", strempty(valid_str)); valid_str ? "for " : "forever", strempty(valid_str));
(void) address_drop(address); (void) address_drop(address);
} else } else
log_link_debug(link, "Kernel removed an address we don't remember: %s/%u (valid %s%s), ignoring.", log_link_debug(link, "Kernel removed an address we don't remember: %s%s%s/%u (valid %s%s), ignoring.",
strnull(buf), prefixlen, strnull(buf), has_peer ? " peer " : "",
has_peer ? strnull(buf_peer) : "", tmp->prefixlen,
valid_str ? "for " : "forever", strempty(valid_str)); valid_str ? "for " : "forever", strempty(valid_str));
break; break;
@ -1264,11 +1278,8 @@ int link_deserialize_addresses(Link *link, const char *addresses) {
assert(link); assert(link);
for (const char *p = addresses;; ) { for (const char *p = addresses;; ) {
_cleanup_(address_freep) Address *tmp = NULL;
_cleanup_free_ char *address_str = NULL; _cleanup_free_ char *address_str = NULL;
union in_addr_union address;
unsigned char prefixlen;
char *prefixlen_str;
int family;
r = extract_first_word(&p, &address_str, NULL, 0); r = extract_first_word(&p, &address_str, NULL, 0);
if (r < 0) if (r < 0)
@ -1276,28 +1287,19 @@ int link_deserialize_addresses(Link *link, const char *addresses) {
if (r == 0) if (r == 0)
return 0; return 0;
prefixlen_str = strchr(address_str, '/'); r = address_new(&tmp);
if (!prefixlen_str) {
log_link_debug(link, "Failed to parse address and prefix length, ignoring: %s", address_str);
continue;
}
*prefixlen_str++ = '\0';
r = sscanf(prefixlen_str, "%hhu", &prefixlen);
if (r != 1) {
log_link_debug(link, "Failed to parse prefixlen: %s", prefixlen_str);
continue;
}
r = in_addr_from_string_auto(address_str, &family, &address);
if (r < 0) {
log_link_debug_errno(link, r, "Failed to parse address: %s", address_str);
continue;
}
r = address_add(link, family, &address, prefixlen, NULL);
if (r < 0) if (r < 0)
log_link_debug_errno(link, r, "Failed to add address: %m"); return log_oom();
r = in_addr_prefix_from_string_auto(address_str, &tmp->family, &tmp->in_addr, &tmp->prefixlen);
if (r < 0) {
log_link_debug_errno(link, r, "Failed to parse address, ignoring: %s", address_str);
continue;
}
r = address_add(link, tmp, NULL);
if (r < 0)
log_link_debug_errno(link, r, "Failed to add address %s, ignoring: %m", address_str);
} }
return 0; return 0;
@ -1580,9 +1582,6 @@ int config_parse_address(
else else
n->in_addr_peer = buffer; n->in_addr_peer = buffer;
if (n->family == AF_INET && n->broadcast.s_addr == 0 && n->prefixlen <= 30)
n->broadcast.s_addr = n->in_addr.in.s_addr | htobe32(0xfffffffflu >> n->prefixlen);
n = NULL; n = NULL;
return 0; return 0;
@ -1854,6 +1853,25 @@ static int address_section_verify(Address *address) {
address->section->filename, address->section->line); address->section->filename, address->section->line);
} }
if (address->family == AF_INET && in_addr_is_null(address->family, &address->in_addr_peer) &&
address->broadcast.s_addr == 0 && address->prefixlen <= 30)
address->broadcast.s_addr = address->in_addr.in.s_addr | htobe32(0xfffffffflu >> address->prefixlen);
else if (address->broadcast.s_addr != 0) {
log_warning("%s: broadcast address is set for IPv6 address or IPv4 address with prefixlength larger than 30. "
"Ignoring Broadcast= setting in the [Address] section from line %u.",
address->section->filename, address->section->line);
address->broadcast.s_addr = 0;
}
if (address->family == AF_INET6 && address->label) {
log_warning("%s: address label is set for IPv6 address in the [Address] section from line %u. "
"Ignoring Label= setting.",
address->section->filename, address->section->line);
address->label = mfree(address->label);
}
if (in_addr_is_localhost(address->family, &address->in_addr) > 0 && if (in_addr_is_localhost(address->family, &address->in_addr) > 0 &&
(address->family == AF_INET || !address->scope_set)) { (address->family == AF_INET || !address->scope_set)) {
/* For IPv4, scope must be always RT_SCOPE_HOST. /* For IPv4, scope must be always RT_SCOPE_HOST.

View File

@ -48,14 +48,14 @@ typedef struct Address {
int address_new(Address **ret); int address_new(Address **ret);
Address *address_free(Address *address); Address *address_free(Address *address);
int address_get(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret); int address_get(Link *link, const Address *in, Address **ret);
bool address_exists(Link *link, int family, const union in_addr_union *in_addr); bool address_exists(Link *link, int family, const union in_addr_union *in_addr);
int address_configure(Address *address, Link *link, link_netlink_message_handler_t callback, bool update, Address **ret); int address_configure(const Address *address, Link *link, link_netlink_message_handler_t callback, bool update, Address **ret);
int address_remove(Address *address, Link *link, link_netlink_message_handler_t callback); int address_remove(const Address *address, Link *link, link_netlink_message_handler_t callback);
bool address_equal(Address *a1, Address *a2); bool address_equal(const Address *a1, const Address *a2);
bool address_is_ready(const Address *a); bool address_is_ready(const Address *a);
int generate_ipv6_eui_64_address(Link *link, struct in6_addr *ret); int generate_ipv6_eui_64_address(const Link *link, struct in6_addr *ret);
DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free); DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free);

View File

@ -563,6 +563,9 @@ static int dhcp_lease_lost(Link *link) {
link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease); link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
link_dirty(link); link_dirty(link);
if (link->dhcp_acd)
(void) sd_ipv4acd_stop(link->dhcp_acd);
return r; return r;
} }
@ -617,26 +620,61 @@ static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
return; return;
} }
static int configure_dhcpv4_duplicate_address_detection(Link *link) { static int dhcp4_configure_dad(Link *link) {
int r;
assert(link);
assert(link->manager);
assert(link->network);
if (!link->network->dhcp_send_decline)
return 0;
if (!link->dhcp_acd) {
r = sd_ipv4acd_new(&link->dhcp_acd);
if (r < 0)
return r;
r = sd_ipv4acd_attach_event(link->dhcp_acd, link->manager->event, 0);
if (r < 0)
return r;
}
r = sd_ipv4acd_set_ifindex(link->dhcp_acd, link->ifindex);
if (r < 0)
return r;
r = sd_ipv4acd_set_mac(link->dhcp_acd, &link->mac);
if (r < 0)
return r;
return 0;
}
static int dhcp4_dad_update_mac(Link *link) {
bool running;
int r; int r;
assert(link); assert(link);
r = sd_ipv4acd_new(&link->network->dhcp_acd); if (!link->dhcp_acd)
return 0;
running = sd_ipv4acd_is_running(link->dhcp_acd);
r = sd_ipv4acd_stop(link->dhcp_acd);
if (r < 0) if (r < 0)
return r; return r;
r = sd_ipv4acd_attach_event(link->network->dhcp_acd, link->manager->event, 0); r = sd_ipv4acd_set_mac(link->dhcp_acd, &link->mac);
if (r < 0) if (r < 0)
return r; return r;
r = sd_ipv4acd_set_ifindex(link->network->dhcp_acd, link->ifindex); if (running) {
if (r < 0) r = sd_ipv4acd_start(link->dhcp_acd, true);
return r; if (r < 0)
return r;
r = sd_ipv4acd_set_mac(link->network->dhcp_acd, &link->mac); }
if (r < 0)
return r;
return 0; return 0;
} }
@ -652,7 +690,7 @@ static int dhcp4_start_acd(Link *link) {
if (!link->dhcp_lease) if (!link->dhcp_lease)
return 0; return 0;
(void) sd_ipv4acd_stop(link->network->dhcp_acd); (void) sd_ipv4acd_stop(link->dhcp_acd);
link->dhcp4_address_bind = false; link->dhcp4_address_bind = false;
@ -660,15 +698,15 @@ static int dhcp4_start_acd(Link *link) {
if (r < 0) if (r < 0)
return r; return r;
r = sd_ipv4acd_get_address(link->network->dhcp_acd, &old); r = sd_ipv4acd_get_address(link->dhcp_acd, &old);
if (r < 0) if (r < 0)
return r; return r;
r = sd_ipv4acd_set_address(link->network->dhcp_acd, &addr.in); r = sd_ipv4acd_set_address(link->dhcp_acd, &addr.in);
if (r < 0) if (r < 0)
return r; return r;
r = sd_ipv4acd_set_callback(link->network->dhcp_acd, dhcp_address_on_acd, link); r = sd_ipv4acd_set_callback(link->dhcp_acd, dhcp_address_on_acd, link);
if (r < 0) if (r < 0)
return r; return r;
@ -679,7 +717,7 @@ static int dhcp4_start_acd(Link *link) {
log_link_debug(link, "Starting IPv4ACD client. Probing DHCPv4 address %s", strna(pretty)); log_link_debug(link, "Starting IPv4ACD client. Probing DHCPv4 address %s", strna(pretty));
} }
r = sd_ipv4acd_start(link->network->dhcp_acd, !in4_addr_equal(&addr.in, &old)); r = sd_ipv4acd_start(link->dhcp_acd, !in4_addr_equal(&addr.in, &old));
if (r < 0) if (r < 0)
return r; return r;
@ -1425,11 +1463,9 @@ int dhcp4_configure(Link *link) {
return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed set to lease lifetime: %m"); return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed set to lease lifetime: %m");
} }
if (link->network->dhcp_send_decline) { r = dhcp4_configure_dad(link);
r = configure_dhcpv4_duplicate_address_detection(link); if (r < 0)
if (r < 0) return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to configure service type: %m");
return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to configure service type: %m");
}
return dhcp4_set_client_identifier(link); return dhcp4_set_client_identifier(link);
} }
@ -1450,6 +1486,10 @@ int dhcp4_update_mac(Link *link) {
if (r < 0) if (r < 0)
return r; return r;
r = dhcp4_dad_update_mac(link);
if (r < 0)
return r;
return 0; return 0;
} }

View File

@ -503,6 +503,7 @@ static void link_free_engines(Link *link) {
link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease); link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease);
link->ndisc = sd_ndisc_unref(link->ndisc); link->ndisc = sd_ndisc_unref(link->ndisc);
link->radv = sd_radv_unref(link->radv); link->radv = sd_radv_unref(link->radv);
link->dhcp_acd = sd_ipv4acd_unref(link->dhcp_acd);
} }
static Link *link_free(Link *link) { static Link *link_free(Link *link) {
@ -622,6 +623,12 @@ int link_stop_clients(Link *link, bool may_keep_dhcp) {
r = log_link_warning_errno(link, k, "Could not stop DHCPv4 client: %m"); r = log_link_warning_errno(link, k, "Could not stop DHCPv4 client: %m");
} }
if (link->dhcp_acd) {
k = sd_ipv4acd_stop(link->dhcp_acd);
if (k < 0)
r = log_link_warning_errno(link, k, "Could not stop IPv4 ACD client for DHCPv4: %m");
}
if (link->ipv4ll) { if (link->ipv4ll) {
k = sd_ipv4ll_stop(link->ipv4ll); k = sd_ipv4ll_stop(link->ipv4ll);
if (k < 0) if (k < 0)
@ -668,7 +675,7 @@ void link_enter_failed(Link *link) {
link_set_state(link, LINK_STATE_FAILED); link_set_state(link, LINK_STATE_FAILED);
link_stop_clients(link, false); (void) link_stop_clients(link, false);
link_dirty(link); link_dirty(link);
} }

View File

@ -9,6 +9,7 @@
#include "sd-dhcp-client.h" #include "sd-dhcp-client.h"
#include "sd-dhcp-server.h" #include "sd-dhcp-server.h"
#include "sd-dhcp6-client.h" #include "sd-dhcp6-client.h"
#include "sd-ipv4acd.h"
#include "sd-ipv4ll.h" #include "sd-ipv4ll.h"
#include "sd-lldp.h" #include "sd-lldp.h"
#include "sd-ndisc.h" #include "sd-ndisc.h"
@ -105,6 +106,7 @@ typedef struct Link {
uint32_t original_mtu; uint32_t original_mtu;
unsigned dhcp4_messages; unsigned dhcp4_messages;
unsigned dhcp4_remove_messages; unsigned dhcp4_remove_messages;
sd_ipv4acd *dhcp_acd;
bool dhcp4_route_failed:1; bool dhcp4_route_failed:1;
bool dhcp4_route_retrying:1; bool dhcp4_route_retrying:1;
bool dhcp4_configured:1; bool dhcp4_configured:1;

View File

@ -725,8 +725,10 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
SET_FOREACH(a, addresses) { SET_FOREACH(a, addresses) {
Address *existing_address; Address *existing_address;
address->in_addr.in6 = *a;
/* see RFC4862 section 5.5.3.e */ /* see RFC4862 section 5.5.3.e */
r = address_get(link, AF_INET6, (union in_addr_union *) a, prefixlen, &existing_address); r = address_get(link, address, &existing_address);
if (r > 0) { if (r > 0) {
lifetime_remaining = existing_address->cinfo.tstamp / 100 + existing_address->cinfo.ifa_valid - time_now / USEC_PER_SEC; lifetime_remaining = existing_address->cinfo.tstamp / 100 + existing_address->cinfo.ifa_valid - time_now / USEC_PER_SEC;
if (lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > lifetime_remaining) if (lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > lifetime_remaining)
@ -743,8 +745,6 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
if (address->cinfo.ifa_valid == 0) if (address->cinfo.ifa_valid == 0)
continue; continue;
address->in_addr.in6 = *a;
r = ndisc_address_configure(address, link, rt); r = ndisc_address_configure(address, link, rt);
if (r < 0) if (r < 0)
return log_link_error_errno(link, r, "Could not set SLAAC address: %m"); return log_link_error_errno(link, r, "Could not set SLAAC address: %m");

View File

@ -636,9 +636,6 @@ static Network *network_free(Network *network) {
strv_free(network->dhcp6_user_class); strv_free(network->dhcp6_user_class);
strv_free(network->dhcp6_vendor_class); strv_free(network->dhcp6_vendor_class);
if (network->dhcp_acd)
sd_ipv4acd_unref(network->dhcp_acd);
strv_free(network->ntp); strv_free(network->ntp);
for (unsigned i = 0; i < network->n_dns; i++) for (unsigned i = 0; i < network->n_dns; i++)
in_addr_full_free(network->dns[i]); in_addr_full_free(network->dns[i]);

View File

@ -5,7 +5,6 @@
#include "sd-bus.h" #include "sd-bus.h"
#include "sd-device.h" #include "sd-device.h"
#include "sd-ipv4acd.h"
#include "bridge.h" #include "bridge.h"
#include "condition.h" #include "condition.h"
@ -123,7 +122,6 @@ struct Network {
bool dhcp_send_release; bool dhcp_send_release;
bool dhcp_send_decline; bool dhcp_send_decline;
DHCPUseDomains dhcp_use_domains; DHCPUseDomains dhcp_use_domains;
sd_ipv4acd *dhcp_acd;
Set *dhcp_deny_listed_ip; Set *dhcp_deny_listed_ip;
Set *dhcp_allow_listed_ip; Set *dhcp_allow_listed_ip;
Set *dhcp_request_options; Set *dhcp_request_options;

View File

@ -159,8 +159,10 @@ static void test_address_equality(void) {
assert_se(in_addr_from_string(AF_INET, "192.168.3.9", &a2->in_addr) >= 0); assert_se(in_addr_from_string(AF_INET, "192.168.3.9", &a2->in_addr) >= 0);
assert_se(address_equal(a1, a2)); assert_se(address_equal(a1, a2));
assert_se(in_addr_from_string(AF_INET, "192.168.3.10", &a1->in_addr_peer) >= 0); assert_se(in_addr_from_string(AF_INET, "192.168.3.10", &a1->in_addr_peer) >= 0);
assert_se(address_equal(a1, a2)); assert_se(!address_equal(a1, a2));
assert_se(in_addr_from_string(AF_INET, "192.168.3.11", &a2->in_addr_peer) >= 0); assert_se(in_addr_from_string(AF_INET, "192.168.3.11", &a2->in_addr_peer) >= 0);
assert_se(!address_equal(a1, a2));
a2->in_addr_peer = a1->in_addr_peer;
assert_se(address_equal(a1, a2)); assert_se(address_equal(a1, a2));
a1->prefixlen = 10; a1->prefixlen = 10;
assert_se(!address_equal(a1, a2)); assert_se(!address_equal(a1, a2));
@ -171,10 +173,13 @@ static void test_address_equality(void) {
assert_se(!address_equal(a1, a2)); assert_se(!address_equal(a1, a2));
a2->family = AF_INET6; a2->family = AF_INET6;
a1->in_addr_peer = a2->in_addr_peer = IN_ADDR_NULL;
assert_se(in_addr_from_string(AF_INET6, "2001:4ca0:4f01::2", &a1->in_addr) >= 0); assert_se(in_addr_from_string(AF_INET6, "2001:4ca0:4f01::2", &a1->in_addr) >= 0);
assert_se(in_addr_from_string(AF_INET6, "2001:4ca0:4f01::2", &a2->in_addr) >= 0); assert_se(in_addr_from_string(AF_INET6, "2001:4ca0:4f01::2", &a2->in_addr) >= 0);
assert_se(address_equal(a1, a2)); assert_se(address_equal(a1, a2));
a1->prefixlen = 8;
assert_se(!address_equal(a1, a2));
a2->prefixlen = 8; a2->prefixlen = 8;
assert_se(address_equal(a1, a2)); assert_se(address_equal(a1, a2));

View File

@ -414,7 +414,7 @@ int ethtool_set_wol(int *ethtool_fd, const char *ifname, WakeOnLan wol) {
return 0; return 0;
} }
int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, netdev_ring_param *ring) { int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring) {
struct ethtool_ringparam ecmd = { struct ethtool_ringparam ecmd = {
.cmd = ETHTOOL_GRINGPARAM .cmd = ETHTOOL_GRINGPARAM
}; };
@ -543,7 +543,7 @@ static int set_features_bit(
return found ? 0 : -ENODATA; return found ? 0 : -ENODATA;
} }
int ethtool_set_features(int *ethtool_fd, const char *ifname, int *features) { int ethtool_set_features(int *ethtool_fd, const char *ifname, const int *features) {
_cleanup_free_ struct ethtool_gstrings *strings = NULL; _cleanup_free_ struct ethtool_gstrings *strings = NULL;
struct ethtool_sfeatures *sfeatures; struct ethtool_sfeatures *sfeatures;
struct ifreq ifr = {}; struct ifreq ifr = {};
@ -754,7 +754,7 @@ int ethtool_set_glinksettings(
int *fd, int *fd,
const char *ifname, const char *ifname,
int autonegotiation, int autonegotiation,
uint32_t advertise[static N_ADVERTISE], const uint32_t advertise[static N_ADVERTISE],
uint64_t speed, uint64_t speed,
Duplex duplex, Duplex duplex,
NetDevPort port) { NetDevPort port) {
@ -813,7 +813,7 @@ int ethtool_set_glinksettings(
return r; return r;
} }
int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels) { int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *channels) {
struct ethtool_channels ecmd = { struct ethtool_channels ecmd = {
.cmd = ETHTOOL_GCHANNELS .cmd = ETHTOOL_GCHANNELS
}; };

View File

@ -101,12 +101,12 @@ int ethtool_get_link_info(int *ethtool_fd, const char *ifname,
int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct ether_addr *ret); int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct ether_addr *ret);
int ethtool_set_speed(int *ethtool_fd, const char *ifname, unsigned speed, Duplex duplex); int ethtool_set_speed(int *ethtool_fd, const char *ifname, unsigned speed, Duplex duplex);
int ethtool_set_wol(int *ethtool_fd, const char *ifname, WakeOnLan wol); int ethtool_set_wol(int *ethtool_fd, const char *ifname, WakeOnLan wol);
int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, netdev_ring_param *ring); int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring);
int ethtool_set_features(int *ethtool_fd, const char *ifname, int *features); int ethtool_set_features(int *ethtool_fd, const char *ifname, const int *features);
int ethtool_set_glinksettings(int *ethtool_fd, const char *ifname, int ethtool_set_glinksettings(int *ethtool_fd, const char *ifname,
int autonegotiation, uint32_t advertise[static N_ADVERTISE], int autonegotiation, const uint32_t advertise[static N_ADVERTISE],
uint64_t speed, Duplex duplex, NetDevPort port); uint64_t speed, Duplex duplex, NetDevPort port);
int ethtool_set_channels(int *ethtool_fd, const char *ifname, netdev_channels *channels); int ethtool_set_channels(int *ethtool_fd, const char *ifname, const netdev_channels *channels);
int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg); int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg);
const char *duplex_to_string(Duplex d) _const_; const char *duplex_to_string(Duplex d) _const_;

View File

@ -8,7 +8,10 @@
#include <sys/prctl.h> #include <sys/prctl.h>
#include <unistd.h> #include <unistd.h>
#include "sd-login.h"
#include "copy.h" #include "copy.h"
#include "env-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h" #include "fileio.h"
#include "io-util.h" #include "io-util.h"
@ -152,8 +155,7 @@ int pager_open(PagerFlags flags) {
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} }
/* Initialize a good charset for less. This is /* Initialize a good charset for less. This is particularly important if we output UTF-8
* particularly important if we output UTF-8
* characters. */ * characters. */
less_charset = getenv("SYSTEMD_LESSCHARSET"); less_charset = getenv("SYSTEMD_LESSCHARSET");
if (!less_charset && is_locale_utf8()) if (!less_charset && is_locale_utf8())
@ -164,7 +166,43 @@ int pager_open(PagerFlags flags) {
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} }
if (pager_args) { /* People might invoke us from sudo, don't needlessly allow less to be a way to shell out
* privileged stuff. If the user set $SYSTEMD_PAGERSECURE, trust their configuration of the
* pager. If they didn't, use secure mode when under euid is changed. If $SYSTEMD_PAGERSECURE
* wasn't explicitly set, and we autodetect the need for secure mode, only use the pager we
* know to be good. */
int use_secure_mode = getenv_bool("SYSTEMD_PAGERSECURE");
bool trust_pager = use_secure_mode >= 0;
if (use_secure_mode == -ENXIO) {
uid_t uid;
r = sd_pid_get_owner_uid(0, &uid);
if (r < 0)
log_debug_errno(r, "sd_pid_get_owner_uid() failed, enabling pager secure mode: %m");
use_secure_mode = r < 0 || uid != geteuid();
} else if (use_secure_mode < 0) {
log_warning_errno(use_secure_mode, "Unable to parse $SYSTEMD_PAGERSECURE, assuming true: %m");
use_secure_mode = true;
}
/* We generally always set variables used by less, even if we end up using a different pager.
* They shouldn't hurt in any case, and ideally other pagers would look at them too. */
if (use_secure_mode)
r = setenv("LESSSECURE", "1", 1);
else
r = unsetenv("LESSSECURE");
if (r < 0) {
log_error_errno(errno, "Failed to adjust environment variable LESSSECURE: %m");
_exit(EXIT_FAILURE);
}
if (trust_pager && pager_args) { /* The pager config might be set globally, and we cannot
* know if the user adjusted it to be appropriate for the
* secure mode. Thus, start the pager specified through
* envvars only when $SYSTEMD_PAGERSECURE was explicitly set
* as well. */
r = loop_write(exe_name_pipe[1], pager_args[0], strlen(pager_args[0]) + 1, false); r = loop_write(exe_name_pipe[1], pager_args[0], strlen(pager_args[0]) + 1, false);
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to write pager name to socket: %m"); log_error_errno(r, "Failed to write pager name to socket: %m");
@ -176,13 +214,14 @@ int pager_open(PagerFlags flags) {
"Failed to execute '%s', using fallback pagers: %m", pager_args[0]); "Failed to execute '%s', using fallback pagers: %m", pager_args[0]);
} }
/* Debian's alternatives command for pagers is /* Debian's alternatives command for pagers is called 'pager'. Note that we do not call
* called 'pager'. Note that we do not call * sensible-pagers here, since that is just a shell script that implements a logic that is
* sensible-pagers here, since that is just a * similar to this one anyway, but is Debian-specific. */
* shell script that implements a logic that
* is similar to this one anyway, but is
* Debian-specific. */
FOREACH_STRING(exe, "pager", "less", "more") { FOREACH_STRING(exe, "pager", "less", "more") {
/* Only less implements secure mode right now. */
if (use_secure_mode && !streq(exe, "less"))
continue;
r = loop_write(exe_name_pipe[1], exe, strlen(exe) + 1, false); r = loop_write(exe_name_pipe[1], exe, strlen(exe) + 1, false);
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to write pager name to socket: %m"); log_error_errno(r, "Failed to write pager name to socket: %m");
@ -193,6 +232,7 @@ int pager_open(PagerFlags flags) {
"Failed to execute '%s', using next fallback pager: %m", exe); "Failed to execute '%s', using next fallback pager: %m", exe);
} }
/* Our builtin is also very secure. */
r = loop_write(exe_name_pipe[1], "(built-in)", strlen("(built-in)") + 1, false); r = loop_write(exe_name_pipe[1], "(built-in)", strlen("(built-in)") + 1, false);
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to write pager name to socket: %m"); log_error_errno(r, "Failed to write pager name to socket: %m");

View File

@ -345,6 +345,7 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = {
.value = .value =
"_llseek\0" "_llseek\0"
"close\0" "close\0"
"close_range\0"
"dup\0" "dup\0"
"dup2\0" "dup2\0"
"dup3\0" "dup3\0"

View File

@ -61,6 +61,12 @@ int show_environment(int argc, char *argv[], void *userdata) {
return 0; return 0;
} }
static void invalid_callback(const char *p, void *userdata) {
_cleanup_free_ char *t = cescape(p);
log_debug("Ignoring invalid environment assignment \"%s\".", strnull(t));
}
int set_environment(int argc, char *argv[], void *userdata) { int set_environment(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
@ -112,9 +118,18 @@ int import_environment(int argc, char *argv[], void *userdata) {
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
if (argc < 2) if (argc < 2) {
r = sd_bus_message_append_strv(m, environ); _cleanup_strv_free_ char **copy = NULL;
else {
copy = strv_copy(environ);
if (!copy)
return log_oom();
strv_env_clean_with_callback(copy, invalid_callback, NULL);
r = sd_bus_message_append_strv(m, copy);
} else {
char **a, **b; char **a, **b;
r = sd_bus_message_open_container(m, 'a', "s"); r = sd_bus_message_open_container(m, 'a', "s");

View File

@ -10,6 +10,8 @@
#include "util.h" #include "util.h"
static void test_strv_env_delete(void) { static void test_strv_env_delete(void) {
log_info("/* %s */", __func__);
_cleanup_strv_free_ char **a = NULL, **b = NULL, **c = NULL, **d = NULL; _cleanup_strv_free_ char **a = NULL, **b = NULL, **c = NULL, **d = NULL;
a = strv_new("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF"); a = strv_new("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF");
@ -30,9 +32,9 @@ static void test_strv_env_delete(void) {
} }
static void test_strv_env_get(void) { static void test_strv_env_get(void) {
char **l; log_info("/* %s */", __func__);
l = STRV_MAKE("ONE_OR_TWO=1", "THREE=3", "ONE_OR_TWO=2", "FOUR=4"); char **l = STRV_MAKE("ONE_OR_TWO=1", "THREE=3", "ONE_OR_TWO=2", "FOUR=4");
assert_se(streq(strv_env_get(l, "ONE_OR_TWO"), "2")); assert_se(streq(strv_env_get(l, "ONE_OR_TWO"), "2"));
assert_se(streq(strv_env_get(l, "THREE"), "3")); assert_se(streq(strv_env_get(l, "THREE"), "3"));
@ -40,6 +42,8 @@ static void test_strv_env_get(void) {
} }
static void test_strv_env_unset(void) { static void test_strv_env_unset(void) {
log_info("/* %s */", __func__);
_cleanup_strv_free_ char **l = NULL; _cleanup_strv_free_ char **l = NULL;
l = strv_new("PIEP", "SCHLUMPF=SMURFF", "NANANANA=YES"); l = strv_new("PIEP", "SCHLUMPF=SMURFF", "NANANANA=YES");
@ -53,6 +57,8 @@ static void test_strv_env_unset(void) {
} }
static void test_strv_env_set(void) { static void test_strv_env_set(void) {
log_info("/* %s */", __func__);
_cleanup_strv_free_ char **l = NULL, **r = NULL; _cleanup_strv_free_ char **l = NULL, **r = NULL;
l = strv_new("PIEP", "SCHLUMPF=SMURFF", "NANANANA=YES"); l = strv_new("PIEP", "SCHLUMPF=SMURFF", "NANANANA=YES");
@ -69,6 +75,8 @@ static void test_strv_env_set(void) {
} }
static void test_strv_env_merge(void) { static void test_strv_env_merge(void) {
log_info("/* %s */", __func__);
_cleanup_strv_free_ char **a = NULL, **b = NULL, **r = NULL; _cleanup_strv_free_ char **a = NULL, **b = NULL, **r = NULL;
a = strv_new("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF"); a = strv_new("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF");
@ -97,6 +105,8 @@ static void test_strv_env_merge(void) {
} }
static void test_env_strv_get_n(void) { static void test_env_strv_get_n(void) {
log_info("/* %s */", __func__);
const char *_env[] = { const char *_env[] = {
"FOO=NO NO NO", "FOO=NO NO NO",
"FOO=BAR BAR", "FOO=BAR BAR",
@ -127,6 +137,8 @@ static void test_env_strv_get_n(void) {
} }
static void test_replace_env(bool braceless) { static void test_replace_env(bool braceless) {
log_info("/* %s(braceless=%s) */", __func__, yes_no(braceless));
const char *env[] = { const char *env[] = {
"FOO=BAR BAR", "FOO=BAR BAR",
"BAR=waldo", "BAR=waldo",
@ -152,6 +164,8 @@ static void test_replace_env(bool braceless) {
} }
static void test_replace_env2(bool extended) { static void test_replace_env2(bool extended) {
log_info("/* %s(extended=%s) */", __func__, yes_no(extended));
const char *env[] = { const char *env[] = {
"FOO=foo", "FOO=foo",
"BAR=bar", "BAR=bar",
@ -180,6 +194,8 @@ static void test_replace_env2(bool extended) {
} }
static void test_replace_env_argv(void) { static void test_replace_env_argv(void) {
log_info("/* %s */", __func__);
const char *env[] = { const char *env[] = {
"FOO=BAR BAR", "FOO=BAR BAR",
"BAR=waldo", "BAR=waldo",
@ -230,24 +246,25 @@ static void test_replace_env_argv(void) {
} }
static void test_env_clean(void) { static void test_env_clean(void) {
_cleanup_strv_free_ char **e; log_info("/* %s */", __func__);
e = strv_new("FOOBAR=WALDO", _cleanup_strv_free_ char **e = strv_new("FOOBAR=WALDO",
"FOOBAR=WALDO", "FOOBAR=WALDO",
"FOOBAR", "FOOBAR",
"F", "F",
"X=", "X=",
"F=F", "F=F",
"=", "=",
"=F", "=F",
"", "",
"0000=000", "0000=000",
"äöüß=abcd", "äöüß=abcd",
"abcd=äöüß", "abcd=äöüß",
"xyz\n=xyz", "xyz\n=xyz",
"xyz=xyz\n", "xyz=xyz\n",
"another=one", "another=one",
"another=final one"); "another=final one",
"BASH_FUNC_foo%%=() { echo foo\n}");
assert_se(e); assert_se(e);
assert_se(!strv_env_is_valid(e)); assert_se(!strv_env_is_valid(e));
assert_se(strv_env_clean(e) == e); assert_se(strv_env_clean(e) == e);
@ -256,13 +273,17 @@ static void test_env_clean(void) {
assert_se(streq(e[0], "FOOBAR=WALDO")); assert_se(streq(e[0], "FOOBAR=WALDO"));
assert_se(streq(e[1], "X=")); assert_se(streq(e[1], "X="));
assert_se(streq(e[2], "F=F")); assert_se(streq(e[2], "F=F"));
assert_se(streq(e[3], "abcd=äöüß")); assert_se(streq(e[3], "0000=000"));
assert_se(streq(e[4], "xyz=xyz\n")); assert_se(streq(e[4], "abcd=äöüß"));
assert_se(streq(e[5], "another=final one")); assert_se(streq(e[5], "xyz=xyz\n"));
assert_se(e[6] == NULL); assert_se(streq(e[6], "another=final one"));
assert_se(streq(e[7], "BASH_FUNC_foo%%=() { echo foo\n}"));
assert_se(e[8] == NULL);
} }
static void test_env_name_is_valid(void) { static void test_env_name_is_valid(void) {
log_info("/* %s */", __func__);
assert_se(env_name_is_valid("test")); assert_se(env_name_is_valid("test"));
assert_se(!env_name_is_valid(NULL)); assert_se(!env_name_is_valid(NULL));
@ -270,11 +291,16 @@ static void test_env_name_is_valid(void) {
assert_se(!env_name_is_valid("xxx\a")); assert_se(!env_name_is_valid("xxx\a"));
assert_se(!env_name_is_valid("xxx\007b")); assert_se(!env_name_is_valid("xxx\007b"));
assert_se(!env_name_is_valid("\007\009")); assert_se(!env_name_is_valid("\007\009"));
assert_se(!env_name_is_valid("5_starting_with_a_number_is_wrong")); assert_se( env_name_is_valid("5_starting_with_a_number_is_unexpected_but_valid"));
assert_se(!env_name_is_valid("#¤%&?_only_numbers_letters_and_underscore_allowed")); assert_se(!env_name_is_valid("#¤%&?_only_numbers_letters_and_underscore_allowed"));
assert_se( env_name_is_valid("BASH_FUNC_foo%%"));
assert_se(!env_name_is_valid("with spaces%%"));
assert_se(!env_name_is_valid("with\nnewline%%"));
} }
static void test_env_value_is_valid(void) { static void test_env_value_is_valid(void) {
log_info("/* %s */", __func__);
assert_se(env_value_is_valid("")); assert_se(env_value_is_valid(""));
assert_se(env_value_is_valid("głąb kapuściany")); assert_se(env_value_is_valid("głąb kapuściany"));
assert_se(env_value_is_valid("printf \"\\x1b]0;<mock-chroot>\\x07<mock-chroot>\"")); assert_se(env_value_is_valid("printf \"\\x1b]0;<mock-chroot>\\x07<mock-chroot>\""));
@ -283,6 +309,8 @@ static void test_env_value_is_valid(void) {
} }
static void test_env_assignment_is_valid(void) { static void test_env_assignment_is_valid(void) {
log_info("/* %s */", __func__);
assert_se(env_assignment_is_valid("a=")); assert_se(env_assignment_is_valid("a="));
assert_se(env_assignment_is_valid("b=głąb kapuściany")); assert_se(env_assignment_is_valid("b=głąb kapuściany"));
assert_se(env_assignment_is_valid("c=\\007\\009\\011")); assert_se(env_assignment_is_valid("c=\\007\\009\\011"));
@ -294,9 +322,13 @@ static void test_env_assignment_is_valid(void) {
assert_se(!env_assignment_is_valid("a b=")); assert_se(!env_assignment_is_valid("a b="));
assert_se(!env_assignment_is_valid("a =")); assert_se(!env_assignment_is_valid("a ="));
assert_se(!env_assignment_is_valid(" b=")); assert_se(!env_assignment_is_valid(" b="));
/* no dots or dashes: http://tldp.org/LDP/abs/html/gotchas.html */ /* Names with dots and dashes makes those variables inaccessible as bash variables (as the syntax
assert_se(!env_assignment_is_valid("a.b=")); * simply does not allow such variable names, see http://tldp.org/LDP/abs/html/gotchas.html). They
assert_se(!env_assignment_is_valid("a-b=")); * are still valid variables according to POSIX though. */
assert_se( env_assignment_is_valid("a.b="));
assert_se( env_assignment_is_valid("a-b="));
/* Those are not ASCII, so not valid according to POSIX (though zsh does allow unicode variable
* names). */
assert_se(!env_assignment_is_valid("\007=głąb kapuściany")); assert_se(!env_assignment_is_valid("\007=głąb kapuściany"));
assert_se(!env_assignment_is_valid("c\009=\007\009\011")); assert_se(!env_assignment_is_valid("c\009=\007\009\011"));
assert_se(!env_assignment_is_valid("głąb=printf \"\x1b]0;<mock-chroot>\x07<mock-chroot>\"")); assert_se(!env_assignment_is_valid("głąb=printf \"\x1b]0;<mock-chroot>\x07<mock-chroot>\""));

View File

@ -11,6 +11,7 @@
#include "path-util.h" #include "path-util.h"
#include "process-util.h" #include "process-util.h"
#include "random-util.h" #include "random-util.h"
#include "rlimit-util.h"
#include "serialize.h" #include "serialize.h"
#include "string-util.h" #include "string-util.h"
#include "tests.h" #include "tests.h"
@ -317,6 +318,100 @@ static void test_read_nr_open(void) {
log_info("nr-open: %i", read_nr_open()); log_info("nr-open: %i", read_nr_open());
} }
static size_t validate_fds(
bool opened,
const int *fds,
size_t n_fds) {
size_t c = 0;
/* Validates that fds in the specified array are one of the following three:
*
* 1. < 0 (test is skipped) or
* 2. opened (if 'opened' param is true) or
* 3. closed (if 'opened' param is false)
*/
for (size_t i = 0; i < n_fds; i++) {
if (fds[i] < 0)
continue;
if (opened)
assert_se(fcntl(fds[i], F_GETFD) >= 0);
else
assert_se(fcntl(fds[i], F_GETFD) < 0 && errno == EBADF);
c++;
}
return c; /* Return number of fds >= 0 in the array */
}
static void test_close_all_fds(void) {
_cleanup_free_ int *fds = NULL, *keep = NULL;
struct rlimit rl;
size_t n_fds, n_keep;
log_info("/* %s */", __func__);
rlimit_nofile_bump(-1);
assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0);
assert_se(rl.rlim_cur > 10);
/* Try to use 5000 fds, but when we can't bump the rlimit to make that happen use the whole limit minus 10 */
n_fds = MIN((rl.rlim_cur & ~1U) - 10U, 5000U);
assert_se((n_fds & 1U) == 0U); /* make sure even number of fds */
/* Allocate the determined number of fds, always two at a time */
assert_se(fds = new(int, n_fds));
for (size_t i = 0; i < n_fds; i += 2)
assert_se(pipe2(fds + i, O_CLOEXEC) >= 0);
/* Validate this worked */
assert_se(validate_fds(true, fds, n_fds) == n_fds);
/* Randomized number of fds to keep, but at most every second */
n_keep = (random_u64() % (n_fds / 2));
/* Now randomly select a number of fds from the array above to keep */
assert_se(keep = new(int, n_keep));
for (size_t k = 0; k < n_keep; k++) {
for (;;) {
size_t p;
p = random_u64() % n_fds;
if (fds[p] >= 0) {
keep[k] = TAKE_FD(fds[p]);
break;
}
}
}
/* Check that all fds from both arrays are still open, and test how many in each are >= 0 */
assert_se(validate_fds(true, fds, n_fds) == n_fds - n_keep);
assert_se(validate_fds(true, keep, n_keep) == n_keep);
/* Close logging fd first, so that we don't confuse it by closing its fd */
log_close();
log_set_open_when_needed(true);
/* Close all but the ones to keep */
assert_se(close_all_fds(keep, n_keep) >= 0);
assert_se(validate_fds(false, fds, n_fds) == n_fds - n_keep);
assert_se(validate_fds(true, keep, n_keep) == n_keep);
/* Close everything else too! */
assert_se(close_all_fds(NULL, 0) >= 0);
assert_se(validate_fds(false, fds, n_fds) == n_fds - n_keep);
assert_se(validate_fds(false, keep, n_keep) == n_keep);
log_set_open_when_needed(false);
log_open();
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG); test_setup_logging(LOG_DEBUG);
@ -330,6 +425,7 @@ int main(int argc, char *argv[]) {
test_rearrange_stdio(); test_rearrange_stdio();
test_fd_duplicate_data_fd(); test_fd_duplicate_data_fd();
test_read_nr_open(); test_read_nr_open();
test_close_all_fds();
return 0; return 0;
} }

View File

@ -748,26 +748,26 @@ static void test_config_parse_pass_environ(void) {
_cleanup_strv_free_ char **passenv = NULL; _cleanup_strv_free_ char **passenv = NULL;
r = config_parse_pass_environ(NULL, "fake", 1, "section", 1, r = config_parse_pass_environ(NULL, "fake", 1, "section", 1,
"PassEnvironment", 0, "A B", "PassEnvironment", 0, "A B",
&passenv, NULL); &passenv, NULL);
assert_se(r >= 0); assert_se(r >= 0);
assert_se(strv_length(passenv) == 2); assert_se(strv_length(passenv) == 2);
assert_se(streq(passenv[0], "A")); assert_se(streq(passenv[0], "A"));
assert_se(streq(passenv[1], "B")); assert_se(streq(passenv[1], "B"));
r = config_parse_pass_environ(NULL, "fake", 1, "section", 1, r = config_parse_pass_environ(NULL, "fake", 1, "section", 1,
"PassEnvironment", 0, "", "PassEnvironment", 0, "",
&passenv, NULL); &passenv, NULL);
assert_se(r >= 0); assert_se(r >= 0);
assert_se(strv_isempty(passenv)); assert_se(strv_isempty(passenv));
r = config_parse_pass_environ(NULL, "fake", 1, "section", 1, r = config_parse_pass_environ(NULL, "fake", 1, "section", 1,
"PassEnvironment", 0, "'invalid name' 'normal_name' A=1 \\", "PassEnvironment", 0, "'invalid name' 'normal_name' A=1 'special_name$$' \\",
&passenv, NULL); &passenv, NULL);
assert_se(r >= 0); assert_se(r >= 0);
assert_se(strv_length(passenv) == 1); assert_se(strv_length(passenv) == 2);
assert_se(streq(passenv[0], "normal_name")); assert_se(streq(passenv[0], "normal_name"));
assert_se(streq(passenv[1], "special_name$$"));
} }
static void test_unit_dump_config_items(void) { static void test_unit_dump_config_items(void) {

View File

@ -11,6 +11,7 @@
#include "conf-files.h" #include "conf-files.h"
#include "conf-parser.h" #include "conf-parser.h"
#include "def.h" #include "def.h"
#include "device-private.h"
#include "device-util.h" #include "device-util.h"
#include "ethtool-util.h" #include "ethtool-util.h"
#include "fd-util.h" #include "fd-util.h"
@ -241,6 +242,7 @@ bool link_config_should_reload(link_config_ctx *ctx) {
} }
int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret) { int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret) {
unsigned name_assign_type = NET_NAME_UNKNOWN;
struct ether_addr permanent_mac = {}; struct ether_addr permanent_mac = {};
unsigned short iftype = 0; unsigned short iftype = 0;
link_config *link; link_config *link;
@ -267,40 +269,91 @@ int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret)
if (r < 0) if (r < 0)
log_device_debug_errno(device, r, "Failed to get permanent MAC address, ignoring: %m"); log_device_debug_errno(device, r, "Failed to get permanent MAC address, ignoring: %m");
(void) link_unsigned_attribute(device, "name_assign_type", &name_assign_type);
LIST_FOREACH(links, link, ctx->links) { LIST_FOREACH(links, link, ctx->links) {
if (net_match_config(link->match_mac, link->match_permanent_mac, link->match_path, link->match_driver, if (net_match_config(link->match_mac, link->match_permanent_mac, link->match_path, link->match_driver,
link->match_type, link->match_name, link->match_property, NULL, NULL, NULL, link->match_type, link->match_name, link->match_property, NULL, NULL, NULL,
device, NULL, &permanent_mac, NULL, iftype, NULL, NULL, 0, NULL, NULL)) { device, NULL, &permanent_mac, NULL, iftype, NULL, NULL, 0, NULL, NULL)) {
if (link->match_name && !strv_contains(link->match_name, "*")) {
unsigned name_assign_type = NET_NAME_UNKNOWN;
(void) link_unsigned_attribute(device, "name_assign_type", &name_assign_type); if (link->match_name && !strv_contains(link->match_name, "*") && name_assign_type == NET_NAME_ENUM)
log_device_warning(device, "Config file %s is applied to device based on potentially unpredictable interface name.",
if (name_assign_type == NET_NAME_ENUM) { link->filename);
log_device_warning(device, "Config file %s applies to device based on potentially unpredictable interface name", else
link->filename); log_device_debug(device, "Config file %s is applied", link->filename);
*ret = link;
return 0;
} else if (name_assign_type == NET_NAME_RENAMED) {
log_device_warning(device, "Config file %s matches device based on renamed interface name, ignoring",
link->filename);
continue;
}
}
log_device_debug(device, "Config file %s is applied", link->filename);
*ret = link; *ret = link;
return 0; return 0;
} }
} }
*ret = NULL;
return -ENOENT; return -ENOENT;
} }
static int link_config_apply_ethtool_settings(int *ethtool_fd, const link_config *config, sd_device *device) {
const char *name;
int r;
assert(ethtool_fd);
assert(config);
assert(device);
r = sd_device_get_sysname(device, &name);
if (r < 0)
return log_device_error_errno(device, r, "Failed to get sysname: %m");
r = ethtool_set_glinksettings(ethtool_fd, name,
config->autonegotiation, config->advertise,
config->speed, config->duplex, config->port);
if (r < 0) {
if (config->port != _NET_DEV_PORT_INVALID)
log_device_warning_errno(device, r, "Could not set port '%s', ignoring: %m", port_to_string(config->port));
if (!eqzero(config->advertise))
log_device_warning_errno(device, r, "Could not set advertise mode, ignoring: %m"); /* TODO: include modes in the log message. */
if (config->speed) {
unsigned speed = DIV_ROUND_UP(config->speed, 1000000);
if (r == -EOPNOTSUPP) {
r = ethtool_set_speed(ethtool_fd, name, speed, config->duplex);
if (r < 0)
log_device_warning_errno(device, r, "Could not set speed to %uMbps, ignoring: %m", speed);
}
}
if (config->duplex != _DUP_INVALID)
log_device_warning_errno(device, r, "Could not set duplex to %s, ignoring: %m", duplex_to_string(config->duplex));
}
r = ethtool_set_wol(ethtool_fd, name, config->wol);
if (r < 0)
log_device_warning_errno(device, r, "Could not set WakeOnLan to %s, ignoring: %m", wol_to_string(config->wol));
r = ethtool_set_features(ethtool_fd, name, config->features);
if (r < 0)
log_device_warning_errno(device, r, "Could not set offload features, ignoring: %m");
if (config->channels.rx_count_set || config->channels.tx_count_set || config->channels.other_count_set || config->channels.combined_count_set) {
r = ethtool_set_channels(ethtool_fd, name, &config->channels);
if (r < 0)
log_device_warning_errno(device, r, "Could not set channels, ignoring: %m");
}
if (config->ring.rx_pending_set || config->ring.rx_mini_pending_set || config->ring.rx_jumbo_pending_set || config->ring.tx_pending_set) {
r = ethtool_set_nic_buffer_size(ethtool_fd, name, &config->ring);
if (r < 0)
log_device_warning_errno(device, r, "Could not set ring buffer, ignoring: %m");
}
if (config->rx_flow_control >= 0 || config->tx_flow_control >= 0 || config->autoneg_flow_control >= 0) {
r = ethtool_set_flow_control(ethtool_fd, name, config->rx_flow_control, config->tx_flow_control, config->autoneg_flow_control);
if (r < 0)
log_device_warning_errno(device, r, "Could not set flow control, ignoring: %m");
}
return 0;
}
static int get_mac(sd_device *device, MACAddressPolicy policy, struct ether_addr *mac) { static int get_mac(sd_device *device, MACAddressPolicy policy, struct ether_addr *mac) {
unsigned addr_type; unsigned addr_type;
bool want_random = policy == MAC_ADDRESS_POLICY_RANDOM; bool want_random = policy == MAC_ADDRESS_POLICY_RANDOM;
@ -357,80 +410,41 @@ static int get_mac(sd_device *device, MACAddressPolicy policy, struct ether_addr
return 1; return 1;
} }
int link_config_apply(link_config_ctx *ctx, link_config *config, static int link_config_apply_rtnl_settings(sd_netlink **rtnl, const link_config *config, sd_device *device) {
sd_device *device, const char **name) { struct ether_addr generated_mac, *mac = NULL;
_cleanup_strv_free_ char **altnames = NULL, **current_altnames = NULL; int ifindex, r;
struct ether_addr generated_mac;
struct ether_addr *mac = NULL; assert(rtnl);
assert(config);
assert(device);
r = sd_device_get_ifindex(device, &ifindex);
if (r < 0)
return log_device_error_errno(device, r, "Could not find ifindex: %m");
if (IN_SET(config->mac_address_policy, MAC_ADDRESS_POLICY_PERSISTENT, MAC_ADDRESS_POLICY_RANDOM)) {
if (get_mac(device, config->mac_address_policy, &generated_mac) > 0)
mac = &generated_mac;
} else
mac = config->mac;
r = rtnl_set_link_properties(rtnl, ifindex, config->alias, mac, config->mtu);
if (r < 0)
log_device_warning_errno(device, r, "Could not set Alias=, MACAddress= or MTU=, ignoring: %m");
return 0;
}
static int link_config_generate_new_name(const link_config_ctx *ctx, const link_config *config, sd_device *device, const char **ret_name) {
unsigned name_type = NET_NAME_UNKNOWN;
const char *new_name = NULL; const char *new_name = NULL;
const char *old_name;
unsigned speed, name_type = NET_NAME_UNKNOWN;
NamePolicy policy; NamePolicy policy;
int r, ifindex; int r;
assert(ctx); assert(ctx);
assert(config); assert(config);
assert(device); assert(device);
assert(name); assert(ret_name);
r = sd_device_get_sysname(device, &old_name);
if (r < 0)
return r;
r = ethtool_set_glinksettings(&ctx->ethtool_fd, old_name,
config->autonegotiation, config->advertise,
config->speed, config->duplex, config->port);
if (r < 0) {
if (config->port != _NET_DEV_PORT_INVALID)
log_warning_errno(r, "Could not set port (%s) of %s: %m", port_to_string(config->port), old_name);
if (!eqzero(config->advertise))
log_warning_errno(r, "Could not set advertise mode: %m"); /* TODO: include modes in the log message. */
if (config->speed) {
speed = DIV_ROUND_UP(config->speed, 1000000);
if (r == -EOPNOTSUPP) {
r = ethtool_set_speed(&ctx->ethtool_fd, old_name, speed, config->duplex);
if (r < 0)
log_warning_errno(r, "Could not set speed of %s to %u Mbps: %m", old_name, speed);
}
}
if (config->duplex != _DUP_INVALID)
log_warning_errno(r, "Could not set duplex of %s to %s: %m", old_name, duplex_to_string(config->duplex));
}
r = ethtool_set_wol(&ctx->ethtool_fd, old_name, config->wol);
if (r < 0)
log_warning_errno(r, "Could not set WakeOnLan of %s to %s: %m",
old_name, wol_to_string(config->wol));
r = ethtool_set_features(&ctx->ethtool_fd, old_name, config->features);
if (r < 0)
log_warning_errno(r, "Could not set offload features of %s: %m", old_name);
if (config->channels.rx_count_set || config->channels.tx_count_set || config->channels.other_count_set || config->channels.combined_count_set) {
r = ethtool_set_channels(&ctx->ethtool_fd, old_name, &config->channels);
if (r < 0)
log_warning_errno(r, "Could not set channels of %s: %m", old_name);
}
if (config->ring.rx_pending_set || config->ring.rx_mini_pending_set || config->ring.rx_jumbo_pending_set || config->ring.tx_pending_set) {
r = ethtool_set_nic_buffer_size(&ctx->ethtool_fd, old_name, &config->ring);
if (r < 0)
log_warning_errno(r, "Could not set ring buffer of %s: %m", old_name);
}
if (config->rx_flow_control >= 0 || config->tx_flow_control >= 0 || config->autoneg_flow_control >= 0) {
r = ethtool_set_flow_control(&ctx->ethtool_fd, old_name, config->rx_flow_control, config->tx_flow_control, config->autoneg_flow_control);
if (r < 0)
log_warning_errno(r, "Could not set flow control of %s: %m", old_name);
}
r = sd_device_get_ifindex(device, &ifindex);
if (r < 0)
return log_device_warning_errno(device, r, "Could not find ifindex: %m");
(void) link_unsigned_attribute(device, "name_assign_type", &name_type); (void) link_unsigned_attribute(device, "name_assign_type", &name_type);
@ -482,24 +496,43 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
break; break;
} }
if (new_name) if (new_name) {
log_device_debug(device, "Policy *%s* yields \"%s\".", name_policy_to_string(policy), new_name); log_device_debug(device, "Policy *%s* yields \"%s\".", name_policy_to_string(policy), new_name);
else if (config->name) { *ret_name = new_name;
new_name = config->name; return 0;
log_device_debug(device, "Policies didn't yield a name, using specified Name=%s.", new_name); }
} else
log_device_debug(device, "Policies didn't yield a name and Name= is not given, not renaming.");
no_rename:
if (IN_SET(config->mac_address_policy, MAC_ADDRESS_POLICY_PERSISTENT, MAC_ADDRESS_POLICY_RANDOM)) { if (config->name) {
if (get_mac(device, config->mac_address_policy, &generated_mac) > 0) log_device_debug(device, "Policies didn't yield a name, using specified Name=%s.", config->name);
mac = &generated_mac; *ret_name = config->name;
} else return 0;
mac = config->mac; }
r = rtnl_set_link_properties(&ctx->rtnl, ifindex, config->alias, mac, config->mtu); log_device_debug(device, "Policies didn't yield a name and Name= is not given, not renaming.");
no_rename:
r = sd_device_get_sysname(device, ret_name);
if (r < 0) if (r < 0)
return log_warning_errno(r, "Could not set Alias=, MACAddress= or MTU= on %s: %m", old_name); return log_device_error_errno(device, r, "Failed to get sysname: %m");
return 0;
}
static int link_config_apply_alternative_names(sd_netlink **rtnl, const link_config *config, sd_device *device, const char *new_name) {
_cleanup_strv_free_ char **altnames = NULL, **current_altnames = NULL;
const char *current_name;
int ifindex, r;
assert(rtnl);
assert(config);
assert(device);
r = sd_device_get_sysname(device, &current_name);
if (r < 0)
return log_device_error_errno(device, r, "Failed to get sysname: %m");
r = sd_device_get_ifindex(device, &ifindex);
if (r < 0)
return log_device_error_errno(device, r, "Could not find ifindex: %m");
if (config->alternative_names) { if (config->alternative_names) {
altnames = strv_copy(config->alternative_names); altnames = strv_copy(config->alternative_names);
@ -539,11 +572,11 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
if (new_name) if (new_name)
strv_remove(altnames, new_name); strv_remove(altnames, new_name);
strv_remove(altnames, old_name); strv_remove(altnames, current_name);
r = rtnl_get_link_alternative_names(&ctx->rtnl, ifindex, &current_altnames); r = rtnl_get_link_alternative_names(rtnl, ifindex, &current_altnames);
if (r < 0) if (r < 0)
log_debug_errno(r, "Failed to get alternative names on %s, ignoring: %m", old_name); log_device_debug_errno(device, r, "Failed to get alternative names, ignoring: %m");
char **p; char **p;
STRV_FOREACH(p, current_altnames) STRV_FOREACH(p, current_altnames)
@ -551,14 +584,63 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
strv_uniq(altnames); strv_uniq(altnames);
strv_sort(altnames); strv_sort(altnames);
r = rtnl_set_link_alternative_names(&ctx->rtnl, ifindex, altnames); r = rtnl_set_link_alternative_names(rtnl, ifindex, altnames);
if (r == -EOPNOTSUPP) if (r < 0)
log_debug_errno(r, "Could not set AlternativeName= or apply AlternativeNamesPolicy= on %s, ignoring: %m", old_name); log_device_full_errno(device, r == -EOPNOTSUPP ? LOG_DEBUG : LOG_WARNING, r,
else if (r < 0) "Could not set AlternativeName= or apply AlternativeNamesPolicy=, ignoring: %m");
return log_warning_errno(r, "Could not set AlternativeName= or apply AlternativeNamesPolicy= on %s: %m", old_name);
*name = new_name; return 0;
}
int link_config_apply(link_config_ctx *ctx, const link_config *config, sd_device *device, const char **ret_name) {
const char *new_name;
DeviceAction a;
int r;
assert(ctx);
assert(config);
assert(device);
assert(ret_name);
r = device_get_action(device, &a);
if (r < 0)
return log_device_error_errno(device, r, "Failed to get ACTION= property: %m");
if (!IN_SET(a, DEVICE_ACTION_ADD, DEVICE_ACTION_BIND, DEVICE_ACTION_MOVE)) {
log_device_debug(device, "Skipping to apply .link settings on '%s' uevent.", device_action_to_string(a));
r = sd_device_get_sysname(device, ret_name);
if (r < 0)
return log_device_error_errno(device, r, "Failed to get sysname: %m");
return 0;
}
r = link_config_apply_ethtool_settings(&ctx->ethtool_fd, config, device);
if (r < 0)
return r;
r = link_config_apply_rtnl_settings(&ctx->rtnl, config, device);
if (r < 0)
return r;
if (a == DEVICE_ACTION_MOVE) {
log_device_debug(device, "Skipping to apply Name= and NamePolicy= on '%s' uevent.", device_action_to_string(a));
r = sd_device_get_sysname(device, &new_name);
if (r < 0)
return log_device_error_errno(device, r, "Failed to get sysname: %m");
} else {
r = link_config_generate_new_name(ctx, config, device, &new_name);
if (r < 0)
return r;
}
r = link_config_apply_alternative_names(&ctx->rtnl, config, device, new_name);
if (r < 0)
return r;
*ret_name = new_name;
return 0; return 0;
} }

View File

@ -77,8 +77,8 @@ int link_load_one(link_config_ctx *ctx, const char *filename);
int link_config_load(link_config_ctx *ctx); int link_config_load(link_config_ctx *ctx);
bool link_config_should_reload(link_config_ctx *ctx); bool link_config_should_reload(link_config_ctx *ctx);
int link_config_get(link_config_ctx *ctx, sd_device *device, struct link_config **ret); int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret);
int link_config_apply(link_config_ctx *ctx, struct link_config *config, sd_device *device, const char **name); int link_config_apply(link_config_ctx *ctx, const link_config *config, sd_device *device, const char **ret_name);
int link_get_driver(link_config_ctx *ctx, sd_device *device, char **ret); int link_get_driver(link_config_ctx *ctx, sd_device *device, char **ret);
const char *name_policy_to_string(NamePolicy p) _const_; const char *name_policy_to_string(NamePolicy p) _const_;

View File

@ -940,16 +940,9 @@ static void event_execute_rules_on_remove(
(void) udev_node_remove(dev); (void) udev_node_remove(dev);
} }
static int udev_event_on_move(UdevEvent *event) { static int udev_event_on_move(sd_device *dev) {
sd_device *dev = event->dev;
int r; int r;
if (sd_device_get_devnum(dev, NULL) < 0) {
r = device_copy_properties(dev, event->dev_db_clone);
if (r < 0)
log_device_debug_errno(dev, r, "Failed to copy properties from cloned sd_device object, ignoring: %m");
}
/* Drop previously added property */ /* Drop previously added property */
r = device_add_property(dev, "ID_RENAMING", NULL); r = device_add_property(dev, "ID_RENAMING", NULL);
if (r < 0) if (r < 0)
@ -1017,7 +1010,7 @@ int udev_event_execute_rules(UdevEvent *event,
(void) udev_watch_end(event->dev_db_clone); (void) udev_watch_end(event->dev_db_clone);
if (action == DEVICE_ACTION_MOVE) { if (action == DEVICE_ACTION_MOVE) {
r = udev_event_on_move(event); r = udev_event_on_move(event->dev);
if (r < 0) if (r < 0)
return r; return r;
} }

View File

@ -1,9 +1,24 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e set -e
TEST_DESCRIPTION="UDEV ID_RENAMING property" TEST_DESCRIPTION="UDEV ID_RENAMING property"
IMAGE_NAME="udev-id-renaming"
TEST_NO_NSPAWN=1 TEST_NO_NSPAWN=1
. $TEST_BASE_DIR/test-functions . $TEST_BASE_DIR/test-functions
QEMU_TIMEOUT=300 QEMU_TIMEOUT=300
test_create_image() {
create_empty_image_rootdir
# Create what will eventually be our root filesystem onto an overlay
(
LOG_LEVEL=5
setup_basic_environment
mask_supporting_services
instmods dummy
generate_module_dependencies
)
}
do_test "$@" 29 do_test "$@" 29

View File

@ -0,0 +1,9 @@
[Match]
Name=dummy98
[Network]
IPv6AcceptRA=no
[Address]
Address=100.64.0.1/32
Peer=100.64.0.2/32

View File

@ -1742,6 +1742,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'25-address-dad-veth-peer.network', '25-address-dad-veth-peer.network',
'25-address-dad-veth99.network', '25-address-dad-veth99.network',
'25-address-link-section.network', '25-address-link-section.network',
'25-address-peer-ipv4.network',
'25-address-preferred-lifetime-zero.network', '25-address-preferred-lifetime-zero.network',
'25-address-static.network', '25-address-static.network',
'25-bind-carrier.network', '25-bind-carrier.network',
@ -1886,6 +1887,21 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
print(output) print(output)
self.assertNotRegex(output, '192.168.100.10/24') self.assertNotRegex(output, '192.168.100.10/24')
def test_address_peer_ipv4(self):
# test for issue #17304
copy_unit_to_networkd_unit_path('25-address-peer-ipv4.network', '12-dummy.netdev')
for trial in range(2):
if trial == 0:
start_networkd()
else:
restart_networkd()
self.wait_online(['dummy98:routable'])
output = check_output('ip -4 address show dev dummy98')
self.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output)
@expectedFailureIfModuleIsNotAvailable('vrf') @expectedFailureIfModuleIsNotAvailable('vrf')
def test_prefix_route(self): def test_prefix_route(self):
copy_unit_to_networkd_unit_path('25-prefix-route-with-vrf.network', '12-dummy.netdev', copy_unit_to_networkd_unit_path('25-prefix-route-with-vrf.network', '12-dummy.netdev',

View File

@ -38,6 +38,22 @@ STATE=$(systemctl show --property=ActiveState --value sys-devices-virtual-net-lo
rm -f /run/udev/rules.d/50-testsuite.rules rm -f /run/udev/rules.d/50-testsuite.rules
udevadm control --reload --timeout=600 udevadm control --reload --timeout=600
# test for issue #16967
ip link add hoge type dummy
udevadm info --wait-for-initialization=10s /sys/devices/virtual/net/hoge
sleep 1
if ! systemctl status sys-devices-virtual-net-hoge.device; then exit 1; fi
if ! systemctl status sys-subsystem-net-devices-hoge.device; then exit 1; fi
ip link set hoge name foobar
udevadm info --wait-for-initialization=10s /sys/devices/virtual/net/foobar
sleep 1
if systemctl status sys-devices-virtual-net-hoge.device; then exit 1; fi
if systemctl status sys-subsystem-net-devices-hoge.device; then exit 1; fi
if ! systemctl status sys-devices-virtual-net-foobar.device; then exit 1; fi
if ! systemctl status sys-subsystem-net-devices-foobar.device; then exit 1; fi
echo OK > /testok echo OK > /testok
exit 0 exit 0