Compare commits
48 Commits
dfc637d0ff
...
1008f5b069
Author | SHA1 | Date |
---|---|---|
Lennart Poettering | 1008f5b069 | |
Lennart Poettering | 21ad331873 | |
Lennart Poettering | 64a7fcc5cd | |
Lennart Poettering | 84fc961082 | |
Lennart Poettering | b0eb40cda4 | |
Lennart Poettering | fc8bc57f6b | |
Lennart Poettering | 4840807c6d | |
Lennart Poettering | 6293d958a4 | |
Lennart Poettering | 7848cb8c57 | |
Lennart Poettering | 670eed4c8c | |
Lennart Poettering | 74aaf59b1a | |
Lennart Poettering | f5fa352f1e | |
Yu Watanabe | fd8f865c9f | |
Zbigniew Jędrzejewski-Szmek | 547f724f7a | |
Zbigniew Jędrzejewski-Szmek | 540e0bad3e | |
Lennart Poettering | eaa2751685 | |
Lennart Poettering | 59c4bbfb93 | |
Lennart Poettering | b8cfa2da7c | |
Lennart Poettering | 441e0fdb90 | |
Lennart Poettering | 6ea0d25c57 | |
Lennart Poettering | 562b01e996 | |
Zbigniew Jędrzejewski-Szmek | 0a42426d79 | |
Yu Watanabe | 5431227400 | |
Yu Watanabe | a7df5cae54 | |
Yu Watanabe | 66f507e1ba | |
Yu Watanabe | 10fa21c0dc | |
Yu Watanabe | 8ff85383b4 | |
Yu Watanabe | a0887abbd8 | |
Yu Watanabe | 5e71868ced | |
Yu Watanabe | 11e9fec259 | |
Zbigniew Jędrzejewski-Szmek | 1b5b507cd2 | |
Yu Watanabe | 21266e60e9 | |
Yu Watanabe | b1476b5210 | |
Yu Watanabe | fe841414ef | |
Yu Watanabe | 9b9c5fff16 | |
Zbigniew Jędrzejewski-Szmek | a4ccce22d9 | |
Zbigniew Jędrzejewski-Szmek | b45c068dd8 | |
Zbigniew Jędrzejewski-Szmek | 0b3456428b | |
Lennart Poettering | 612ebf6c91 | |
Yu Watanabe | efdaeb88f0 | |
Yu Watanabe | 6d4bcea4ca | |
Yu Watanabe | 7f67b01e3f | |
Yu Watanabe | f576730256 | |
Yu Watanabe | e0e789c1e9 | |
Yu Watanabe | 51d9aec0ff | |
Yu Watanabe | 2e17fed5f3 | |
Yu Watanabe | cadc7ed2e2 | |
Yu Watanabe | 87bc687a8c |
2
TODO
2
TODO
|
@ -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".
|
||||||
|
|
||||||
|
|
117
man/bootctl.xml
117
man/bootctl.xml
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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_); \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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(m⋅log(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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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]);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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]);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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));
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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>\""));
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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, ¤t_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, ¤t_altnames);
|
r = rtnl_get_link_alternative_names(rtnl, ifindex, ¤t_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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
[Match]
|
||||||
|
Name=dummy98
|
||||||
|
|
||||||
|
[Network]
|
||||||
|
IPv6AcceptRA=no
|
||||||
|
|
||||||
|
[Address]
|
||||||
|
Address=100.64.0.1/32
|
||||||
|
Peer=100.64.0.2/32
|
|
@ -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',
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue